Token Transfers: Aptos to EVM
This tutorial demonstrates how to transfer tokens from the Aptos blockchain to an Ethereum Virtual Machine (EVM) chain using Chainlink CCIP. You will learn how to build a CCIP message on Aptos, send it using a script, and verify the transfer on the destination chain.
Introduction
This tutorial covers transferring tokens from Aptos Testnet to Ethereum Sepolia without any additional data payload or program execution. When you transfer tokens using CCIP:
- Tokens are burned or locked in pools on the source chain (Aptos).
- Equivalent tokens are minted or released from the destination pool (Ethereum Sepolia).
- The process is managed by CCIP.
What You Will Build
In this tutorial, you will:
- Use a script to configure a CCIP message for a token-only transfer.
- Send CCIP-BnM test tokens from Aptos Testnet to an Ethereum Sepolia address.
- Pay for CCIP transaction fees using either native APT or LINK tokens.
- Monitor and verify your cross-chain transfer.
Understanding Token Transfers from Aptos to EVM
This tutorial focuses on token-only transfers from your wallet on Aptos Testnet to an address on Ethereum Sepolia. Key points specific to token-only transfers:
- Burn and Mint: In this tutorial, CCIP-BnM tokens are burned on Aptos Testnet, and equivalent CCIP-BnM tokens are minted on Ethereum Sepolia.
- Fee Payment: Transaction fees are paid on Aptos using either native APT or LINK tokens.
- Message Construction: A script constructs and sends the transaction payload by calling the
ccip_send
entry function on the Aptos CCIP Router module.
CCIP Message Configuration
The most important part of implementing a token transfer is correctly configuring the arguments for the ccip_send
function. The scripts/aptos2evm/ccipSendTokenRouter.ts
script handles this. Here's a look at how the arguments are prepared within the script:
// Inside the script, the transaction payload is constructed like this:
const transaction = await aptos.transaction.build.simple({
sender: account.accountAddress,
data: {
function: `${ccipRouterModuleAddr}::router::ccip_send`, // CCIP Router address and function
functionArguments: [
destChainSelector, // Chain selector for Ethereum Sepolia
MoveVector.U8(Hex.hexInputToUint8Array(paddedReceiverArray)), // Your EVM receiver address
MoveVector.U8([]), // Empty data for token-only transfer
[ccipBnMTokenAddr], // The BnM token address on Aptos
MoveVector.U64([TOKEN_AMOUNT_TO_SEND]), // The amount of BnM tokens
[TOKEN_STORE_ADDR], // The token store, e.g., '0x0' for the primary store
feeToken, // The fee token address (APT or LINK)
feeTokenStore, // The fee token store address
extraArgs, // Encoded extra arguments with gas limit
],
},
})
Critical Configuration Settings
When setting up your CCIP message for a token transfer, these parameters are crucial:
Required Parameters
data
: MUST be an empty vector ([]
) for token-only transfers.extraArgs
: Must be properly encoded based on your destination chain type.
EVM Destination Configuration
For EVM destinations (covered in this tutorial):
- Set
gasLimit
to0
- Set
allowOutOfOrderExecution
totrue
- The script handles encoding by calling
encodeGenericExtraArgsV2(0, true)
SVM Destination Configuration
The ccipSendTokenRouter.ts
script in this tutorial is designed specifically for EVM destinations and will not work for SVM destinations without modifications.
For SVM destinations such as Solana, you will need to adapt the script's extraArgs
encoding logic.
Refer to the following resources for more information:
How the Script Works
The aptos2evm/ccipSendTokenRouter.ts
script handles the interaction with the CCIP Router module on your behalf. Here's what happens behind the scenes:
- Context Initialization: The script initializes the Aptos client and loads your private key from the
.env
file. - Argument Parsing: It reads your command-line arguments (
--destChain
,--feeToken
,--amount
,--evmReceiver
) to determine the destination, fee payment method, amount to send, and EVM receiver address. - Parameter Preparation: It formats the EVM receiver address and token amount correctly for the Aptos blockchain.
- Transaction Building: It constructs the
ccip_send
transaction with all necessary arguments, including the destination selector, receiver, token details, and encoded extra args. - Simulation and Execution:
- It simulates the transaction to prevent failures.
- It signs and sends the transaction to the Aptos Testnet.
- It waits for the transaction to be confirmed and prints the transaction hash.
Running the Token Transfer
Prerequisites Check
Before running the script:
-
Ensure you've completed the setup steps outlined in the prerequisites.
-
Make sure your
.env
file contains yourPRIVATE_KEY_HEX
(of your wallet on Aptos Testnet from which you're sending CCIP-BnM tokens) andETHEREUM_SEPOLIA_RPC_URL
. -
Verify you have sufficient APT and CCIP-BnM token balances in your Aptos wallet.
Execute the Script
Run the following command from your terminal to transfer CCIP-BnM tokens from Aptos Testnet to Ethereum Sepolia, paying the fee in native APT. You can change the value for the --amount
flag to send a different number of tokens.
npx ts-node scripts/aptos2evm/ccipSendTokenRouter.ts --destChain sepolia --feeToken native --amount 0.001 --evmReceiver <YOUR_EVM_WALLET_ADDRESS>
To pay with LINK instead, change the feeToken
argument:
npx ts-node scripts/aptos2evm/ccipSendTokenRouter.ts --destChain sepolia --feeToken link --amount 0.001 --evmReceiver <YOUR_EVM_WALLET_ADDRESS>
Understanding the Output
When the script executes successfully, you'll see the logs similar to the following:
✅ Transaction successful: https://explorer.aptoslabs.com/txn/0xa5ca115b1f13bb7ace83218f81b60510df29074bfee1f64ce17b00b5737391bc?network=testnet
🆔 CCIP Message ID: 0x657994d25cb3e50d2ae510f0ac9ea7fff845f57c2e901e3d4ceefb2401408daf
🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/0x657994d25cb3e50d2ae510f0ac9ea7fff845f57c2e901e3d4ceefb2401408daf
- The transaction hash is displayed along with the Aptos Explorer URL.
- The output provides the unique
CCIP Message ID
, which is essential for tracking your transfer. - The CCIP Explorer URL allows you to monitor the message status across chains.
Verification and Monitoring
After sending your token transfer, you can verify its arrival on Ethereum Sepolia in the following ways:
Check Message Execution
Use the CCIP Explorer to check the message status
Use the CCIP Explorer link provided in the transaction output to track your message status across chains. The explorer gives an overview of the entire cross-chain transaction life cycle.
🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/<YOUR_CCIP_MESSAGE_ID>
Programmatically check the message status
After you receive a CCIP Message ID, you can use the aptos2evm/checkMsgExecutionStateOnEvm.ts
script to see if the message was successfully delivered. This script first fetches the CCIPMessageSent
event on Aptos to get the unique Message ID, then polls the destination chain (Ethereum Sepolia) for the corresponding ExecutionStateChanged
event.
After 1-2 minutes, run the script using the CCIP Message ID you received from the previous step.
Command:
npx ts-node scripts/aptos2evm/checkMsgExecutionStateOnEvm.ts --msgId <YOUR_CCIP_MESSAGE_ID> --destChain sepolia
Replace <YOUR_CCIP_MESSAGE_ID>
with the actual CCIP Message ID from the log output.
Output: When the message has been successfully delivered, you will see the following output:
Execution state for CCIP message 0x657994d25cb3e50d2ae510f0ac9ea7fff845f57c2e901e3d4ceefb2401408daf is SUCCESS
Verify Token Balance
Once the script confirms a SUCCESS
state, you can perform a final verification on a block explorer.
- Visit the Sepolia Etherscan Explorer.
- Search for your EVM wallet address.
- Under the "ERC20 Token Txns" tab, you should see the incoming transfer of CCIP-BnM tokens.