Cross-Chain Tokens with Production Multisig Governance

This tutorial implements production-grade cross-chain tokens using Chainlink CCIP with dual-layer multisig governance between Solana Devnet and Ethereum Sepolia. You'll build a production-ready governance architecture following Path A from the CCIP Cross-Chain Token Integration Guide.

What You will Build

This tutorial builds a cross-chain token with enterprise-grade governance using Chainlink CCIP between Solana Devnet and Ethereum Sepolia.

ComponentImplementationSecurity Model
Ethereum SepoliaERC20 token with CCIP BurnMint poolEOA-controlled (tutorial)
Solana DevnetSPL token with dual-layer governanceSquads + SPL multisig
Cross-Chain TransferBidirectional token transfersAutonomous operations

Key Innovation: Dual-Layer Governance

This tutorial implements dual-layer multisig governance that separates concerns:

  • Layer 1 (Squads): Controls CCIP administration, pool ownership, protocol configuration
  • Layer 2 (SPL Multisig): Controls mint authority with Pool Signer PDA for autonomous operations

Production Benefits:

  • Comprehensive Security: Two independent governance layers
  • Operational Autonomy: Pool Signer PDA enables autonomous cross-chain transfers
  • Administrative Control: Squads provides enterprise-grade governance

Implementation Path:

  1. Deploy EVM token and pool on Ethereum Sepolia
  2. Create governed SPL tokens with Squads as mint authority
  3. Establish dual-layer multisig (Squads โ†’ SPL multisig transfer)
  4. Configure cross-chain pools
  5. Test bidirectional transfers and validate governance

Prerequisites

System Requirements

Install these tools before starting:

Package Managers:

  • Node.js v20+: Required for all repositories
  • pnpm: Required for base58 Generator (npm install -g pnpm)
  • yarn: Required for Solana Starter Kit (npm install -g yarn)

Solana Development:

Wallets:

  • Solana wallet: Phantom or Backpack for Devnet operations
  • Ethereum wallet: MetaMask for Sepolia testnet operations

Repository Setup

Clone and install dependencies for all three repositories:

Terminal 1: base58 Generator

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

Terminal 2: EVM Hardhat

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

Terminal 3: Solana Starter Kit

git clone https://github.com/smartcontractkit/solana-starter-kit.git
cd solana-starter-kit
yarn install

Create and configure the .env file:

# Create .env file in the project root
cat > .env << 'EOF'
EVM_PRIVATE_KEY=your_private_key_here
EVM_RPC_URL=your_rpc_url_here
EOF

Replace the placeholder values with:

  • EVM_PRIVATE_KEY: Your Ethereum wallet private key (for Sepolia testnet operations)
  • EVM_RPC_URL: Your Ethereum RPC URL (from Alchemy, Infura, or another provider)

Environment Configuration

Solana Configuration:

# Set Solana CLI to devnet
solana config set --url https://api.devnet.solana.com

# Create keypair (ONLY IF NEEDED)
solana-keygen new --outfile ~/.config/solana/id.json

# Check Config

solana config get

# Fund wallet
solana airdrop 3

Ethereum Sepolia Configuration:

# In Terminal 2 (Hardhat directory)
npx env-enc set-pw
npx env-enc set

Required environment variables:

  • ETHEREUM_SEPOLIA_RPC_URL: RPC endpoint (Alchemy or Infura)
  • PRIVATE_KEY: Testnet wallet private key
  • ETHERSCAN_API_KEY: API key from Etherscan

Testnet Tokens:

  • Solana Devnet: Use solana airdrop 3 for SOL
  • Ethereum Sepolia: Use Chainlink faucets for LINK and ETH

Squads Multisig Setup

Step 1: Prepare Signers

Create multiple wallet addresses for your multisig signers:

# Create additional signers using Solana CLI
solana-keygen new --outfile ~/.config/solana/signer2.json
solana-keygen new --outfile ~/.config/solana/signer3.json

# Get signer addresses
solana address --keypair ~/.config/solana/id.json
solana address --keypair ~/.config/solana/signer2.json
solana address --keypair ~/.config/solana/signer3.json

# Fund signers (minimum 0.1 SOL each for transaction fees)
solana transfer <SIGNER2_ADDRESS> 0.1 --allow-unfunded-recipient
solana transfer <SIGNER3_ADDRESS> 0.1 --allow-unfunded-recipient

Alternatively, create signers in Phantom wallet and fund them:

# Transfer SOL to Phantom-created addresses
solana transfer <PHANTOM_SIGNER_ADDRESS> 0.5 --allow-unfunded-recipient

Step 2: Create Your Squad

  1. Visit devnet.squads.so
  2. Connect your Solana wallet (e.g., Phantom/Backpack)
  3. Click "Create New Squad"
  4. Configure your multisig:
    • Squad Name: Choose a descriptive name (e.g., "CCIP Token Governance")
    • Members: Add wallet addresses of all signers
    • Threshold: Set approval threshold (recommended: 2/3 or 3/5)

Step 3: Record Critical Addresses

After Squad creation, navigate to Settings tab and record these addresses.

In Terminal 1 (base58 Generator), export the vault address:

# CRITICAL: Use the VAULT address, NOT the multisig address
export SOL_SQUAD_VAULT_MULTISIG="YOUR_VAULT_ADDRESS_HERE"

Verify the export:

echo "Squads Vault Address: $SOL_SQUAD_VAULT_MULTISIG"

Step 4: Test Your Squad

Perform a small test transaction to verify setup:

  1. Send a small amount of SOL to your Squad vault
  2. Create a test transaction in Squads UI (e.g., SOL transfer to your wallet)
  3. Confirm all signers can approve transactions
  4. Execute the transaction

For detailed setup guidance, see the Squads Documentation.

Token Creation Option

Tutorial Approach

This tutorial implements production-grade cross-chain tokens using a three-terminal workflow across specialized repositories:

TerminalRepositoryPurposeCommands
Terminal 1CCIP Solana base58 GeneratorGenerate governance transactionspnpm bs58
Terminal 2Smart Contract Examples (Hardhat)Deploy EVM componentsnpx hardhat
Terminal 3Solana Starter KitTest cross-chain transfersyarn

Key Implementation Notes

  • Terminal 1 generates base58-encoded transactions for Squads multisig governance
  • Terminal 2 uses EOA for tutorial simplicity; production deployments should use multisig wallets (e.g., Safe)
  • Terminal 3 validates end-to-end cross-chain functionality

