Transfer Tokens Between Chains from Smart Contracts
In this tutorial, you will use Chainlink CCIP to transfer tokens from a smart contract to an account on a different blockchain. First, you will pay for the CCIP fees on the source blockchain using LINK. Then, you will use the same contract to pay CCIP fees in native gas tokens. For example, you would use ETH on Ethereum or AVAX on Avalanche.
Before you begin
-
You should understand how to write, compile, deploy, and fund a smart contract. If you need to brush up on the basics, read this tutorial, which will guide you through using the Solidity programming language, interacting with the MetaMask wallet and working within the Remix Development Environment.
-
Your account must have some AVAX and LINK tokens on Avalanche Fuji. Learn how to Acquire testnet LINK.
-
Check the CCIP Directory to confirm that the tokens you will transfer are supported for your lane. In this example, you will transfer tokens from Avalanche Fuji to Ethereum Sepolia so check the list of supported tokens here.
-
Learn how to acquire CCIP test tokens. Following this guide, you should have CCIP-BnM tokens, and CCIP-BnM should appear in the list of your tokens in MetaMask.
-
Learn how to fund your contract. This guide shows how to fund your contract in LINK, but you can use the same guide to fund your contract with any ERC20 tokens as long as they appear in the list of tokens in MetaMask.
Tutorial
In this tutorial, you will transfer CCIP-BnM tokens from a contract on Avalanche Fuji to an account on Ethereum Sepolia. First, you will pay CCIP fees in LINK, then you will pay CCIP fees in native gas. The destination account can be an EOA (Externally Owned Account) or a smart contract. Moreover, 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.
undefined
Deploy your contracts
To use this contract:
-
Compile your contract.
-
Deploy and fund your sender contract on Avalanche Fuji:
-
Open MetaMask and select the Avalanche Fuji network.
-
In Remix IDE, click Deploy & Run Transactions and select Injected Provider - MetaMask from the environment list. Remix will then interact with your MetaMask wallet to communicate with Avalanche Fuji.
-
Fill in your blockchain's router and LINK contract addresses. The router address can be found on the CCIP Directory and the LINK contract address on the LINK token contracts page. For Avalanche Fuji:
- The router address is
0xF694E193200268f9a4868e4Aa017A0118C9a8177, - The LINK contract address is
0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846.
- The router address is
-
Click the transact button. After you confirm the transaction, the contract address appears on the Deployed Contracts list. Note your contract address.
-
Open MetaMask and fund your contract with CCIP-BnM tokens. You can transfer
0.002CCIP-BnM to your contract.
-
-
Enable your contract to transfer tokens to Ethereum Sepolia:
- In Remix IDE, under Deploy & Run Transactions, open the list of functions for your smart contract deployed on Avalanche Fuji.
- Call the
allowlistDestinationChainfunction with16015286601757825753as the destination chain selector, andtrueas allowed. Each chain selector is found on the CCIP Directory.
Transfer tokens and pay in LINK
You will transfer 0.001 CCIP-BnM. The CCIP fees for using CCIP will be paid in LINK. Read this explanation for a detailed description of the code example.
-
Open MetaMask and connect to Avalanche Fuji. Fund your contract with LINK tokens. You can transfer
70LINK to your contract. Note: The LINK tokens are used to pay for CCIP fees.Note: This transaction fee is significantly higher than normal due to gas spikes on Sepolia. To run this example, you can get additional testnet LINK from faucets.chain.link or use a supported testnet other than Sepolia.
-
Transfer CCIP-BnM from Avalanche Fuji:
-
Open MetaMask and select the network Avalanche Fuji.
-
In Remix IDE, under Deploy & Run Transactions, open the list of functions for your smart contract deployed on Avalanche Fuji.
-
Fill in the arguments of the transferTokensPayLINK function:
Argument Value and Description _destinationChainSelector 16015286601757825753
CCIP Chain identifier of the destination blockchain (Ethereum Sepolia in this example). You can find each chain selector on the CCIP Directory._receiver Your account address on Ethereum Sepolia.
The destination account address. It could be a smart contract or an EOA._token 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4
The CCIP-BnM contract address at the source chain (Avalanche Fuji in this example). You can find all the addresses for each supported blockchain on the CCIP Directory._amount 1000000000000000
The token amount (0.001 CCIP-BnM). -
Click the transact button and confirm the transaction on MetaMask.
-
Once the transaction is successful, note the transaction hash. Here is an example of a transaction on Avalanche Fuji.
-
-
Open the CCIP explorer and search your cross-chain transaction using the transaction hash.
-
The CCIP transaction is completed once the status is marked as "Success". The data field is empty because you are only transferring tokens.
-
Check the receiver account on the destination chain:
-
Note the destination transaction hash from the CCIP explorer.
0x083fc1a79ffcfd617426fd71dff87ca16db2e4333e62a28cdd13d4bec0926bcbin this example. -
Open the block explorer for your destination chain. For Ethereum Sepolia, open etherscan.
-
Search the transaction hash.
-
Notice in the Tokens Transferred section that CCIP-BnM tokens have been transferred to your account (0.001 CCIP-BnM).
-
Transfer tokens and pay in native
You will transfer 0.001 CCIP-BnM. The CCIP fees for using CCIP will be paid in Avalanche Fuji's native AVAX. Read this explanation for a detailed description of the code example.
-
Open MetaMask and connect to Avalanche Fuji. Fund your contract with native gas tokens. You can transfer
0.2AVAX to your contract. Note: The native gas tokens are used to pay for CCIP fees. -
Transfer CCIP-BnM from Avalanche Fuji:
-
Open MetaMask and select the network Avalanche Fuji.
-
In Remix IDE, under Deploy & Run Transactions, open the list of transactions of your smart contract deployed on Avalanche Fuji.
-
Fill in the arguments of the transferTokensPayNative function:
Argument Value and Description _destinationChainSelector 16015286601757825753
CCIP Chain identifier of the destination blockchain (Ethereum Sepolia in this example). You can find each chain selector on the CCIP Directory._receiver Your account address on Ethereum Sepolia.
The destination account address. It could be a smart contract or an EOA._token 0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4
The CCIP-BnM contract address at the source chain (Avalanche Fuji in this example). You can find all the addresses for each supported blockchain on the CCIP Directory.._amount 1000000000000000
The token amount (0.001 CCIP-BnM). -
Click the transact button and confirm the transaction on MetaMask.
-
Once the transaction is successful, note the transaction hash. Here is an example of a transaction on Avalanche Fuji.
-
-
Open the CCIP explorer and search your cross-chain transaction using the transaction hash.
-
The CCIP transaction is completed once the status is marked as "Success". The data field is empty because you only transfer tokens. Note that CCIP fees are denominated in LINK. Even if CCIP fees are paid using native gas tokens, node operators will be paid in LINK.
-
Check the receiver account on the destination chain:
-
Note the destination transaction hash from the CCIP explorer.
0xf403d828fa377d657af67f12e99ff435974299c27ba2d57c53494d29bbbfc938in this example. -
Open the block explorer for your destination chain. For Ethereum Sepolia, open etherscan.
-
Search the transaction hash.
-
Notice in the Tokens Transferred section that CCIP-BnM tokens have been transferred to your account (0.001 CCIP-BnM).
-
Explanation
The smart contract featured in this tutorial is designed to interact with CCIP to transfer a supported token to an account on a destination chain. The contract code contains supporting comments clarifying the functions, events, and underlying logic. This section further explains initializing the contract and transferring tokens.
Initializing of the contract
When you deploy the contract, you define the router address and LINK contract address of the blockchain where you deploy the contract. The contract uses the router address to interact with the router to estimate the CCIP fees and the transmission of CCIP messages.
Transferring tokens and pay in LINK
The transferTokensPayLINK function undertakes six primary operations:
-
Call the
_buildCCIPMessageprivate function to construct a CCIP-compatible message using theEVM2AnyMessagestruct:-
The
_receiveraddress is encoded in bytes to accommodate non-EVM destination blockchains with distinct address formats. The encoding is achieved through abi.encode. -
The
datais empty because you only transfer tokens. -
The
tokenAmountsis an array, with each element comprising aEVMTokenAmountstruct that contains the token address and amount. The array contains one element where the_token(token address) and_amount(token amount) are passed by the user when calling thetransferTokensPayLINKfunction. -
The
extraArgsspecifies thegasLimitfor relaying the message to the recipient contract on the destination blockchain. In this example, thegasLimitis set to0because the contract only transfers tokens and does not expect function calls on the destination blockchain. -
The
_feeTokenAddressdesignates the token address used for CCIP fees. Here,address(linkToken)signifies payment in LINK.
-
-
Computes the fees by invoking the router's
getFeefunction. -
Ensures your contract balance in LINK is enough to cover the fees.
-
Grants the router contract permission to deduct the fees from the contract's LINK balance.
-
Grants the router contract permission to deduct the amount from the contract's CCIP-BnM balance.
-
Dispatches the CCIP message to the destination chain by executing the router's
ccipSendfunction.
Note: As a security measure, the transferTokensPayLINK function is protected by the onlyAllowlistedChain to ensure the contract owner has allowlisted a destination chain.
Transferring tokens and pay in native
The transferTokensPayNative function undertakes five primary operations:
-
Call the
_buildCCIPMessageprivate function to construct a CCIP-compatible message using theEVM2AnyMessagestruct:-
The
_receiveraddress is encoded in bytes to accommodate non-EVM destination blockchains with distinct address formats. The encoding is achieved through abi.encode. -
The
datais empty because you only transfer tokens. -
The
tokenAmountsis an array, with each element comprising anEVMTokenAmountstruct containing the token address and amount. The array contains one element where the_token(token address) and_amount(token amount) are passed by the user when calling thetransferTokensPayNativefunction. -
The
extraArgsspecifies thegasLimitfor relaying the message to the recipient contract on the destination blockchain. In this example, thegasLimitis set to0because the contract only transfers tokens and does not expect function calls on the destination blockchain. -
The
_feeTokenAddressdesignates the token address used for CCIP fees. Here,address(0)signifies payment in native gas tokens (ETH).
-
-
Computes the fees by invoking the router's
getFeefunction. -
Ensures your contract balance in native gas is enough to cover the fees.
-
Grants the router contract permission to deduct the amount from the contract's CCIP-BnM balance.
-
Dispatches the CCIP message to the destination chain by executing the router's
ccipSendfunction. Note:msg.valueis set because you pay in native gas.
Note: As a security measure, the transferTokensPayNative function is protected by the onlyAllowlistedChain, ensuring the contract owner has allowlisted a destination chain.