From 1de7bc439d03f438336a8030161557fb6ad209a7 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Wed, 28 Aug 2024 15:07:13 +0200 Subject: [PATCH 01/14] updated evm helpers --- packages/evm/src/baseTransfer.ts | 0 packages/evm/src/fungible.ts | 24 +- packages/evm/src/generic.ts | 69 ++---- packages/evm/src/index.ts | 1 - ...elpers.test.ts => assetTransferHelpers.ts} | 97 ++++---- .../evm/src/utils/__test__/depositFns.test.ts | 40 +--- .../__test__/genericTransferHelpers.test.ts | 73 ++++++ .../evm/src/utils/assetTransferHelpers.ts | 128 ++++++++++ packages/evm/src/utils/depositFn.ts | 50 ++++ packages/evm/src/utils/depositFns.ts | 124 ---------- .../evm/src/utils/genericTransferHelpers.ts | 48 ++++ packages/evm/src/utils/helpers.ts | 222 ------------------ packages/evm/src/utils/index.ts | 3 +- 13 files changed, 374 insertions(+), 505 deletions(-) delete mode 100644 packages/evm/src/baseTransfer.ts rename packages/evm/src/utils/__test__/{helpers.test.ts => assetTransferHelpers.ts} (60%) create mode 100644 packages/evm/src/utils/__test__/genericTransferHelpers.test.ts create mode 100644 packages/evm/src/utils/assetTransferHelpers.ts create mode 100644 packages/evm/src/utils/depositFn.ts delete mode 100644 packages/evm/src/utils/depositFns.ts create mode 100644 packages/evm/src/utils/genericTransferHelpers.ts delete mode 100644 packages/evm/src/utils/helpers.ts diff --git a/packages/evm/src/baseTransfer.ts b/packages/evm/src/baseTransfer.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/evm/src/fungible.ts b/packages/evm/src/fungible.ts index f04f77f28..f510587b3 100644 --- a/packages/evm/src/fungible.ts +++ b/packages/evm/src/fungible.ts @@ -15,11 +15,11 @@ import { EvmTransfer } from './evmTransfer.js'; import type { EvmFee } from './types.js'; import { approve, - createERCDepositData, createTransactionRequest, - erc20Transfer, + executeDeposit, getERC20Allowance, } from './utils/index.js'; +import { createFungibleDepositData } from './utils/assetTransferHelpers.js'; interface EvmFungibleTransferRequest extends EvmTransferParams { sourceAddress: string; @@ -131,7 +131,11 @@ class EvmFungibleAssetTransfer extends EvmTransfer { } protected getDepositData(): string { - return createERCDepositData(this.amount, this.destinationAddress, this.destination.parachainId); + return createFungibleDepositData({ + destination: this.destination, + recipientAddress: this.destinationAddress, + amount: this.amount, + }); } /** @@ -204,13 +208,13 @@ class EvmFungibleAssetTransfer extends EvmTransfer { await this.verifyAccountBalance(fee); - const transferTx = await erc20Transfer({ - depositData: this.getDepositData(), - bridgeInstance: bridge, - domainId: this.destination.id.toString(), - resourceId: this.resource.resourceId, - feeData: fee, - }); + const transferTx = await executeDeposit( + this.destination.id.toString(), + this.resource.resourceId, + this.getDepositData(), + fee, + bridge, + ); return createTransactionRequest(transferTx); } diff --git a/packages/evm/src/generic.ts b/packages/evm/src/generic.ts index 9bddad99c..0646de618 100644 --- a/packages/evm/src/generic.ts +++ b/packages/evm/src/generic.ts @@ -14,12 +14,9 @@ import type { EvmTransferParams } from './evmTransfer.js'; import { EvmTransfer } from './evmTransfer.js'; import { getFeeInformation } from './fee/getFeeInformation.js'; import type { TransactionRequest } from './types.js'; -import { - createPermissionlessGenericDepositData, - serializeGenericCallParameters, -} from './utils/helpers.js'; -import { genericMessageTransfer } from './utils/index.js'; +import { executeDeposit } from './utils/index.js'; import { createTransactionRequest } from './utils/transaction.js'; +import { createGenericCallDepositData } from './utils/genericTransferHelpers.js'; /** * Required parameters for initiating a generic @@ -162,26 +159,6 @@ class GenericMessageTransfer< ): void { this.functionParameters = parameters; } - /** - * Prepare function call encodings - * @returns {{ executionData: string; executionFunctionSignature: string; }} - */ - private prepareFunctionCallEncodings(): { - executionData: string; - executeFunctionSignature: string; - } { - const contractInterface = new ethers.utils.Interface( - JSON.stringify(this.destinationContractAbi), - ); - - let executionData = ``; - if (Array.isArray(this.functionParameters)) { - executionData = serializeGenericCallParameters(this.functionParameters); - } - - const executeFunctionSignature = contractInterface.getSighash(this.functionName); - return { executionData, executeFunctionSignature }; - } /** * Get the cross chain generic message transfer * transaction @@ -192,30 +169,19 @@ class GenericMessageTransfer< const isValid = await this.isValidTransfer(); if (!isValid) throw new Error('Invalid Transfer.'); - const { executeFunctionSignature, executionData } = this.prepareFunctionCallEncodings(); - const { resourceId } = this.resource; - - const executeContractAddress = this.destinationContractAddress; const sourceDomain = this.config.getDomainConfig(this.source); - const domainId = this.config.getDomainConfig(this.destination).id.toString(); const provider = new Web3Provider(this.sourceNetworkProvider); - const depositor = this.sourceAddress; - const maxFee = this.maxFee.toString(); const bridgeInstance = Bridge__factory.connect(sourceDomain.bridge, provider); const feeData = await this.getFee(); + const depositData = this.getDepositData(); - const transaction = await genericMessageTransfer({ - executeFunctionSignature, - executeContractAddress, - maxFee, - depositor, - executionData, - bridgeInstance, - domainId, - resourceId, + const transaction = await executeDeposit( + this.destination.id.toString(), + this.resource.resourceId, + depositData, feeData, - overrides, - }); + bridgeInstance, + ); return createTransactionRequest(transaction); } @@ -225,13 +191,14 @@ class GenericMessageTransfer< * @returns {string} */ protected getDepositData(): string { - const { executeFunctionSignature, executionData } = this.prepareFunctionCallEncodings(); - return createPermissionlessGenericDepositData( - executeFunctionSignature, - this.destinationContractAddress, - this.maxFee.toString(), - this.sourceAddress, - executionData, - ); + return createGenericCallDepositData({ + abi: this.destinationContractAbi, + functionName: this.functionName, + functionParams: this.functionParameters, + contractAddress: this.destinationContractAddress, + destination: this.destination, + maxFee: this.maxFee, + depositor: this.sourceAddress as `0x${string}`, + }); } } diff --git a/packages/evm/src/index.ts b/packages/evm/src/index.ts index 3fe2b63fd..2078f4695 100644 --- a/packages/evm/src/index.ts +++ b/packages/evm/src/index.ts @@ -2,5 +2,4 @@ export * from './fee/index.js'; export * from './utils/index.js'; export * from './generic.js'; export * from './fungible.js'; -export * from './utils/helpers.js'; export * from './types.js'; diff --git a/packages/evm/src/utils/__test__/helpers.test.ts b/packages/evm/src/utils/__test__/assetTransferHelpers.ts similarity index 60% rename from packages/evm/src/utils/__test__/helpers.test.ts rename to packages/evm/src/utils/__test__/assetTransferHelpers.ts index 1a00a534b..f29866c79 100644 --- a/packages/evm/src/utils/__test__/helpers.test.ts +++ b/packages/evm/src/utils/__test__/assetTransferHelpers.ts @@ -1,42 +1,62 @@ -import { BigNumber, utils } from 'ethers'; - +import { utils } from 'ethers'; import { - getEVMRecipientAddressInBytes, - getSubstrateRecipientAddressInBytes, - createERCDepositData, - toHex, - constructSubstrateRecipient, - serializeGenericCallParameters, -} from '../helpers.js'; + createFungibleDepositData, + createSubstrateMultiLocationObject, + serializeEvmAddress, + serializeSubstrateAddress, +} from '../assetTransferHelpers'; +import { Network } from '@buildwithsygma/core'; +import { arrayify } from '@ethersproject/bytes'; describe('createERCDepositData', () => { it('should return the correct deposit data', () => { - const tokenAmount = BigInt(100); + const amount = BigInt(100); const recipientAddress = '0x1234567890123456789012345678901234567890'; const expectedDepositData = '0x000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000141234567890123456789012345678901234567890'; - const depositData = createERCDepositData(tokenAmount, recipientAddress); + const depositData = createFungibleDepositData({ + recipientAddress, + amount, + destination: { + name: 'EVM', + type: Network.EVM, + caipId: '11', + chainId: 1, + id: 1, + }, + }); expect(depositData).toEqual(expectedDepositData); }); it('should return the correct deposit data - substrate', () => { - const tokenAmount = BigInt(100); + const amount = BigInt(100); const recipientAddress = '46Hb742ujLfMA1nGsw95xTbjt6SzGSiNgXsjPQXyz3PoQuNQ'; const expectedDepositData = '0x00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000027010200511f0100fac48520983815e2022ded67ca8d27b73d51b1b022284c48b4eccbb7a328d80f'; - const depositData = createERCDepositData(tokenAmount, recipientAddress, 2004); + const depositData = createFungibleDepositData({ + recipientAddress, + amount, + destination: { + name: 'EVM', + type: Network.SUBSTRATE, + caipId: '11', + chainId: 1, + id: 1, + parachainId: 2004, + }, + }); expect(depositData).toEqual(expectedDepositData); }); }); -describe('constructSubstrateRecipient', () => { +describe('createSubstrateMultiLocationObject', () => { it('should create a valid Substrate Multilocation Object with parachain id', () => { const substrateAddress = '5CDQJk6kxvBcjauhrogUc9B8vhbdXhRscp1tGEUmniryF1Vt'; - const result = constructSubstrateRecipient(substrateAddress, 2004); + const result = createSubstrateMultiLocationObject(substrateAddress, 2004); const expectedResult = '{"parents":1,"interior":{"X2":[{"parachain":2004},{"AccountId32":{"network":{"any":null},"id":"0x06a220edf5f82b84fc5f9270f8a30a17636bf29c05a5c16279405ca20918aa39"}}]}}'; expect(result).toEqual(expectedResult); @@ -44,38 +64,19 @@ describe('constructSubstrateRecipient', () => { it('should create a valid Substrate Multilocation Object', () => { const substrateAddress = '5CDQJk6kxvBcjauhrogUc9B8vhbdXhRscp1tGEUmniryF1Vt'; - const result = constructSubstrateRecipient(substrateAddress); + const result = createSubstrateMultiLocationObject(substrateAddress); const expectedResult = '{"parents":0,"interior":{"X1":{"AccountId32":{"network":{"any":null},"id":"0x06a220edf5f82b84fc5f9270f8a30a17636bf29c05a5c16279405ca20918aa39"}}}}'; expect(result).toEqual(expectedResult); }); }); -describe('serializeGenericCallParameters', () => { - it('should seriailze parameters correctly', () => { - const result = - '0x0000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000064'; - - const params = [ - '0x98729c03c4D5e820F5e8c45558ae07aE63F97461', - BigInt(50), - true, - false, - BigNumber.from(100), - ]; - - const serialized = serializeGenericCallParameters(params); - - expect(serialized).toEqual(result); - }); -}); - -describe('getEVMRecipientAddressInBytes', () => { +describe('serializeEvmAddress', () => { it('should convert an EVM address to a Uint8Array of bytes', () => { const evmAddress = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'; expect(utils.isAddress(evmAddress)).toBeTruthy(); - const result = getEVMRecipientAddressInBytes(evmAddress); + const result = serializeEvmAddress(evmAddress); const expectedResult = utils.arrayify(evmAddress); expect(result).toEqual(expectedResult); @@ -89,7 +90,7 @@ describe('getSubstrateRecipientAddressInBytes', () => { expect(utils.isAddress(substrateAddress)).toBeFalsy(); - const result = getSubstrateRecipientAddressInBytes(substrateAddress); + const result = arrayify(serializeSubstrateAddress(substrateAddress)); const expectedResult = Uint8Array.from([ 0, 1, 1, 0, 6, 162, 32, 237, 245, 248, 43, 132, 252, 95, 146, 112, 248, 163, 10, 23, 99, 107, 242, 156, 5, 165, 193, 98, 121, 64, 92, 162, 9, 24, 170, 57, @@ -104,7 +105,7 @@ describe('getSubstrateRecipientAddressInBytes', () => { expect(utils.isAddress(substrateAddress)).toBeFalsy(); - const result = getSubstrateRecipientAddressInBytes(substrateAddress, 1001); + const result = arrayify(serializeSubstrateAddress(substrateAddress, 1001)); const expectedResult = Uint8Array.from([ 1, 2, 0, 165, 15, 1, 0, 6, 162, 32, 237, 245, 248, 43, 132, 252, 95, 146, 112, 248, 163, 10, 23, 99, 107, 242, 156, 5, 165, 193, 98, 121, 64, 92, 162, 9, 24, 170, 57, @@ -114,21 +115,3 @@ describe('getSubstrateRecipientAddressInBytes', () => { expect(result).toBeInstanceOf(Uint8Array); }); }); - -describe('toHex', () => { - test('should convert string to hex', () => { - const result = toHex('1234', 6); - expect(result).toBe('0x0000000004d2'); - }); - - test('should convert number to hex', () => { - const result = toHex(5678, 8); - expect(result).toBe('0x000000000000162e'); - }); - - test('should convert BigNumber to hex', () => { - const num = BigNumber.from('900000000000000000000000'); - const result = toHex(num, 32); - expect(result).toBe('0x00000000000000000000000000000000000000000000be951906eba2aa800000'); - }); -}); diff --git a/packages/evm/src/utils/__test__/depositFns.test.ts b/packages/evm/src/utils/__test__/depositFns.test.ts index 9a7da51fc..dae0b70d1 100644 --- a/packages/evm/src/utils/__test__/depositFns.test.ts +++ b/packages/evm/src/utils/__test__/depositFns.test.ts @@ -1,9 +1,9 @@ import { FeeHandlerType } from '@buildwithsygma/core'; import type { Bridge, ERC721MinterBurnerPauser } from '@buildwithsygma/sygma-contracts'; -import { type ethers, type ContractReceipt, type PopulatedTransaction } from 'ethers'; +import { type ContractReceipt, type PopulatedTransaction } from 'ethers'; import type { EvmFee } from '../../types.js'; -import * as EVM from '../depositFns.js'; +import * as EVM from '../depositFn.js'; jest.mock( '@buildwithsygma/sygma-contracts', @@ -25,7 +25,6 @@ describe('deposit functions', () => { let resourceId: string; let depositData: string; let feeData: EvmFee; - let bridgeInstance: Bridge; beforeEach(() => { domainId = 'domainId'; @@ -37,7 +36,6 @@ describe('deposit functions', () => { tokenAddress: '0x00', handlerAddress: '0x9867', }; - bridgeInstance = { deposit: jest.fn() } as unknown as Bridge; jest.clearAllMocks(); }); @@ -143,38 +141,4 @@ describe('deposit functions', () => { ).rejects.toThrowError('Deposit failed'); }); }); - - describe('erc20Transfer', () => { - it('should successfully execute', async () => { - jest.spyOn(EVM, 'executeDeposit').mockResolvedValueOnce({} as ethers.PopulatedTransaction); - bridgeInstance = { - signer: { - getAddress: jest.fn().mockResolvedValue('0xMyaddress'), - }, - } as unknown as Bridge; - - const depositData = - '0x0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000001498729c03c4d5e820f5e8c45558ae07ae63f97461'; - - const erc20Params = { - amount: BigInt('100'), - recipientAddress: '0x98729c03c4D5e820F5e8c45558ae07aE63F97461', - bridgeInstance, - domainId, - resourceId, - feeData, - depositData, - }; - await EVM.erc20Transfer(erc20Params); - - expect(EVM.executeDeposit).toBeCalledWith( - domainId, - resourceId, - depositData, - feeData, - bridgeInstance, - undefined, - ); - }); - }); }); diff --git a/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts b/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts new file mode 100644 index 000000000..ecf9092ff --- /dev/null +++ b/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts @@ -0,0 +1,73 @@ +import { Network } from '@buildwithsygma/core'; +import { createGenericCallDepositData } from '../genericTransferHelpers'; + +const CONTRACT_ABI = [ + { + inputs: [ + { + internalType: 'address', + name: '_depositer', + type: 'address', + }, + { + internalType: 'address', + name: '_index', + type: 'address', + }, + { + internalType: 'uint256', + name: '_value', + type: 'uint256', + }, + ], + name: 'store', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: '_index', + type: 'address', + }, + ], + name: 'retrieve', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +describe('createGenericCallDepositData', () => { + it('should create correct generic call deposit data', async () => { + const depositor = '0x98729c03c4D5e820F5e8c45558ae07aE63F97461'; + + const genericCallParams = { + abi: CONTRACT_ABI, + functionName: 'store', + functionParams: [depositor, depositor, BigInt(3052070251)], + contractAddress: '0x4bE595ab5A070663B314970Fc10C049BBA0ad489', + destination: { + name: 'EVM', + type: Network.EVM, + caipId: '11', + chainId: 1, + id: 1, + }, + maxFee: BigInt(3000000), + depositor: depositor as `0x${string}`, + }; + + const depositData = createGenericCallDepositData(genericCallParams); + console.log(depositData); + expect(true).toBe(true); + }); +}); diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts new file mode 100644 index 000000000..7e0f10de8 --- /dev/null +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -0,0 +1,128 @@ +import { Domain, Network } from '@buildwithsygma/core'; +import { AbiCoder } from '@ethersproject/abi'; +import { arrayify, concat, hexlify, hexZeroPad } from '@ethersproject/bytes'; +import { TypeRegistry } from '@polkadot/types'; +import { decodeAddress } from '@polkadot/util-crypto'; +import { BigNumber } from 'ethers'; + +const ACTIONS_ARRAY_ABI = + 'tuple(uint256 nativeValue, address callTo, address approveTo, address tokenSend, address tokenReceive, bytes data)'; + +interface FungibleDepositAction { + nativeValue: bigint; + callTo: string; + approveTo: string; + tokenSend: string; + tokenReceive: string; + data: string; +} + +interface FungbileDepositParams { + destination: Domain; + recipientAddress: string; + amount: bigint; + optionalGas?: bigint; + optionalMessage?: { + transactionId: string; + actions: FungibleDepositAction[]; + receiver: string; + }; +} + +export function serializeEvmAddress(evmAddress: `0x${string}`): Uint8Array { + return arrayify(evmAddress); +} + +export function createSubstrateMultiLocationObject( + substrateAddress: string, + parachainId?: number, +): string { + let decodedAddress = decodeAddress(substrateAddress); + let hexlifiedAddress = hexlify(decodedAddress); + const parents = parachainId ? 1 : 0; + const interior = parachainId + ? { + X2: [ + { + parachain: parachainId, + }, + { + AccountId32: { + network: { any: null }, + id: hexlifiedAddress, + }, + }, + ], + } + : { + X1: { + AccountId32: { + network: { any: null }, + id: hexlifiedAddress, + }, + }, + }; + + return JSON.stringify({ parents, interior }); +} + +export function serializeSubstrateAddress( + substrateAddress: string, + parachainId?: number, +): Uint8Array { + const multilocationObject = createSubstrateMultiLocationObject(substrateAddress, parachainId); + const registry = new TypeRegistry(); + return registry.createType('MultiLocation', JSON.parse(multilocationObject)).toU8a(); +} + +export function createFungibleDepositData(depositParams: FungbileDepositParams): string { + const { recipientAddress, destination, amount, optionalGas, optionalMessage } = depositParams; + let recipientAddressSerialized: Uint8Array; + + switch (destination.type) { + case Network.EVM: { + recipientAddressSerialized = serializeEvmAddress(recipientAddress as `0x${string}`); + break; + } + case Network.SUBSTRATE: { + recipientAddressSerialized = serializeSubstrateAddress( + recipientAddress, + destination.parachainId, + ); + break; + } + default: { + throw new Error('Unsupported destination network type.'); + } + } + + const HEX_PADDING = 32; + const amountInHex = BigNumber.from(amount).toHexString(); + const zeroPaddedAmount = hexZeroPad(amountInHex, HEX_PADDING); + const addressLenInHex = BigNumber.from(recipientAddressSerialized.length).toHexString(); + const zeroPaddedAddrLen = hexZeroPad(addressLenInHex, HEX_PADDING); + let depositData = concat([zeroPaddedAmount, zeroPaddedAddrLen, recipientAddressSerialized]); + + if (optionalGas) { + const optionalGasInHex = BigNumber.from(optionalGas).toHexString(); + const zeroPaddedOptionalGas = hexZeroPad(optionalGasInHex, HEX_PADDING); + depositData = concat([depositData, zeroPaddedOptionalGas]); + } + + if (optionalMessage) { + const { transactionId, actions, receiver } = optionalMessage; + const abiCoder = new AbiCoder(); + const optionalMessageEncoded = abiCoder.encode( + ['bytes32', ACTIONS_ARRAY_ABI, 'address'], + [transactionId, actions, receiver], + ); + + const optionalMessageSeriailzed = arrayify(optionalMessageEncoded); + const optionalMsgLenInHex = BigNumber.from(optionalMessageSeriailzed.length).toHexString(); + const zeroPaddedOptionalMsgLen = hexZeroPad(optionalMsgLenInHex, HEX_PADDING); + + depositData = concat([depositData, zeroPaddedOptionalMsgLen, optionalMessageSeriailzed]); + } + + return hexlify(depositData); +} diff --git a/packages/evm/src/utils/depositFn.ts b/packages/evm/src/utils/depositFn.ts new file mode 100644 index 000000000..256b9ecb2 --- /dev/null +++ b/packages/evm/src/utils/depositFn.ts @@ -0,0 +1,50 @@ +import { FeeHandlerType } from '@buildwithsygma/core'; +import type { Bridge } from '@buildwithsygma/sygma-contracts'; +import type { PopulatedTransaction, 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 = { + // * "twap" and "basic" both deduct in native currency + value: feeData.type == FeeHandlerType.PERCENTAGE ? 0 : feeData.fee, + gasLimit: ASSET_TRANSFER_GAS_LIMIT, + }; + + const payableOverrides = { + ...transactionSettings, + ...overrides, + }; + + const depositTransaction = await bridgeInstance.populateTransaction.deposit( + domainId, + resourceId, + depositData, + '0x', + payableOverrides, + ); + + return depositTransaction; +}; diff --git a/packages/evm/src/utils/depositFns.ts b/packages/evm/src/utils/depositFns.ts deleted file mode 100644 index 5596779ec..000000000 --- a/packages/evm/src/utils/depositFns.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { FeeHandlerType } from '@buildwithsygma/core'; -import type { Bridge } from '@buildwithsygma/sygma-contracts'; -import type { PopulatedTransaction, ethers } from 'ethers'; -import { BigNumber } from 'ethers'; - -import type { EvmFee, FungibleTransferParams } from '../types.js'; - -import { createPermissionlessGenericDepositData } from './helpers.js'; - -export const ASSET_TRANSFER_GAS_LIMIT: BigNumber = BigNumber.from(300000); - -/** - * Perform an erc20 transfer - * - * @example - * const params = { - * amount: '100', - * recipientAddress: '0x1234567890123456789012345678901234567890', - * bridgeInstance: new Bridge(), // Bridge instance from the sygma-contracts - * domainId: '1', - * resourceId: '0x000000000000000001', - * feeData: { ... }, // fee data - * } - * const transaction = await erc20Transfer(params) - * - * @category Bridge deposit - * @param {Erc20TransferParamsType} params - The parameters for the erc20 transfer function. - * @returns {Promise} - The populated transaction. - */ -export const erc20Transfer = async ({ - bridgeInstance, - domainId, - resourceId, - feeData, - depositData, - overrides, -}: FungibleTransferParams): Promise => { - // pass data to smartcontract function and create a transaction - return executeDeposit(domainId, resourceId, depositData, feeData, bridgeInstance, overrides); -}; - -/** - * 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 = { - // * "twap" and "basic" both deduct in native currency - value: feeData.type == FeeHandlerType.PERCENTAGE ? 0 : feeData.fee, - gasLimit: ASSET_TRANSFER_GAS_LIMIT, - }; - - const payableOverrides = { - ...transactionSettings, - ...overrides, - }; - - const depositTransaction = await bridgeInstance.populateTransaction.deposit( - domainId, - resourceId, - depositData, - '0x', - payableOverrides, - ); - - return depositTransaction; -}; - -type GenericMessageParams = { - executeFunctionSignature: string; - executeContractAddress: string; - maxFee: string; - depositor: string; - executionData: string; - domainId: string; - resourceId: string; - bridgeInstance: Bridge; - feeData: EvmFee; - overrides?: ethers.PayableOverrides; -}; - -/** - * Create a generic cross chain message transaction - * using typechain and ethers - * @category Generic Transfer - * @param {GenericMessageParams} param0 - * @returns {Promise} - */ -export const genericMessageTransfer = async ({ - executeFunctionSignature, - executeContractAddress, - maxFee, - depositor, - executionData, - bridgeInstance, - domainId, - resourceId, - feeData, - overrides, -}: GenericMessageParams): Promise => { - const depositData = createPermissionlessGenericDepositData( - executeFunctionSignature, - executeContractAddress, - maxFee, - depositor, - executionData, - ); - return executeDeposit(domainId, resourceId, depositData, feeData, bridgeInstance, overrides); -}; diff --git a/packages/evm/src/utils/genericTransferHelpers.ts b/packages/evm/src/utils/genericTransferHelpers.ts new file mode 100644 index 000000000..5c78f413e --- /dev/null +++ b/packages/evm/src/utils/genericTransferHelpers.ts @@ -0,0 +1,48 @@ +import { Domain, Network } from '@buildwithsygma/core'; +import { hexZeroPad } from '@ethersproject/bytes'; +import { Abi } from 'abitype'; +import { BigNumber, ethers } from 'ethers'; + +interface GenericDepositParams { + abi: Abi; + functionName: string; + functionParams: Array | any; + contractAddress: string; + destination: Domain; + maxFee: BigInt; + depositor: `0x${string}`; +} + +const getZeroPaddedLength = (hexString: string, padding: number): string => + hexZeroPad(BigNumber.from(hexString.substring(2).length / 2).toHexString(), padding).substring(2); + +export function createGenericCallDepositData(genericTransferParams: GenericDepositParams): string { + const { abi, functionName, functionParams, contractAddress, maxFee, destination, depositor } = + genericTransferParams; + + if (destination.type === Network.EVM) { + const contractInterface = new ethers.utils.Interface(JSON.stringify(abi)); + + const paddedMaxFee = hexZeroPad(BigNumber.from(maxFee).toHexString(), 32); + const funcData = contractInterface.encodeFunctionData(functionName, functionParams); + const funcSig = funcData.substring(0, 10); + const funcParamEncoded = funcData.substring(10); + + const funcSigLen = getZeroPaddedLength(funcSig, 2); + const contractAddrLen = getZeroPaddedLength(contractAddress, 1); + const dataDepositorLen = getZeroPaddedLength(depositor, 1); + + return ( + paddedMaxFee + + funcSigLen + + funcSig.substring(2) + + contractAddrLen + + contractAddress.substring(2) + + dataDepositorLen + + depositor.substring(2) + + funcParamEncoded + ); + } + + throw new Error('Unsupported destination network type.'); +} diff --git a/packages/evm/src/utils/helpers.ts b/packages/evm/src/utils/helpers.ts deleted file mode 100644 index 0ad7f4b9c..000000000 --- a/packages/evm/src/utils/helpers.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { TypeRegistry } from '@polkadot/types/create'; -import { decodeAddress } from '@polkadot/util-crypto'; -import { utils, BigNumber } from 'ethers'; -/** - * Constructs the deposit data for a erc20 transaction. - * - * @example - * // Amount of tokens: - * const amount = '1'; - * // EVM address - * const evmAddress = '0x1234567890123456789012345678901234567890'; - * createERCDepositData(amount, evmAddress); - * - * - * @param {bigint} tokenAmount - The amount of tokens to be transferred. - * @param {string} recipientAddress - The address of the recipient. - * @param {number} parachainId - Optional parachain id if the substrate destination targets another parachain. - * @returns {string} The deposit data as hex string - */ -export const createERCDepositData = ( - tokenAmount: bigint, - recipientAddress: string, - parachainId?: number, -): string => { - let recipientAddressInBytes; - if (utils.isAddress(recipientAddress)) { - recipientAddressInBytes = getEVMRecipientAddressInBytes(recipientAddress); - } else { - recipientAddressInBytes = getSubstrateRecipientAddressInBytes(recipientAddress, parachainId); - } - const depositDataBytes = constructMainDepositData( - BigNumber.from(tokenAmount), - recipientAddressInBytes, - ); - const depositData = utils.hexlify(depositDataBytes); - return depositData; -}; -/** - * Constructs the main deposit data for a given token and recipient. - * - * @category Helpers - * @param {BigNumber} tokenStats - The amount of ERC20 tokens or the token ID of ERC721 tokens. - * @param {Uint8Array} destRecipient - The recipient address in bytes array - * @returns {Uint8Array} The main deposit data in bytes array - */ -const constructMainDepositData = (tokenStats: BigNumber, destRecipient: Uint8Array): Uint8Array => { - const data: Uint8Array = utils.concat([ - utils.hexZeroPad(tokenStats.toHexString(), 32), // Amount (ERC20) or Token Id (ERC721) - utils.hexZeroPad(BigNumber.from(destRecipient.length).toHexString(), 32), // length of recipient - destRecipient, // Recipient - ]); - return data; -}; -/** - * Converts a Substrate recipient address to a JSON multilocation. - * - * @param {string} recipientAddress - The recipient address as a string. - * @returns {string} The recipient address as a stringified Substrate Multilocation Object - */ -export const constructSubstrateRecipient = ( - recipientAddress: string, - parachainId?: number, -): string => { - const addressPublicKeyBytes = decodeAddress(recipientAddress); - const addressPublicKeyHexString = utils.hexlify(addressPublicKeyBytes); - if (parachainId) { - return JSON.stringify({ - parents: 1, - interior: { - X2: [ - { - parachain: parachainId, - }, - { - AccountId32: { - network: { any: null }, - id: addressPublicKeyHexString, - }, - }, - ], - }, - }); - } - - return JSON.stringify({ - parents: 0, - interior: { - X1: { - AccountId32: { - network: { any: null }, - id: addressPublicKeyHexString, - }, - }, - }, - }); -}; -/** - * Converts a EVM recipient address to a Uint8Array of bytes. - * - * @param {string} recipientAddress - The recipient address, as a string. - * @returns {Uint8Array} The recipient address as a Uint8Array of bytes - */ -export const getEVMRecipientAddressInBytes = (recipientAddress: string): Uint8Array => { - return utils.arrayify(recipientAddress); -}; -/** - * Converts a Substrate recipient multilocation to a Uint8Array of bytes. - * - * @param {string} recipientAddress - The recipient address, as a string - * @returns {Uint8Array} The recipient address as a Uint8Array of bytes - */ -export const getSubstrateRecipientAddressInBytes = ( - recipientAddress: string, - parachainId?: number, -): Uint8Array => { - const registry = new TypeRegistry(); - const result = registry - .createType( - 'MultiLocation', - JSON.parse(constructSubstrateRecipient(recipientAddress, parachainId)), - ) - .toU8a(); - - return result; -}; -/** - * Return hex data padded to the number defined as padding - * based on ethers.utils.hexZeroPad - * - * @category Helpers - * @param covertThis - data to convert - * @param padding - number to padd the data - * @returns {string} - */ -export const toHex = ( - covertThis: string | number | BigNumber | bigint, - padding: number, -): string => { - const amount = covertThis instanceof BigNumber ? covertThis : BigNumber.from(covertThis); - return utils.hexZeroPad(utils.hexlify(amount), padding); -}; -/** - * JS types to 0x Hex string - * required to initiate contract - * calls on destination EVM chain - * @param {string | BigNumber | number | boolean | bigint} param - * @returns {`0x{string}`} - */ -function createGenericCallParameter(param: string | BigNumber | number | boolean | bigint): string { - const DEFAULT_PADDING = 32; - switch (typeof param) { - case 'boolean': - return toHex(Number(param), DEFAULT_PADDING).substring(2); - case 'bigint': - case 'number': - case 'string': - return toHex(param, DEFAULT_PADDING).substring(2); - case 'object': - if (param instanceof BigNumber) { - return toHex(param, DEFAULT_PADDING).substring(2); - } - throw new Error('Unsupported parameter type.'); - case 'symbol': - case 'undefined': - case 'function': - throw new Error('Unsupported parameter type.'); - } -} -/** - * Convert JS primitive types to hex encoded - * strings for EVM function calls - * @param {Array} params - * @returns {string} - */ -export function serializeGenericCallParameters( - params: Array, -): string { - /** - * .slice(1) is used because first parameter will always be an - * address by default, and this parameter is not specified by - * the user, relayers add it so this param is discarded by SDK - * However, this param should still be part of ABI otherwise - * messages won't be passed correctly - */ - const serialized = params - .slice(1) - .map(item => createGenericCallParameter(item)) - .join(''); - return `0x${serialized}`; -} - -/** - * Creates the data for permissionless generic handler - * - * @category Helpers - * @param executeFunctionSignature - execution function signature - * @param executeContractAddress - execution contract address - * @param maxFee - max fee defined - * @param depositor - address of depositor on source chain - * @param executionData - the data to pass as parameter of the function being called on destination chain - * @returns {string} - */ -export const createPermissionlessGenericDepositData = ( - executeFunctionSignature: string, - executeContractAddress: string, - maxFee: string, - depositor: string, - executionData: string, -): string => { - return ( - '0x' + - toHex(maxFee, 32).substring(2) + // uint256 - toHex(executeFunctionSignature.substring(2).length / 2, 2).substring(2) + // uint16 - executeFunctionSignature.substring(2) + // bytes - toHex(executeContractAddress.substring(2).length / 2, 1).substring(2) + // uint8 - executeContractAddress.substring(2) + // bytes - toHex(depositor.substring(2).length / 2, 1).substring(2) + // uint8 - depositor.substring(2) + - executionData.substring(2) - ) // bytes - .toLowerCase(); -}; diff --git a/packages/evm/src/utils/index.ts b/packages/evm/src/utils/index.ts index 92d64e49a..baf460d6d 100644 --- a/packages/evm/src/utils/index.ts +++ b/packages/evm/src/utils/index.ts @@ -1,5 +1,4 @@ export * from './approveAndCheckFns.js'; export * from './balances.js'; -export * from './depositFns.js'; +export * from './depositFn.js'; export * from './transaction.js'; -export * from './helpers.js'; From 9a32d5f8afd60ba41a2386126af9e5f5b4fcac57 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Wed, 28 Aug 2024 15:41:48 +0200 Subject: [PATCH 02/14] fix breaking tests --- .../evm/src/utils/__test__/genericTransferHelpers.test.ts | 8 +++++--- packages/evm/src/utils/genericTransferHelpers.ts | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts b/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts index ecf9092ff..ee3061c6d 100644 --- a/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts +++ b/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts @@ -53,7 +53,7 @@ describe('createGenericCallDepositData', () => { const genericCallParams = { abi: CONTRACT_ABI, functionName: 'store', - functionParams: [depositor, depositor, BigInt(3052070251)], + functionParams: [depositor, depositor, BigInt(42069)], contractAddress: '0x4bE595ab5A070663B314970Fc10C049BBA0ad489', destination: { name: 'EVM', @@ -66,8 +66,10 @@ describe('createGenericCallDepositData', () => { depositor: depositor as `0x${string}`, }; + const expectedDepositData = + '0x00000000000000000000000000000000000000000000000000000000002dc6c00004ba154fea144be595ab5a070663b314970fc10c049bba0ad4891498729c03c4d5e820f5e8c45558ae07ae63f9746100000000000000000000000098729c03c4d5e820f5e8c45558ae07ae63f97461000000000000000000000000000000000000000000000000000000000000a455'; + const depositData = createGenericCallDepositData(genericCallParams); - console.log(depositData); - expect(true).toBe(true); + expect(depositData.toLowerCase()).toEqual(expectedDepositData.toLowerCase()); }); }); diff --git a/packages/evm/src/utils/genericTransferHelpers.ts b/packages/evm/src/utils/genericTransferHelpers.ts index 5c78f413e..08ba78c86 100644 --- a/packages/evm/src/utils/genericTransferHelpers.ts +++ b/packages/evm/src/utils/genericTransferHelpers.ts @@ -26,7 +26,8 @@ export function createGenericCallDepositData(genericTransferParams: GenericDepos const paddedMaxFee = hexZeroPad(BigNumber.from(maxFee).toHexString(), 32); const funcData = contractInterface.encodeFunctionData(functionName, functionParams); const funcSig = funcData.substring(0, 10); - const funcParamEncoded = funcData.substring(10); + /** 0x (2) + function signature (8) + first param which is always set to depositer by relayer (64) */ + const funcParamEncoded = funcData.substring(74); const funcSigLen = getZeroPaddedLength(funcSig, 2); const contractAddrLen = getZeroPaddedLength(contractAddress, 1); From e177d721c829c1b70360583a8707375e5dd78841 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Wed, 28 Aug 2024 16:07:35 +0200 Subject: [PATCH 03/14] Fix lint issues and tests --- packages/evm/src/fungible.ts | 2 +- packages/evm/src/generic.ts | 6 ++-- ...elpers.ts => assetTransferHelpers.test.ts} | 7 ++-- .../__test__/genericTransferHelpers.test.ts | 15 +++++---- .../evm/src/utils/assetTransferHelpers.ts | 7 ++-- .../evm/src/utils/genericTransferHelpers.ts | 32 +++++++++++++++---- packages/utils/src/liquidity.ts | 2 +- 7 files changed, 48 insertions(+), 23 deletions(-) rename packages/evm/src/utils/__test__/{assetTransferHelpers.ts => assetTransferHelpers.test.ts} (99%) diff --git a/packages/evm/src/fungible.ts b/packages/evm/src/fungible.ts index f510587b3..09780916d 100644 --- a/packages/evm/src/fungible.ts +++ b/packages/evm/src/fungible.ts @@ -13,13 +13,13 @@ import { BigNumber, constants, type PopulatedTransaction, utils } from 'ethers'; import type { EvmTransferParams } from './evmTransfer.js'; import { EvmTransfer } from './evmTransfer.js'; import type { EvmFee } from './types.js'; +import { createFungibleDepositData } from './utils/assetTransferHelpers.js'; import { approve, createTransactionRequest, executeDeposit, getERC20Allowance, } from './utils/index.js'; -import { createFungibleDepositData } from './utils/assetTransferHelpers.js'; interface EvmFungibleTransferRequest extends EvmTransferParams { sourceAddress: string; diff --git a/packages/evm/src/generic.ts b/packages/evm/src/generic.ts index 0646de618..6312d5138 100644 --- a/packages/evm/src/generic.ts +++ b/packages/evm/src/generic.ts @@ -8,15 +8,16 @@ import type { ExtractAbiFunction, ExtractAbiFunctionNames, } from 'abitype'; -import { constants, ethers } from 'ethers'; +import type { ethers } from 'ethers'; +import { constants } from 'ethers'; import type { EvmTransferParams } from './evmTransfer.js'; import { EvmTransfer } from './evmTransfer.js'; import { getFeeInformation } from './fee/getFeeInformation.js'; import type { TransactionRequest } from './types.js'; +import { createGenericCallDepositData } from './utils/genericTransferHelpers.js'; import { executeDeposit } from './utils/index.js'; import { createTransactionRequest } from './utils/transaction.js'; -import { createGenericCallDepositData } from './utils/genericTransferHelpers.js'; /** * Required parameters for initiating a generic @@ -181,6 +182,7 @@ class GenericMessageTransfer< depositData, feeData, bridgeInstance, + overrides, ); return createTransactionRequest(transaction); diff --git a/packages/evm/src/utils/__test__/assetTransferHelpers.ts b/packages/evm/src/utils/__test__/assetTransferHelpers.test.ts similarity index 99% rename from packages/evm/src/utils/__test__/assetTransferHelpers.ts rename to packages/evm/src/utils/__test__/assetTransferHelpers.test.ts index f29866c79..5ae74edfc 100644 --- a/packages/evm/src/utils/__test__/assetTransferHelpers.ts +++ b/packages/evm/src/utils/__test__/assetTransferHelpers.test.ts @@ -1,12 +1,13 @@ +import { Network } from '@buildwithsygma/core'; +import { arrayify } from '@ethersproject/bytes'; import { utils } from 'ethers'; + import { createFungibleDepositData, createSubstrateMultiLocationObject, serializeEvmAddress, serializeSubstrateAddress, -} from '../assetTransferHelpers'; -import { Network } from '@buildwithsygma/core'; -import { arrayify } from '@ethersproject/bytes'; +} from '../assetTransferHelpers.js'; describe('createERCDepositData', () => { it('should return the correct deposit data', () => { diff --git a/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts b/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts index ee3061c6d..13bc1dd79 100644 --- a/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts +++ b/packages/evm/src/utils/__test__/genericTransferHelpers.test.ts @@ -1,5 +1,6 @@ import { Network } from '@buildwithsygma/core'; -import { createGenericCallDepositData } from '../genericTransferHelpers'; + +import { createGenericCallDepositData } from '../genericTransferHelpers.js'; const CONTRACT_ABI = [ { @@ -47,13 +48,13 @@ const CONTRACT_ABI = [ ] as const; describe('createGenericCallDepositData', () => { - it('should create correct generic call deposit data', async () => { - const depositor = '0x98729c03c4D5e820F5e8c45558ae07aE63F97461'; + it('should create correct generic call deposit data', () => { + const depositor = '0x98729c03c4D5e820F5e8c45558ae07aE63F97461' as const; const genericCallParams = { abi: CONTRACT_ABI, functionName: 'store', - functionParams: [depositor, depositor, BigInt(42069)], + functionParams: [depositor, depositor, BigInt(42069)] as const, contractAddress: '0x4bE595ab5A070663B314970Fc10C049BBA0ad489', destination: { name: 'EVM', @@ -63,13 +64,15 @@ describe('createGenericCallDepositData', () => { id: 1, }, maxFee: BigInt(3000000), - depositor: depositor as `0x${string}`, + depositor: depositor, }; const expectedDepositData = '0x00000000000000000000000000000000000000000000000000000000002dc6c00004ba154fea144be595ab5a070663b314970fc10c049bba0ad4891498729c03c4d5e820f5e8c45558ae07ae63f9746100000000000000000000000098729c03c4d5e820f5e8c45558ae07ae63f97461000000000000000000000000000000000000000000000000000000000000a455'; - const depositData = createGenericCallDepositData(genericCallParams); + const depositData = createGenericCallDepositData( + genericCallParams, + ); expect(depositData.toLowerCase()).toEqual(expectedDepositData.toLowerCase()); }); }); diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts index 7e0f10de8..acf150ee7 100644 --- a/packages/evm/src/utils/assetTransferHelpers.ts +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -1,4 +1,5 @@ -import { Domain, Network } from '@buildwithsygma/core'; +import type { Domain } from '@buildwithsygma/core'; +import { Network } from '@buildwithsygma/core'; import { AbiCoder } from '@ethersproject/abi'; import { arrayify, concat, hexlify, hexZeroPad } from '@ethersproject/bytes'; import { TypeRegistry } from '@polkadot/types'; @@ -37,8 +38,8 @@ export function createSubstrateMultiLocationObject( substrateAddress: string, parachainId?: number, ): string { - let decodedAddress = decodeAddress(substrateAddress); - let hexlifiedAddress = hexlify(decodedAddress); + const decodedAddress = decodeAddress(substrateAddress); + const hexlifiedAddress = hexlify(decodedAddress); const parents = parachainId ? 1 : 0; const interior = parachainId ? { diff --git a/packages/evm/src/utils/genericTransferHelpers.ts b/packages/evm/src/utils/genericTransferHelpers.ts index 08ba78c86..654c3d285 100644 --- a/packages/evm/src/utils/genericTransferHelpers.ts +++ b/packages/evm/src/utils/genericTransferHelpers.ts @@ -1,22 +1,37 @@ -import { Domain, Network } from '@buildwithsygma/core'; +import type { Domain } from '@buildwithsygma/core'; +import { Network } from '@buildwithsygma/core'; import { hexZeroPad } from '@ethersproject/bytes'; -import { Abi } from 'abitype'; +import type { + Abi, + AbiParametersToPrimitiveTypes, + ExtractAbiFunction, + ExtractAbiFunctionNames, +} from 'abitype'; import { BigNumber, ethers } from 'ethers'; -interface GenericDepositParams { +interface GenericDepositParams< + ContractAbi extends Abi, + FunctionName extends ExtractAbiFunctionNames, +> { abi: Abi; functionName: string; - functionParams: Array | any; + functionParams: AbiParametersToPrimitiveTypes< + ExtractAbiFunction['inputs'], + 'inputs' + >; contractAddress: string; destination: Domain; - maxFee: BigInt; + maxFee: bigint; depositor: `0x${string}`; } const getZeroPaddedLength = (hexString: string, padding: number): string => hexZeroPad(BigNumber.from(hexString.substring(2).length / 2).toHexString(), padding).substring(2); -export function createGenericCallDepositData(genericTransferParams: GenericDepositParams): string { +export function createGenericCallDepositData< + ContractAbi extends Abi, + FunctionName extends ExtractAbiFunctionNames, +>(genericTransferParams: GenericDepositParams): string { const { abi, functionName, functionParams, contractAddress, maxFee, destination, depositor } = genericTransferParams; @@ -24,7 +39,10 @@ export function createGenericCallDepositData(genericTransferParams: GenericDepos const contractInterface = new ethers.utils.Interface(JSON.stringify(abi)); const paddedMaxFee = hexZeroPad(BigNumber.from(maxFee).toHexString(), 32); - const funcData = contractInterface.encodeFunctionData(functionName, functionParams); + const funcData = contractInterface.encodeFunctionData( + functionName, + functionParams as unknown as Array, + ); const funcSig = funcData.substring(0, 10); /** 0x (2) + function signature (8) + first param which is always set to depositer by relayer (64) */ const funcParamEncoded = funcData.substring(74); diff --git a/packages/utils/src/liquidity.ts b/packages/utils/src/liquidity.ts index 55460e558..0f8191af5 100644 --- a/packages/utils/src/liquidity.ts +++ b/packages/utils/src/liquidity.ts @@ -42,7 +42,7 @@ export async function hasEnoughLiquidity( handler.address, ); - const transferValue = transfer as Awaited>; + const transferValue = transfer; return transferValue.amount <= evmHandlerBalance; } case Network.SUBSTRATE: { From b065751280901c8845fbe6de829301874d707d25 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Tue, 3 Sep 2024 12:18:11 +0200 Subject: [PATCH 04/14] add `optionalMessage` and `optionalGas` to external interface --- packages/evm/src/fungible.ts | 11 ++++++++++- packages/evm/src/utils/assetTransferHelpers.ts | 12 +++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/evm/src/fungible.ts b/packages/evm/src/fungible.ts index 09780916d..8d6957c00 100644 --- a/packages/evm/src/fungible.ts +++ b/packages/evm/src/fungible.ts @@ -13,7 +13,10 @@ import { BigNumber, constants, type PopulatedTransaction, utils } from 'ethers'; import type { EvmTransferParams } from './evmTransfer.js'; import { EvmTransfer } from './evmTransfer.js'; import type { EvmFee } from './types.js'; -import { createFungibleDepositData } from './utils/assetTransferHelpers.js'; +import { + createFungibleDepositData, + FungibleTransferOptionalMessage, +} from './utils/assetTransferHelpers.js'; import { approve, createTransactionRequest, @@ -26,6 +29,8 @@ interface EvmFungibleTransferRequest extends EvmTransferParams { amount: bigint; destinationAddress: string; securityModel?: SecurityModel; + optionalGas?: bigint; + optionalMessage?: FungibleTransferOptionalMessage; } /** @@ -99,6 +104,8 @@ class EvmFungibleAssetTransfer extends EvmTransfer { protected destinationAddress: string = ''; protected securityModel: SecurityModel; protected adjustedAmount: bigint = BigInt(0); + protected optionalGas?: bigint; + protected optionalMessage?: FungibleTransferOptionalMessage; private specifiedAmount: bigint; // Original value to transfer without deductions constructor(transfer: EvmFungibleTransferRequest, config: Config) { @@ -135,6 +142,8 @@ class EvmFungibleAssetTransfer extends EvmTransfer { destination: this.destination, recipientAddress: this.destinationAddress, amount: this.amount, + optionalGas: this.optionalGas, + optionalMessage: this.optionalMessage, }); } diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts index acf150ee7..5337d0732 100644 --- a/packages/evm/src/utils/assetTransferHelpers.ts +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -18,16 +18,18 @@ interface FungibleDepositAction { data: string; } +export interface FungibleTransferOptionalMessage { + transactionId: string; + actions: FungibleDepositAction[]; + receiver: string; +} + interface FungbileDepositParams { destination: Domain; recipientAddress: string; amount: bigint; optionalGas?: bigint; - optionalMessage?: { - transactionId: string; - actions: FungibleDepositAction[]; - receiver: string; - }; + optionalMessage?: FungibleTransferOptionalMessage; } export function serializeEvmAddress(evmAddress: `0x${string}`): Uint8Array { From 491417038baec1cc96bc48e48f410c895280927b Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Tue, 3 Sep 2024 12:46:00 +0200 Subject: [PATCH 05/14] fix lint --- packages/evm/src/fungible.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/evm/src/fungible.ts b/packages/evm/src/fungible.ts index 8d6957c00..3a73c3c26 100644 --- a/packages/evm/src/fungible.ts +++ b/packages/evm/src/fungible.ts @@ -13,10 +13,8 @@ import { BigNumber, constants, type PopulatedTransaction, utils } from 'ethers'; import type { EvmTransferParams } from './evmTransfer.js'; import { EvmTransfer } from './evmTransfer.js'; import type { EvmFee } from './types.js'; -import { - createFungibleDepositData, - FungibleTransferOptionalMessage, -} from './utils/assetTransferHelpers.js'; +import type { FungibleTransferOptionalMessage } from './utils/assetTransferHelpers.js'; +import { createFungibleDepositData } from './utils/assetTransferHelpers.js'; import { approve, createTransactionRequest, From 1c27389bc7ba2b9359d2cd4ea252b2057182333b Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Tue, 3 Sep 2024 15:46:02 +0200 Subject: [PATCH 06/14] fixed ethers encoding error --- packages/evm/src/fungible.ts | 4 +++- packages/evm/src/types.ts | 19 ------------------- .../evm/src/utils/assetTransferHelpers.ts | 5 +++-- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/packages/evm/src/fungible.ts b/packages/evm/src/fungible.ts index 3a73c3c26..72aa20cc7 100644 --- a/packages/evm/src/fungible.ts +++ b/packages/evm/src/fungible.ts @@ -22,7 +22,7 @@ import { getERC20Allowance, } from './utils/index.js'; -interface EvmFungibleTransferRequest extends EvmTransferParams { +export interface EvmFungibleTransferRequest extends EvmTransferParams { sourceAddress: string; amount: bigint; destinationAddress: string; @@ -109,6 +109,8 @@ class EvmFungibleAssetTransfer extends EvmTransfer { constructor(transfer: EvmFungibleTransferRequest, config: Config) { super(transfer, config); this.specifiedAmount = transfer.amount; + this.optionalGas = transfer.optionalGas; + this.optionalMessage = transfer.optionalMessage; if (isValidAddressForNetwork(transfer.destinationAddress, this.destination.type)) this.destinationAddress = transfer.destinationAddress; diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index 9c1c71b47..136cd3393 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -5,9 +5,6 @@ import type { FeeHandlerType, SecurityModel, } from '@buildwithsygma/core'; -import type { Bridge } from '@buildwithsygma/sygma-contracts'; -import type { ethers } from 'ethers'; - export interface TransactionRequest { to: string; value: bigint; @@ -51,19 +48,3 @@ export type GenericTransferRequest = { /** An EVM resource is accepted as either the resource object or it's Sygma ID */ export type EvmResourceish = string | EvmResource; - -/** @internal */ -export type FungibleTransferParams = { - /** The unique identifier for the destination network on the bridge. */ - domainId: string; - /** The unique identifier for the resource being transferred. */ - resourceId: string; - /** The bridge instance used for the transfer. */ - bridgeInstance: Bridge; - /** The fee data associated with the ERC20 token transfer, including the gas price and gas limit. */ - feeData: EvmFee; - /** Deposit data including amount of tokens, length and recipient address */ - depositData: string; - /** Optional overrides for the transaction, such as gas price, gas limit, or value. */ - overrides?: ethers.PayableOverrides; -}; diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts index 5337d0732..66361dc2b 100644 --- a/packages/evm/src/utils/assetTransferHelpers.ts +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -7,7 +7,7 @@ import { decodeAddress } from '@polkadot/util-crypto'; import { BigNumber } from 'ethers'; const ACTIONS_ARRAY_ABI = - 'tuple(uint256 nativeValue, address callTo, address approveTo, address tokenSend, address tokenReceive, bytes data)'; + 'tuple(uint256 nativeValue, address callTo, address approveTo, address tokenSend, address tokenReceive, bytes data)[]'; interface FungibleDepositAction { nativeValue: bigint; @@ -115,9 +115,10 @@ export function createFungibleDepositData(depositParams: FungbileDepositParams): if (optionalMessage) { const { transactionId, actions, receiver } = optionalMessage; const abiCoder = new AbiCoder(); + const optionalMessageEncoded = abiCoder.encode( ['bytes32', ACTIONS_ARRAY_ABI, 'address'], - [transactionId, actions, receiver], + [transactionId, actions.map(action => Object.values(action)), receiver], ); const optionalMessageSeriailzed = arrayify(optionalMessageEncoded); From dcc9f740c20634e964d95996ddc932ffb429727a Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Tue, 3 Sep 2024 16:15:08 +0200 Subject: [PATCH 07/14] fix lint --- packages/evm/src/types.ts | 1 + packages/evm/src/utils/assetTransferHelpers.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index 136cd3393..d4c978988 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -5,6 +5,7 @@ import type { FeeHandlerType, SecurityModel, } from '@buildwithsygma/core'; + export interface TransactionRequest { to: string; value: bigint; diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts index 66361dc2b..d045c564b 100644 --- a/packages/evm/src/utils/assetTransferHelpers.ts +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -118,7 +118,7 @@ export function createFungibleDepositData(depositParams: FungbileDepositParams): const optionalMessageEncoded = abiCoder.encode( ['bytes32', ACTIONS_ARRAY_ABI, 'address'], - [transactionId, actions.map(action => Object.values(action)), receiver], + [transactionId, actions.map(action => Object.values(action) as Array), receiver], ); const optionalMessageSeriailzed = arrayify(optionalMessageEncoded); From 638e36dde3f94f18b8bce43af944e310fb809fcf Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Wed, 4 Sep 2024 18:45:33 +0200 Subject: [PATCH 08/14] updated example script --- .../src/transfer.ts | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/examples/evm-to-evm-fungible-transfer/src/transfer.ts b/examples/evm-to-evm-fungible-transfer/src/transfer.ts index c9925cf1e..48f2f898a 100644 --- a/examples/evm-to-evm-fungible-transfer/src/transfer.ts +++ b/examples/evm-to-evm-fungible-transfer/src/transfer.ts @@ -1,8 +1,10 @@ import type { Eip1193Provider } from "@buildwithsygma/core"; -import { Environment } from "@buildwithsygma/core"; -import { createEvmFungibleAssetTransfer } from "@buildwithsygma/evm"; +import { + createEvmFungibleAssetTransfer, + EvmFungibleTransferRequest, +} from "@buildwithsygma/evm"; import dotenv from "dotenv"; -import { Wallet, providers } from "ethers"; +import { Wallet, ethers, providers } from "ethers"; import Web3HttpProvider from "web3-providers-http"; dotenv.config(); @@ -14,11 +16,14 @@ if (!privateKey) { } const SEPOLIA_CHAIN_ID = 11155111; -const AMOY_CHAIN_ID = 80002; +const AMOY_CHAIN_ID = 84532; const RESOURCE_ID = - "0x0000000000000000000000000000000000000000000000000000000000000300"; + "0x0000000000000000000000000000000000000000000000000000000000001200"; const SEPOLIA_RPC_URL = - process.env.SEPOLIA_RPC_URL || "https://eth-sepolia-public.unifra.io"; + process.env.SEPOLIA_RPC_URL || + "https://eth-sepolia.g.alchemy.com/v2/MeCKDrpxLkGOn4LMlBa3cKy1EzzOzwzG"; + +const paramAddress = "0x98729c03c4D5e820F5e8c45558ae07aE63F97461" as const; const explorerUrls: Record = { [SEPOLIA_CHAIN_ID]: "https://sepolia.etherscan.io", @@ -35,15 +40,37 @@ export async function erc20Transfer(): Promise { const sourceAddress = await wallet.getAddress(); const destinationAddress = await wallet.getAddress(); - const params = { + const contractInterface = new ethers.utils.Interface( + '[{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"Sender","type":"address"},{"indexed":false,"internalType":"string","name":"Name","type":"string"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"_destination","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"burglarize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"claimName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenSent","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"handleAcrossMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenSent","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"_message","type":"bytes"}],"name":"handleV3AcrossMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"names","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]' + ); + + const params: EvmFungibleTransferRequest = { source: SEPOLIA_CHAIN_ID, destination: AMOY_CHAIN_ID, sourceNetworkProvider: web3Provider as unknown as Eip1193Provider, resource: RESOURCE_ID, - amount: BigInt(1) * BigInt(1e18), - destinationAddress: destinationAddress, - environment: (process.env.SYGMA_ENV as Environment) || Environment.TESTNET, + amount: BigInt(1) * BigInt(1e4), + destinationAddress: ethers.constants.AddressZero, // this needs to be AddressZero to call DefaultMessageReceiver on dest sourceAddress: sourceAddress, + optionalGas: BigInt(500000), + optionalMessage: { + receiver: destinationAddress, // fall back for leftovers GasNativeToken based on execution + actions: [ + { + nativeValue: BigInt(0), + callTo: "0x3F9A68fF29B3d86a6928C44dF171A984F6180009", //the call contract + approveTo: "0x3F9A68fF29B3d86a6928C44dF171A984F6180009", // contract + tokenSend: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // token address + tokenReceive: ethers.constants.AddressZero, + data: contractInterface.encodeFunctionData("claimName", [ + "Saad Ahmed", + destinationAddress, + BigInt(500000), + ]), + }, + ], + transactionId: ethers.utils.formatBytes32String("Testing"), + }, }; const transfer = await createEvmFungibleAssetTransfer(params); From 10cac40afb188a7bbf31bbee48486fabc86ee662 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Thu, 5 Sep 2024 11:15:24 +0200 Subject: [PATCH 09/14] revert transfer example --- .../src/transfer.ts | 30 ++----------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/examples/evm-to-evm-fungible-transfer/src/transfer.ts b/examples/evm-to-evm-fungible-transfer/src/transfer.ts index 48f2f898a..8197698b9 100644 --- a/examples/evm-to-evm-fungible-transfer/src/transfer.ts +++ b/examples/evm-to-evm-fungible-transfer/src/transfer.ts @@ -4,7 +4,7 @@ import { EvmFungibleTransferRequest, } from "@buildwithsygma/evm"; import dotenv from "dotenv"; -import { Wallet, ethers, providers } from "ethers"; +import { Wallet, providers } from "ethers"; import Web3HttpProvider from "web3-providers-http"; dotenv.config(); @@ -23,8 +23,6 @@ const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL || "https://eth-sepolia.g.alchemy.com/v2/MeCKDrpxLkGOn4LMlBa3cKy1EzzOzwzG"; -const paramAddress = "0x98729c03c4D5e820F5e8c45558ae07aE63F97461" as const; - const explorerUrls: Record = { [SEPOLIA_CHAIN_ID]: "https://sepolia.etherscan.io", }; @@ -40,37 +38,15 @@ export async function erc20Transfer(): Promise { const sourceAddress = await wallet.getAddress(); const destinationAddress = await wallet.getAddress(); - const contractInterface = new ethers.utils.Interface( - '[{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"Sender","type":"address"},{"indexed":false,"internalType":"string","name":"Name","type":"string"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"address","name":"_destination","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"burglarize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"claimName","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenSent","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"handleAcrossMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenSent","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"bytes","name":"_message","type":"bytes"}],"name":"handleV3AcrossMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"names","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]' - ); - const params: EvmFungibleTransferRequest = { source: SEPOLIA_CHAIN_ID, destination: AMOY_CHAIN_ID, sourceNetworkProvider: web3Provider as unknown as Eip1193Provider, resource: RESOURCE_ID, - amount: BigInt(1) * BigInt(1e4), - destinationAddress: ethers.constants.AddressZero, // this needs to be AddressZero to call DefaultMessageReceiver on dest + amount: BigInt(1) * BigInt(1e6), + destinationAddress: destinationAddress, // this needs to be AddressZero to call DefaultMessageReceiver on dest sourceAddress: sourceAddress, optionalGas: BigInt(500000), - optionalMessage: { - receiver: destinationAddress, // fall back for leftovers GasNativeToken based on execution - actions: [ - { - nativeValue: BigInt(0), - callTo: "0x3F9A68fF29B3d86a6928C44dF171A984F6180009", //the call contract - approveTo: "0x3F9A68fF29B3d86a6928C44dF171A984F6180009", // contract - tokenSend: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // token address - tokenReceive: ethers.constants.AddressZero, - data: contractInterface.encodeFunctionData("claimName", [ - "Saad Ahmed", - destinationAddress, - BigInt(500000), - ]), - }, - ], - transactionId: ethers.utils.formatBytes32String("Testing"), - }, }; const transfer = await createEvmFungibleAssetTransfer(params); From 64e21273308317b26ed436e30f478d071630d915 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Thu, 5 Sep 2024 12:53:34 +0200 Subject: [PATCH 10/14] action items ordering bug fix --- packages/evm/src/utils/assetTransferHelpers.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts index d045c564b..24f2e2661 100644 --- a/packages/evm/src/utils/assetTransferHelpers.ts +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -118,7 +118,18 @@ export function createFungibleDepositData(depositParams: FungbileDepositParams): const optionalMessageEncoded = abiCoder.encode( ['bytes32', ACTIONS_ARRAY_ABI, 'address'], - [transactionId, actions.map(action => Object.values(action) as Array), receiver], + [ + transactionId, + actions.map(action => [ + action.nativeValue, + action.callTo, + action.approveTo, + action.tokenSend, + action.tokenReceive, + action.data, + ]), + receiver, + ], ); const optionalMessageSeriailzed = arrayify(optionalMessageEncoded); From 993760ecc562f77e5e5beb67c096570f81fc7ede Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Thu, 5 Sep 2024 17:04:03 +0200 Subject: [PATCH 11/14] move exports --- packages/evm/src/fungibleAssetTransfer.ts | 10 ++++++++-- packages/evm/src/types.ts | 17 +++++++++++++++++ packages/evm/src/utils/assetTransferHelpers.ts | 16 +--------------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/evm/src/fungibleAssetTransfer.ts b/packages/evm/src/fungibleAssetTransfer.ts index ae05a31c1..e08ad5855 100644 --- a/packages/evm/src/fungibleAssetTransfer.ts +++ b/packages/evm/src/fungibleAssetTransfer.ts @@ -5,9 +5,13 @@ import { Web3Provider } from '@ethersproject/providers'; import { BigNumber, constants, type PopulatedTransaction, utils } from 'ethers'; import { AssetTransfer } from './evmAssetTransfer.js'; -import type { EvmFee, FungibleTransferParams, TransactionRequest } from './types.js'; +import type { + EvmFee, + FungibleTransferOptionalMessage, + FungibleTransferParams, + TransactionRequest, +} from './types.js'; import { approve, getERC20Allowance } from './utils/approveAndCheckFns.js'; -import type { FungibleTransferOptionalMessage } from './utils/assetTransferHelpers.js'; import { createFungibleDepositData } from './utils/assetTransferHelpers.js'; import { createTransactionRequest } from './utils/transaction.js'; @@ -51,6 +55,8 @@ class FungibleAssetTransfer extends AssetTransfer { super(transfer, config); this.specifiedAmount = transfer.amount; this.securityModel = transfer.securityModel ?? SecurityModel.MPC; + this.optionalGas = transfer.optionalGas; + this.optionalMessage = transfer.optionalMessage; } /** diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index 4509740b4..96771214d 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -45,6 +45,21 @@ export type EvmFee = { /** An EVM resource is accepted as either the resource object or it's Sygma ID */ export type EvmResourceish = string | EvmResource; +interface FungibleDepositAction { + nativeValue: bigint; + callTo: string; + approveTo: string; + tokenSend: string; + tokenReceive: string; + data: string; +} + +export interface FungibleTransferOptionalMessage { + transactionId: string; + actions: FungibleDepositAction[]; + receiver: string; +} + export interface EvmTransferParams extends BaseTransferParams { sourceAddress: string; sourceNetworkProvider: Eip1193Provider; @@ -59,6 +74,8 @@ export interface EvmAssetTransferParams extends EvmTransferParams { export interface FungibleTransferParams extends EvmAssetTransferParams { amount: bigint; securityModel?: SecurityModel; + optionalGas?: bigint; + optionalMessage: FungibleTransferOptionalMessage; } export interface NonFungibleTransferParams extends EvmAssetTransferParams { diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts index 80a2b05fb..f5e9b02ae 100644 --- a/packages/evm/src/utils/assetTransferHelpers.ts +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -5,25 +5,11 @@ import { arrayify, concat, hexlify, hexZeroPad } from '@ethersproject/bytes'; import { TypeRegistry } from '@polkadot/types'; import { decodeAddress } from '@polkadot/util-crypto'; import { BigNumber } from 'ethers'; +import { FungibleTransferOptionalMessage } from '../types'; const ACTIONS_ARRAY_ABI = 'tuple(uint256 nativeValue, address callTo, address approveTo, address tokenSend, address tokenReceive, bytes data)[]'; -interface FungibleDepositAction { - nativeValue: bigint; - callTo: string; - approveTo: string; - tokenSend: string; - tokenReceive: string; - data: string; -} - -export interface FungibleTransferOptionalMessage { - transactionId: string; - actions: FungibleDepositAction[]; - receiver: string; -} - interface FungbileDepositParams { destination: Domain; recipientAddress: string; From e327425329fa9e0128209101c35b6ade03fb3a11 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Thu, 5 Sep 2024 17:18:05 +0200 Subject: [PATCH 12/14] fix lint --- packages/evm/src/utils/assetTransferHelpers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/evm/src/utils/assetTransferHelpers.ts b/packages/evm/src/utils/assetTransferHelpers.ts index f5e9b02ae..48b0b4a98 100644 --- a/packages/evm/src/utils/assetTransferHelpers.ts +++ b/packages/evm/src/utils/assetTransferHelpers.ts @@ -5,7 +5,8 @@ import { arrayify, concat, hexlify, hexZeroPad } from '@ethersproject/bytes'; import { TypeRegistry } from '@polkadot/types'; import { decodeAddress } from '@polkadot/util-crypto'; import { BigNumber } from 'ethers'; -import { FungibleTransferOptionalMessage } from '../types'; + +import type { FungibleTransferOptionalMessage } from '../types.js'; const ACTIONS_ARRAY_ABI = 'tuple(uint256 nativeValue, address callTo, address approveTo, address tokenSend, address tokenReceive, bytes data)[]'; From c8081a96d50267103c423e3696dbd68faa728235 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Thu, 5 Sep 2024 17:24:32 +0200 Subject: [PATCH 13/14] optionalMessage is optional --- packages/evm/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index 96771214d..e9b93af1b 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -75,7 +75,7 @@ export interface FungibleTransferParams extends EvmAssetTransferParams { amount: bigint; securityModel?: SecurityModel; optionalGas?: bigint; - optionalMessage: FungibleTransferOptionalMessage; + optionalMessage?: FungibleTransferOptionalMessage; } export interface NonFungibleTransferParams extends EvmAssetTransferParams { From 352280602abcbf0386981206ef6aede39c225a70 Mon Sep 17 00:00:00 2001 From: Saad Ahmed Siddiqui Date: Mon, 9 Sep 2024 14:14:49 +0200 Subject: [PATCH 14/14] updated comments and examples --- examples/evm-to-evm-fungible-transfer/src/transfer.ts | 6 +++--- packages/evm/src/utils/depositFn.ts | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/evm-to-evm-fungible-transfer/src/transfer.ts b/examples/evm-to-evm-fungible-transfer/src/transfer.ts index 1012550e0..d08fa2723 100644 --- a/examples/evm-to-evm-fungible-transfer/src/transfer.ts +++ b/examples/evm-to-evm-fungible-transfer/src/transfer.ts @@ -1,7 +1,7 @@ import { getSygmaScanLink, type Eip1193Provider } from "@buildwithsygma/core"; import { createFungibleAssetTransfer, - EvmFungibleTransferRequest, + FungibleTransferParams, } from "@buildwithsygma/evm"; import dotenv from "dotenv"; import { Wallet, providers } from "ethers"; @@ -38,13 +38,13 @@ export async function erc20Transfer(): Promise { const sourceAddress = await wallet.getAddress(); const destinationAddress = await wallet.getAddress(); - const params: EvmFungibleTransferRequest = { + const params: FungibleTransferParams = { source: SEPOLIA_CHAIN_ID, destination: AMOY_CHAIN_ID, sourceNetworkProvider: web3Provider as unknown as Eip1193Provider, resource: RESOURCE_ID, amount: BigInt(1) * BigInt(1e6), - destinationAddress: destinationAddress, // this needs to be AddressZero to call DefaultMessageReceiver on dest + recipientAddress: destinationAddress, sourceAddress: sourceAddress, optionalGas: BigInt(500000), }; diff --git a/packages/evm/src/utils/depositFn.ts b/packages/evm/src/utils/depositFn.ts index 256b9ecb2..a1dd054a4 100644 --- a/packages/evm/src/utils/depositFn.ts +++ b/packages/evm/src/utils/depositFn.ts @@ -28,7 +28,10 @@ export const executeDeposit = async ( overrides?: ethers.PayableOverrides, ): Promise => { const transactionSettings = { - // * "twap" and "basic" both deduct in native currency + /** + * @remarks + * "twap" and "basic" both deduct in native currency + */ value: feeData.type == FeeHandlerType.PERCENTAGE ? 0 : feeData.fee, gasLimit: ASSET_TRANSFER_GAS_LIMIT, };