Cross-Chain Token Setup: BurnMint with Direct Mint Authority Transfer

This comprehensive tutorial demonstrates how to create and configure cross-chain tokens using Chainlink's Cross-Chain Interoperability Protocol (CCIP) between Solana Devnet and Ethereum Sepolia. You will implement the direct mint authority transfer approach within Path A from the CCIP Cross-Chain Token Integration Guide.

What You Will Build

This tutorial implements the direct mint authority transfer variant of Path A from the CCIP Cross-Chain Token Integration Guide. This approach is designed for development and testing environments where you transfer complete mint authority to the Pool Signer PDA for simplified setup.

Cross-Chain Token Architecture

This tutorial implements the Burn and Mint token handling mechanism between Solana Devnet and Ethereum Sepolia. You'll deploy two BurnMint pools (one on each chain) that work together to maintain consistent token supply across chains.

How Burn and Mint Works:

  1. Source Chain: Burns tokens from sender's account
  2. CCIP Protocol: Transmits message cross-chain
  3. Destination Chain: Mints equivalent tokens to the receiver

Component Overview

ComponentImplementationAuthority Model
Ethereum SepoliaERC20 token with CCIP BurnMint poolMultiple minters: EOA + Pool
Solana DevnetSPL token with CCIP BurnMint poolSingle mint authority: Pool Signer PDA

Authority Model Differences

  • Ethereum: Your EOA + Pool both have mint privileges (multiple minters supported)
  • Solana: Pool Signer PDA has exclusive mint authority (single authority constraint)

For complete details on token handling mechanisms, see Token Handling Mechanisms.

Prerequisites

This tutorial uses a two-terminal workflow across two repositories. Install the system tools below, clone both repos, then complete environment setup before starting Phase 1.

System Requirements

  • Node.js v22 or higher: Verify with node -v (nvm recommended)
  • pnpm: Required for the BS58 generator (npm install -g pnpm)
  • Solana CLI: Installation guide (includes spl-token)
  • Git: For cloning repositories
  • CCIP CLI: For cross-chain transfer testing in the final phase

Install the CCIP CLI globally:

npm install -g @chainlink/ccip-cli
ccip-cli --help

See the CCIP CLI documentation for RPC and wallet configuration.

Tutorial Workflow

TerminalRepositoryPurposeCommands
Terminal 1CCIP Solana BS58 GeneratorSolana setup and configurationpnpm bs58
Terminal 2Smart Contract Examples (Hardhat)EVM deploy and configurationnpx hardhat
EitherGlobal @chainlink/ccip-cliCross-chain transfer testing (final)ccip-cli send

Terminal 1: CCIP Solana BS58 Generator

Clone and install (skip git clone if you already have the repo):

git clone https://github.com/smartcontractkit/ccip-solana-bs58-generator.git
cd ccip-solana-bs58-generator
pnpm install

Configure Solana CLI for devnet:

First, check whether your environment is already set up:

solana config get
solana address
solana balance

If the RPC URL is already https://api.devnet.solana.com, your keypair path is correct, and you have sufficient SOL, skip to Terminal 2 setup below.

Otherwise, run only the steps you need:

# Set devnet (skip if config get already shows devnet)
solana config set --url https://api.devnet.solana.com

# Point to your keypair (skip if config get already shows the path you want)
solana config set --keypair ~/.config/solana/id.json

# Create a keypair only if the file does not exist yet
solana-keygen new --outfile ~/.config/solana/id.json

# Fund your wallet if balance is low
solana airdrop 2
solana balance

Terminal 2: Smart Contract Examples (Hardhat)

Clone, install, and compile (skip git clone if you already have the repo):

git clone https://github.com/smartcontractkit/smart-contract-examples.git
cd smart-contract-examples/ccip/cct/hardhat
npm install
npm run compile

Set up encrypted environment variables:

# Required at the start of each session
npx env-enc set-pw

# Verify existing variables (skip npx env-enc set if all required vars are already configured)
npx env-enc view