base58 Transaction Execution Workflow

Transaction Generation and Validation:

  1. CLI Simulation: Each base58 transaction is automatically simulated during generation
  2. Error Handling: If simulation fails, error logs appear in terminal - do not upload failed transactions to Squads
  3. Success Indicator: Successful simulation shows transaction preview and base58 output

Squads Multisig Execution Process:

  1. Propose: A signer imports the base58 transaction into Squads UI โ†’ "Add instruction" โ†’ "Import base58 encoded tx" โ†’ Initiate Transaction
  2. Approve: Required threshold (M) of signers review and approve the transaction
  3. Simulate (Recommended): Before execution, signers can simulate through Squads interface to preview onchain effects
  4. Execute: After threshold approval, any signer can execute the transaction

Environment Variables

Variables use prefixes to prevent confusion across repositories:

PrefixUsageExamples
ETH_*Ethereum addressesETH_TOKEN_ADDRESS, ETH_POOL_ADDRESS
SOL_*Solana addressesSOL_TOKEN_MINT, SOL_POOL_ADDRESS
CCIP_*Protocol constantsCCIP_POOL_PROGRAM, CCIP_ROUTER_PROGRAM

Phase 1: EVM Chain Setup (Ethereum Sepolia)

In this phase, you will deploy ERC20 tokens and configure CCIP BurnMint pools on Ethereum Sepolia. This setup is identical across all Path A variants and provides the foundation for cross-chain operations.

Step 1: Prepare EVM Environment

First, set up your terminal and verify your environment:

# Terminal 2: Navigate to EVM repository

# Verify location and project structure

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

pwd

# Verify private key variables are set:

npx env-enc view

Step 2: Deploy ERC20 Token

Deploy your cross-chain token on Ethereum Sepolia:

# Deploy ERC20 token
npx hardhat deployToken \
  --name "AEM Token" \
  --symbol "BnmAEM" \
  --decimals 18 \
  --verifycontract true \
  --network sepolia

# Copy the token address from the output above

Set the token address variable:

# REPLACE with actual address from deployment output
export ETH_TOKEN_ADDRESS="<INSERT_YOUR_ACTUAL_TOKEN_ADDRESS>"

Verify the address is set correctly:

echo "โœ… Ethereum Token Address: $ETH_TOKEN_ADDRESS"

Step 3: Deploy and Configure CCIP BurnMint Pool

Deploy the BurnMint token pool:

# Deploy BurnMint pool
npx hardhat deployTokenPool \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --localtokendecimals 18 \
  --pooltype burnMint \
  --verifycontract true \
  --network sepolia

Set the pool address and configure:

# REPLACE with actual address from deployment output
export ETH_POOL_ADDRESS="<INSERT_YOUR_ACTUAL_POOL_ADDRESS>"

Verify configuration

echo "โœ… Ethereum Pool Address: $ETH_POOL_ADDRESS"

Step 4: Mint Initial Token Supply

Mint tokens for testing:

# Mint 1000 tokens for testing
npx hardhat mintTokens \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --amount 1000000000000000000000 \
  --network sepolia

# Note: Balance can be checked in your wallet or through block explorer

echo "โœ… Tokens minted successfully"

Step 5: Claim CCIP Admin Role

In this step, you will use the claimAdmin task to register your EOA as the administrator for the deployed token on Ethereum Sepolia. This process involves calling the RegistryModuleOwnerCustom contract, which will fetch the CCIP admin of the token and set it up as the admin in the registry.

# Claim admin role for CCIP token registry
npx hardhat claimAdmin \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --network sepolia

Step 6: Accept CCIP Admin Role

In this step, you will use the acceptAdminRole task to accept the admin role for the deployed token on Ethereum Sepolia. Once you have claimed the role, accepting the role finalizes your control over the token administration.

# Accept admin role to complete CCIP registration
npx hardhat acceptAdminRole \
--tokenaddress $ETH_TOKEN_ADDRESS \
--network sepolia

Step 7: Register Pool with Token

In this step, you will use the setPool task to register the BurnMint token pool with the token in Ethereum's TokenAdminRegistry contract. This function sets the pool contract address for the token, enabling it for CCIP cross-chain transfers. Only the token administrator can call this function.

# Register token pool with TokenAdminRegistry contract
npx hardhat setPool \
--tokenaddress $ETH_TOKEN_ADDRESS \
--pooladdress $ETH_POOL_ADDRESS \
--network sepolia

Phase 1 Complete: Save your variables:

# Save Phase 1 variables for later use
echo "export ETH_TOKEN_ADDRESS=\"$ETH_TOKEN_ADDRESS\"" > ~/.phase1_vars
echo "export ETH_POOL_ADDRESS=\"$ETH_POOL_ADDRESS\"" >> ~/.phase1_vars

echo "=== Phase 1 Complete - EVM Setup ==="
echo "โœ… ETH Token: $ETH_TOKEN_ADDRESS"
echo "โœ… ETH Pool: $ETH_POOL_ADDRESS"

Phase 2: Solana Setup with Production Dual Multisig Governance

In this phase, you will implement the production-grade dual-layer multisig governance architecture on Solana Devnet.

Step 1: Prepare base58 Environment

# Terminal 1: Verify location
# Should output: ccip-solana-bs58-generator
pwd

Set up CCIP constants on Solana Devnet (DO NOT CHANGE THESE)

export CCIP_POOL_PROGRAM="41FGToCmdaWa1dgZLKFAjvmx6e6AjVTX7SVRibvsMGVB"
export CCIP_ROUTER_PROGRAM="Ccip842gzYHhvdDkSyi2YVCoAWPbYJoApMFzSxQroE9C"
export CCIP_FEE_QUOTER_PROGRAM="FeeQPGkKDeRV1MgoYfMH6L8o3KeuYjwUZrgn4LRKfjHi"

Note: You can find the Router, Fee Quoter, and self-serve pool programs addresses in the CCIP Directory.

Get your Solana wallet address for token recipient:

export SOL_WALLET_ADDRESS=$(solana address)

Verify all required variables are set:

echo "โœ… Pool Program: $CCIP_POOL_PROGRAM"
echo "โœ… Router Program: $CCIP_ROUTER_PROGRAM"
echo "โœ… Squad Vault (from Prerequisites): $SOL_SQUAD_VAULT_MULTISIG"
echo "โœ… Recipient Wallet: $SOL_WALLET_ADDRESS"

