Cross-Chain Token Setup: BurnMint with SPL Token Multisig Tutorial

This educational tutorial demonstrates how to create and configure cross-chain tokens using Chainlink's Cross-Chain Interoperability Protocol (CCIP) between Solana Devnet and Ethereum Sepolia using SPL token multisig concepts. You will learn to implement the SPL token multisig approach within Path A from the CCIP Cross-Chain Token Integration Guide.

Path A Mint Authority Options:

  • Direct Transfer: Transfer mint authority directly to Pool Signer PDA - suitable for development and testing (see tutorial)
  • Multisig Setup (this tutorial): Learn SPL token multisig concepts with Pool Signer PDA as a member - foundation for production systems

This tutorial focuses on demonstrating multisig architecture concepts, helping you understand governance controls while maintaining autonomous cross-chain token transfers through BurnMint token pools.

Tutorial Approach

This tutorial provides step-by-step instructions and explains what each command does. For detailed implementation code explanations, refer to the comprehensive READMEs in both repositories:

The READMEs contain detailed technical explanations, troubleshooting guides, and advanced configuration options.

Prerequisites

This tutorial requires setting up two different repositories in separate terminal windows. Follow the setup instructions for both environments before proceeding.

Development Environment Requirements

System Requirements:

  • Anchor and Solana CLI Tools: Install following the installation guide. Requires Rust to be installed.
  • Node.js v20 or higher: Use the nvm package to install and manage versions. Verify with node -v
  • Yarn: For dependency management
  • Git: For cloning repositories

Terminal 1: Solana Starter Kit Setup

Clone and setup the Solana Starter Kit:

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

Install dependencies:

yarn install

Configure your Solana environment:

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

# Set your keypair (create one if needed)
solana config set --keypair ~/.config/solana/id.json

# If you do not have a keypair, create one:
solana-keygen new --outfile ~/.config/solana/id.json

Fund your Solana wallet:

# Get your wallet address
solana address

# Request SOL from the devnet faucet
solana airdrop 2

Verify your setup:

# Check your SOL balance
solana balance

# Verify you are on devnet
solana config get

Terminal 2: Smart Contract Examples Setup

Clone the repository and navigate to the Hardhat project:

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

Install and compile dependencies:

npm install
npm run compile

Set up encrypted environment variables:

# Set encryption password
npx env-enc set-pw

# Configure environment variables
npx env-enc set

Required environment variables for Ethereum Sepolia:

Fund your wallet:

Introduction

This tutorial covers the complete implementation of cross-chain tokens using the production multisig approach within Path A from the CCIP Cross-Chain Token Integration Guide. When you complete this tutorial, you will have:

  • Ethereum Sepolia: ERC20 token with CCIP BurnMint pool
  • Solana Devnet: SPL token with CCIP BurnMint pool and multisig governance
  • Bidirectional Transfers: Working token transfers in both directions
  • Production Security: Multisig governance with autonomous CCIP operations

What You Will Build

This tutorial provides a complete implementation of the production multisig variant of Path A from the CCIP Cross-Chain Token Integration Guide. This approach is designed for production environments where you implement proper governance controls while maintaining autonomous CCIP operations.

Key Approach: You will create an SPL token multisig that includes the Pool Signer PDA as a required member, enabling both autonomous CCIP operations and governance-controlled minting.

In this tutorial, you will:

  • Deploy and configure ERC20 tokens on Ethereum Sepolia
  • Create SPL tokens with SPL token multisig on Solana Devnet
  • Implement self-service CCIP administrator registration
  • Configure BurnMint token pools with multisig mint authority
  • Set up Address Lookup Tables for efficient cross-chain operations
  • Test bidirectional token transfers and monitor transactions

Understanding Multisig Configuration

Before proceeding with the implementation, it is crucial to understand the multisig configuration used in this tutorial and its production implications.

SPL Token Multisig Overview

This tutorial implements a 1-of-2 SPL token multisig configuration where:

  • Threshold (M): 1 signature required
  • Total Signers (N): 2 signers total
  • Pool Signer PDA: Appears 1 time (= M) as a required signer
  • Admin Signer: Your wallet appears 1 time for manual control (tutorial simplification)