# Add or update variables only if missing
npx env-enc set

Required variables for Ethereum Sepolia:

Fund your EVM wallet:

  • Acquire ETH on Ethereum Sepolia for transaction gas and CCIP fees (Chainlink faucet or Google Cloud Faucet)
  • LINK is optional — use --fee-token LINK on EVM → Solana sends if you prefer paying CCIP fees in LINK

Cross-Chain Transfers (CCIP CLI)

The final tutorial phase uses globally installed ccip-cli (not the BS58 generator):

  • Solana → EVM: Run from Terminal 1 with --wallet ~/.config/solana/id.json
  • EVM → Solana: Run from Terminal 2 (Hardhat directory). Prefer --wallet hardhat:<name> (Hardhat keystore) so the signing key stays encrypted. Hardhat tasks load env-enc automatically; ccip-cli does not. As an alternative, view the same key with npx env-enc view and export PRIVATE_KEY manually for the send command.

RPC endpoints (required for source and destination chains). You can provide them any of these ways — see CCIP CLI configuration:

  • Command line: pass --rpc on each command (repeat for multiple networks), or --rpcs with comma-separated URLs
  • Environment variables: export RPC_* variables (e.g., RPC_SEPOLIA, RPC_SOLANA_DEVNET) or the tutorial's SOLANA_DEVNET_RPC and ETHEREUM_SEPOLIA_RPC_URL exports
  • File: create a .env file in the directory where you run ccip-cli (default --rpcs-file, one URL per line)

See the tutorial's Configure CCIP CLI section for the recommended --rpc examples.

Environment Variables

Variables use prefixes to prevent confusion across repositories and tools:

PrefixUsageExamples
ETH_*Ethereum addressesETH_TOKEN_ADDRESS, ETH_POOL_ADDRESS
SOL_*Solana addressesSOL_TOKEN_MINT, SOL_POOL_ADDRESS, SOL_WALLET_ADDRESS
SOL_CCIP_*Solana CCIP program IDsSOL_CCIP_POOL_PROGRAM, SOL_CCIP_ROUTER, SOL_CCIP_FEE_QUOTER_PROGRAM

Tutorial Approach

This tutorial provides step-by-step instructions with detailed explanations of what each command does and why. You'll work primarily in Terminal 1 (CCIP Solana BS58 Generator) with occasional switches to Terminal 2 (EVM).

Environment Variable Management: This tutorial uses phase-based variable files (e.g., ~/.phase1_vars, ~/.ccip_complete_vars) to eliminate manual variable re-entry when switching between terminals. Each phase saves its variables to files that subsequent phases can load automatically.

For deeper technical implementation details, refer to:

EOA execution: In Terminal 1, append --execute to each pnpm bs58 command so your local Solana wallet signs and sends transactions directly. Set --authority to your wallet address ($SOL_WALLET_ADDRESS). Read-only commands (get-state, get-chain-config, derive-accounts) do not use --execute.

Phase 1: Ethereum Sepolia Token Setup

In this phase, you'll deploy and configure your ERC20 token with CCIP BurnMint pools on Ethereum Sepolia.

Step 1: Deploy ERC20 Token

Deploy a burnable and mintable ERC20 token that will serve as your cross-chain asset:

# Deploy BurnMint ERC20 token with verification

npx hardhat deployToken \
  --name "BnM Cross-Chain Token" \
  --symbol "BnMAEM" \
  --decimals 18 \
  --verifycontract \
  --network ethereumSepolia

Set the token address variable:

# REPLACE with your actual deployed token address
export ETH_TOKEN_ADDRESS="<INSERT_YOUR_TOKEN_ADDRESS_HERE>"

Verify it's set correctly:

echo "Ethereum Token: $ETH_TOKEN_ADDRESS"

Step 2: Deploy BurnMint Token Pool

Deploy a CCIP token pool that will manage burning and minting operations:

# Deploy BurnMint pool for your token
npx hardhat deployTokenPool \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --localtokendecimals 18 \
  --pooltype burnMint \
  --verifycontract \
  --network ethereumSepolia

