NEW

Chainlink Data Streams have officially launched on mainnet. Sign up for early access.

Back

Transfer Tokens Between EOAs

In this tutorial, you will use Chainlink CCIP to transfer tokens directly from your EOA (Externally Owned Account) to an account on a different blockchain. First, you will pay for CCIP fees on the source blockchain using LINK. Then, you will run the same example paying for CCIP fees in native gas, such as ETH on Ethereum or MATIC on Polygon.

Before you begin

  1. Install Node.js 18. Optionally, you can use the nvm package to switch between Node.js versions with nvm use 18.

    node -v
    $ node -v
    v18.7.0
  2. Your EOA (Externally Owned Account) must have both ETH and LINK tokens on Ethereum Sepolia to pay for the gas fees and CCIP fees.

  3. Check the Supported Networks page to confirm that the tokens you will transfer are supported for your lane. In this example, you will transfer tokens from Ethereum Sepolia to Polygon Mumbai so check the list of supported tokens here.

  4. Learn how to acquire CCIP test tokens. After following this guide, your EOA (Externally Owned Account) should have CCIP-BnM tokens, and CCIP-BnM should appear in the list of your tokens in MetaMask.

  5. In a terminal, clone the smart-contract-examples repository and change to the smart-contract-examples/ccip-offchain/javascript directory.

    git clone https://github.com/smartcontractkit/smart-contract-examples.git && \
    cd smart-contract-examples/ccip-offchain/javascript
  6. Run npm install to install the dependencies.

    npm install
  7. For higher security, the starter kit imports @chainlink/env-enc. Use this tool to encrypt your environment variables at rest.

    1. Set an encryption password for your environment variables.

      npx env-enc set-pw
    2. Run npx env-enc set to configure a .env.enc file with the basic variables that you need to send your requests to Polygon Mumbai.

      • ETHEREUM_SEPOLIA_RPC_URL: Set a URL for the Ethereum Sepolia testnet. You can sign up for a personal endpoint from Alchemy, Infura, or another node provider service.

      • POLYGON_MUMBAI_RPC_URL: Set a URL for the Polygon Mumbai testnet. You can sign up for a personal endpoint from Alchemy, Infura, or another node provider service.

      • PRIVATE_KEY: Find the private key for your testnet wallet. If you use MetaMask, follow the instructions to Export a Private Key.Note: The off-chain script uses your private key to sign any transactions you make such as transferring tokens.

      npx env-enc set

Tutorial

In this example, you will transfer CCIP-BnM tokens from your EOA on Ethereum Sepolia to an account on Polygon Mumbai. The destination account could be an EOA (Externally Owned Account) or a smart contract. The example shows how to transfer CCIP-BnM tokens, but you can re-use the same example to transfer other tokens as long as they are supported for your lane.

For this example, CCIP fees are paid in LINK tokens. To learn how to pay CCIP fees in native ETH, read the Pay in native section. To see a detailed description of the example code, read the code explanation section.

To transfer tokens and pay in LINK, use the following command:

node src/transfer-tokens.js sourceChain destinationChain destinationAccount tokenAddress amount feeTokenAddress

The feeTokenAddress parameter specifies the token address for paying CCIP fees. The supported tokens for paying fees include LINK, the native gas token of the source blockchain (ETH for Ethereum), and the wrapped native gas token (WETH for Ethereum).