Step 2: Create SPL Token (Layer 2 Foundation)

This command generates a transaction that creates a new SPL token mint with the following features:

  • Mint Authority: Set to your Squad vault multisig for governance control
  • Metaplex Metadata: Includes token name, symbol, and URI for cross-platform compatibility
  • Initial Supply: Automatically mints 5,000 tokens (5,000,000,000,000 smallest units) to your wallet
  • Deterministic Address: Uses a seed-based approach for predictable mint addresses

Generate the SPL token creation transaction (customize parameters or reuse the example):

# Generate SPL token creation transaction with initial supply
pnpm bs58 spl-token \
  --env devnet \
  --instruction create-mint \
  --authority $SOL_SQUAD_VAULT_MULTISIG \
  --decimals 9 \
  --with-metaplex true \
  --name "AEM" \
  --symbol "CCIP-AEM" \
  --uri "https://cyan-pleasant-anteater-613.mypinata.cloud/ipfs/bafkreieirlwjqbtzniqsgcjebzexlcspcmvd4woh3ajvf2p4fuivkenw6i" \
  --initial-supply 5000000000000 \
  --recipient $SOL_WALLET_ADDRESS

After execution, set the token mint:

# Copy the mint address from the base58 generator output above
export SOL_TOKEN_MINT="<YOUR_ACTUAL_MINT_ADDRESS>"

Verify the token mint:

echo "โœ… Token Mint: $SOL_TOKEN_MINT"

Step 3: Initialize BurnMint Token Pool (Before Multisig Setup)

This command creates only one new account and establishes references to existing infrastructure:

What Gets Created:

  • Pool State PDA: Creates the onchain Pool State account that stores your token's CCIP configuration

What Gets Referenced (Not Created):

  • Authority Assignment: Sets your Squad vault as the pool owner with full configuration control
  • Router Integration: References the existing global CCIP router and RMN (Risk Management Network)
  • Pool Signer PDA: The Pool Signer PDA is mathematically derived but not created - it will be used on-demand during cross-chain operations

The initialize-pool instruction creates only the Pool State account for your SPL token mint, establishing you as the pool owner while referencing existing global infrastructure.

Generate the pool initialization transaction:

# Generate pool initialization transaction
pnpm bs58 burnmint-token-pool --env devnet --instruction initialize-pool \
  --program-id $CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG

Account Breakdown from the Transaction (Example):

AccountAddressPurpose
#12pGY9WAjanpR3RnY5hQ1a23uDNomzFCAD5HMBgo8nH6MPool State PDA - Your main pool configuration
#23T1wVJporm2JpGojRV831TTQ1nexkBqFWhKnE5LwedciToken Mint (your token)
#359eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYAuthority (your Squad vault)
#411111111111111111111111111111111System Program
#541FGToCmdaWa1dgZLKFAjvmx6e6AjVTX7SVRibvsMGVBCCIP Pool Program
#64sVSCJqG9ZKEvnpN38qTzb7Kc8QdHakBgB87HN3FYRazProgram Data PDA
#7E4Bsi43kX3iwXAFia2ebm1mS5Xkmmdv3minZDnfo7ZzfGlobal Config PDA - Program-wide configuration settings

Key Addresses You Need:

  • Account #1 โ†’ SOL_POOL_ADDRESS (writable account = pool state)
  • Pool Signer PDA โ†’ Must be derived separately (NOT in transaction accounts)

After execution, set the pool state address from the transaction:

# Set the pool state address from Account #1 in the transaction above
export SOL_POOL_ADDRESS="2pGY9WAjanpR3RnY5hQ1a23uDNomzFCAD5HMBgo8nH6M"

Step 4: Derive Pool Signer PDA

The Pool Signer PDA is the critical address that will serve as the autonomous signing authority for cross-chain mint and burn operations.

# Derive all pool-related accounts including the Pool Signer PDA
pnpm bs58 utils \
  --env devnet \
  --instruction derive-accounts \
  --program-type burnmint-token-pool \
  --program-id "$CCIP_POOL_PROGRAM" \
  --mint "$SOL_TOKEN_MINT"

Set the Pool Signer PDA address for use in subsequent steps:

# Set the Pool Signer PDA from the derivation output above
export SOL_POOL_SIGNER_PDA="<YOUR_ACTUAL_POOL_SIGNER_PDA>"

Verify both addresses are set correctly:

echo "Pool Address: $SOL_POOL_ADDRESS"
echo "Pool Signer PDA: $SOL_POOL_SIGNER_PDA"

Step 5: Register CCIP Administrator

This two-step process establishes your Squad vault as the CCIP token administrator, enabling you to enable your token in CCIP. Since your Squad vault currently holds the mint authority, you can complete this registration using the self-service registration flow without external assistance.

Why This Works: The Router's owner_propose_administrator instruction verifies onchain that the caller matches the token's mint_authority field. Your Squad vault has this authority, enabling PATH A self-service registration.

Sub-step 5a: Propose Administrator

The owner_propose_administrator instruction creates a TokenAdminRegistry PDA for your token and sets your Squad vault as the pending administrator:

# Generate administrator registration transaction
pnpm bs58 router --env devnet --instruction owner-propose-administrator \
  --program-id $CCIP_ROUTER_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG \
  --token-admin-registry-admin $SOL_SQUAD_VAULT_MULTISIG

Account Breakdown from the Transaction (Example):

AccountAddressDescription
#13Yrg9E4ySAeRezgQY99NNarAmFLtixapga9MZb6y2dt3Router Config PDA (read-only)
#24maPuX2fcDmCkQv1Qa6RmQa2yGpeZLxiqKy9ybwazGft๐ŸŽฏ Token Admin Registry PDA - Gets created/updated for your token
#33T1wVJporm2JpGojRV831TTQ1nexkBqFWhKnE5LwedciToken Mint (your token)
#459eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYAuthority/Payer (your Squad vault)
#511111111111111111111111111111111System Program

What This Transaction Does: This transaction proposes your Squad vault as the administrator for your token in the Router's Token Admin Registry. Account #2 is the Token Admin Registry PDA that stores who has administrative control over your token's cross-chain operations.

Sub-step 5b: Accept Administrator Role

