Skip to content

Commit

Permalink
feat(evm): Added support for creating cross chain non fungible asset …
Browse files Browse the repository at this point in the history
…transfer transactions (#460)

feat(evm)!: Added support for creating cross chain non fungible asset
transfer transactions

## Description
- Added two BaseClasses between ERC20, ERC721 and Generic transfer
classes as there was room for reasonable abstraction among them.
- Renamed main files with more descriptive names
- Fixed breaking tests

Breaking Changes:
- `createEvmFungibleAssetTransfer` renamed to
`createFungibleAssetTransfer` as it doesn't provide unnecessary context.
- `setAmount` renamed to `setTransferAmount` to match with getter name
in `FungibleAssetTransfer`
- `amount` within `FungibleAssetTransfer` is now `protected` and its
getter is called `transferAmount`
- `destinationAddress` address has been renamed to `recipientAddress` in
fungible asset transfer class

Closes:$ #345 

## How Has This Been Tested? Testing details.
- [X] unit tests for `NonFungibleAssetTransfer` class.
- [X] units tests for `createNonFungibleAssetTransfer` function.

## Types of changes
- [X] New feature (non-breaking change which adds functionality)
- [X] Breaking change (fix or feature that would cause existing
functionality to change)
- [X] Documentation

## Checklist:
- [X] I have commented my code, particularly in hard-to-understand
areas.
- [X] I have ensured that all acceptance criteria (or expected behavior)
from issue are met
- [x] I have added tests to cover my changes.
- [x] I have ensured that all the checks are passing and green, I've
signed the CLA bot
  • Loading branch information
saadahmsiddiqui authored Sep 5, 2024
1 parent fcb5a68 commit 6c40781
Show file tree
Hide file tree
Showing 30 changed files with 1,133 additions and 419 deletions.
1 change: 0 additions & 1 deletion examples/evm-to-evm-fungible-transfer/.env.sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
PRIVATE_KEY=""
CRONOS_RPC_URL="CRONOS_RPC_URL_HERE"
SEPOLIA_RPC_URL="SEPOLIA_RPC_URL_HERE"
SYGMA_ENV="testnet"
13 changes: 8 additions & 5 deletions examples/evm-to-evm-fungible-transfer/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Sygma SDK ERC20 Example

This is an example script that demonstrates the functionality of the SDK using the Sygma ecosystem. The script showcases an ERC20 token transfer between the same account on two different testnets using the Sygma SDK.
This is an example script that demonstrates the functionality of the SDK using the Sygma ecosystem. The script showcases an ERC20 token transfer between the same account on two different testnets using the Sygma SDK.

## Prerequisites

Expand All @@ -10,7 +10,7 @@ Before running the script, ensure that you have the following:
- Yarn (version 3.4.1 or higher)
- A development wallet funded with `ERC20LRTest` tokens from the [Sygma faucet](https://faucet-ui-stage.buildwithsygma.com/)
- The [exported private key](https://support.metamask.io/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key) of your development wallet
- [Sepolia ETH](https://www.alchemy.com/faucets/ethereum-sepolia) for gas
- [Sepolia ETH](https://www.alchemy.com/faucets/ethereum-sepolia) for gas
- An Ethereum [provider](https://www.infura.io/) (in case the hardcoded RPC within the script does not work)

## Getting started
Expand Down Expand Up @@ -63,21 +63,24 @@ To send an ERC20 example transfer run:
yarn run transfer
```

The example will use `ethers` in conjuction with the sygma-sdk to
create a transfer from `Cronos` to `Sepolia` with a test ERC20 token.
The example will use `ethers` in conjuction with the sygma-sdk to
create a transfer from `Sepolia` to `Holesky` with a test ERC20 token.

Replace the placeholder values in the `.env` file with your own Ethereum wallet private key.

**Note**

To replace default rpc Cronos and Sepolia urls use env variables:

- `SEPOLIA_RPC_URL="SEPOLIA_RPC_URL_HERE"`

## Script Functionality

This example script performs the following steps:

- initializes the SDK and establishes a connection to the Ethereum provider.
- retrieves the list of supported domains and resources from the SDK configuration.
- Searches for the ERC20 token resource with the specified symbol
- Searches for the ERC20 token resource with the specified symbol
- Searches for the Cronos and Sepolia domains in the list of supported domains based on their chain IDs
- Constructs a transfer object that defines the details of the ERC20 token transfer
- Retrieves the fee required for the transfer from the SDK.
Expand Down
9 changes: 9 additions & 0 deletions examples/evm-to-evm-fungible-transfer/src/environment.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Environment } from "@buildwithsygma/core";

declare global {
namespace NodeJS {
interface ProcessEnv {
SYGMA_ENV: Environment;
}
}
}
20 changes: 11 additions & 9 deletions examples/evm-to-evm-fungible-transfer/src/transfer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Eip1193Provider } from "@buildwithsygma/core";
import { Environment } from "@buildwithsygma/core";
import { createEvmFungibleAssetTransfer } from "@buildwithsygma/evm";
import { Eip1193Provider, getSygmaScanLink } from "@buildwithsygma/core";
import {
createFungibleAssetTransfer,
FungibleTransferParams,
} from "@buildwithsygma/evm";
import dotenv from "dotenv";
import { Wallet, providers } from "ethers";
import Web3HttpProvider from "web3-providers-http";
Expand Down Expand Up @@ -35,18 +37,18 @@ export async function erc20Transfer(): Promise<void> {
const sourceAddress = await wallet.getAddress();
const destinationAddress = await wallet.getAddress();

const params = {
const params: FungibleTransferParams = {
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,
sourceAddress: sourceAddress,
recipientAddress: destinationAddress,
sourceAddress,
};

const transfer = await createEvmFungibleAssetTransfer(params);
const transfer = await createFungibleAssetTransfer(params);

const approvals = await transfer.getApprovalTransactions();
console.log(`Approving Tokens (${approvals.length})...`);
for (const approval of approvals) {
Expand All @@ -61,7 +63,7 @@ export async function erc20Transfer(): Promise<void> {
const response = await wallet.sendTransaction(transferTx);
await response.wait();
console.log(
`Deposited, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}`
`Depositted, transaction: ${getSygmaScanLink(response.hash, process.env.SYGMA_ENV)}`
);
}

Expand Down
5 changes: 5 additions & 0 deletions examples/evm-to-evm-non-fungible-transfer/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PRIVATE_KEY=""
CRONOS_RPC_URL="CRONOS_RPC_URL_HERE"
SEPOLIA_RPC_URL="SEPOLIA_RPC_URL_HERE"
SYGMA_ENV="testnet"
TOKEN_ID=""
88 changes: 88 additions & 0 deletions examples/evm-to-evm-non-fungible-transfer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
## Sygma SDK ERC721 Example

This is an example script that demonstrates the functionality of the SDK using the Sygma ecosystem. The script showcases an ERC721 token transfer between the same account on two different testnets using the Sygma SDK.

## Prerequisites

Before running the script, ensure that you have the following:

- Node.js
- Yarn (version 3.4.1 or higher)
<!-- - A development wallet funded with `ERC20LRTest` tokens from the [Sygma faucet](https://faucet-ui-stage.buildwithsygma.com/) -->
- The [exported private key](https://support.metamask.io/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key) of your development wallet
- [Sepolia ETH](https://www.alchemy.com/faucets/ethereum-sepolia) for gas
- An Ethereum [provider](https://www.infura.io/) (in case the hardcoded RPC within the script does not work)

## Getting started

### 1. Clone the repository

To get started, clone this repository to your local machine with:

```bash
git clone [email protected]:sygmaprotocol/sygma-sdk.git
cd sygma-sdk/
```

### 2. Install dependencies

Install the project dependencies by running:

```bash
yarn install
```

### 3. Build the sdk

To start the example you need to build the sdk first with:

```bash
yarn build:all
```

## Usage

This example uses the `dotenv` module to manage private keys. To run the example, you will need to configure your environment variable to include your test development account's [exported private key](https://support.metamask.io/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key). A `.env.sample` is provided as a template.

**DO NOT COMMIT PRIVATE KEYS WITH REAL FUNDS TO GITHUB. DOING SO COULD RESULT IN COMPLETE LOSS OF YOUR FUNDS.**

Create a `.env` file in the evm-to-evm example folder:

```bash
cd examples/evm-to-evm-non-fungible-transfer
touch .env
```

Replace between the quotation marks your exported private key:

`PRIVATE_KEY="YOUR_PRIVATE_KEY_HERE"`

To send an ERC20 example transfer run:

```bash
yarn run transfer
```

<!-- The example will use `ethers` in conjuction with the sygma-sdk to
create a transfer from `Sepolia` to `Holesky` with a test ERC721 token. -->

Replace the placeholder values in the `.env` file with your own Ethereum wallet private key.

**Note**

To replace default rpc Cronos and Sepolia urls use env variables:

- `SEPOLIA_RPC_URL="SEPOLIA_RPC_URL_HERE"`

## Script Functionality

This example script performs the following steps:

- initializes the SDK and establishes a connection to the Ethereum provider.
- retrieves the list of supported domains and resources from the SDK configuration.
- Searches for the ERC721 token resource with the specified symbol
- Searches for the Cronos and Sepolia domains in the list of supported domains based on their chain IDs
- Constructs a transfer object that defines the details of the ERc721 token transfer
- Retrieves the fee required for the transfer from the SDK.
- Builds the necessary approval transactions for the transfer and sends them using the Ethereum wallet. The approval transactions are required to authorize the transfer of ERc721 tokens.
- Builds the final transfer transaction and sends it using the Ethereum wallet.
37 changes: 37 additions & 0 deletions examples/evm-to-evm-non-fungible-transfer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@buildwithsygma/evm-to-evm-non-fungible-transfer-example",
"version": "0.1.0",
"type": "module",
"description": "Sygma sdk examples",
"sideEffects": false,
"repository": {
"type": "git",
"url": "https://github.com/sygmaprotocol/sygma-sdk"
},
"keywords": [
"sygma",
"sygmaprotocol",
"buildwithsygma",
"web3",
"bridge",
"ethereum"
],
"scripts": {
"transfer": "tsx src/transfer.ts"
},
"author": "Sygmaprotocol Product Team",
"license": "LGPL-3.0-or-later",
"devDependencies": {
"dotenv": "^16.3.1",
"eslint": "8",
"ts-node": "10.9.1",
"typescript": "5.0.4"
},
"dependencies": {
"@buildwithsygma/core": "workspace:^",
"@buildwithsygma/evm": "workspace:^",
"ethers": "5.7.2",
"tsx": "^4.15.4",
"web3-providers-http": "1.10.4"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Environment } from "@buildwithsygma/core";

declare global {
namespace NodeJS {
interface ProcessEnv {
SYGMA_ENV: Environment;
}
}
}
74 changes: 74 additions & 0 deletions examples/evm-to-evm-non-fungible-transfer/src/transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {
Eip1193Provider,
Environment,
getSygmaScanLink,
} from "@buildwithsygma/core";
import {
createNonFungibleAssetTransfer,
NonFungibleTransferParams,
} from "@buildwithsygma/evm";
import dotenv from "dotenv";
import { Wallet, providers } from "ethers";
import Web3HttpProvider from "web3-providers-http";

dotenv.config();

const privateKey = process.env.PRIVATE_KEY;

if (!privateKey) {
throw new Error("Missing environment variable: PRIVATE_KEY");
}

const SEPOLIA_CHAIN_ID = 11155111;
const CRONOS_TESTNET_CHAIN_ID = 338;
const RESOURCE_ID =
"0x0000000000000000000000000000000000000000000000000000000000000200";
const SEPOLIA_RPC_URL =
process.env.SEPOLIA_RPC_URL || "https://eth-sepolia-public.unifra.io";

const explorerUrls: Record<number, string> = {
[SEPOLIA_CHAIN_ID]: "https://sepolia.etherscan.io",
};
const getTxExplorerUrl = (params: {
txHash: string;
chainId: number;
}): string => `${explorerUrls[params.chainId]}/tx/${params.txHash}`;

export async function erc721Transfer(): Promise<void> {
const web3Provider = new Web3HttpProvider(SEPOLIA_RPC_URL);
const ethersWeb3Provider = new providers.Web3Provider(web3Provider);
const wallet = new Wallet(privateKey ?? "", ethersWeb3Provider);
const sourceAddress = await wallet.getAddress();
const destinationAddress = await wallet.getAddress();

const params: NonFungibleTransferParams = {
source: SEPOLIA_CHAIN_ID,
destination: CRONOS_TESTNET_CHAIN_ID,
sourceNetworkProvider: web3Provider as unknown as Eip1193Provider,
resource: RESOURCE_ID,
tokenId: process.env.TOKEN_ID as string,
recipientAddress: destinationAddress,
sourceAddress,
};

const transfer = await createNonFungibleAssetTransfer(params);

const approvals = await transfer.getApprovalTransactions();
console.log(`Approving Tokens (${approvals.length})...`);
for (const approval of approvals) {
const response = await wallet.sendTransaction(approval);
await response.wait();
console.log(
`Approved, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}`
);
}

const transferTx = await transfer.getTransferTransaction();
const response = await wallet.sendTransaction(transferTx);
await response.wait();
console.log(
`Depositted, transaction: ${getSygmaScanLink(response.hash, process.env.SYGMA_ENV)}`
);
}

erc721Transfer().finally(() => {});
19 changes: 19 additions & 0 deletions examples/evm-to-evm-non-fungible-transfer/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"composite": true,
"module": "ES2022",
"allowJs": true,
"declaration": true,
"sourceMap": true,
"declarationMap": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"esModuleInterop": true,
"downlevelIteration": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node"
},
"include": ["src"]
}
16 changes: 9 additions & 7 deletions examples/substrate-to-evm-fungible-transfer/src/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,17 @@ const substrateTransfer = async (): Promise<void> => {
`Transaction included at blockHash ${status.asInBlock.toString()}`
);
} else if (status.isFinalized) {
const blockNumber = results.blockNumber.toNumber();
const blockNumber = results.blockNumber?.toNumber();
const extrinsicIndex = results.txIndex;

console.log(
`Transaction finalized at blockHash ${status.asFinalized.toString()}`
);
console.log(
`Explorer URL: ${getSygmaExplorerTransferUrl({ blockNumber, extrinsicIndex })}`
);
if (blockNumber && extrinsicIndex) {
console.log(
`Transaction finalized at blockHash ${status.asFinalized.toString()}`
);
console.log(
`Explorer URL: ${getSygmaExplorerTransferUrl({ blockNumber, extrinsicIndex })}`
);
}
unsub();
process.exit(0);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export type ParachainId = number;

export type HexString = `0x${string}`;

export enum RouteType {
GMP = 'gmp',
FUNGIBLE = 'fungible',
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ export function isValidSubstrateAddress(address: string): boolean {
* @returns {boolean}
*/
export function isValidEvmAddress(address: string): boolean {
return !!ethers.utils.isAddress(address);
return ethers.utils.isAddress(address);
}

/**
Expand Down
Loading

0 comments on commit 6c40781

Please sign in to comment.