Complete the following steps in your terminal:

  1. Send 1,000,000,000,000,000 (0.001 CCIP-BnM ) from your EOA on Ethereum Sepolia to another account on Polygon Mumbai:

    node src/transfer-tokens.js ethereumSepolia polygonMumbai YOUR_ACCOUNT 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05 1000000000000000 0x779877A7B0D9E8603169DdbD7836e478b4624789

    Command arguments:

    ArgumentExplanation
    node src/transfer-tokens.jsNode.js will execute the JavaScript code inside the transfer-tokens.js file.
    ethereumSepoliaThis specifies the source blockchain, in this case, Ethereum Sepolia.
    polygonMumbaiThis specifies the destination blockchain, which is Polygon Mumbai in this case.
    YOUR_ACCOUNTThis is the account address on the destination blockchain. You can replace this with your account address.
    0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05This is the CCIP-BnM token contract address on Ethereum Sepolia. The contract addresses for each network can be found on the Supported Networks page.
    1000000000000000This is the amount of CCIP-BnM tokens to be transferred. In this example, 0.001 CCIP-BnM are transferred.
    0x779877A7B0D9E8603169DdbD7836e478b4624789Since you will pay for CCIP fees in LINK, this is the LINK token contract address on Ethereum Sepolia. The LINK contract address can be found on the Link Token contracts page.
  2. Once you execute the command, you should see the following logs:

    $ node src/transfer-tokens.js ethereumSepolia polygonMumbai 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A  0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05 1000000000000000 0x779877A7B0D9E8603169DdbD7836e478b4624789
    Estimated fees (wei): 45004872518799270
    approved router 0x0bf3de8c5d3e8a2b34d2beeb17abfcebaf363a59 to spend 1000000000000000 of token 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05. Transaction: 0x5216b9c85d576a6d97c80edaadd890f2a7ae0132e54e7bda9a102374de3c3003
    approved router 0x0bf3de8c5d3e8a2b34d2beeb17abfcebaf363a59 to spend  fees 45004872518799270 of token 0x779877A7B0D9E8603169DdbD7836e478b4624789. Transaction: 0x63ca647e8fd8af177eea85ea02f18e6ece00f7c0e7d7917b050221141cd47a72
    
    ✅ 1000000000000000 of Tokens(0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05) Sent to account 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A on destination chain polygonMumbai using CCIP. Transaction hash 0x7dda59032f1f157a830755264907139602f3fee73666938e12d17c26923c9e07 -  Message id is 0x4ea8080f2a51377247e93b5f45c00330b8c4fde3043e99847d0cab734f473df2
    
    Wait for message 0x4ea8080f2a51377247e93b5f45c00330b8c4fde3043e99847d0cab734f473df2 to be executed on the destination chain - Check the explorer https://ccip.chain.link/msg/0x4ea8080f2a51377247e93b5f45c00330b8c4fde3043e99847d0cab734f473df2
    ...
  3. Analyze the logs:

    • The script communicates with the router to calculate the transaction fees required to transfer tokens, which amounts to 45,004,872,518,799,270 Juels (equivalent to 0,045 LINK).
    • The script engages with the Link token contract, authorizing the router contract to spend 45,004,872,518,799,270 Juels for the fees and 1000000000000000 (0.001 CCIP-BnM) from your Externally Owned Account (EOA) balance.
    • The script initiates a transaction through the router to transfer 1000000000000000 (0.001 CCIP-BnM) to your account on Polygon Mumbai. It also returns the CCIP message ID.
    • The script continuously monitors the destination blockchain (Polygon Mumbai) to track the progress and completion of the cross-chain transaction.
  4. While the script is waiting for the cross-chain transaction to proceed, open the CCIP explorer and search your cross-chain transaction using the message ID. Notice that the status is Waiting for finality.


    Chainlink CCIP Explorer transaction details
  5. After several minutes (the waiting time depends on the finality of the source blockchain), the script will complete the polling process, and the following logs will be displayed:

    Message 0x4ea8080f2a51377247e93b5f45c00330b8c4fde3043e99847d0cab734f473df2 has not been processed yet on the destination chain.Try again in 60sec - Check the explorer https://ccip.chain.link/msg/0x4ea8080f2a51377247e93b5f45c00330b8c4fde3043e99847d0cab734f473df2
    
    ✅Status of message 0x4ea8080f2a51377247e93b5f45c00330b8c4fde3043e99847d0cab734f473df2 is SUCCESS - Check the explorer https://ccip.chain.link/msg/0x4ea8080f2a51377247e93b5f45c00330b8c4fde3043e99847d0cab734f473df2
  6. Open the CCIP explorer and use the message ID to find your cross-chain transaction.

    Chainlink CCIP Explorer transaction details
  7. The data field is empty because only tokens are transferred. The gas limit is set to 0 because the transaction is directed to an Externally Owned Account (EOA). With an empty data field, no function calls on a smart contract are expected on the destination chain.

Transfer tokens and pay in native

In this example, you will transfer CCIP-BnM tokens from your EOA on Ethereum Sepolia to an account on Polygon Mumbai. The destination account could be an EOA (Externally Owned Account) or a smart contract. The example shows how to transfer LINK tokens, but you can re-use the same example to transfer other tokens as long as they are supported for your lane.

For this example, CCIP fees are paid in Sepolia's native ETH. To learn how to pay CCIP fees in LINK, read the Pay in LINK section. To see a detailed description of the example code, read the code explanation section.

To transfer tokens and pay in native, use the following command:

node src/transfer-tokens.js sourceChain destinationChain destinationAccount tokenAddress amount