Why This Configuration Works

Pool Autonomy: The Pool Signer PDA appears M=1 times, enabling autonomous CCIP operations without manual governance intervention.

Admin Control: Your wallet can independently mint tokens for other use cases (airdrops, rewards, etc.) without pool involvement. Note: This tutorial uses a single wallet for simplicity, but production deployments should use a governance multisig such as Squads.

Flexibility: Both the pool and admin wallet can mint tokens independently, providing operational flexibility while maintaining security.

Production Scalability: This pattern scales to higher thresholds (2-of-4, 3-of-6, etc.) for enhanced production security.

For comprehensive multisig configuration options and security considerations, see Mint Authority Management.

Technical Architecture

Cross-Chain Token Mechanism

The BurnMint mechanism ensures total supply consistency across chains by burning tokens on the source chain and minting equivalent tokens on the destination chain.

Key Infrastructure

The critical component for this tutorial is the Chainlink-deployed Pool Program that you will use to initialize your token pool:

  • Pool Program: 3BrkN1XcyeafuMZxomLZBUVdasEtpdMmpWfsEQmzN7vo
    • This is the standard BurnMint pool program deployed by Chainlink for self-serve mode
    • You will use this program address to initialize your own token pool
    • All other components (token addresses, PDAs, etc.) will be unique to your deployment

Understanding the Tutorial Multisig Setup

This tutorial creates a 1-of-2 SPL token multisig with the following signers:

  • Pool Signer PDA: Enables autonomous CCIP operations (burns/mints for cross-chain transfers)
  • Admin Wallet: Your single-signature wallet for manual operations (tutorial simplification)

Tutorial vs Production:

  • Tutorial: Uses single wallet as admin signer for simplicity and learning
  • Production: Replace admin wallet with proper governance multisig (e.g., Squads)

Important Distinction: This SPL token multisig only controls mint authority. For comprehensive governance (CCIP admin roles, pool ownership), you need a separate governance system.

Phase 1: Ethereum Sepolia Token Setup

In this step, you will use Hardhat tasks to deploy an ERC20 token contract and a corresponding burn and mint token pool on Ethereum Sepolia. The tasks interact with the BurnMintERC20 contract for token deployment and the BurnMintTokenPool contract for pool creation.

Deploy ERC20 Token

Using the deployToken task, deploy a burnable and mintable ERC20 token on Ethereum Sepolia:

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

Deploy Token Pool

In this step, you will use the deployTokenPool task to deploy a CCIP BurnMint token pool for the token on Ethereum Sepolia. This task interacts with the BurnMintTokenPool contract and grants the necessary mint and burn privileges to the pool.

# Set token address from deployment
export ETH_TOKEN_ADDRESS=0xd3d2f66dd4Eead2cF2cD4C6219Eb1e1E80e6109d

# Deploy BurnMint pool

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

Configure CCIP Administration

Mint Tokens

In this step, you will use the mintTokens task to mint tokens on Ethereum Sepolia for your Externally Owned Account (EOA). Since you assigned mint and burn privileges to your EOA when deploying the tokens, you can now mint tokens for testing purposes. This ensures that you have enough tokens in your EOA to perform cross-chain transfers later.

# Mint initial token supply for testing
npx hardhat mintTokens \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --amount 1000000000000000000000 \
  --network sepolia

Claim Admin

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
npx hardhat claimAdmin \
  --tokenaddress $ETH_TOKEN_ADDRESS \
  --network sepolia

Accept 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
npx hardhat acceptAdminRole \
 --tokenaddress $ETH_TOKEN_ADDRESS \
 --network sepolia

Phase 2: Solana Devnet Token Setup

In this phase, you will create an SPL token, initialize the CCIP token pool, and complete CCIP registration BEFORE setting up the production multisig. This sequence is critical because the self-service registration requires you to hold the mint authority.

Create SPL Token

In this step, you will use the svm:token:create script to create an SPL token with Metaplex metadata support on Solana Devnet. This script leverages the TokenManager library to create a comprehensive token with metadata, initial supply, and proper configuration for CCIP integration.

