diff --git a/examples/evm-to-evm-fungible-transfer/src/transfer.ts b/examples/evm-to-evm-fungible-transfer/src/transfer.ts index 371f6718f..7a7468b48 100644 --- a/examples/evm-to-evm-fungible-transfer/src/transfer.ts +++ b/examples/evm-to-evm-fungible-transfer/src/transfer.ts @@ -55,7 +55,7 @@ export async function erc20Transfer(): Promise { const response = await wallet.sendTransaction(approval); await response.wait(); console.log( - `Approved, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}` + `Approved, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}`, ); } @@ -63,7 +63,7 @@ export async function erc20Transfer(): Promise { const response = await wallet.sendTransaction(transferTx); await response.wait(); console.log( - `Depositted, transaction: ${getSygmaScanLink(response.hash, process.env.SYGMA_ENV)}` + `Depositted, transaction: ${getSygmaScanLink(response.hash, process.env.SYGMA_ENV)}`, ); } diff --git a/examples/evm-to-substrate-fungible-transfer/.env.sample b/examples/evm-to-substrate-fungible-transfer/.env.sample new file mode 100644 index 000000000..383a13dc8 --- /dev/null +++ b/examples/evm-to-substrate-fungible-transfer/.env.sample @@ -0,0 +1,3 @@ +PRIVATE_KEY="" +SOURCE_EVM_RPC_URL="SEPOLIA_RPC_URL_HERE" +SYGMA_ENV="testnet" \ No newline at end of file diff --git a/examples/evm-to-substrate-fungible-transfer/README.md b/examples/evm-to-substrate-fungible-transfer/README.md new file mode 100644 index 000000000..5fa1c8388 --- /dev/null +++ b/examples/evm-to-substrate-fungible-transfer/README.md @@ -0,0 +1,85 @@ +## Sygma SDK Substrate Asset Transfer Example + +This is an example script that demonstrates the functionality of the SDK using the Sygma ecosystem. The script showcases a EVM Asset transfer between a EVM network and an Substrate network using the Sygma SDK. + +## Prerequisites + +Before running the script, ensure that you have the following: + +- Node.js installed on your machine +- Yarn (version 3.4.1 or higher) +- A EVM development wallet funded with `SepoliaETH` tokens +- The Private key for your EVM development wallet +- An Substrate wallet to receive tokens into (the example presets an existing wallet address already) + +## Getting started + +### 1. Clone the repository + +To get started, clone this repository to your local machine with: + +```bash +git clone git@github.com: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 sdk:build +``` + +## 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-substrate-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 `Tangle` with a test ERC20 token. + +Replace the placeholder values in the `.env` file with your own Ethereum wallet private key. + + +**Note** + +To replace default Sepolia url use env variables: +- `SOURCE_EVM_RPC_URL="EVM_RPC_URL"` +## 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 Sepolia and Tangle 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. +- 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 ERC20 tokens. +- Builds the final transfer transaction and sends it using the Ethereum wallet. diff --git a/examples/evm-to-substrate-fungible-transfer/package.json b/examples/evm-to-substrate-fungible-transfer/package.json new file mode 100644 index 000000000..7db16c93d --- /dev/null +++ b/examples/evm-to-substrate-fungible-transfer/package.json @@ -0,0 +1,38 @@ +{ + "name": "@buildwithsygma/evm-to-substrate-fungible-transfer-example", + "version": "0.1.0", + "license": "LGPL-3.0-or-later", + "description": "Sygma SDK EVM to Substrate Example", + "type": "module", + "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", + "devDependencies": { + "dotenv": "^16.3.1", + "eslint": "8", + "ts-node": "10.9.1", + "typescript": "5.0.4" + }, + "dependencies": { + "@buildwithsygma/core": "workspace:*", + "@buildwithsygma/substrate": "workspace:*", + "@polkadot/api": "^12.3.1", + "@polkadot/keyring": "^12.6.2", + "@polkadot/util-crypto": "^12.6.2", + "tsx": "^4.15.4" + } +} diff --git a/examples/evm-to-substrate-fungible-transfer/src/environment.d.ts b/examples/evm-to-substrate-fungible-transfer/src/environment.d.ts new file mode 100644 index 000000000..3749b0e87 --- /dev/null +++ b/examples/evm-to-substrate-fungible-transfer/src/environment.d.ts @@ -0,0 +1,11 @@ +import type { Environment } from "@buildwithsygma/core"; + +declare global { + namespace NodeJS { + interface ProcessEnv { + SYGMA_ENV: Environment; + PRIVATE_KEY: string; + SOURCE_EVM_RPC_URL: string; + } + } +} diff --git a/examples/evm-to-substrate-fungible-transfer/src/transfer.ts b/examples/evm-to-substrate-fungible-transfer/src/transfer.ts new file mode 100644 index 000000000..07cddaafa --- /dev/null +++ b/examples/evm-to-substrate-fungible-transfer/src/transfer.ts @@ -0,0 +1,67 @@ +import type { Eip1193Provider } from "@buildwithsygma/core"; +import { createEvmFungibleAssetTransfer } 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 TANGLE_CHAIN_ID = 3799; +const RESOURCE_ID = + "0x0000000000000000000000000000000000000000000000000000000000002000"; +const SEPOLIA_RPC_URL = + process.env.SOURCE_EVM_RPC_URL ?? + "https://eth-sepolia.g.alchemy.com/v2/MeCKDrpxLkGOn4LMlBa3cKy1EzzOzwzG"; + +const explorerUrls: Record = { + [SEPOLIA_CHAIN_ID]: "https://sepolia.etherscan.io", +}; +const getTxExplorerUrl = (params: { + txHash: string; + chainId: number; +}): string => `${explorerUrls[params.chainId]}/tx/${params.txHash}`; + +export async function erc20Transfer(): Promise { + 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 = "5GjowPEaFNnwbrmpPuDmBVdF2e7n3cHwk2LnUwHXsaW5KtEL"; + + const params = { + source: SEPOLIA_CHAIN_ID, + destination: TANGLE_CHAIN_ID, + sourceNetworkProvider: web3Provider as unknown as Eip1193Provider, + resource: RESOURCE_ID, + amount: BigInt(1) * BigInt(1e18), + destinationAddress: destinationAddress, + sourceAddress: sourceAddress, + }; + + const transfer = await createEvmFungibleAssetTransfer(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( + `Deposited, transaction: ${getTxExplorerUrl({ txHash: response.hash, chainId: SEPOLIA_CHAIN_ID })}`, + ); +} + +erc20Transfer().finally(() => {}); diff --git a/examples/evm-to-substrate-fungible-transfer/tsconfig.json b/examples/evm-to-substrate-fungible-transfer/tsconfig.json new file mode 100644 index 000000000..711d332ca --- /dev/null +++ b/examples/evm-to-substrate-fungible-transfer/tsconfig.json @@ -0,0 +1,21 @@ +{ + "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" + ] +} \ No newline at end of file diff --git a/examples/substrate-to-evm-fungible-transfer/.env.sample b/examples/substrate-to-evm-fungible-transfer/.env.sample index 1f4e71baa..6d9d03039 100644 --- a/examples/substrate-to-evm-fungible-transfer/.env.sample +++ b/examples/substrate-to-evm-fungible-transfer/.env.sample @@ -1,3 +1,3 @@ PRIVATE_MNEMONIC="YOUR TWELVE WORD MNEMONIC HERE WITH SPACES" -RHALA_RPC_URL="RHALA_RPC_URL_HERE" +SOURCE_SUBSTRATE_RPC_URL="TANGLE_RPC_URL_HERE" SYGMA_ENV="testnet" \ No newline at end of file diff --git a/examples/substrate-to-evm-fungible-transfer/src/transfer.ts b/examples/substrate-to-evm-fungible-transfer/src/transfer.ts index df1951a51..d4f5709a1 100644 --- a/examples/substrate-to-evm-fungible-transfer/src/transfer.ts +++ b/examples/substrate-to-evm-fungible-transfer/src/transfer.ts @@ -13,13 +13,13 @@ if (!MNEMONIC) { } const SEPOLIA_CHAIN_ID = 11155111; -const RHALA_CHAIN_ID = 5231; +const TANGLE_CHAIN_ID = 3799; const RESOURCE_ID_SYGMA_USD = - "0x0000000000000000000000000000000000000000000000000000000000001100"; -const recipient = "0x98729c03c4D5e820F5e8c45558ae07aE63F97461"; -const RHALA_RPC_URL = - process.env.RHALA_RPC_URL ?? "wss://rhala-node.phala.network/ws"; + "0x0000000000000000000000000000000000000000000000000000000000002000"; +const recipient = "0xE39bb23F17a2cf7C9a8C4918376A32036A8867db"; +const TANGLE_RPC_URL = + process.env.SOURCE_SUBSTRATE_RPC_URL ?? "wss://rpc.tangle.tools"; const SYGMA_EXPLORER_URL = "https://scan.test.buildwithsygma.com"; const getSygmaExplorerTransferUrl = (params: { @@ -33,18 +33,17 @@ const substrateTransfer = async (): Promise => { const keyring = new Keyring({ type: "sr25519" }); await cryptoWaitReady(); const account = keyring.addFromUri(MNEMONIC); - const wsProvider = new WsProvider(RHALA_RPC_URL); + const wsProvider = new WsProvider(TANGLE_RPC_URL); const api = await ApiPromise.create({ provider: wsProvider }); const transferParams: SubstrateAssetTransferRequest = { - source: RHALA_CHAIN_ID, + source: TANGLE_CHAIN_ID, destination: SEPOLIA_CHAIN_ID, sourceNetworkProvider: api, sourceAddress: account.address, resource: RESOURCE_ID_SYGMA_USD, - amount: BigInt("1"), + amount: BigInt(1) * BigInt(1e18), destinationAddress: recipient, - sourceAddress: account.address, }; const transfer = await createSubstrateFungibleAssetTransfer(transferParams); @@ -56,7 +55,7 @@ const substrateTransfer = async (): Promise => { if (status.isInBlock) { console.log( - `Transaction included at blockHash ${status.asInBlock.toString()}` + `Transaction included at blockHash ${status.asInBlock.toString()}`, ); } else if (status.isFinalized) { const blockNumber = results.blockNumber?.toNumber(); @@ -64,10 +63,10 @@ const substrateTransfer = async (): Promise => { if (blockNumber && extrinsicIndex) { console.log( - `Transaction finalized at blockHash ${status.asFinalized.toString()}` + `Transaction finalized at blockHash ${status.asFinalized.toString()}`, ); console.log( - `Explorer URL: ${getSygmaExplorerTransferUrl({ blockNumber, extrinsicIndex })}` + `Explorer URL: ${getSygmaExplorerTransferUrl({ blockNumber, extrinsicIndex })}`, ); } unsub(); diff --git a/yarn.lock b/yarn.lock index d934b80ba..6477fb165 100644 --- a/yarn.lock +++ b/yarn.lock @@ -492,6 +492,23 @@ __metadata: languageName: unknown linkType: soft +"@buildwithsygma/evm-to-substrate-fungible-transfer-example@workspace:examples/evm-to-substrate-fungible-transfer": + version: 0.0.0-use.local + resolution: "@buildwithsygma/evm-to-substrate-fungible-transfer-example@workspace:examples/evm-to-substrate-fungible-transfer" + dependencies: + "@buildwithsygma/core": "workspace:*" + "@buildwithsygma/substrate": "workspace:*" + "@polkadot/api": "npm:^12.3.1" + "@polkadot/keyring": "npm:^12.6.2" + "@polkadot/util-crypto": "npm:^12.6.2" + dotenv: "npm:^16.3.1" + eslint: "npm:8" + ts-node: "npm:10.9.1" + tsx: "npm:^4.15.4" + typescript: "npm:5.0.4" + languageName: unknown + linkType: soft + "@buildwithsygma/evm@workspace:^, @buildwithsygma/evm@workspace:packages/evm": version: 0.0.0-use.local resolution: "@buildwithsygma/evm@workspace:packages/evm"