Set the pool address variable:

# REPLACE with your actual deployed pool address
export ETH_POOL_ADDRESS="<INSERT_YOUR_POOL_ADDRESS_HERE>"

Verify both addresses are set:

echo "Ethereum Token: $ETH_TOKEN_ADDRESS"
echo "Ethereum Pool: $ETH_POOL_ADDRESS"

Step 3: Mint Initial Token Supply

Mint tokens to your wallet for testing cross-chain transfers:

# Mint 1000 tokens (with 18 decimals)
npx hardhat mintTokens \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --amount 1000000000000000000000 \
  --network ethereumSepolia

Step 4: Register as CCIP Administrator

Register yourself as the CCIP administrator for your token. This two-step process (claim + accept) ensures secure ownership transfer:

Claim Admin Role

# Claim admin role for your token
npx hardhat claimAdmin \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --network ethereumSepolia

Accept Admin Role

# Accept the admin role to finalize
npx hardhat acceptAdminRole \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --network ethereumSepolia

Step 5: Save Phase 1 Variables

Save your EVM configuration for use in later phases:

# Save Phase 1 variables for cross-terminal use
cat > ~/.phase1_vars << EOF
export ETH_TOKEN_ADDRESS="$ETH_TOKEN_ADDRESS"
export ETH_POOL_ADDRESS="$ETH_POOL_ADDRESS"
EOF

echo "=== Phase 1 Complete - EVM Setup ==="
echo "✅ ETH Token: $ETH_TOKEN_ADDRESS"
echo "✅ ETH Pool: $ETH_POOL_ADDRESS"
echo "✅ Variables saved to ~/.phase1_vars"

Phase 2: Solana Devnet Token Setup

Now we'll create and configure the Solana side of your cross-chain token system.

Step 1: Prepare Environment

Load EVM variables from Phase 1 and set Solana CCIP constants:

# Load Phase 1 EVM variables
source ~/.phase1_vars

# Your Solana wallet (used as --authority for all EOA commands)
export SOL_WALLET_ADDRESS=$(solana address)

# CCIP program IDs on Solana Devnet (DO NOT CHANGE THESE)
export SOL_CCIP_POOL_PROGRAM="41FGToCmdaWa1dgZLKFAjvmx6e6AjVTX7SVRibvsMGVB"
export SOL_CCIP_ROUTER="Ccip842gzYHhvdDkSyi2YVCoAWPbYJoApMFzSxQroE9C"
export SOL_CCIP_FEE_QUOTER_PROGRAM="FeeQPGkKDeRV1MgoYfMH6L8o3KeuYjwUZrgn4LRKfjHi"

# Ethereum Sepolia chain selector (used in remote chain config)
export ETH_SEPOLIA_CHAIN_SELECTOR="16015286601757825753"

echo "✅ Wallet: $SOL_WALLET_ADDRESS"
echo "✅ Pool Program: $SOL_CCIP_POOL_PROGRAM"
echo "✅ Router Program: $SOL_CCIP_ROUTER"

Step 2: Create SPL Token

Create an SPL token with metadata and initial supply using direct EOA execution:

# Create SPL token with Metaplex metadata and initial supply
pnpm bs58 --env devnet --execute spl-token \
  --instruction create-mint \
  --authority $SOL_WALLET_ADDRESS \
  --decimals 9 \
  --with-metaplex true \
  --name "AEM" \
  --symbol "CCIP-AEM" \
  --uri "https://cyan-pleasant-anteater-613.mypinata.cloud/ipfs/bafkreieirlwjqbtzniqsgcjebzexlcspcmvd4woh3ajvf2p4fuivkenw6i" \
  --initial-supply 1000000000000 \
  --recipient $SOL_WALLET_ADDRESS

Set the token mint variable:

# Copy the mint address from the command output above
export SOL_TOKEN_MINT="<INSERT_YOUR_MINT_ADDRESS_HERE>"

Verify the variables:

