Token Transfers: EVM to SVM
This tutorial demonstrates how to transfer tokens from an Ethereum Virtual Machine (EVM) chain to a Solana wallet using Chainlink CCIP. You will learn how to build a CCIP message on an EVM chain, send it to the CCIP router, and verify the transfer on the destination chain.
Introduction
This tutorial covers transferring tokens from Ethereum Sepolia to a Solana wallet without any additional data payload or program execution.
What You will Build
In this tutorial, you will:
- Configure a CCIP message for token-only transfers
- Send CCIP-BnM test tokens from Ethereum Sepolia to a Solana wallet
- Pay for CCIP transaction fees using LINK tokens
- Monitor and verify your cross-chain transfer
Prerequisites
Before starting EVM to SVM tutorials, ensure you have:
Development Environment
-
Node.js v20 or higher. You can use the nvm package to install and switch between Node.js versions. Once installed, you can verify the installation by running
node -vin your terminal:node -vExample output:
$ node -v v23.11.0 -
Git for cloning the repository.
-
Yarn for installing dependencies.
-
Anchor and Solana CLI Tools: Install Anchor and Solana CLI Tools following the installation guide. This requires Rust to be installed.
Wallets
-
An Ethereum wallet with a private key. If you use MetaMask, you can follow this guide to export your private key.
-
A Solana wallet address if you are testing token transfers. If you don't have one, you can generate a new Solana keypair file using the Solana CLI:
solana-keygen new --outfile ~/.config/solana/id.jsonGet your wallet address by running:
solana addressExample output:
$ solana address EPUjBP3Xf76K1VKsDSc6GupBWE8uykNksCLJgXZn87CB
RPC URLs
Get an Ethereum RPC URL. You can sign up for a personal endpoint from Alchemy, Infura, or another node provider service.
Setup Instructions
-
Clone the repository:
git clone https://github.com/smartcontractkit/solana-starter-kit.git && cd solana-starter-kit -
Install dependencies:
yarn install -
Create a
.envfile in the project root based on the provided example:EVM_PRIVATE_KEY=your_private_key_here EVM_RPC_URL=your_rpc_url_here -
Replace the
EVM_PRIVATE_KEYandEVM_RPC_URLvalues with your own Ethereum private key and RPC URL that you obtained in the previous steps.
Tokens for Testing
You'll need the following tokens on Ethereum Sepolia testnet to complete the tutorials:
Required Tokens
| Token | Purpose | How to Obtain |
|---|---|---|
| ETH | Transaction gas fees | Chainlink Faucet or alternative sources such as the Google Cloud Faucet |
| LINK | CCIP fees | Chainlink Faucet |
| CCIP-BnM | Token transfer examples | Use the command below |
Getting CCIP-BnM Tokens
Run the following command in your terminal to get 1 (18 decimals) CCIP-BnM token:
yarn evm:token:drip
You should see output similar to this:
$ yarn evm:token:drip
...
[drip-tokens] [INFO] [1/1] Executing operation...
[drip-tokens] [INFO] Dripping tokens to 0x9d087fC03ae39b088326b67fA3C788236645b717
[drip-tokens] [INFO] ✓ Operation 1/1 successful (Block: 8235790, Gas: 33845)
[drip-tokens] [INFO] Batch operation completed: 1/1 successful
[drip-tokens] [INFO] Final CCIP-BnM balance: 25.129
[drip-tokens] [INFO] Total CCIP-BnM gained: 1.0
[drip-tokens] [INFO]
==== Results ====
[drip-tokens] [INFO] Operations completed: 1/1
[drip-tokens] [INFO] Initial Balance: 24.129 CCIP-BnM
[drip-tokens] [INFO] Final Balance: 25.129 CCIP-BnM
[drip-tokens] [INFO] Gained: 1.0 CCIP-BnM
[drip-tokens] [INFO]
🎉 Drip operations completed!
Understanding Token Transfers to SVM
This tutorial focuses on token-only transfers from EVM chains to Solana wallets. For detailed information about CCIP message structure and parameters, refer to the guide on building CCIP messages from EVM to SVM.
Key points specific to token-only transfers:
- No Program Execution: Tokens are transferred directly to a wallet without program execution
- Mandatory Settings:
computeUnitsMUST be set to 0receiverfield should be set to the default PublicKey (11111111111111111111111111111111)- The actual recipient is specified in the
tokenReceiverfield datafield should be empty (0x)allowOutOfOrderExecutionmust betrue
For more details on the CCIP message structure, extraArgs, and how the SVM account model works, refer to the guide on building CCIP messages from EVM to SVM.
Implementing Token Transfers
In this section, you'll implement a token transfer from Ethereum Sepolia to Solana Devnet using the example script located at ccip-scripts/evm/router/1_token-transfer.ts in the starter kit.
Token Transfer Configuration
The most important part of implementing a token transfer is configuring the CCIP message correctly. Here's the key configuration from the script:
const MESSAGE_CONFIG = {
tokenAmounts: [
{
address: config.tokenAddress, // BnM token on Ethereum Sepolia
amount: "1000000000000000", // 0.001 tokens with 18 decimals
},
],
feeToken: FeeTokenType.LINK, // LINK is used for CCIP fees
data: "0x", // Empty for token transfers
extraArgs: {
computeUnits: 0, // No execution on destination - MUST be 0
allowOutOfOrderExecution: true, // MUST be true for SVM destinations
accountIsWritableBitmap: BigInt(0), // No accounts needed
tokenReceiver: "EPUjBP3Xf76K1VKsDSc6GupBWE8uykNksCLJgXZn87CB", // Recipient's Solana wallet - Change this to your own wallet address
accounts: [], // No accounts needed for token-only transfers
},
receiver: PublicKey.default.toString(), // Use default PublicKey for token transfers
}
How the Script Works
The token transfer script handles the complete cross-chain transfer process without requiring you to write any code. The script:
- Configures the CCIP message with the parameters shown above
- Calculates the CCIP fees required for the transfer
- Approves the router to spend your CCIP-BnM tokens (the tokens being transferred)
- Approves the router to spend your LINK tokens (for paying CCIP fees)
- Calls the CCIP router contract to execute the cross-chain transfer
- Returns the transaction details and message ID for tracking
For those interested in implementation details, the script is well-commented and follows a straightforward flow. You can review the source code at ccip-scripts/evm/router/1_token-transfer.ts in the starter kit repository.
Running the Token Transfer
Prerequisites Check
- Ensure you've completed the setup steps outlined earlier
- Make sure your
.envfile contains the required values
Execute the Script
Run the following command after modifying the script directly:
yarn evm:transfer
Understanding the Output
When the script executes successfully, you'll see output similar to this:
==== Environment Information ====
chainId ethereum-sepolia
[token-transfer] [INFO] Router Address: 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59
chainId ethereum-sepolia
[ccip-messenger] [INFO] Creating client for chain: Ethereum Sepolia (ethereum-sepolia)
[token-transfer] [INFO] Wallet Address: 0x9d087fC03ae39b088326b67fA3C788236645b717
[token-transfer] [INFO] Native Balance: 600.023652720198652224 ETH
[token-transfer] [INFO] Token: CCIP-BnM (0x316496C5dA67D052235B9952bc42db498d6c520b)
[token-transfer] [INFO] Token Balance: 25.129 CCIP-BnM
[token-transfer] [INFO] Transfer Amount: 0.1 CCIP-BnM
[token-transfer] [INFO] Creating CCIP message request
[token-transfer] [INFO] Using fee token: 0x779877A7B0D9E8603169DdbD7836e478b4624789
[token-transfer] [INFO]
==== Transfer Summary ====
[token-transfer] [INFO] Source Chain: Ethereum Sepolia
[token-transfer] [INFO] Destination Chain: Solana Devnet (16423721717087811551)
[token-transfer] [INFO] Sender: 0x9d087fC03ae39b088326b67fA3C788236645b717
[token-transfer] [INFO] Receiver: 11111111111111111111111111111111
[token-transfer] [INFO] Token Receiver: EPUjBP3Xf76K1VKsDSc6GupBWE8uykNksCLJgXZn87CB
[token-transfer] [INFO] Fee Token: 0x779877A7B0D9E8603169DdbD7836e478b4624789
[token-transfer] [INFO]
Token Transfers:
[token-transfer] [INFO] 1. 0.1 CCIP-BnM (0x316496C5dA67D052235B9952bc42db498d6c520b)
[token-transfer] [INFO]
Extra Args: Solana-specific, 458 bytes
[token-transfer] [INFO]
Sending CCIP message...
[ccip-messenger] [INFO] Estimated fee: 27526004358896610
[ccip-messenger] [INFO] Approving 0.033031205230675932 LINK for CCIP Router
[ccip-messenger] [INFO] This token is being used as the fee token with a 20% buffer included
[ccip-messenger] [INFO] Approving 33031205230675932 tokens for 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59
[ccip-messenger] [INFO] LINK approved for CCIP Router
[ccip-messenger] [INFO] ✅ Verified on-chain allowance for LINK: 0.033031205230675932 (required: 0.033031205230675932)
[ccip-messenger] [INFO] Approving 0.1 CCIP-BnM for CCIP Router
[ccip-messenger] [INFO] Approving 100000000000000000 tokens for 0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59
[ccip-messenger] [INFO] CCIP-BnM approved for CCIP Router
[ccip-messenger] [INFO] ✅ Verified on-chain allowance for CCIP-BnM: 0.1 (required: 0.1)
[ccip-messenger] [INFO] Sending CCIP message...
[ccip-messenger] [INFO] Sending CCIP message...
[ccip-messenger] [INFO] Transaction sent: 0x0c90bf80c1f7ff161adbe0846df5026bf78944a3aa46019d97a05bda2b94f014
[ccip-messenger] [INFO] Transaction sent: 0x0c90bf80c1f7ff161adbe0846df5026bf78944a3aa46019d97a05bda2b94f014
[ccip-messenger] [INFO] Message ID: 0x29ec89a867da8ebde045f30bddd663396a4abc0e8374ac2d8083612805fa7fb4
[token-transfer] [INFO]
==== Transfer Results ====
[token-transfer] [INFO] Transaction Hash: 0x0c90bf80c1f7ff161adbe0846df5026bf78944a3aa46019d97a05bda2b94f014
[token-transfer] [INFO] Transaction URL: https://sepolia.etherscan.io/tx/0x0c90bf80c1f7ff161adbe0846df5026bf78944a3aa46019d97a05bda2b94f014
[token-transfer] [INFO] Message ID: 0x29ec89a867da8ebde045f30bddd663396a4abc0e8374ac2d8083612805fa7fb4
[token-transfer] [INFO] CCIP Explorer: https://ccip.chain.link/msg/0x29ec89a867da8ebde045f30bddd663396a4abc0e8374ac2d8083612805fa7fb4
[token-transfer] [INFO] Destination Chain Selector: 16423721717087811551
[token-transfer] [INFO] Sequence Number: 12
[token-transfer] [INFO]
Message tracking for Solana destinations:
[token-transfer] [INFO] Please check the CCIP Explorer link to monitor your message status.
[token-transfer] [INFO]
✅ Transaction completed on the source chain
The output provides important information to track your transfer:
- Transaction hash and URL for viewing on Etherscan
- Message ID for tracking in the CCIP Explorer. In this example, the message ID is
0x29ec89a867da8ebde045f30bddd663396a4abc0e8374ac2d8083612805fa7fb4.
Behind the Scenes: Message Encoding for Token Transfers
While this tutorial focuses on configuration and execution, it's important to understand that the script uses several utility functions to properly encode CCIP messages. If you're implementing your own token transfer solution outside this starter kit, you will need to handle these encoding steps yourself.
Key utilities used by the script include:
createCCIPMessageRequest(): Creates a properly formatted CCIP message with all required fieldsvalidateTokenAmounts(): Checks token balances and transaction validitygetTokenDetails(): Fetches token metadata for the proper display of amountssetupClientContext(): Sets up connections to the blockchain with the right configuration
These utilities handle critical tasks such as:
- Converting token amounts between decimal and raw formats
- Properly encoding SVM wallet addresses for token receipt
- Setting up the required ExtraArgs structure for SVM destinations
- Managing token approvals and fee calculations
If you're building your own implementation, we recommend examining the source code in:
ccip-scripts/evm/utils/message-utils.tsccip-scripts/evm/utils/setup-client.tsccip-lib/evm/core/client/CCIPMessenger.ts
Understanding these utilities will help you implement your own cross-chain token transfers correctly.
Verification and Monitoring
After sending your token transfer, you'll need to:
-
Track progress with Message ID: Use the CCIP Explorer link provided in the output to track your message status across chains. You must wait for the transfer to be successful before proceeding to verification.
-
Solana Explorer: Once the transfer is successful, verify token receipt on the Solana Explorer:
- Visit Solana Explorer
- Search for your Solana wallet address
- Look for the transferred token in the "Tokens" section