Complete the following steps in your terminal:

  1. Send 1,000,000,000,000,000 (0.001 CCIP-BnM ) from your EOA on Ethereum Sepolia to another account on Polygon Mumbai:

    node src/transfer-tokens.js ethereumSepolia polygonMumbai YOUR_ACCOUNT 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05 1000000000000000

    Command arguments:

    ArgumentExplanation
    node src/transfer-tokens.jsNode.js will execute the JavaScript code inside the transfer-tokens.js file.
    ethereumSepoliaThis specifies the source blockchain, in this case, Ethereum Sepolia.
    polygonMumbaiThis specifies the destination blockchain, which is Polygon Mumbai in this case.
    YOUR_ACCOUNTThis is the account address on the destination blockchain. Replace this with your account address.
    0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05This is the CCIP-BnM token contract address on Ethereum Sepolia. The contract addresses for each network can be found on the Supported Networks page.
    1000000000000000This is the amount of CCIP-BnM tokens to be transferred. In this example, 0.001 CCIP-BnM are transferred.
  2. After you execute the command, you should see the following logs:

    $ node src/transfer-tokens.js ethereumSepolia polygonMumbai 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A  0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05 1000000000000000
    Estimated fees (wei): 278051815072596
    approved router 0x0bf3de8c5d3e8a2b34d2beeb17abfcebaf363a59 to spend 1000000000000000 of token 0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05. Transaction: 0x85d91b16c08546ad5e8e7791cfe4ebf59cc986f805244230872487fe8ad6862d
    
    ✅ 1000000000000000 of Tokens(0xFd57b4ddBf88a4e07fF4e34C487b99af2Fe82a05) Sent to account 0x83dC44a4C00DFf69d0A0c7c94B20b53a4933BE0A on destination chain polygonMumbai using CCIP. Transaction hash 0x358c2abccd75c52aeabf5b330fda2b17a4c29bfc2612fc85a8619d22c3d1be00 -  Message id is 0x1dc9f4d0e798802b19668a26b59681b97c02ad16bef3765eae6c5232f1d2e050
    
    Wait for message 0x1dc9f4d0e798802b19668a26b59681b97c02ad16bef3765eae6c5232f1d2e050 to be executed on the destination chain - Check the explorer https://ccip.chain.link/msg/0x1dc9f4d0e798802b19668a26b59681b97c02ad16bef3765eae6c5232f1d2e050
    ...
  3. Analyze the logs:

    • The script communicates with the router to calculate the transaction fees required to transfer tokens, which amounts to 278,051,815,072,596 wei (equivalent to 0.000278 ETH).
    • The script engages with the CCIP-BnM token contract, authorizing the router contract to deduct 0.001 CCIP-BnM from your Externally Owned Account (EOA) balance.
    • The script initiates a transaction through the router to transfer 0.001 CCIP-BnM tokens to your destination account on Polygon Mumbai. It also returns the CCIP message ID.
    • The script continuously monitors the destination blockchain (Polygon Mumbai) to track the progress and completion of the cross-chain transaction.
  4. The transaction time depends on the finality of the source blockchain. After several minutes, the script will complete the polling process and the following logs will be displayed:

    Message 0xa57bd1a6a343b78e1b05a78b1725bfd43a418601ca3d2d24118c8a5f7a81e4ae is not processed yet on destination chain.Try again in 60sec - Check the explorer https://ccip.chain.link/msg/0xa57bd1a6a343b78e1b05a78b1725bfd43a418601ca3d2d24118c8a5f7a81e4ae
    
    ✅Status of message 0xa57bd1a6a343b78e1b05a78b1725bfd43a418601ca3d2d24118c8a5f7a81e4ae is SUCCESS - Check the explorer https://ccip.chain.link/msg/0xa57bd1a6a343b78e1b05a78b1725bfd43a418601ca3d2d24118c8a5f7a81e4ae
  5. Open the CCIP explorer and use the message ID to find your cross-chain transaction.

    Chainlink CCIP Explorer transaction details
  6. The data field is empty because only tokens are transferred. The gas limit is set to 0 because the transaction is directed to an Externally Owned Account (EOA), so no function calls are expected on the destination chain.

Code explanation

The Javascript featured in this tutorial is designed to interact with CCIP to transfer tokens. The contract code includes several code comments to clarify each step, but this section explains the key elements.

Imports

The script starts by importing the necessary modules and data. It imports ethers.js and ABIs (Application Binary Interface) from a config file for different contracts and configurations.

Handling arguments

The handleArguments function validates and parses the command line arguments passed to the script.

Main function: transferTokens

This asynchronous function, transferTokens performs the token transfer.

Initialization

The script initializes ethers providers to communicate with the blockchains in this section. It parses source and destination router addresses and blockchain selectors. A signer is created to sign transactions.

Token validity check

The script fetches a list of supported tokens for the destination chain and checks if the token you want to transfer is supported.

Building the CCIP message

A Cross-Chain Interoperability Protocol (CCIP) message is built, which will be sent to the router contract. It includes the destination account, amount, token address, and additional parameters.

Fee calculation

The script calls the router to estimate the fees for transferring tokens.

Transferring tokens

This section handles the actual transferring of tokens. It covers three cases:

  • Fees paid using the native gas token: The contract makes one approval for the transfer amount. The fees are included in the value transaction field.
  • Fees paid using an asset different from the native gas token and the token being transferred: The contracts makes two approvals. The first approval is for the transfer amount and the second approval is for the fees.
  • Fees paid using the same asset that is being transferred, but not the native gas token: The contract makes a single approval for the sum of the transfer amount and fees.

The script waits for the transaction to be validated and stores the transaction receipt.

Fetching message ID

The router's ccipSend function returns a message ID. The script simulates a call to the blockchain to fetch the message ID that the router returned.

Checking the status on the destination chain

The script polls the off-ramp contracts on the destination chain to wait for the message to be executed. If the message is executed, it returns the status. Otherwise, the message times out after 40 minutes.

What's next

Stay updated on the latest Chainlink news