echo "Solana Token Mint: $SOL_TOKEN_MINT"
echo "Solana pool program: $SOL_CCIP_POOL_PROGRAM"

Step 3: Initialize CCIP Token Pool

Initialize the token pool configuration on Solana:

# Initialize CCIP pool for your token
pnpm bs58 --env devnet --execute burnmint-token-pool \
  --instruction initialize-pool \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_WALLET_ADDRESS

Save the Pool Signer PDA and Pool Config PDA. Derive them from the onchain pool configuration:

# Derive Pool Signer PDA and Pool State PDA
pnpm bs58 --env devnet utils \
  --instruction derive-accounts \
  --program-type burnmint-token-pool \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT

Copy the Pool Signer PDA and Pool State PDA addresses from the output, then export them:

export SOL_POOL_SIGNER_PDA="<INSERT_YOUR_POOL_SIGNER_PDA_HERE>"
export SOL_POOL_CONFIG_PDA="<INSERT_YOUR_POOL_STATE_PDA_HERE>"

Verify the variables:

echo "Pool Signer PDA: $SOL_POOL_SIGNER_PDA"
echo "Pool Config PDA: $SOL_POOL_CONFIG_PDA"

Step 4: Create Pool Token Account

In this step, you will create an Associated Token Account (ATA) for the Pool Signer PDA. This ATA is required for the pool to hold and manage tokens during cross-chain transfer operations.

# Create token account owned by the Pool Signer PDA
# Your wallet (--fee-payer) pays rent; the Pool Signer PDA is the account owner
spl-token create-account $SOL_TOKEN_MINT \
  --owner $SOL_POOL_SIGNER_PDA \
  --fee-payer $HOME/.config/solana/id.json

Step 5: Register as CCIP Administrator

Propose Admin

In this step, you will propose yourself as the CCIP administrator for the Solana token.

# Propose yourself as administrator
pnpm bs58 --env devnet --execute router \
  --instruction owner-propose-administrator \
  --program-id $SOL_CCIP_ROUTER \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_WALLET_ADDRESS \
  --token-admin-registry-admin $SOL_WALLET_ADDRESS

Accept Admin

In this step, you will accept the administrator role for the Solana token. This process establishes your control over the token's CCIP configuration on Solana.

# Accept the administrator role
pnpm bs58 --env devnet --execute router \
  --instruction accept-admin-role \
  --program-id $SOL_CCIP_ROUTER \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_WALLET_ADDRESS

Step 6: Transfer Mint Authority to Pool Signer PDA

# Transfer mint authority to Pool Signer PDA
spl-token authorize $SOL_TOKEN_MINT mint $SOL_POOL_SIGNER_PDA

Verify the authority transfer:

spl-token display $SOL_TOKEN_MINT

Step 7: Save Phase 2 Variables

Save your Solana configuration for use in cross-chain setup:

# Save Phase 2 variables for cross-terminal use
cat > ~/.phase2_vars << EOF
export SOL_TOKEN_MINT="$SOL_TOKEN_MINT"
export SOL_POOL_SIGNER_PDA="$SOL_POOL_SIGNER_PDA"
export SOL_POOL_CONFIG_PDA="$SOL_POOL_CONFIG_PDA"
export SOL_CCIP_POOL_PROGRAM="$SOL_CCIP_POOL_PROGRAM"
export SOL_CCIP_ROUTER="$SOL_CCIP_ROUTER"
export SOL_CCIP_FEE_QUOTER_PROGRAM="$SOL_CCIP_FEE_QUOTER_PROGRAM"
export ETH_SEPOLIA_CHAIN_SELECTOR="$ETH_SEPOLIA_CHAIN_SELECTOR"
export SOL_WALLET_ADDRESS="$SOL_WALLET_ADDRESS"
EOF

echo "=== Phase 2 Complete - Solana Setup ==="
echo "✅ SOL Token: $SOL_TOKEN_MINT"
echo "✅ Pool Signer PDA: $SOL_POOL_SIGNER_PDA"
echo "✅ Pool Config PDA: $SOL_POOL_CONFIG_PDA"
echo "✅ Variables saved to ~/.phase2_vars"

