Multi-Chain
The SDK provides a unified Chain interface across all supported blockchain families. This guide covers connecting to each chain, cross-family messaging, and chain-specific details.
For EVM-only usage, see Sending Messages and Tracking Messages — they cover the full workflow without multi-chain considerations.
Supported Chains
| Chain Class | Family | Wallet Type | Send | Execute | Token Pools |
|---|---|---|---|---|---|
EVMChain | EVM | ethers Signer or viemWallet(client) | Yes | Yes | Yes |
SolanaChain | Solana | Anchor-compatible Wallet | Yes | Yes | Yes |
AptosChain | Aptos | AptosAsyncAccount | Yes | Yes | Yes |
SuiChain | Sui | Keypair | No | Yes | No |
TONChain | TON | TONWallet | Yes | Yes | No |
Sui is execution-only — it can execute incoming messages but cannot send cross-chain messages. Token pool queries are not implemented on Sui or TON.
Connecting
- EVM
- Solana
- Aptos
- TON
import { EVMChain } from '@chainlink/ccip-sdk'
const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
console.log('Connected to:', chain.network.name)
console.log('Chain selector:', chain.network.chainSelector)
import { SolanaChain } from '@chainlink/ccip-sdk'
const chain = await SolanaChain.fromUrl('https://api.devnet.solana.com')
console.log('Connected to:', chain.network.name)
import { AptosChain } from '@chainlink/ccip-sdk'
const chain = await AptosChain.fromUrl('https://fullnode.testnet.aptoslabs.com/v1')
console.log('Connected to:', chain.network.name)
import { TONChain } from '@chainlink/ccip-sdk'
const chain = await TONChain.fromUrl('https://toncenter.com/api/v2/jsonRPC')
console.log('Connected to:', chain.network.name)
All chains accept the same ChainContext options:
const chain = await EVMChain.fromUrl('https://rpc.sepolia.org', {
logger: console, // Custom logger
apiClient: null, // Disable CCIP API (RPC-only mode)
})
Unified Interface
All chain classes implement the Chain interface, so you can write chain-agnostic code:
import {
type Chain,
EVMChain,
SolanaChain,
AptosChain,
networkInfo,
ChainFamily,
} from '@chainlink/ccip-sdk'
function createChain(rpcUrl: string, family: ChainFamily): Promise<Chain> {
switch (family) {
case ChainFamily.EVM:
return EVMChain.fromUrl(rpcUrl)
case ChainFamily.Solana:
return SolanaChain.fromUrl(rpcUrl)
case ChainFamily.Aptos:
return AptosChain.fromUrl(rpcUrl)
default:
throw new Error(`Unsupported family: ${family}`)
}
}
// Works with any chain
async function getMessages(chain: Chain, txHash: string) {
const requests = await chain.getMessagesInTx(txHash)
for (const req of requests) {
console.log('Message ID:', req.message.messageId)
}
return requests
}
Cross-Chain Token Transfers
For token-only transfers (no data), the SDK auto-populates extraArgs with sensible defaults for the destination chain. You only need receiver and tokenAmounts:
import { EVMChain, networkInfo } from '@chainlink/ccip-sdk'
import { parseUnits } from 'viem'
const source = await EVMChain.fromUrl('https://rpc.sepolia.org')
const router = '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59'
// Same code works regardless of destination chain family
const destSelector = networkInfo('solana-devnet').chainSelector // or 'avalanche-testnet-fuji', 'aptos-testnet'
const tokenInfo = await source.getTokenInfo(tokenAddress)
const amount = parseUnits('10', tokenInfo.decimals)
const message = {
receiver: 'RecipientAddressOnDestChain...',
tokenAmounts: [{ token: tokenAddress, amount }],
}
const fee = await source.getFee({ router, destChainSelector: destSelector, message })
await source.sendMessage({
router,
destChainSelector: destSelector,
message,
wallet,
})
The SDK handles computeUnits (Solana), gasLimit (EVM/Aptos), tokenReceiver, allowOutOfOrderExecution, and other chain-specific fields automatically.
Cross-Family Messaging
For messages with data (arbitrary messaging or programmable token transfers), you must provide chain-specific extraArgs. The SDK encodes them automatically — just pass the right fields.
For full details on receiver contract setup and account configuration, see the dedicated tutorials:
- Build CCIP messages to Solana
- Build CCIP messages from Solana
- Build CCIP messages to Aptos
- Build CCIP messages from Aptos
- EVM → Solana
- Solana → EVM
- EVM → Aptos
- Aptos → EVM
import { EVMChain, networkInfo } from '@chainlink/ccip-sdk'
const source = await EVMChain.fromUrl('https://rpc.sepolia.org')
const destSelector = networkInfo('solana-devnet').chainSelector
const message = {
receiver: 'SolanaProgramAddress...',
data: '0x1234...',
tokenAmounts: [],
extraArgs: {
computeUnits: 200000n,
accountIsWritableBitmap: 0n,
allowOutOfOrderExecution: true,
tokenReceiver: '', // Required when sending data + tokens
accounts: [], // Additional accounts the program needs
},
}
const fee = await source.getFee({ router, destChainSelector: destSelector, message })
When sending data + tokens, tokenReceiver must be set to the Solana wallet or PDA that will receive the tokens.
import { SolanaChain, networkInfo } from '@chainlink/ccip-sdk'
const source = await SolanaChain.fromUrl('https://api.devnet.solana.com')
const destSelector = networkInfo('ethereum-testnet-sepolia').chainSelector
const message = {
receiver: '0xEVMReceiverAddress...',
data: Buffer.from('hello'),
tokenAmounts: [],
extraArgs: {
gasLimit: 200000n,
allowOutOfOrderExecution: false,
},
}
import { EVMChain, networkInfo } from '@chainlink/ccip-sdk'
const source = await EVMChain.fromUrl('https://rpc.sepolia.org')
const destSelector = networkInfo('aptos-testnet').chainSelector
const message = {
receiver: '0xAptosModuleAddress...',
data: '0x1234...',
tokenAmounts: [],
extraArgs: {
gasLimit: 200000n,
allowOutOfOrderExecution: true,
},
}
const fee = await source.getFee({ router, destChainSelector: destSelector, message })
import { AptosChain, networkInfo } from '@chainlink/ccip-sdk'
const source = await AptosChain.fromUrl('https://fullnode.testnet.aptoslabs.com/v1')
const destSelector = networkInfo('ethereum-testnet-sepolia').chainSelector
const message = {
receiver: '0xEVMReceiverAddress...',
data: new Uint8Array([0x12, 0x34]),
tokenAmounts: [],
extraArgs: {
gasLimit: 200000n,
allowOutOfOrderExecution: false,
},
}
Extra Args by Destination
The extraArgs fields depend on the destination chain family:
| Destination | Extra Args Type | Key Fields |
|---|---|---|
| EVM | EVMExtraArgsV1/V2/V3 | gasLimit, allowOutOfOrderExecution |
| Solana | SVMExtraArgsV1 | computeUnits, accounts, accountIsWritableBitmap, tokenReceiver, allowOutOfOrderExecution |
| Aptos | EVMExtraArgsV2 | gasLimit, allowOutOfOrderExecution |
| Sui | SuiExtraArgsV1 | gasLimit, allowOutOfOrderExecution, tokenReceiver, receiverObjectIds |
| TON | EVMExtraArgsV2 | gasLimit, allowOutOfOrderExecution |
For token-only transfers (no data), you can omit extraArgs entirely — the SDK fills in defaults.
Solana Notes
Transaction Flow
Solana uses transaction signatures (not hashes). Use getTransaction() to fetch details:
const chain = await SolanaChain.fromUrl('https://api.devnet.solana.com')
const tx = await chain.getTransaction(signature)
const messages = await chain.getMessagesInTx(tx)
Unsigned Transactions
For browser wallets (Phantom, Solflare), generate unsigned transactions and let the wallet handle signing, compute budget, and priority fees:
import { SolanaChain } from '@chainlink/ccip-sdk'
import { TransactionMessage, VersionedTransaction } from '@solana/web3.js'
const chain = await SolanaChain.fromUrl('https://api.devnet.solana.com')
const unsignedTx = await chain.generateUnsignedSendMessage({
sender: walletPublicKey.toBase58(),
router: routerAddress,
destChainSelector,
message,
})
// Build versioned transaction
const { blockhash } = await connection.getLatestBlockhash()
const messageV0 = new TransactionMessage({
payerKey: walletPublicKey,
recentBlockhash: blockhash,
instructions: unsignedTx.instructions,
}).compileToV0Message(unsignedTx.lookupTables)
const transaction = new VersionedTransaction(messageV0)
const signature = await sendTransaction(transaction, connection)
Related
- Sending Messages — EVM send workflow, message types, extra args
- Tracking Messages — Track message lifecycle
- Querying Data — Network info, balances, fees
- Token Pools — Pool configuration and rate limits