diff --git a/packages/core/src/config/localConfig.ts b/packages/core/src/config/localConfig.ts index e75f07a19..e233e6d94 100644 --- a/packages/core/src/config/localConfig.ts +++ b/packages/core/src/config/localConfig.ts @@ -8,6 +8,7 @@ export const localConfig: SygmaConfig = { caipId: '', chainId: 1337, name: 'Ethereum 1', + nativeTokenAdapter: '', type: Network.EVM, bridge: '0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68', handlers: [ @@ -76,6 +77,7 @@ export const localConfig: SygmaConfig = { caipId: '', chainId: 1338, name: 'evm2', + nativeTokenAdapter: '', type: Network.EVM, bridge: '0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68', handlers: [ diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 0f05deb04..dfd1d4c7c 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -132,6 +132,7 @@ export type FeeHandler = { export interface EthereumConfig extends BaseConfig { handlers: Array; + nativeTokenAdapter: string; feeRouter: string; feeHandlers: Array; } diff --git a/packages/evm/package.json b/packages/evm/package.json index 3172dd749..7f484f1b8 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -57,7 +57,7 @@ }, "dependencies": { "@buildwithsygma/core": "workspace:^", - "@buildwithsygma/sygma-contracts": "^2.8.0", + "@buildwithsygma/sygma-contracts": "^2.10.1", "@ethersproject/abi": "^5.7.0", "@ethersproject/bytes": "^5.7.0", "@ethersproject/contracts": "^5.7.0", diff --git a/packages/evm/src/evmAssetTransfer.ts b/packages/evm/src/evmAssetTransfer.ts index 615164f6f..22a14bf10 100644 --- a/packages/evm/src/evmAssetTransfer.ts +++ b/packages/evm/src/evmAssetTransfer.ts @@ -7,7 +7,7 @@ import { constants, utils } from 'ethers'; import { EvmTransfer } from './evmTransfer.js'; import type { EvmAssetTransferParams, EvmFee, TransactionRequest } from './types.js'; -import { executeDeposit } from './utils/depositFn.js'; +import { getTransactionOverrides } from './utils/depositFn.js'; import { createTransactionRequest } from './utils/transaction.js'; /** @@ -72,16 +72,15 @@ export abstract class AssetTransfer extends EvmTransfer implements IAssetTransfe const hasBalance = await this.hasEnoughBalance(fee); if (!hasBalance) throw new Error('Insufficient token balance'); - const transferTx = await executeDeposit( - this.destination.id.toString(), + const transferTransaction = await bridge.populateTransaction.deposit( + this.destinationDomain.id.toString(), this.resource.resourceId, this.getDepositData(), - fee, - bridge, - overrides, + '0x', + getTransactionOverrides(fee, overrides), ); - return createTransactionRequest(transferTx); + return createTransactionRequest(transferTransaction); } /** diff --git a/packages/evm/src/fungibleAssetTransfer.ts b/packages/evm/src/fungibleAssetTransfer.ts index cf85f46fc..b27f0dd41 100644 --- a/packages/evm/src/fungibleAssetTransfer.ts +++ b/packages/evm/src/fungibleAssetTransfer.ts @@ -1,6 +1,10 @@ -import type { EvmResource } from '@buildwithsygma/core'; +import type { EthereumConfig, EvmResource } from '@buildwithsygma/core'; import { Config, FeeHandlerType, ResourceType, SecurityModel } from '@buildwithsygma/core'; -import { Bridge__factory, ERC20__factory } from '@buildwithsygma/sygma-contracts'; +import { + Bridge__factory, + ERC20__factory, + NativeTokenAdapter__factory, +} from '@buildwithsygma/sygma-contracts'; import { Web3Provider } from '@ethersproject/providers'; import { BigNumber, constants, utils } from 'ethers'; import type { ethers, PopulatedTransaction } from 'ethers'; @@ -13,7 +17,8 @@ import type { TransactionRequest, } from './types.js'; import { approve, getERC20Allowance } from './utils/approveAndCheckFns.js'; -import { createFungibleDepositData } from './utils/assetTransferHelpers.js'; +import { createAssetDepositData } from './utils/assetTransferHelpers.js'; +import { getNativeTokenDepositTransaction } from './utils/nativeTokenDepositHelpers.js'; import { createTransactionRequest } from './utils/transaction.js'; /** @@ -60,13 +65,22 @@ class FungibleAssetTransfer extends AssetTransfer { this.optionalMessage = transfer.optionalMessage; } + protected isNativeTransfer(): boolean { + const { symbol, type } = this.resource; + const { nativeTokenSymbol } = this.config.getDomainConfig(this.sourceDomain); + + return ( + type === ResourceType.FUNGIBLE && symbol?.toLowerCase() === nativeTokenSymbol.toLowerCase() + ); + } + /** * Returns encoded deposit * data * @returns {string} */ protected getDepositData(): string { - return createFungibleDepositData({ + return createAssetDepositData({ destination: this.destination, recipientAddress: this.recipientAddress, amount: this.adjustedAmount, @@ -130,6 +144,10 @@ class FungibleAssetTransfer extends AssetTransfer { public async getApprovalTransactions( overrides?: ethers.Overrides, ): Promise> { + if (this.isNativeTransfer()) { + return []; + } + const provider = new Web3Provider(this.sourceNetworkProvider); const sourceDomainConfig = this.config.getDomainConfig(this.source); const bridge = Bridge__factory.connect(sourceDomainConfig.bridge, provider); @@ -163,6 +181,44 @@ class FungibleAssetTransfer extends AssetTransfer { return approvals.map(approval => createTransactionRequest(approval)); } + + protected async getNativeTokenDepositTransaction( + overrides?: ethers.Overrides, + ): Promise { + const domainConfig = this.config.getDomainConfig(this.source) as EthereumConfig; + const provider = new Web3Provider(this.sourceNetworkProvider); + const nativeTokenAdapter = NativeTokenAdapter__factory.connect( + domainConfig.nativeTokenAdapter, + provider, + ); + + const fee = await this.getFee(); + fee.fee += this.transferAmount; + + const payableOverrides: ethers.PayableOverrides = { ...overrides, value: fee.fee }; + + return await getNativeTokenDepositTransaction( + { + destinationNetworkId: this.destination.id.toString(), + destinationNetworkType: this.destination.type, + parachainId: this.destination.parachainId, + recipientAddress: this.recipientAddress, + optionalMessage: this.optionalMessage, + optionalGas: this.optionalGas, + depositData: this.getDepositData(), + }, + nativeTokenAdapter, + payableOverrides, + ); + } + + public async getTransferTransaction(overrides?: ethers.Overrides): Promise { + if (this.isNativeTransfer()) { + return await this.getNativeTokenDepositTransaction(overrides); + } + + return super.getTransferTransaction(overrides); + } } /** diff --git a/packages/evm/src/genericMessageTransfer.ts b/packages/evm/src/genericMessageTransfer.ts index 4ec67b6d6..ad6c49ccf 100644 --- a/packages/evm/src/genericMessageTransfer.ts +++ b/packages/evm/src/genericMessageTransfer.ts @@ -14,8 +14,8 @@ import { constants } from 'ethers'; import { EvmTransfer } from './evmTransfer.js'; import { getFeeInformation } from './fee/getFeeInformation.js'; import type { GenericMessageTransferParams, TransactionRequest } from './types.js'; +import { getTransactionOverrides } from './utils/depositFn.js'; import { createGenericCallDepositData } from './utils/genericTransferHelpers.js'; -import { executeDeposit } from './utils/index.js'; import { createTransactionRequest } from './utils/transaction.js'; /** @@ -143,16 +143,15 @@ class GenericMessageTransfer< const feeData = await this.getFee(); const depositData = this.getDepositData(); - const transaction = await executeDeposit( + const transferTransaction = await bridgeInstance.populateTransaction.deposit( this.destination.id.toString(), this.resource.resourceId, depositData, - feeData, - bridgeInstance, - overrides, + '0x', + getTransactionOverrides(feeData, overrides), ); - return createTransactionRequest(transaction); + return createTransactionRequest(transferTransaction); } /** * Get prepared additional deposit data diff --git a/packages/evm/src/nonFungibleAssetTransfer.ts b/packages/evm/src/nonFungibleAssetTransfer.ts index ab1285c76..52b1dfd89 100644 --- a/packages/evm/src/nonFungibleAssetTransfer.ts +++ b/packages/evm/src/nonFungibleAssetTransfer.ts @@ -9,7 +9,7 @@ import { providers } from 'ethers'; import { AssetTransfer } from './evmAssetTransfer.js'; import type { EvmFee, NonFungibleTransferParams, TransactionRequest } from './types.js'; -import { createFungibleDepositData } from './utils/assetTransferHelpers.js'; +import { createAssetDepositData } from './utils/assetTransferHelpers.js'; import { approve, isApproved } from './utils/index.js'; import { createTransactionRequest } from './utils/transaction.js'; @@ -31,7 +31,7 @@ class NonFungibleAssetTransfer extends AssetTransfer { * @returns {string} */ protected getDepositData(): string { - return createFungibleDepositData({ + return createAssetDepositData({ destination: this.destination, recipientAddress: this.recipientAddress, tokenId: this.tokenId, diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index 10858b69a..443a3d91e 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -102,3 +102,12 @@ export interface GenericMessageTransferParams< destinationContractAddress: string; maxFee: bigint; } + +export type NativeTokenDepositArgsWithoutMessage = [string, string]; +export type NativeTokenDepositArgsWithGeneralMessage = [string, string]; +export type NativeTokenDepositArgsWithEVMMessage = [string, string, bigint, string]; +export type NativeTokenDepositMethods = + | 'deposit' + | 'depositToEVM' + | 'depositGeneral' + | 'depositToEVMWithMessage'; diff --git a/packages/evm/src/utils/__test__/assetTransferHelpers.test.ts b/packages/evm/src/utils/__test__/assetTransferHelpers.test.ts index e1b8f5574..fbe6b9946 100644 --- a/packages/evm/src/utils/__test__/assetTransferHelpers.test.ts +++ b/packages/evm/src/utils/__test__/assetTransferHelpers.test.ts @@ -3,7 +3,7 @@ import { arrayify } from '@ethersproject/bytes'; import { utils } from 'ethers'; import { - createFungibleDepositData, + createAssetDepositData, createSubstrateMultiLocationObject, serializeEvmAddress, serializeSubstrateAddress, @@ -16,7 +16,7 @@ describe('createERCDepositData', () => { const expectedDepositData = '0x000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000141234567890123456789012345678901234567890'; - const depositData = createFungibleDepositData({ + const depositData = createAssetDepositData({ recipientAddress, amount, destination: { @@ -37,7 +37,7 @@ describe('createERCDepositData', () => { const expectedDepositData = '0x00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000027010200511f0100fac48520983815e2022ded67ca8d27b73d51b1b022284c48b4eccbb7a328d80f'; - const depositData = createFungibleDepositData({ + const depositData = createAssetDepositData({ recipientAddress, amount, destination: { @@ -59,7 +59,7 @@ describe('createERCDepositData', () => { const expectedDepositData = '0x0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000002a746231717366797a6c3932707637776b79616a3074666a6474777663736a383430703030346a676c7670'; - const depositData = createFungibleDepositData({ + const depositData = createAssetDepositData({ recipientAddress, amount: tokenAmount, destination: { diff --git a/packages/evm/src/utils/__test__/depositFns.test.ts b/packages/evm/src/utils/__test__/depositFns.test.ts index dae0b70d1..420490f3f 100644 --- a/packages/evm/src/utils/__test__/depositFns.test.ts +++ b/packages/evm/src/utils/__test__/depositFns.test.ts @@ -1,144 +1,18 @@ import { FeeHandlerType } from '@buildwithsygma/core'; -import type { Bridge, ERC721MinterBurnerPauser } from '@buildwithsygma/sygma-contracts'; -import { type ContractReceipt, type PopulatedTransaction } from 'ethers'; import type { EvmFee } from '../../types.js'; -import * as EVM from '../depositFn.js'; - -jest.mock( - '@buildwithsygma/sygma-contracts', - () => - ({ - ...jest.requireActual('@buildwithsygma/sygma-contracts'), - Bridge__factory: { - connect: jest.fn() as unknown as Bridge, - }, - ERC721MinterBurnerPauser__factory: { - connect: jest.fn() as unknown as ERC721MinterBurnerPauser, - }, - }) as unknown, -); +import { getTransactionOverrides } from '../depositFn.js'; // Define a test suite -describe('deposit functions', () => { - let domainId: string; - let resourceId: string; - let depositData: string; - let feeData: EvmFee; - - beforeEach(() => { - domainId = 'domainId'; - resourceId = 'resourceId'; - depositData = 'depositData'; - feeData = { +describe('getTransactionOverrides', () => { + it('should assign correct values', () => { + const evmFee: EvmFee = { + fee: 1n, type: FeeHandlerType.BASIC, - fee: BigInt('100'), - tokenAddress: '0x00', - handlerAddress: '0x9867', + handlerAddress: '', }; - jest.clearAllMocks(); - }); - describe('executeDeposit', () => { - it('should successfully execute deposit', async () => { - const bridgeInstance = { - populateTransaction: { - deposit: jest.fn().mockResolvedValueOnce({ - wait: jest.fn().mockResolvedValueOnce({} as ContractReceipt), - } as unknown as PopulatedTransaction), - }, - }; - - const result = await EVM.executeDeposit( - domainId, - resourceId, - depositData, - feeData, - bridgeInstance as unknown as Bridge, - ); - - expect(result).toBeDefined(); - expect(bridgeInstance.populateTransaction.deposit).toHaveBeenCalledTimes(1); - }); - - it('should successfully call deposit method on contract without overrides', async () => { - const bridgeInstance = { - populateTransaction: { - deposit: jest.fn().mockResolvedValueOnce({ - wait: jest.fn().mockResolvedValueOnce({} as ContractReceipt), - } as unknown as PopulatedTransaction), - }, - }; - - const result = await EVM.executeDeposit( - domainId, - resourceId, - depositData, - feeData, - bridgeInstance as unknown as Bridge, - ); - - expect(result).toBeDefined(); - expect(bridgeInstance.populateTransaction.deposit).toHaveBeenCalledTimes(1); - expect(bridgeInstance.populateTransaction.deposit).toHaveBeenCalledWith( - 'domainId', - 'resourceId', - 'depositData', - '0x', - { value: feeData.fee, gasLimit: EVM.ASSET_TRANSFER_GAS_LIMIT }, - ); - }); - - it('should successfully call deposit method on contract without overrides and with dynamic (oracle) fee', async () => { - feeData = { - type: FeeHandlerType.PERCENTAGE, - fee: BigInt('100'), - tokenAddress: '0x00', - handlerAddress: '0x123', - }; - const bridgeInstance = { - populateTransaction: { - deposit: jest.fn().mockResolvedValueOnce({ - wait: jest.fn().mockResolvedValueOnce({} as ContractReceipt), - } as unknown as PopulatedTransaction), - }, - }; - - const result = await EVM.executeDeposit( - domainId, - resourceId, - depositData, - feeData, - bridgeInstance as unknown as Bridge, - ); - - expect(result).toBeDefined(); - expect(bridgeInstance.populateTransaction.deposit).toHaveBeenCalledTimes(1); - expect(bridgeInstance.populateTransaction.deposit).toHaveBeenCalledWith( - 'domainId', - 'resourceId', - 'depositData', - '0x', - { gasLimit: EVM.ASSET_TRANSFER_GAS_LIMIT, value: 0 }, - ); - }); - - it('should handle error on execute deposit', async () => { - const bridgeInstance = { - populateTransaction: { - deposit: jest.fn().mockRejectedValueOnce(new Error('Deposit failed')), - }, - }; - - await expect( - EVM.executeDeposit( - domainId, - resourceId, - depositData, - feeData, - bridgeInstance as unknown as Bridge, - ), - ).rejects.toThrowError('Deposit failed'); - }); + const overrides = getTransactionOverrides(evmFee); + expect(overrides.value).toEqual(evmFee.fee); }); }); diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts index 5a071fe21..3ba96ed4d 100644 --- a/packages/evm/src/utils/assetTransferHelpers.ts +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -11,7 +11,7 @@ import type { FungibleTransferOptionalMessage } from '../types.js'; const ACTIONS_ARRAY_ABI = 'tuple(uint256 nativeValue, address callTo, address approveTo, address tokenSend, address tokenReceive, bytes data)[]'; -interface FungbileDepositParams { +interface AssetDepositParams { destination: Domain; recipientAddress: string; amount?: bigint; @@ -66,33 +66,59 @@ export function serializeSubstrateAddress( return registry.createType('MultiLocation', JSON.parse(multilocationObject)).toU8a(); } -export function createFungibleDepositData(depositParams: FungbileDepositParams): string { - const { recipientAddress, destination, amount, tokenId, optionalGas, optionalMessage } = - depositParams; - let recipientAddressSerialized: Uint8Array; - - switch (destination.type) { +export function serializeDestinationAddress( + address: string, + type: Network, + parachainId?: number, +): Uint8Array { + switch (type) { case Network.EVM: { - recipientAddressSerialized = serializeEvmAddress(recipientAddress as `0x${string}`); - break; + return serializeEvmAddress(address as `0x${string}`); } case Network.SUBSTRATE: { - recipientAddressSerialized = serializeSubstrateAddress( - recipientAddress, - destination.parachainId, - ); - break; + return serializeSubstrateAddress(address, parachainId); } case Network.BITCOIN: { - recipientAddressSerialized = utils.arrayify( - `${utils.hexlify(utils.toUtf8Bytes(recipientAddress))}`, - ); - break; + return utils.arrayify(`${utils.hexlify(utils.toUtf8Bytes(address))}`); } default: { throw new Error('Unsupported destination network type.'); } } +} + +export function encodeOptionalMessage(optionalMessage: FungibleTransferOptionalMessage): string { + const { transactionId, actions, receiver } = optionalMessage; + const abiCoder = new AbiCoder(); + + const optionalMessageEncoded = abiCoder.encode( + ['bytes32', ACTIONS_ARRAY_ABI, 'address'], + [ + transactionId, + actions.map(action => [ + action.nativeValue, + action.callTo, + action.approveTo, + action.tokenSend, + action.tokenReceive, + action.data, + ]), + receiver, + ], + ); + + return optionalMessageEncoded; +} + +export function createAssetDepositData(depositParams: AssetDepositParams): string { + const { recipientAddress, destination, amount, tokenId, optionalGas, optionalMessage } = + depositParams; + + const recipientAddressSerialized: Uint8Array = serializeDestinationAddress( + recipientAddress, + destination.type, + destination.parachainId, + ); const val = amount !== undefined ? amount : tokenId !== undefined ? tokenId : null; if (val === null) throw new Error('Token Amount Or ID is required.'); @@ -102,6 +128,7 @@ export function createFungibleDepositData(depositParams: FungbileDepositParams): const zeroPaddedAmount = hexZeroPad(tokenAmountOrIdInHex, HEX_PADDING); const addressLenInHex = BigNumber.from(recipientAddressSerialized.length).toHexString(); const zeroPaddedAddrLen = hexZeroPad(addressLenInHex, HEX_PADDING); + let depositData = concat([zeroPaddedAmount, zeroPaddedAddrLen, recipientAddressSerialized]); if (optionalMessage !== undefined && optionalGas !== undefined) { @@ -109,25 +136,7 @@ export function createFungibleDepositData(depositParams: FungbileDepositParams): const zeroPaddedOptionalGas = hexZeroPad(optionalGasInHex, HEX_PADDING); depositData = concat([depositData, zeroPaddedOptionalGas]); - const { transactionId, actions, receiver } = optionalMessage; - const abiCoder = new AbiCoder(); - - const optionalMessageEncoded = abiCoder.encode( - ['bytes32', ACTIONS_ARRAY_ABI, 'address'], - [ - transactionId, - actions.map(action => [ - action.nativeValue, - action.callTo, - action.approveTo, - action.tokenSend, - action.tokenReceive, - action.data, - ]), - receiver, - ], - ); - + const optionalMessageEncoded = encodeOptionalMessage(optionalMessage); const optionalMessageSeriailzed = arrayify(optionalMessageEncoded); const optionalMsgLenInHex = BigNumber.from(optionalMessageSeriailzed.length).toHexString(); const zeroPaddedOptionalMsgLen = hexZeroPad(optionalMsgLenInHex, HEX_PADDING); diff --git a/packages/evm/src/utils/depositFn.ts b/packages/evm/src/utils/depositFn.ts index a1dd054a4..68b6f2bb4 100644 --- a/packages/evm/src/utils/depositFn.ts +++ b/packages/evm/src/utils/depositFn.ts @@ -1,53 +1,22 @@ import { FeeHandlerType } from '@buildwithsygma/core'; -import type { Bridge } from '@buildwithsygma/sygma-contracts'; -import type { PopulatedTransaction, ethers } from 'ethers'; +import type { ethers } from 'ethers'; import { BigNumber } from 'ethers'; import type { EvmFee } from '../types.js'; export const ASSET_TRANSFER_GAS_LIMIT: BigNumber = BigNumber.from(300000); -/** - * Executes a deposit operation using the specified parameters and returns a populated transaction. - * - * - * @category Bridge deposit - * @param {string} domainId - The unique identifier for destination network. - * @param {string} resourceId - The resource ID associated with the token. - * @param {string} depositData - The deposit data required for the operation. - * @param {FeeDataResult} feeData - The fee data result for the deposit operation. - * @param {Bridge} bridgeInstance - The bridge instance used to perform the deposit operation. - * @returns {Promise} Unsigned transaction - */ -export const executeDeposit = async ( - domainId: string, - resourceId: string, - depositData: string, - feeData: EvmFee, - bridgeInstance: Bridge, - overrides?: ethers.PayableOverrides, -): Promise => { - const transactionSettings = { - /** - * @remarks - * "twap" and "basic" both deduct in native currency - */ - value: feeData.type == FeeHandlerType.PERCENTAGE ? 0 : feeData.fee, +export function getTransactionOverrides( + fee: EvmFee, + overrides?: ethers.Overrides, +): ethers.PayableOverrides { + const sygmaOverrides = { gasLimit: ASSET_TRANSFER_GAS_LIMIT, + value: fee.type === FeeHandlerType.PERCENTAGE ? 0 : fee.fee, }; - const payableOverrides = { - ...transactionSettings, + return { + ...sygmaOverrides, ...overrides, }; - - const depositTransaction = await bridgeInstance.populateTransaction.deposit( - domainId, - resourceId, - depositData, - '0x', - payableOverrides, - ); - - return depositTransaction; -}; +} diff --git a/packages/evm/src/utils/nativeTokenDepositHelpers.ts b/packages/evm/src/utils/nativeTokenDepositHelpers.ts new file mode 100644 index 000000000..5d9acde68 --- /dev/null +++ b/packages/evm/src/utils/nativeTokenDepositHelpers.ts @@ -0,0 +1,125 @@ +import type { ParachainId } from '@buildwithsygma/core'; +import { Network } from '@buildwithsygma/core'; +import type { NativeTokenAdapter } from '@buildwithsygma/sygma-contracts'; +import { hexlify } from '@ethersproject/bytes'; +import type { ethers } from 'ethers'; + +import type { + FungibleTransferOptionalMessage, + NativeTokenDepositArgsWithEVMMessage, + NativeTokenDepositArgsWithGeneralMessage, + NativeTokenDepositArgsWithoutMessage, + NativeTokenDepositMethods, + TransactionRequest, +} from '../types.js'; + +import { serializeDestinationAddress, encodeOptionalMessage } from './assetTransferHelpers.js'; +import { createTransactionRequest } from './transaction.js'; + +export function getNativeTokenDepositMethod( + destinationNetworkType: Network, + optionalMessage?: FungibleTransferOptionalMessage, +): NativeTokenDepositMethods { + if (!optionalMessage) { + if (destinationNetworkType === Network.EVM) { + return 'depositToEVM'; + } + return 'deposit'; + } else { + if (destinationNetworkType === Network.EVM) { + return 'depositToEVMWithMessage'; + } + + return 'depositGeneral'; + } +} + +interface NativeDepositParams { + method: NativeTokenDepositMethods; + recipientAddress: string; + destinationNetworkType: Network; + destinationNetworkId: string; + parachainId?: ParachainId; + optionalMessage?: FungibleTransferOptionalMessage; + optionalGas?: bigint; + depositData: string; +} + +export function getNativeTokenDepositContractArgs( + args: NativeDepositParams, +): + | NativeTokenDepositArgsWithoutMessage + | NativeTokenDepositArgsWithEVMMessage + | NativeTokenDepositArgsWithGeneralMessage { + const { + method, + destinationNetworkId, + recipientAddress, + destinationNetworkType, + parachainId, + optionalMessage, + optionalGas, + depositData, + } = args; + + switch (method) { + case 'deposit': + case 'depositToEVM': + return [ + destinationNetworkId, + hexlify(serializeDestinationAddress(recipientAddress, destinationNetworkType, parachainId)), + ]; + case 'depositToEVMWithMessage': + return [ + destinationNetworkId, + recipientAddress, + optionalGas!, + encodeOptionalMessage(optionalMessage!), + ]; + case 'depositGeneral': + return [destinationNetworkId, `0x${depositData.substring(66)}`]; + } +} + +export async function getNativeTokenDepositTransaction( + depositParams: Omit, + nativeTokenAdapter: NativeTokenAdapter, + overrides?: ethers.PayableOverrides, +): Promise { + const method = getNativeTokenDepositMethod( + depositParams.destinationNetworkType, + depositParams.optionalMessage, + ); + + const args = getNativeTokenDepositContractArgs({ + method, + ...depositParams, + }); + + switch (method) { + case 'deposit': + case 'depositToEVM': + return createTransactionRequest( + await nativeTokenAdapter.populateTransaction[method]( + ...(args as NativeTokenDepositArgsWithoutMessage), + overrides, + ), + ); + case 'depositToEVMWithMessage': + return createTransactionRequest( + await nativeTokenAdapter.populateTransaction[method]( + ...(args as NativeTokenDepositArgsWithEVMMessage), + overrides, + ), + ); + case 'depositGeneral': + return createTransactionRequest( + await nativeTokenAdapter.populateTransaction[method]( + ...(args as NativeTokenDepositArgsWithGeneralMessage), + overrides, + ), + ); + default: + throw new Error('Unsupported method'); + } +} diff --git a/yarn.lock b/yarn.lock index f369eeba7..446a2f8eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -553,7 +553,7 @@ __metadata: resolution: "@buildwithsygma/evm@workspace:packages/evm" dependencies: "@buildwithsygma/core": "workspace:^" - "@buildwithsygma/sygma-contracts": "npm:^2.8.0" + "@buildwithsygma/sygma-contracts": "npm:^2.10.1" "@ethersproject/abi": "npm:^5.7.0" "@ethersproject/bytes": "npm:^5.7.0" "@ethersproject/contracts": "npm:^5.7.0" @@ -642,15 +642,16 @@ __metadata: languageName: node linkType: hard -"@buildwithsygma/sygma-contracts@npm:^2.8.0": - version: 2.9.0 - resolution: "@buildwithsygma/sygma-contracts@npm:2.9.0" +"@buildwithsygma/sygma-contracts@npm:^2.10.1": + version: 2.10.1 + resolution: "@buildwithsygma/sygma-contracts@npm:2.10.1" dependencies: "@openzeppelin/contracts": "npm:4.5.0" "@uniswap/v3-periphery": "npm:^1.4.4" + solmate: "npm:^6.2.0" peerDependencies: ethers: ">= 5.0.0" - checksum: 0f98a64da674e1264caaf429665a23b7db63365d7a8c5a38d8e23f0b728faecd6bfd3a0c43616bd4aa43133229324f14d4c3d0e2e24225d223574d5e94af9263 + checksum: c6cddf6a427a4d3ab1321fbc21f9e47a64661823ffdb55db230ce9874878125735aad260d3aef454f7681f2d2f7d446882f82f1cf3e7f69209e183b69f375c4a languageName: node linkType: hard @@ -10573,6 +10574,13 @@ __metadata: languageName: node linkType: hard +"solmate@npm:^6.2.0": + version: 6.7.0 + resolution: "solmate@npm:6.7.0" + checksum: 7c51286ac486de2cace00fd136a210d01e2dc28403373583ce5acd49178f1cf4421d3f3a945f28af36e7fcedcea194238b2b5eb53be78a04b9e04f32ea8d286f + languageName: node + linkType: hard + "source-map-support@npm:0.5.13": version: 0.5.13 resolution: "source-map-support@npm:0.5.13"