Phase 3: Cross-Chain Configuration

Configure bidirectional connectivity between your token pools on both chains.

Step 1: Configure Solana Pool

Stay in Terminal 1 (CCIP Solana BS58 Generator)

Load the Ethereum addresses from Phase 1:

# Load Phase 1 variables
source ~/.phase1_vars

# Verify all variables are available
echo "✅ ETH Token: $ETH_TOKEN_ADDRESS"
echo "✅ ETH Pool: $ETH_POOL_ADDRESS"
echo "✅ SOL Token: $SOL_TOKEN_MINT"
echo "✅ SOL Pool Program: $SOL_CCIP_POOL_PROGRAM"

Configure Remote Chain

In this step, you will initialize the configuration for Ethereum Sepolia as a remote chain. This creates the basic chain configuration with token information but without pool addresses (those will be added in the next step).

# Initialize remote chain configuration
pnpm bs58 --env devnet --execute burnmint-token-pool \
  --instruction init-chain-remote-config \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_WALLET_ADDRESS \
  --remote-chain-selector $ETH_SEPOLIA_CHAIN_SELECTOR \
  --pool-addresses '[]' \
  --token-address $ETH_TOKEN_ADDRESS \
  --decimals 9

Add Remote Pool Address

In this step, you will use update the previously created chain configuration with the Ethereum pool address. This completes the configuration by telling the Solana pool which Ethereum pool it should interact with for cross-chain transfers.

# Add Ethereum pool address to configuration
pnpm bs58 --env devnet --execute burnmint-token-pool \
  --instruction edit-chain-remote-config \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_WALLET_ADDRESS \
  --remote-chain-selector $ETH_SEPOLIA_CHAIN_SELECTOR \
  --pool-addresses "[\"$ETH_POOL_ADDRESS\"]" \
  --token-address $ETH_TOKEN_ADDRESS \
  --decimals 9

Step 2: Configure Ethereum Pool

Switch to Terminal 2 (Smart Contract Examples)

pwd
# Should output: ../smart-contract-examples/ccip/cct/hardhat

Load Variables from Previous Phases

Load all variables needed for EVM cross-chain configuration:

# Load Phase 1 and Phase 2 variables
source ~/.phase1_vars
source ~/.phase2_vars

# Verify all variables are loaded
echo "✅ ETH Token: $ETH_TOKEN_ADDRESS"
echo "✅ ETH Pool: $ETH_POOL_ADDRESS"
echo "✅ SOL Token: $SOL_TOKEN_MINT"
echo "✅ SOL Pool Config PDA: $SOL_POOL_CONFIG_PDA"
echo "✅ Pool Program: $SOL_CCIP_POOL_PROGRAM"

Configure Remote Chain

In this step, you will configure the Ethereum pool to recognize the Solana token and pool. This tells the Ethereum pool which Solana pool (via its Pool Config PDA) and token it should interact with for cross-chain transfers.

# Configure Ethereum pool to recognize Solana chain
npx hardhat applyChainUpdates \
  --pooladdress $ETH_POOL_ADDRESS \
  --remotechain solanaDevnet \
  --remotepooladdresses $SOL_POOL_CONFIG_PDA \
  --remotetokenaddress $SOL_TOKEN_MINT \
  --network ethereumSepolia

Phase 4: Pool Registration

Register your token pools with their respective Token Admin Registries to enable cross-chain operations.

Step 1: Register Ethereum Pool

Stay in Terminal 2 (Smart Contract Examples)

pwd
# Should output: ../smart-contract-examples/ccip/cct/hardhat

Register the BurnMint token pool with your token in the TokenAdminRegistry:

# Register the pool with your token
npx hardhat setPool \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --pooladdress $ETH_POOL_ADDRESS \
  --network ethereumSepolia

Step 2: Register Solana Pool

Switch to Terminal 1 (CCIP Solana BS58 Generator)