# Create SPL token with default configuration
yarn svm:token:create

Set Environment Variables

Set up environment variables for easier reference throughout the tutorial:

# Set your token mint address from the previous output
export TOKEN_MINT=9qfyXQhZt1ZUvEX1kKephoTqpiXnh1FBskRFB6udRwQG

# Set your admin wallet (tutorial simplification - use governance multisig in production)
export ADMIN_WALLET=$(solana address)

# Set pool program (Chainlink self-service BurnMint pool program)
export POOL_PROGRAM=3BrkN1XcyeafuMZxomLZBUVdasEtpdMmpWfsEQmzN7vo

# Display for verification
echo "Token Mint: $TOKEN_MINT"
echo "Admin Wallet: $ADMIN_WALLET"
echo "Pool Program: $POOL_PROGRAM"

Initialize CCIP Token Pool

In this step, you will use the svm:pool:initialize script to initialize a CCIP token pool for your SPL token. This process creates the necessary on-chain state for cross-chain operations and establishes the Pool Signer PDA that will manage token operations.

# Initialize pool for your token
yarn svm:pool:initialize \
  --token-mint $TOKEN_MINT \
  --burn-mint-pool-program $POOL_PROGRAM

Verify Pool Creation

In this step, you will use the svm:pool:get-info script to verify that your token pool was initialized correctly. This command queries the on-chain state and displays comprehensive information about your pool configuration, including the Pool Signer PDA and current ownership.

# Verify pool creation
yarn svm:pool:get-info \
  --token-mint $TOKEN_MINT \
  --burn-mint-pool-program $POOL_PROGRAM

Create Pool Token Account

In this step, you will use the svm:pool:create-token-account script to 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 ATA for Pool Signer PDA
yarn svm:pool:create-token-account \
  --token-mint $TOKEN_MINT \
  --burn-mint-pool-program $POOL_PROGRAM

Set Pool Signer Environment Variable

Set the Pool Signer PDA for use in multisig creation:

# Set Pool Signer PDA from the output above
export POOL_SIGNER_PDA=7WTnVd9Q6de3GgPCpDfHX5S2T5XzGDeMDZRev3FVbZo5

Administrator Registration

In this step, you will use the svm:admin:propose-administrator and svm:admin:accept-admin-role scripts to register yourself as the CCIP administrator for the Solana token. This process establishes your control over the token's CCIP configuration on Solana.

# Propose yourself as CCIP administrator (requires mint authority)

yarn svm:admin:propose-administrator \
 --token-mint $TOKEN_MINT

# Accept the proposed administrator role
yarn svm:admin:accept-admin-role \
  --token-mint $TOKEN_MINT

Create Production Multisig

NOW that CCIP registration is complete, create the SPL token multisig that will serve as the mint authority:

# Create 1-of-2 multisig with Pool Signer PDA and admin wallet (tutorial setup)
spl-token create-multisig 1 $POOL_SIGNER_PDA $ADMIN_WALLET

Set the multisig address environment variable:

# Set multisig address from the output above
export MULTISIG_ADDRESS=EFpxujcKwYiLhNZyt9NWUsDqFDMUGbsRBdqqvxrfuvGj

Transfer Mint Authority to Multisig

Now transfer the mint authority from your wallet to the multisig:

# Transfer mint authority to multisig
spl-token authorize $TOKEN_MINT mint $MULTISIG_ADDRESS

Verify Multisig Configuration

Verify that the multisig has been properly configured and the mint authority has been transferred:

# Check token mint authority
spl-token display $TOKEN_MINT

# Check multisig details
spl-token display $MULTISIG_ADDRESS

Phase 3: Cross-Chain Configuration

In this step, you will configure bidirectional connectivity between the token pools on both chains. Each chain uses different tools: Solana uses Starter Kit scripts to configure its pool to recognize Ethereum tokens and pools, while Ethereum uses Hardhat tasks to configure its pool to recognize Solana tokens and pools.

Configure Solana โ†’ Ethereum

First, set the environment variables needed for this terminal session:

