Skip to content

Commit

Permalink
feat(evm): Expanding SDK to support Native transfer with optional con… (
Browse files Browse the repository at this point in the history
#545)

# Implementation details

With [PR#266](sygmaprotocol/sygma-solidity#266),
we introduced the ability to define contract calls together with native
deposit. As SDK didn't integrate the initial implementation of native
handlers, it needs to be expanded so it can be used to create:

Deposit of native currency (tokens)
Deposit of native currency (tokens) + contract call definition
The Native flow differs slightly from regular ERC20/721/1155, as the
deposit transaction should not land on Bridge.sol but on
NativeAdapter.sol contract. We have already expanded the testnet [shared
configuration to have this as a new property of the
domain](https://github.com/sygmaprotocol/sygma-shared-configuration/blob/main/testnet/shared-config-test.json#L10)
- nativeTokenAdapter.

## Closes: #521 

# Testing details

Unit Tests: Develop unit tests that cover scenarios where both Native
transfers and contract calls are involved.
Error Handling: Test how the SDK handles invalid or failed contract
calls within the transaction flow.

# Acceptance Criteria



- [ ] The SDK supports interactions with the new
NativeAdapter->NativeHandler, allowing for both Native transfers and
optional contract calls within the same transaction.
- [ ] The SDK maintains backward compatibility and continues to function
as expected with existing handlers.
- [ ] All new functionality is covered by tests, ensuring reliable and
consistent behavior.
  • Loading branch information
saadahmsiddiqui authored Sep 26, 2024
1 parent 7056ca2 commit eb57424
Show file tree
Hide file tree
Showing 14 changed files with 292 additions and 241 deletions.
2 changes: 2 additions & 0 deletions packages/core/src/config/localConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const localConfig: SygmaConfig = {
caipId: '',
chainId: 1337,
name: 'Ethereum 1',
nativeTokenAdapter: '',
type: Network.EVM,
bridge: '0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68',
handlers: [
Expand Down Expand Up @@ -76,6 +77,7 @@ export const localConfig: SygmaConfig = {
caipId: '',
chainId: 1338,
name: 'evm2',
nativeTokenAdapter: '',
type: Network.EVM,
bridge: '0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68',
handlers: [
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export type FeeHandler = {

export interface EthereumConfig extends BaseConfig<Network.EVM> {
handlers: Array<Handler>;
nativeTokenAdapter: string;
feeRouter: string;
feeHandlers: Array<FeeHandler>;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 6 additions & 7 deletions packages/evm/src/evmAssetTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -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);
}

/**
Expand Down
64 changes: 60 additions & 4 deletions packages/evm/src/fungibleAssetTransfer.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';

/**
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -130,6 +144,10 @@ class FungibleAssetTransfer extends AssetTransfer {
public async getApprovalTransactions(
overrides?: ethers.Overrides,
): Promise<Array<TransactionRequest>> {
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);
Expand Down Expand Up @@ -163,6 +181,44 @@ class FungibleAssetTransfer extends AssetTransfer {

return approvals.map(approval => createTransactionRequest(approval));
}

protected async getNativeTokenDepositTransaction(
overrides?: ethers.Overrides,
): Promise<TransactionRequest> {
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<TransactionRequest> {
if (this.isNativeTransfer()) {
return await this.getNativeTokenDepositTransaction(overrides);
}

return super.getTransferTransaction(overrides);
}
}

/**
Expand Down
11 changes: 5 additions & 6 deletions packages/evm/src/genericMessageTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions packages/evm/src/nonFungibleAssetTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions packages/evm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
8 changes: 4 additions & 4 deletions packages/evm/src/utils/__test__/assetTransferHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { arrayify } from '@ethersproject/bytes';
import { utils } from 'ethers';

import {
createFungibleDepositData,
createAssetDepositData,
createSubstrateMultiLocationObject,
serializeEvmAddress,
serializeSubstrateAddress,
Expand All @@ -16,7 +16,7 @@ describe('createERCDepositData', () => {
const expectedDepositData =
'0x000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000141234567890123456789012345678901234567890';

const depositData = createFungibleDepositData({
const depositData = createAssetDepositData({
recipientAddress,
amount,
destination: {
Expand All @@ -37,7 +37,7 @@ describe('createERCDepositData', () => {
const expectedDepositData =
'0x00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000027010200511f0100fac48520983815e2022ded67ca8d27b73d51b1b022284c48b4eccbb7a328d80f';

const depositData = createFungibleDepositData({
const depositData = createAssetDepositData({
recipientAddress,
amount,
destination: {
Expand All @@ -59,7 +59,7 @@ describe('createERCDepositData', () => {
const expectedDepositData =
'0x0000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000002a746231717366797a6c3932707637776b79616a3074666a6474777663736a383430703030346a676c7670';

const depositData = createFungibleDepositData({
const depositData = createAssetDepositData({
recipientAddress,
amount: tokenAmount,
destination: {
Expand Down
Loading

0 comments on commit eb57424

Please sign in to comment.