The accept_admin_role instruction completes the registration process by having the pending administrator (your Squad vault) explicitly accept the CCIP token administrator role:

# Generate administrator role acceptance transaction
pnpm bs58 router --env devnet --instruction accept-admin-role \
  --program-id $CCIP_ROUTER_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG

Account Breakdown from the Transaction (Example):

AccountAddressDescription
#13Yrg9E4ySAeRezgQY99NNarAmFLtixapga9MZb6y2dt3Router Config PDA (read-only)
#24maPuX2fcDmCkQv1Qa6RmQa2yGpeZLxiqKy9ybwazGft๐ŸŽฏ Token Admin Registry PDA - Updates status to "active"
#33T1wVJporm2JpGojRV831TTQ1nexkBqFWhKnE5LwedciToken Mint (your token)
#459eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYAuthority (your Squad vault - must be pending admin)

What This Transaction Does: This is the acceptance step of the two-phase administrator registration. It updates the Token Admin Registry PDA (Account #2) from "pending administrator" to "active administrator" status. Your Squad vault explicitly accepts the administrator role, completing the secure registration process.

Key Difference: Only 4 accounts (vs. 5 in the propose step) because we're updating an existing registry entry, not creating a new one.

After both transactions are executed, your Squad vault will be the CCIP token administrator. See Registration & Administration for more details.

Step 6: Create SPL Token Multisig (Layer 2 Mint Authority)

This command creates the second layer of your dual-multisig architecture by establishing an SPL token multisig that will control mint authority. The create-multisig instruction creates a deterministic multisig account that includes both your Pool Signer PDA and Squad vault as authorized signers.

What This Accomplishes:

  • SPL Token Multisig Creation: Establishes a new multisig account
  • Dual Authority Setup: Includes both Pool Signer PDA (for autonomous CCIP operations) and Squad vault (for governance control)
  • Threshold Configuration: Sets threshold to 1, allowing either signer to authorize mint operations
  • Layer 2 Foundation: Creates the mint authority that will be transferred from your Squad vault in the next step

Token Program Detection: The CLI automatically detects whether your mint uses SPL Token v1 or Token-2022 and creates the appropriate multisig type.

# Generate SPL token multisig creation transaction
pnpm bs58 spl-token --env devnet --instruction create-multisig \
  --authority "$SOL_SQUAD_VAULT_MULTISIG" \
  --seed "production-multisig" \
  --mint "$SOL_TOKEN_MINT" \
  --signers "[\"$SOL_POOL_SIGNER_PDA\", \"$SOL_SQUAD_VAULT_MULTISIG\"]" \
  --threshold 1

Account Breakdown from the Transaction (Example):

AccountAddressDescription
#159eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYPayer/Authority (your Squad vault)
#26c5UGRRQzPvZY4ZJZJiPGRYMw5e32uX8GfFzvm35k3jK๐ŸŽฏ SPL Token Multisig (newly created)

What This Transaction Does: This creates an SPL Token Multisig account that will control your token's mint authority. The CLI automatically detected SPL Token v1 and created the appropriate multisig type.

Key Details:

  • Generated Multisig Address: 6c5UGRRQzPvZY4ZJZJiPGRYMw5e32uX8GfFzvm35k3jK
  • Multisig Signers: Pool Signer PDA + Squad vault
  • Threshold: 1 (either signer can authorize mint operations)
  • Hybrid Control: Enables both automated CCIP operations and human governance

Execute this transaction through Squads and set the variable for the next step:

export SOL_SPL_MULTISIG="<INSERT_ACTUAL_SPL_MULTISIG_ADDRESS>"

Verify the variable:

echo "โœ… SPL Multisig: $SOL_SPL_MULTISIG"

Step 7: Transfer Mint Authority (Layer 2 Mint Authority)

Transfer mint authority from your Squad vault to the SPL Token Multisig, completing the dual-layer governance architecture. This enables both automated CCIP operations and human governance control.

What This Command Does:

  • Authority Transfer: Moves mint control from Squad vault to SPL Token Multisig
  • SPL Token Operation: Uses the native SetAuthority instruction for mint authority
  • Governance Architecture: Establishes the final production-ready control structure
  • Dual Control Setup: Enables both Pool Signer PDA (autonomous) and Squad vault (governance) control
  • Irreversible Change: Once executed, only the SPL Token Multisig can mint tokens
# Generate mint authority transfer transaction
pnpm bs58 spl-token --env devnet --instruction transfer-mint-authority \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG \
  --new-mint-authority $SOL_SPL_MULTISIG

Account Breakdown from the Transaction (Example):

AccountAddressDescription
#13T1wVJporm2JpGojRV831TTQ1nexkBqFWhKnE5LwedciToken Mint (writable - authority updated)
#259eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYCurrent Authority (signer - Squad vault)

Key Details:

  • Current Authority: 59eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtY (Squad vault)
  • New Authority: 6c5UGRRQzPvZY4ZJZJiPGRYMw5e32uX8GfFzvm35k3jK (SPL Token Multisig)
  • Token Mint: 3T1wVJporm2JpGojRV831TTQ1nexkBqFWhKnE5Lwedci (authority field updated)

Step 8: Test Token Minting (Verify Dual-Layer Control)

Verify that your Squad vault can still mint tokens after the authority transfer. This proves the dual-layer governance is working correctly: Squad vault โ†’ SPL Token Multisig โ†’ Token minting.

Purpose of This Test:

  • Verify Authority Transfer: Confirm the SPL Token Multisig now controls mint authority
  • Prove Squad Control: Demonstrate that Squad vault can still mint through the multisig
  • Validate Architecture: Test the dual-layer governance model works as designed
# Generate token minting transaction using SPL multisig

pnpm bs58 spl-token --env devnet --instruction mint \
  --authority "$SOL_SQUAD_VAULT_MULTISIG" \
  --mint "$SOL_TOKEN_MINT" \
  --amount 1000000000 \
  --recipient "$SOL_WALLET_ADDRESS" \
  --multisig "$SOL_SPL_MULTISIG" \
  --multisig-signers "[\"$SOL_SQUAD_VAULT_MULTISIG\"]"

Account Breakdown from the Transaction (Example):

AccountAddressDescription
#13T1wVJporm2JpGojRV831TTQ1nexkBqFWhKnE5LwedciToken Mint (writable - supply updated)
#284FvKxAQpHtSaFLguFygoPPEaRorZpTjs8rmMCxuwqUFRecipient's ATA (writable - receives tokens)
#36c5UGRRQzPvZY4ZJZJiPGRYMw5e32uX8GfFzvm35k3jKSPL Token Multisig (read-only - mint authority)
#459eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYSquad Vault (signer - multisig member)

What This Transaction Does: This is an SPL Token Multisig Mint operation that proves your dual-layer governance is working correctly. The Squad vault acts as a signer for the SPL Token Multisig that now controls mint authority.

Key Details:

  • Amount: 1,000,000,000 smallest units = 1 token (with 9 decimals)
  • Recipient: Your wallet address (EPUjBP3Xf76K1VKsDSc6GupBWE8uykNksCLJgXZn87CB)
  • Mint Authority: SPL Token Multisig (6c5UGRRQzPvZY4ZJZJiPGRYMw5e32uX8GfFzvm35k3jK)
  • Multisig Signer: Squad vault (59eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtY)
  • Token Program: SPL Token v1 (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA)

Transaction Flow:

  1. Authority Verification: SPL Token program verifies the multisig has mint authority
  2. Signature Validation: Program confirms Squad vault is an authorized multisig signer
  3. Token Creation: 1 token is minted and added to total supply
  4. Token Transfer: New tokens are deposited into your wallet's ATA

Phase 2 Complete: Save your variables:

# Save all Solana variables
cat > ~/.phase2_vars << EOF
export CCIP_POOL_PROGRAM="$CCIP_POOL_PROGRAM"
export CCIP_ROUTER_PROGRAM="$CCIP_ROUTER_PROGRAM"
export CCIP_FEE_QUOTER_PROGRAM="$CCIP_FEE_QUOTER_PROGRAM"
export SOL_SQUAD_VAULT_MULTISIG="$SOL_SQUAD_VAULT_MULTISIG"
export SOL_TOKEN_MINT="$SOL_TOKEN_MINT"
export SOL_POOL_ADDRESS="$SOL_POOL_ADDRESS"
export SOL_POOL_SIGNER_PDA="$SOL_POOL_SIGNER_PDA"
export SOL_SPL_MULTISIG="$SOL_SPL_MULTISIG"
export SOL_WALLET_ADDRESS="$SOL_WALLET_ADDRESS"
EOF

echo "=== Phase 2 Complete - Dual Multisig Setup ==="
echo "โœ… Token Mint: $SOL_TOKEN_MINT"
echo "โœ… Pool Address: $SOL_POOL_ADDRESS"
echo "โœ… Pool Signer PDA: $SOL_POOL_SIGNER_PDA"
echo "โœ… Squads Multisig: $SOL_SQUAD_VAULT_MULTISIG"
echo "โœ… SPL Multisig: $SOL_SPL_MULTISIG"
echo "โœ… Wallet Address: $SOL_WALLET_ADDRESS"

Phase 3: Cross-Chain Configuration

In this phase, you will configure the cross-chain connection and complete the CCIP setup.

Step 1: Load Phase 1 Variables

Load Phase 1 variables and set the Ethereum Sepolia chain selector for cross-chain configuration.

What This Step Does:

  • Terminal Verification: Confirms you're in the correct base58 generator repository
  • Variable Loading: Imports EVM token and pool addresses from Phase 1
  • Chain Selector Setup: Establishes Ethereum Sepolia chain selector for cross-chain configuration
# Verify you are in the correct terminal (terminal 1)
# Should output: /Users/.../ccip-solana-bs58-generator
pwd

# Load Phase 1 EVM variables

source ~/.phase1_vars

# Set chain selector for Ethereum Sepolia

export ETHEREUM_SEPOLIA_CHAIN_SELECTOR="16015286601757825753"

# Verify variables are loaded

echo "โœ… ETH Token: $ETH_TOKEN_ADDRESS"
echo "โœ… ETH Pool: $ETH_POOL_ADDRESS"
echo "โœ… Chain Selector: $ETHEREUM_SEPOLIA_CHAIN_SELECTOR"

Step 2: Configure Cross-Chain Pool Settings

Configure your token pool for cross-chain transfers to Ethereum Sepolia. This process involves two sequential operations:

  1. Initialize Chain Remote Config: Create the basic cross-chain configuration using init_chain_remote_config
  2. Edit Chain Remote Config: Add the remote pool address using edit_chain_remote_config

Step 2A: Initialize Chain Remote Config

Initialize the basic remote chain configuration for Ethereum Sepolia. Pool addresses must be empty at initialization and rate limits are not configured at this stage.

# Initialize basic cross-chain configuration (no rate limits, no pool addresses)
pnpm bs58 burnmint-token-pool --env devnet --instruction init-chain-remote-config \
  --program-id $CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG \
  --remote-chain-selector $ETHEREUM_SEPOLIA_CHAIN_SELECTOR \
  --token-address $ETH_TOKEN_ADDRESS \
  --decimals "18"

Account Breakdown from the Transaction (Example):

AccountAddressDescription
#12pGY9WAjanpR3RnY5hQ1a23uDNomzFCAD5HMBgo8nH6MPool State PDA (read-only - validation)
#28zdWLvsAgLQkRmBDATR3j8xVQzcdwrRNZtPZH5kU37jgChain Remote Config PDA (writable - created)
#359eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYSquad Vault (signer, writable - pool authority)
#411111111111111111111111111111111System Program (read-only - account creation)

What This Command Does: This initializes the cross-chain configuration for your Solana token to enable CCIP bridging to Ethereum Sepolia. This creates the foundational connection without rate limits or pool addresses.

Key Details:

  • Remote Chain: Ethereum Sepolia (chain selector: 16015286601757825753)
  • Token Mapping: Links to ERC20 token 0x7c57A9d966c3E6e344621C512d510f76575640ED
  • Decimal Precision: 18 decimals (standard EVM token format)
  • Basic Setup: No rate limits or pool addresses configured at this stage
  • Account Creation: Creates a new Chain Remote Config PDA for this cross-chain relationship

Transaction Flow:

  1. Authority Verification: Confirms Squad vault owns the pool state
  2. PDA Derivation: Calculates Chain Remote Config PDA using pool state + chain selector
  3. Account Creation: Creates new account with rent-exempt balance
  4. Basic Configuration: Stores minimal cross-chain parameters (no rate limits, no pool addresses)

Step 2B: Edit Chain Remote Config (Add Remote Pool Address)

After initializing the chain remote config, add the remote pool address to enable bidirectional cross-chain transfers. This uses edit_chain_remote_config to specify the Ethereum pool address.

# Configure chain connection and remote pool address
pnpm bs58 burnmint-token-pool --env devnet --instruction edit-chain-remote-config \
  --program-id $CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG \
  --remote-chain-selector $ETHEREUM_SEPOLIA_CHAIN_SELECTOR \
  --pool-addresses "[\"$ETH_POOL_ADDRESS\"]" \
  --token-address $ETH_TOKEN_ADDRESS \
  --decimals "18"

Account Breakdown from the Transaction:

AccountAddressDescription
#12pGY9WAjanpR3RnY5hQ1a23uDNomzFCAD5HMBgo8nH6MPool State PDA (read-only - authority validation)
#28zdWLvsAgLQkRmBDATR3j8xVQzcdwrRNZtPZH5kU37jgChain Remote Config PDA (writable - configuration updated)
#359eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYSquad Vault Authority (signer, writable - fee payer)
#411111111111111111111111111111111System Program (read-only - account operations)

Transaction Flow:

  1. Authority Verification: Confirms Squad vault owns the pool state
  2. PDA Access: Updates the existing Chain Remote Config PDA
  3. Pool Address Addition: Adds the remote pool to the the remote pool list for Ethereum Sepolia

Step 3: Configure Rate Limits (Optional)

This command configures inbound and outbound rate limiting for token transfers between your Solana token and Ethereum Sepolia. Rate limits act as "token buckets" that control the flow of tokens across chains.

# Configure rate limits for cross-chain token transfers
pnpm bs58 burnmint-token-pool \
  --env devnet \
  --instruction set-chain-rate-limit \
  --program-id $CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG \
  --remote-chain-selector $ETHEREUM_SEPOLIA_CHAIN_SELECTOR \
  --inbound-enabled "true" \
  --inbound-capacity "20000000000" \
  --inbound-rate "100000000" \
  --outbound-enabled "true" \
  --outbound-capacity "18000000000" \
  --outbound-rate "100000000"

Transaction Accounts (3 total):

AccountAddressTypePurpose
#12pGY9WAjanpR3RnY5hQ1a23uDNomzFCAD5HMBgo8nH6MRead-onlyPool State PDA - Main pool configuration account
#28zdWLvsAgLQkRmBDATR3j8xVQzcdwrRNZtPZH5kU37jgWritableChain Config PDA - Chain-specific config (stores rate limits)
#359eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYSigner, WritableAuthority - Your Squad vault that can modify rate limits

Step 4: Create Address Lookup Table

Create an Address Lookup Table (ALT) containing all accounts needed for CCIP router operations.

What This Command Does:

This command creates an Address Lookup Table (ALT) that stores frequently used accounts for CCIP operations. ALTs are a Solana optimization that reduces transaction size and costs by replacing full 32-byte addresses with small 1-byte indices.

# Generate address lookup table creation transaction
pnpm bs58 router --env devnet --instruction create-lookup-table \
  --program-id $CCIP_ROUTER_PROGRAM \
  --fee-quoter-program-id $CCIP_FEE_QUOTER_PROGRAM \
  --pool-program-id $CCIP_POOL_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG \
  --additional-addresses "[\"$SOL_SPL_MULTISIG\"]"

Account Breakdown from the Transaction:

AccountAddressDescription
#12s6yx6xCBgEQBrv5h4dF7TQvvKUWTJBEsueqTnZZHLwYALT Account (writable - being created)
#259eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYALT Authority (signer - will own the table)
#359eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYFee Payer (signer, writable - pays creation costs)
#411111111111111111111111111111111System Program (read-only - handles account creation)

What This Command Does:

This command creates the ALT infrastructure needed for efficient CCIP operations by storing frequently used account addresses in a lookup table.

Transaction Flow:

  1. ALT Creation: Creates lookup table account 2s6yx6x... with Squad Vault as authority
  2. Core Address Addition: Adds essential CCIP accounts (programs, PDAs) to the table
  3. SPL Multisig Addition: Adds your SPL multisig $SOL_SPL_MULTISIG via --additional-addresses
  4. Index Assignment: Each address gets a unique 1-byte index for future reference

ALT Contents (What Gets Stored): The created lookup table will contain indices for these accounts in this exact order:

IndexAccount AddressPurpose
02s6yx6xCBgEQBrv5h4dF7TQvvKUWTJBEsueqTnZZHLwYALT Address (Self-Reference)
14maPuX2fcDmCkQv1Qa6RmQa2yGpeZLxiqKy9ybwazGftToken Admin Registry PDA
241FGToCmdaWa1dgZLKFAjvmx6e6AjVTX7SVRibvsMGVBPool Program ID
32pGY9WAjanpR3RnY5hQ1a23uDNomzFCAD5HMBgo8nH6MPool State PDA
4BKkeydRQWKDc5dR7euwhYi47TDLV99o8pD83ncGA2LdZPool Signer's Associated Token Account
58NTqDH8dFj7aU5FBWeNMJwjsR1gZMR7criaEaDMEE24rPool Signer PDA
6TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DAToken Program ID (SPL Token v1)
73T1wVJporm2JpGojRV831TTQ1nexkBqFWhKnE5LwedciToken Mint
8GTz54AfhMRkKzFVse8jX8rjAx21fA86ZmVBF1v5VUC3uFee Quoter Token Config PDA
9H6ZviaabTYZqUPgiSoMDbeVthcNW9ULcAuUu3zRLFqDRRouter External Token Pools Signer PDA
106c5UGRRQzPvZY4ZJZJiPGRYMw5e32uX8GfFzvm35k3jKSPL Token Multisig โ† Your Additional Address!

Transaction Efficiency Impact:

  • Before ALT: Each account = 32 bytes (10 accounts = 320 bytes)
  • After ALT: Each account = 1 byte index (10 accounts = 10 bytes)
  • Savings: ~310 bytes per future CCIP transaction!

Set Variables:

Copy the ALT address from the transaction output and set the environment variable:

export SOL_ADDRESS_LOOKUP_TABLE="<YOUR_ALT_ADDRESS>"

Verify the variable is set:

echo "โœ… Address Lookup Table: $SOL_ADDRESS_LOOKUP_TABLE"

Step 5: Register Pool with Router

Register your token pool with the CCIP Router using set_pool. This enables the router to route cross-chain transfers through your token pool.

What This Command Does:

  • Pool Registration: Connects your token pool to the CCIP Router for cross-chain operations
  • Lookup Table Integration: Uses the ALT created in the previous step for efficient account management
  • Router Configuration: Enables the router to call your pool's token operations
# Generate pool registration transaction
pnpm bs58 router --env devnet --instruction set-pool \
  --program-id $CCIP_ROUTER_PROGRAM \
  --mint $SOL_TOKEN_MINT \
  --authority $SOL_SQUAD_VAULT_MULTISIG \
  --pool-lookup-table $SOL_ADDRESS_LOOKUP_TABLE \
  --writable-indexes "[3,4,7]"

Account Breakdown from the Transaction:

AccountAddressDescription
#13Yrg9E4ySAeRezgQY99NNarAmFLtixapga9MZb6y2dt3Router Config PDA (read-only - configuration reference)
#24maPuX2fcDmCkQv1Qa6RmQa2yGpeZLxiqKy9ybwazGftToken Admin Registry PDA (writable - pool registration)
#33T1wVJporm2JpGojRV831TTQ1nexkBqFWhKnE5LwedciToken Mint (read-only - token identification)
#42s6yx6xCBgEQBrv5h4dF7TQvvKUWTJBEsueqTnZZHLwYAddress Lookup Table (read-only - ALT registration)
#559eNrRrxrZMdqJxS7J3WGaV4MLLog2er14kePiWVjXtYSquad Vault Authority (signer, writable - admin & fees)

What This Command Does:

This command registers your token pool with the CCIP Router. It configures the router to use your Address Lookup Table and specifies which accounts need write access during CCIP operations.

Key Configuration:

  • ALT Integration: Uses the lookup table 2s6yx6x... you just created
  • Writable Indexes: [3,4,7] - Specific accounts from the ALTthat need write access during CCIP operations

Transaction Flow:

  1. Authority Verification: Confirms Squad Vault has CCIP token admin permissions
  2. Pool Registration: Updates Token Admin Registry with pool configuration
  3. ALT Association: Links your token with the specific lookup table
  4. Permission Setup: Defines which accounts can be modified during cross-chain operations

Writable Indexes Analysis: [3,4,7]

Based on the ALT order established earlier:

IndexALT AccountWhy Writable
3Pool State PDAGets updated during cross-chain operations
4Pool Token ATATokens get minted/burned during transfers
7Token MintSupply gets modified during mint/burn operations

Phase 3 Complete: Save all variables:

# Save all variables for testing phase
cat > ~/.all_vars << EOF
# Phase 1 - EVM
export ETH_TOKEN_ADDRESS="$ETH_TOKEN_ADDRESS"
export ETH_POOL_ADDRESS="$ETH_POOL_ADDRESS"

# Phase 2 - Solana

export CCIP_POOL_PROGRAM="$CCIP_POOL_PROGRAM"
export CCIP_ROUTER_PROGRAM="$CCIP_ROUTER_PROGRAM"
export SOL_SQUAD_VAULT_MULTISIG="$SOL_SQUAD_VAULT_MULTISIG"
export SOL_WALLET_ADDRESS="$SOL_WALLET_ADDRESS"
export SOL_TOKEN_MINT="$SOL_TOKEN_MINT"
export SOL_POOL_ADDRESS="$SOL_POOL_ADDRESS"
export SOL_POOL_SIGNER_PDA="$SOL_POOL_SIGNER_PDA"
export SOL_SPL_MULTISIG="$SOL_SPL_MULTISIG"
export SOL_ADDRESS_LOOKUP_TABLE="$SOL_ADDRESS_LOOKUP_TABLE"

# Chain Selectors

export ETHEREUM_SEPOLIA_CHAIN_SELECTOR="$ETHEREUM_SEPOLIA_CHAIN_SELECTOR"
export SOLANA_CHAIN_SELECTOR="$SOLANA_CHAIN_SELECTOR"
EOF

echo "=== Phase 3 Complete - Cross-Chain Configuration ==="
echo "โœ… Address Lookup Table: $SOL_ADDRESS_LOOKUP_TABLE"
echo "โœ… Cross-chain connection configured"
echo "โœ… All variables saved to ~/.all_vars"

Phase 4: EVM Cross-Chain Setup

Configure the Ethereum pool to recognize the Solana chain and set production rate limits.

Step 1: Load Phase 3 Variables

Ensure you're in Terminal 2 (Hardhat) and load all the variables from Phase 3.

# Verify you are in the Hardhat directory (Terminal 2)
# Should be: /.../smart-contract-examples/ccip/cct/hardhat
pwd

# Load all environment variables from Phase 3
source ~/.all_vars

# Verify key variables are loaded
echo "โœ… ETH Token: $ETH_TOKEN_ADDRESS"
echo "โœ… ETH Pool: $ETH_POOL_ADDRESS"
echo "โœ… SOL Token: $SOL_TOKEN_MINT"
echo "โœ… SOL Pool: $SOL_POOL_ADDRESS"
echo "โœ… SOL ALT: $SOL_ADDRESS_LOOKUP_TABLE"

Step 2: Configure Cross-Chain Settings

Configure the Ethereum pool to recognize the Solana chain and set production rate limits using applyChainUpdates.

# Configure cross-chain settings WITH rate limits
npx hardhat applyChainUpdates \
 --pooladdress $ETH_POOL_ADDRESS \
 --remotechain solanaDevnet \
 --remotepooladdresses $SOL_POOL_ADDRESS \
  --remotetokenaddress $SOL_TOKEN_MINT \
  --outboundratelimitenabled true \
  --outboundratelimitcapacity 18000000000000000000 \
  --outboundratelimitrate 100000000000000000 \
  --inboundratelimitenabled true \
  --inboundratelimitcapacity 20000000000000000000 \
  --inboundratelimitrate 100000000000000000 \
 --network sepolia

Phase 5: Testing and Validation

Step 1: Load Phase 4 Variables

Before testing cross-chain transfers, ensure your terminal environment is properly configured:

# Check current directory
# This should be the solana-starter-kit directory (Terminal 3)
pwd

Load all environment variables

source ~/.all_vars

Set Ethereum receiver for testing

export ETH_RECEIVER_ADDRESS="<YOUR_ETH_RECEIVER_ADDRESS>"

Verify critical variables for Solana Starter Kit

echo "SOL_TOKEN_MINT: $SOL_TOKEN_MINT"
echo "ETH_TOKEN_ADDRESS: $ETH_TOKEN_ADDRESS"
echo "ETH_RECEIVER_ADDRESS: $ETH_RECEIVER_ADDRESS"
echo "SOL_WALLET_ADDRESS: $SOL_WALLET_ADDRESS"

Transfer Direction 1: Solana โ†’ Ethereum

Test the production multisig setup with a cross-chain transfer from Solana Devnet to Ethereum Sepolia:

Prepare for Testing

Before testing cross-chain transfers, you need to create the pool's Associated Token Account (ATA) and prepare your tokens:

Check Token Balance
# Check your current token balance
spl-token balance $SOL_TOKEN_MINT
Create Pool Associated Token Account
# Create Associated Token Account for the Pool Signer PDA

yarn svm:pool:create-token-account \
  --token-mint $SOL_TOKEN_MINT \
  --burn-mint-pool-program $CCIP_POOL_PROGRAM
Delegate Token Authority
# Delegate tokens for CCIP transfers
yarn svm:token:delegate --token-mint $SOL_TOKEN_MINT

Transfer tokens from Solana Devnet to Ethereum Sepolia

# Execute cross-chain transfer from Solana to Ethereum
yarn svm:token-transfer --token-mint $SOL_TOKEN_MINT --token-amount 1000000 --receiver $ETH_RECEIVER_ADDRESS

Monitor and Verify Transaction

Upon successful execution, the system generates critical tracking identifiers for transaction monitoring and verification.

Transaction Identifiers:

  • Transaction Signature: 4T6coWNspJ7TDhsUFVZ7S1huzVxy6dSjbPfrFw4THGRBEt8X9UYfFggrxYLD4Hnm11Hqdnzg9vJTbrNSjp7ycRvf
  • CCIP Message ID: 0x21d6ca53d5ab59caa9d5a1d58a5f8040f9695043dcad5ddc0059710dff0113c3

CCIP Explorer (Primary monitoring interface):

https://ccip.chain.link/msg/0x21d6ca53d5ab59caa9d5a1d58a5f8040f9695043dcad5ddc0059710dff0113c3

The CCIP Explorer provides comprehensive transaction visibility:

  • Source chain (Solana) transaction confirmation
  • CCIP message processing and routing
  • Destination chain (Ethereum) message delivery
  • Token minting completion on Ethereum

Solana Explorer (Source chain verification):

https://explorer.solana.com/tx/4T6coWNspJ7TDhsUFVZ7S1huzVxy6dSjbPfrFw4THGRBEt8X9UYfFggrxYLD4Hnm11Hqdnzg9vJTbrNSjp7ycRvf?cluster=devnet

Transfer Direction 2: Ethereum โ†’ Solana

Transfer tokens from Ethereum Sepolia to Solana Devnet

# Execute cross-chain transfer from Ethereum to Solana
yarn evm:transfer --token $ETH_TOKEN_ADDRESS --amount 1000000000000000000 --token-receiver $SOL_WALLET_ADDRESS

Monitor and Verify Transaction

Upon successful execution, the system generates distinct tracking identifiers for comprehensive monitoring across both blockchain networks.

Transaction Identifiers:

  • Ethereum Transaction Hash: 0x55e6e9c295c7d32bc0d708f3363e42aad37298f9f30b4db0889e06bfac761157
  • CCIP Message ID: 0x11ef3640d8030bae75c2e4ca863a122bbf28fbbcd1dc83be0f77a25091442c72

CCIP Explorer (Primary monitoring interface):

https://ccip.chain.link/msg/0x11ef3640d8030bae75c2e4ca863a122bbf28fbbcd1dc83be0f77a25091442c72

The CCIP Explorer provides comprehensive transaction visibility:

  • Source chain (Ethereum) transaction confirmation
  • CCIP message processing and routing
  • Destination chain (Solana) message delivery
  • Token minting completion on Solana network

Ethereum Sepolia Explorer (Source chain verification):

https://sepolia.etherscan.io/tx/0x55e6e9c295c7d32bc0d708f3363e42aad37298f9f30b4db0889e06bfac761157

Test Solana Rate Limit Enforcement (Optional)

# Attempt to transfer 25 tokens (exceeds rate limit capacity of 20)
yarn svm:token-transfer \
  --token-mint $SOL_TOKEN_MINT \
  --token-amount 25000000000 \
  --receiver $ETH_RECEIVER_ADDRESS

The transaction correctly failed with RLMaxCapacityExceeded error because:

  • Attempted: 25 tokens (25000000000 in smallest units)
  • Limit: 20 tokens maximum capacity (20000000000)
  • Result: Rate limiter blocked the excessive transfer

Key Error Details:

  • Error Code: RLMaxCapacityExceeded (Error Number: 6015)
  • Source: programs/base-token-pool/src/rate_limiter.rs:48
  • Meaning: The token bucket capacity was exceeded

This demonstrates that your rate limits are properly configured and actively protecting against transfers that exceed the configured limits.

Test EVM Rate Limit Enforcement (Optional)

Test rate limits from the EVM side (Ethereum to Solana) using the same 25-token limit.

# Attempt to transfer 25 tokens from Ethereum (exceeds rate limit capacity of 20)
yarn evm:transfer \
  --token $ETH_TOKEN_ADDRESS \
  --amount 25000000000000000000 \
  --token-receiver $SOL_WALLET_ADDRESS

The transaction correctly failed because:

  • Attempted: 25 tokens (25000000000000000000 wei - 18 decimals)
  • Limit: 18 tokens maximum capacity
  • Error Selector: 0x1a76572a (TokenMaxCapacityExceeded)
  • Decoded Values: Capacity: 18000000000000000000, Attempted: 25000000000000000000, Token: 0x9cE471d0a7bE21ee32276dde49104fe02c812906

This demonstrates that rate limits are enforced on both chains, protecting the cross-chain bridge from excessive transfers. The EVM error selector corresponds to the TokenMaxCapacityExceeded error in the CCIP EVM v1.6.0 errors reference.

Get the latest Chainlink content straight to your inbox.