# Set Ethereum token and pool addresses from Phase 1
export ETH_TOKEN_ADDRESS=0xd3d2f66dd4Eead2cF2cD4C6219Eb1e1E80e6109d
export ETH_POOL_ADDRESS=0x1a65BA94731a6541efDbeb1E72A7b1E9771B64b7

Initialize Chain Remote Configuration

In this step, you will use the svm:pool:init-chain-remote-config script to 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 for Ethereum Sepolia
yarn svm:pool:init-chain-remote-config \
  --token-mint $TOKEN_MINT \
  --burn-mint-pool-program $POOL_PROGRAM \
  --remote-chain ethereum-sepolia \
  --token-address $ETH_TOKEN_ADDRESS \
  --decimals 9

Add Ethereum Pool Address

In this step, you will use the svm:pool:edit-chain-remote-config script to 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 the configuration
yarn svm:pool:edit-chain-remote-config \
  --token-mint $TOKEN_MINT \
  --burn-mint-pool-program $POOL_PROGRAM \
  --remote-chain ethereum-sepolia \
  --pool-addresses $ETH_POOL_ADDRESS \
  --token-address $ETH_TOKEN_ADDRESS \
  --decimals 9

Verify Configuration

In this step, you will use the svm:pool:get-chain-config script to verify that the Solana pool configuration for Ethereum Sepolia has been set up correctly with both the token address and pool address.

# Verify the chain configuration is complete
yarn svm:pool:get-chain-config \
  --token-mint $TOKEN_MINT \
  --burn-mint-pool-program $POOL_PROGRAM \
  --remote-chain ethereum-sepolia

Configure Ethereum โ†’ Solana

First, set the environment variables needed for this terminal session:

# Set variables from previous phases (these were created in different terminals)
export ETH_TOKEN_ADDRESS=0xd3d2f66dd4Eead2cF2cD4C6219Eb1e1E80e6109d
export ETH_POOL_ADDRESS=0x1a65BA94731a6541efDbeb1E72A7b1E9771B64b7
export TOKEN_MINT=9qfyXQhZt1ZUvEX1kKephoTqpiXnh1FBskRFB6udRwQG
export POOL_PROGRAM=3BrkN1XcyeafuMZxomLZBUVdasEtpdMmpWfsEQmzN7vo

Calculate Solana Pool Config PDA

In this step, you will use the calculateCCIPTokenPoolPDA.ts script to calculate the Pool Config Program Derived Address (PDA) for your Solana token pool. This PDA represents the configuration account of your Solana token pool and is what the Ethereum pool needs to know about to communicate with your Solana pool during cross-chain transfers.

# Calculate the Pool Config PDA for Ethereum cross-chain configuration
npx tsx scripts/calculateCCIPTokenPoolPDA.ts \
  $TOKEN_MINT \
  $POOL_PROGRAM

Export Pool Config PDA

Export the calculated Pool Config PDA to use in the next command:

# Export the Pool Config PDA (use the address from the previous step output)
export SOLANA_POOL_CONFIG_PDA=B6yVeSSESLtmyaqwnzWDAxg8ZrSz58Z9WezYaK1SK9jc

Apply Chain Updates

In this step, you will use the applyChainUpdates Hardhat task to 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 $SOLANA_POOL_CONFIG_PDA \
  --remotetokenaddress $TOKEN_MINT \
  --network sepolia

Phase 4: Pool Registration

In this step, you will register the token pools with their respective tokens on both chains. This is the final configuration step that enables cross-chain operations by linking tokens to their pools in the CCIP registry.

Pool registration works differently on each platform:

  • Ethereum: Links the token directly to its pool contract address
  • Solana: Links the token to an Address Lookup Table containing all necessary pool accounts

Ethereum Sepolia Pool Registration

In this step, you will use the setPool Hardhat 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

Solana Devnet Pool Registration

Create Address Lookup Table

Create an Address Lookup Table (ALT) containing all required accounts for CCIP operations:

# Create Address Lookup Table with all required accounts + your multisig address
yarn svm:admin:create-alt \
  --token-mint $TOKEN_MINT \
  --pool-program $POOL_PROGRAM \
  --additional-addresses $MULTISIG_ADDRESS

