Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(evm): Update SDK to Support New ERC20Handler with Optional Contract Call #504

Merged
merged 16 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions examples/evm-to-evm-fungible-transfer/src/transfer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Eip1193Provider, getSygmaScanLink } from "@buildwithsygma/core";
import { getSygmaScanLink, type Eip1193Provider } from "@buildwithsygma/core";
import {
createFungibleAssetTransfer,
FungibleTransferParams,
Expand All @@ -16,11 +16,12 @@ 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 explorerUrls: Record<number, string> = {
[SEPOLIA_CHAIN_ID]: "https://sepolia.etherscan.io",
Expand All @@ -42,9 +43,10 @@ export async function erc20Transfer(): Promise<void> {
destination: AMOY_CHAIN_ID,
sourceNetworkProvider: web3Provider as unknown as Eip1193Provider,
resource: RESOURCE_ID,
amount: BigInt(1) * BigInt(1e18),
amount: BigInt(1) * BigInt(1e6),
recipientAddress: destinationAddress,
sourceAddress,
sourceAddress: sourceAddress,
optionalGas: BigInt(500000),
};

const transfer = await createFungibleAssetTransfer(params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ const substrateTransfer = async (): Promise<void> => {
resource: RESOURCE_ID_SYGMA_USD,
amount: BigInt("1"),
destinationAddress: recipient,
sourceAddress: account.address,
};

const transfer = await createSubstrateFungibleAssetTransfer(transferParams);
Expand Down
20 changes: 11 additions & 9 deletions packages/evm/src/evmAssetTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import type { Config } from '@buildwithsygma/core';
import { isValidAddressForNetwork } from '@buildwithsygma/core';
import { Bridge__factory } from '@buildwithsygma/sygma-contracts';
import { Web3Provider } from '@ethersproject/providers';
import type { PayableOverrides } from 'ethers';
import { constants, utils } from 'ethers';

import { EvmTransfer } from './evmTransfer.js';
import type { EvmAssetTransferParams, EvmFee, TransactionRequest } from './types.js';
import { assetTransfer } from './utils/index.js';
import { executeDeposit } from './utils/depositFn.js';
import { createTransactionRequest } from './utils/transaction.js';

/**
Expand Down Expand Up @@ -62,7 +63,7 @@ export abstract class AssetTransfer extends EvmTransfer implements IAssetTransfe
* Get transfer transaction
* @returns {Promise<TransactionRequest>}
*/
public async getTransferTransaction(): Promise<TransactionRequest> {
public async getTransferTransaction(overrides?: PayableOverrides): Promise<TransactionRequest> {
const domainConfig = this.config.getDomainConfig(this.source);
const provider = new Web3Provider(this.sourceNetworkProvider);
const bridge = Bridge__factory.connect(domainConfig.bridge, provider);
Expand All @@ -71,13 +72,14 @@ 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 assetTransfer({
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,
overrides,
);

return createTransactionRequest(transferTx);
}
Expand Down
22 changes: 19 additions & 3 deletions packages/evm/src/fungibleAssetTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ 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 { createERCDepositData } from './utils/helpers.js';
import { createFungibleDepositData } from './utils/assetTransferHelpers.js';
import { createTransactionRequest } from './utils/transaction.js';

/**
Expand Down Expand Up @@ -36,6 +41,9 @@ class FungibleAssetTransfer extends AssetTransfer {
protected declare adjustedAmount: bigint;
protected specifiedAmount: bigint;

protected optionalGas?: bigint;
protected optionalMessage?: FungibleTransferOptionalMessage;

/**
* Returns amount to be transferred considering the fee
*/
Expand All @@ -47,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;
}

/**
Expand All @@ -55,7 +65,13 @@ class FungibleAssetTransfer extends AssetTransfer {
* @returns {string}
*/
protected getDepositData(): string {
return createERCDepositData(this.adjustedAmount, this.recipient, this.destination.parachainId);
return createFungibleDepositData({
destination: this.destination,
recipientAddress: this.recipientAddress,
amount: this.adjustedAmount,
optionalGas: this.optionalGas,
optionalMessage: this.optionalMessage,
});
}

/**
Expand Down
100 changes: 28 additions & 72 deletions packages/evm/src/genericMessageTransfer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { EthereumConfig, EvmResource, SubstrateResource } from '@buildwithsygma/core';
import type { EthereumConfig } from '@buildwithsygma/core';
import { Config, Network, ResourceType } from '@buildwithsygma/core';
import { Bridge__factory } from '@buildwithsygma/sygma-contracts';
import { Web3Provider } from '@ethersproject/providers';
Expand All @@ -8,16 +8,14 @@ import type {
ExtractAbiFunction,
ExtractAbiFunctionNames,
} from 'abitype';
import { constants, ethers } from 'ethers';
import type { ethers } from 'ethers';
import { constants } from 'ethers';

import { EvmTransfer } from './evmTransfer.js';
import { getFeeInformation } from './fee/getFeeInformation.js';
import type { GenericMessageTransferParams, TransactionRequest } from './types.js';
import {
createPermissionlessGenericDepositData,
serializeGenericCallParameters,
} from './utils/helpers.js';
import { genericMessageTransfer } from './utils/index.js';
import { createGenericCallDepositData } from './utils/genericTransferHelpers.js';
import { executeDeposit } from './utils/index.js';
import { createTransactionRequest } from './utils/transaction.js';

/**
Expand Down Expand Up @@ -57,43 +55,6 @@ class GenericMessageTransfer<
this.maxFee = params.maxFee;
}

/**
* Get prepared additional deposit data
* in hex string format
* @returns {string}
*/
protected getDepositData(): string {
const { executeFunctionSignature, executionData } = this.prepareFunctionCallEncodings();
return createPermissionlessGenericDepositData(
executeFunctionSignature,
this.destinationContractAddress,
this.maxFee.toString(),
this.sourceAddress,
executionData,
);
}

/**
* 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 };
}

/**
* Checks validity of the transfer
* @returns {Promise<boolean>}
Expand Down Expand Up @@ -166,17 +127,6 @@ class GenericMessageTransfer<
): void {
this.functionParameters = parameters;
}

public setResource(resource: EvmResource | SubstrateResource): void {
if (
resource.type !== ResourceType.PERMISSIONED_GENERIC &&
resource.type !== ResourceType.PERMISSIONLESS_GENERIC
) {
throw new Error('Unsupported Resource type.');
}
this.transferResource = resource;
}

/**
* Get the cross chain generic message transfer
* transaction
Expand All @@ -187,33 +137,39 @@ 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,
bridgeInstance,
overrides,
});
);

return createTransactionRequest(transaction);
}
/**
* Get prepared additional deposit data
* in hex string format
* @returns {string}
*/
protected getDepositData(): string {
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}`,
});
}
}

/**
Expand Down
8 changes: 6 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 { createERCDepositData } from './utils/helpers.js';
import { createFungibleDepositData } from './utils/assetTransferHelpers.js';
import { approve, isApproved } from './utils/index.js';
import { createTransactionRequest } from './utils/transaction.js';

Expand All @@ -31,7 +31,11 @@ class NonFungibleAssetTransfer extends AssetTransfer {
* @returns {string}
*/
protected getDepositData(): string {
return createERCDepositData(BigInt(this.tokenId), this.recipient, this.destination.parachainId);
return createFungibleDepositData({
destination: this.destination,
recipientAddress: this.recipientAddress,
tokenId: this.tokenId,
});
}

/**
Expand Down
17 changes: 17 additions & 0 deletions packages/evm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down
Loading