pwd
# Should show: .../ccip-solana-bs58-generator

Load variables from previous phases:

source ~/.phase1_vars
source ~/.phase2_vars

Create Address Lookup Table

Address Lookup Tables (ALT) optimize Solana transactions by compressing addresses. With EOA execution, create and populate the ALT in one step:

# Create and populate ALT for your token configuration
pnpm bs58 --env devnet --execute router \
  --instruction create-lookup-table \
  --program-id $SOL_CCIP_ROUTER \
  --fee-quoter-program-id $SOL_CCIP_FEE_QUOTER_PROGRAM \
  --pool-program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_WALLET_ADDRESS

Save the ALT address:

Replace with your ALT address from the output:

export SOL_ALT_ADDRESS="<INSERT_YOUR_ALT_ADDRESS>"

Verify the ALT address is set correctly:

echo "ALT Address: $SOL_ALT_ADDRESS"

Register Solana Pool

In this step, you will register the token pool with Solana's Router TokenAdminRegistry. This instruction sets the Address Lookup Table as the pool definition for the token, enabling it for CCIP cross-chain transfers. The writable_indices parameter specifies which accounts in the ALT need write access during transactions.

# Register pool with token admin registry
pnpm bs58 --env devnet --execute router \
  --instruction set-pool \
  --program-id $SOL_CCIP_ROUTER \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_WALLET_ADDRESS \
  --pool-lookup-table $SOL_ALT_ADDRESS \
  --writable-indexes "[3,4,7]"

Step 3: Save Complete Configuration

Save all variables for the testing phase:

# Save complete configuration for testing
cat > ~/.ccip_complete_vars << EOF
# Phase 1 - EVM
export ETH_TOKEN_ADDRESS="$ETH_TOKEN_ADDRESS"
export ETH_POOL_ADDRESS="$ETH_POOL_ADDRESS"

# Phase 2 - Solana
export SOL_TOKEN_MINT="$SOL_TOKEN_MINT"
export SOL_POOL_SIGNER_PDA="$SOL_POOL_SIGNER_PDA"
export SOL_POOL_CONFIG_PDA="$SOL_POOL_CONFIG_PDA"
export SOL_CCIP_POOL_PROGRAM="$SOL_CCIP_POOL_PROGRAM"
export SOL_CCIP_ROUTER="$SOL_CCIP_ROUTER"
export SOL_CCIP_FEE_QUOTER_PROGRAM="$SOL_CCIP_FEE_QUOTER_PROGRAM"
export ETH_SEPOLIA_CHAIN_SELECTOR="$ETH_SEPOLIA_CHAIN_SELECTOR"
export SOL_WALLET_ADDRESS="$SOL_WALLET_ADDRESS"

# Phase 4 - ALT
export SOL_ALT_ADDRESS="$SOL_ALT_ADDRESS"
EOF

echo "=== Complete Configuration Saved ==="
echo "✅ All variables saved to ~/.ccip_complete_vars"
echo "✅ Ready for cross-chain testing"

Phase 5: Pre-Transfer Setup

Before validating your configuration, complete final setup steps.

Step 1: Verify Pool Signer PDA

Confirm the Pool Signer PDA matches your saved configuration:

# Optional: read onchain pool state to verify addresses
pnpm bs58 --env devnet burnmint-token-pool \
  --instruction get-state \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT

Compare the onchain Pool Signer with your saved variable:

echo "Saved Pool Signer PDA: $SOL_POOL_SIGNER_PDA"

Step 2: Delegate Token Authority

In this step, you will delegate token approval to the CCIP fee-billing signer PDA, which enables CCIP to transfer tokens on your behalf when sending cross-chain messages.

# Derive the CCIP fee-billing signer PDA
export SOL_CCIP_FEE_BILLING_SIGNER=$(solana find-program-derived-address $SOL_CCIP_ROUTER string:fee_billing_signer | head -1)

echo "SOL_CCIP_FEE_BILLING_SIGNER=$SOL_CCIP_FEE_BILLING_SIGNER"