Set ALT environment variable:

export ALT_ADDRESS=9TG3CoF2XyLAH8usfCm7ADJQNxopEAu4CyiWmYCEVuUw

Register Pool with Router

Register your token pool with the CCIP Router using the Address Lookup Table:

# Register pool with CCIP Router using the ALT
yarn svm:admin:set-pool \
  --token-mint $TOKEN_MINT \
  --lookup-table $ALT_ADDRESS \
  --writable-indices 3,4,7

Phase 5: Testing Cross-Chain Transfers

Test the complete cross-chain token transfer functionality in both directions using the production multisig configuration.

Transfer Direction 1: Solana โ†’ Ethereum

Prepare for Testing

Your Associated Token Account (ATA) was already created during token creation in Phase 1. Now verify your token balance and prepare for cross-chain transfers:

# Check your current token balance (should show 1000 from Phase 1)
spl-token balance $TOKEN_MINT

# Delegate tokens for CCIP transfers

yarn svm:token:delegate --token-mint $TOKEN_MINT

# Verify the delegation was successful

yarn svm:token:check --token-mint $TOKEN_MINT

Execute Transfer

# Execute cross-chain transfer from Solana to Ethereum
yarn svm:token-transfer --token-mint $TOKEN_MINT

Customization Options:

You can override the default transfer amount and destination receiver:

# Override amount and receiver address
yarn svm:token-transfer --token-mint $TOKEN_MINT --token-amount 1000000 --receiver 0x9d087fC03ae39b088326b67fA3C788236645b717

Parameters:

  • --token-amount: Amount to transfer (in token base units)
  • --receiver: EVM address that will receive the tokens on the destination chain

Monitor and Verify Transaction

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

Transaction Identifiers:

  • Transaction Signature: 5iq6ZVF7TePnaM4SZmo5xFkGpawmqsp6racfECqsbEzeXQJfmnS4A2QMkRyoqwxZKww1LhhicN8G1EenkfkF1TW7
  • CCIP Message ID: 0xec5c9fca307facf1296014510a65f7b95449991bf59182c0724dcdd2db4d77ef

CCIP Explorer (Primary monitoring interface):

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

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/5iq6ZVF7TePnaM4SZmo5xFkGpawmqsp6racfECqsbEzeXQJfmnS4A2QMkRyoqwxZKww1LhhicN8G1EenkfkF1TW7?cluster=devnet

Transfer Direction 2: Ethereum โ†’ Solana

Execute Transfer

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

Customization Options:

You can override the default transfer amount and destination receiver:

# Override amount and receiver address
yarn evm:transfer --token $ETH_TOKEN_ADDRESS --amount 1000000000000000000 --token-receiver EPUjBP3Xf76K1VKsDSc6GupBWE8uykNksCLJgXZn87CB

Parameters:

  • --amount: Amount to transfer (in wei for 18-decimal tokens)
  • --token-receiver: Solana wallet address that will receive the tokens on the destination chain

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: 0x5fe6adda5c98831731860800e1f779915b51974f5433166bf50169d139c9499a
  • CCIP Message ID: 0x3d28c9aa40c6059c7b592c9824b5ca85c6ba3e4c5afa84a4b3fcd495cba93828

CCIP Explorer (Primary monitoring interface):

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

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/0x5fe6adda5c98831731860800e1f779915b51974f5433166bf50169d139c9499a

Optional: Verify Mint Authority Control

Demonstrate Manual Minting Through Multisig

This optional section demonstrates that transferring mint authority to the multisig doesn't mean "losing control" - you can still mint tokens manually through the Admin Wallet (the non-PDA signer in your multisig). This proves the multisig setup is working correctly and that you retain administrative capabilities.

# Demonstrate manual minting through the multisig
# This proves you haven't "lost" mint authority - it's just managed through the multisig
spl-token mint $TOKEN_MINT 1 \
  --owner $MULTISIG_ADDRESS \
  --multisig-signer $HOME/.config/solana/id.json

Get the latest Chainlink content straight to your inbox.