Approve the fee-billing signer to transfer tokens from your ATA:

# --amount is u64 max (2^64 - 1): max delegation for CCIP fee billing
pnpm bs58 --env devnet --execute spl-token \
  --instruction approve \
  --authority $SOL_WALLET_ADDRESS \
  --mint $SOL_TOKEN_MINT \
  --delegate $SOL_CCIP_FEE_BILLING_SIGNER \
  --amount 18446744073709551615

Step 3: Verify Delegation

Check that your token account is delegated to the fee-billing signer:

# Find your Associated Token Account (ATA) address
export SOL_TOKEN_ACCOUNT=$(spl-token accounts $SOL_TOKEN_MINT --owner $SOL_WALLET_ADDRESS --addresses-only)

echo "SOL_TOKEN_ACCOUNT=$SOL_TOKEN_ACCOUNT"

Display token account and delegation status:

spl-token display $SOL_TOKEN_ACCOUNT

Phase 6: Test Cross-Chain Transfers

Validate your configuration, then execute bidirectional token transfers using the CCIP CLI.

Confirm you are in the correct directory (Terminal 1):

pwd
# Should show: .../ccip-solana-bs58-generator

Step 1: Load Complete Configuration

# Load complete configuration
source ~/.ccip_complete_vars
source ~/.phase2_vars

# Verify all variables
echo "=== Configuration Validation ==="
echo "✅ ETH Token: $ETH_TOKEN_ADDRESS"
echo "✅ ETH Pool: $ETH_POOL_ADDRESS"
echo "✅ SOL Token: $SOL_TOKEN_MINT"
echo "✅ Pool Signer PDA: $SOL_POOL_SIGNER_PDA"
echo "✅ ALT Address: $SOL_ALT_ADDRESS"
echo "✅ Wallet: $SOL_WALLET_ADDRESS"

Step 2: Verify Solana Pool and Chain Config

# Verify pool state
pnpm bs58 --env devnet burnmint-token-pool \
  --instruction get-state \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT

# Verify Ethereum Sepolia remote chain config
pnpm bs58 --env devnet burnmint-token-pool \
  --instruction get-chain-config \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --remote-chain-selector $ETH_SEPOLIA_CHAIN_SELECTOR

# Verify token balance and delegation
spl-token balance $SOL_TOKEN_MINT

Step 3: Verify Ethereum Pool (Terminal 2)

Switch to Terminal 2 (Smart Contract Examples - Hardhat):

npx hardhat getPoolConfig \
  --pooladdress $ETH_POOL_ADDRESS \
  --network ethereumSepolia

Confirm the Ethereum pool recognizes Solana Devnet with your $SOL_POOL_CONFIG_PDA and $SOL_TOKEN_MINT.

Configure CCIP CLI

Export these RPC URLs once before your first transfer:

export SOLANA_DEVNET_RPC="https://api.devnet.solana.com"
export ETHEREUM_SEPOLIA_RPC_URL="<YOUR_ETHEREUM_SEPOLIA_RPC_URL>"

export ETH_CCIP_ROUTER="0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59"

Use the same ETHEREUM_SEPOLIA_RPC_URL from your env-enc setup in Terminal 2 (npx env-enc view). On ccip-cli send, the source chain RPC quotes fees and submits the transaction; for token-only transfers in this tutorial, that is the only RPC strictly required to send. Pass both RPCs on cross-chain commands anyway: the destination RPC is required for ccip-cli show --wait and for optional send features such as --estimate-gas-limit or send --wait.

Transfer Solana → Ethereum

Terminal 1 (CCIP Solana BS58 Generator):

source ~/.ccip_complete_vars

export ETH_RECEIVER_ADDRESS="<YOUR_ETHEREUM_RECEIVER_ADDRESS>"


ccip-cli send \
  -s solana-devnet \
  -r $SOL_CCIP_ROUTER \
  -d ethereum-testnet-sepolia \
  --to $ETH_RECEIVER_ADDRESS \
  -t $SOL_TOKEN_MINT=0.001 \
  --wallet ~/.config/solana/id.json \
  --rpc $SOLANA_DEVNET_RPC \
  --rpc $ETHEREUM_SEPOLIA_RPC_URL

The -t flag uses human-readable amounts (0.001 tokens with 9 decimals = 1,000,000 smallest units).

# Replace with the transaction hash or message ID from the send output
ccip-cli show <TX_HASH_OR_MESSAGE_ID> --wait \
  --rpc $SOLANA_DEVNET_RPC \
  --rpc $ETHEREUM_SEPOLIA_RPC_URL

Transfer Ethereum → Solana

Switch to Terminal 2 (Smart Contract Examples - Hardhat):

ccip-cli does not load Hardhat env-enc automatically. Run these commands from the Hardhat project directory. Prefer Hardhat keystore over exporting a private key in plain text.

source ~/.ccip_complete_vars

ccip-cli send \
  -s ethereum-testnet-sepolia \
  -r $ETH_CCIP_ROUTER \
  -d solana-devnet \
  --to $SOL_WALLET_ADDRESS \
  -t $ETH_TOKEN_ADDRESS=1.0 \
  --wallet hardhat:<YOUR_KEYSTORE_ACCOUNT_NAME> \
  --rpc $ETHEREUM_SEPOLIA_RPC_URL \
  --rpc $SOLANA_DEVNET_RPC

Use the keystore account name from your Hardhat project (see Hardhat keystore). This keeps the signing key encrypted instead of in shell history.

For token-only transfers to Solana, --to is the destination wallet that receives minted tokens. CCIP fees are paid in native Sepolia ETH by default — ensure your wallet has sufficient ETH for gas and fees.

ccip-cli show <TX_HASH_OR_MESSAGE_ID> --wait \
  --rpc $ETHEREUM_SEPOLIA_RPC_URL \
  --rpc $SOLANA_DEVNET_RPC

Congratulations! Your cross-chain token infrastructure is fully configured on both chains.

Reference: Verification Commands

Use these commands to verify your setup at any point during the tutorial. Each section focuses on a specific component of your cross-chain configuration.

Solana Pool Verification

Terminal 1 (CCIP Solana BS58 Generator)

# Verify pool configuration and status
pnpm bs58 --env devnet burnmint-token-pool \
  --instruction get-state \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT

What this shows:

  • Pool configuration details
  • Pool signer PDA information
  • Token account balances
  • Pool operational status

Solana Chain Configuration

Terminal 1 (CCIP Solana BS58 Generator)

# Verify cross-chain configuration with Ethereum Sepolia
pnpm bs58 --env devnet burnmint-token-pool \
  --instruction get-chain-config \
  --program-id $SOL_CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --remote-chain-selector $ETH_SEPOLIA_CHAIN_SELECTOR

What this shows:

  • Remote chain configuration
  • Token address mappings
  • Pool address mappings
  • Cross-chain connectivity status

Solana Token Balance

Terminal 1 (CCIP Solana BS58 Generator)

# Check your token balance
spl-token balance $SOL_TOKEN_MINT

What this shows:

  • Current token balance in your wallet
  • Token account details
  • Delegation status

Ethereum Pool Verification

Terminal 2 (Smart Contract Examples)

# Verify Ethereum pool configuration
npx hardhat getPoolConfig \
  --pooladdress $ETH_POOL_ADDRESS \
  --network ethereumSepolia

What this shows:

  • Pool contract configuration
  • Remote chain settings
  • Rate limiting parameters
  • Pool operational status

Cross-Chain Transfer Status

Both Terminals

# Monitor CCIP message status (replace with your message ID)
# From the transfer output, look for: "Message ID: 0x..."
# Then visit: https://ccip.chain.link/msg/0x...

What this shows:

  • Transfer execution status
  • Cross-chain message progress
  • Completion confirmation
  • Error details (if any)

What's next

Get the latest Chainlink content straight to your inbox.