Skip to main content
Version: 0.96.0

Multi-Chain

Supported Chains

Chain ClassFamilyNetworks
EVMChainEVMEthereum, Arbitrum, Optimism, Avalanche, Base, Polygon, BSC
SolanaChainSolanaSolana Mainnet, Devnet
AptosChainAptosAptos Mainnet, Testnet
SuiChainSuiSui Mainnet, Testnet
TONChainTONTON Mainnet, Testnet

Connecting

TypeScript
import { EVMChain } from '@chainlink/ccip-sdk'

// From RPC URL
const ethereum = await EVMChain.fromUrl('https://rpc.sepolia.org')
const arbitrum = await EVMChain.fromUrl('https://sepolia-rollup.arbitrum.io/rpc')

// With custom logger
const chain = await EVMChain.fromUrl(
'https://rpc.sepolia.org',
{ logger: console }
)

console.log('Connected to:', chain.network.name)
console.log('Chain selector:', chain.network.chainSelector)

Unified Interface

All chain classes implement the Chain interface for chain-agnostic code:

TypeScript
import { Chain, EVMChain, SolanaChain } from '@chainlink/ccip-sdk'

async function trackMessage(chain: Chain, txHash: string) {
const requests = await chain.getMessagesInTx(txHash)
return requests
}

// Use with EVM
const evmChain = await EVMChain.fromUrl('https://rpc.sepolia.org')
const evmRequests = await trackMessage(evmChain, '0x1234...')

// Use with Solana
const solanaChain = await SolanaChain.fromUrl('https://api.devnet.solana.com')
const solanaRequests = await trackMessage(solanaChain, 'base58signature...')

Cross-Family Messaging

EVM to Solana

TypeScript
import { EVMChain, encodeExtraArgs, networkInfo } from '@chainlink/ccip-sdk'

const source = await EVMChain.fromUrl('https://rpc.sepolia.org')

const solanaReceiver = '...' // Solana program address

const message = {
receiver: solanaReceiver,
data: '0x1234...',
tokenAmounts: [],
feeToken: '0x' + '0'.repeat(40),
extraArgs: encodeExtraArgs({
computeUnits: 200000n,
accountIsWritableBitmap: 0n,
allowOutOfOrderExecution: true,
tokenReceiver: '',
accounts: [],
}),
}

const destSelector = networkInfo('solana-devnet').chainSelector
const fee = await source.getFee({
router: source.network.router,
destChainSelector: destSelector,
message,
})

Solana to EVM

TypeScript
import { SolanaChain, encodeExtraArgs, networkInfo } from '@chainlink/ccip-sdk'

const source = await SolanaChain.fromUrl('https://api.devnet.solana.com')

const message = {
receiver: '0xEVMReceiverAddress...',
data: Buffer.from('hello'),
tokenAmounts: [],
extraArgs: encodeExtraArgs({
gasLimit: 200000n,
allowOutOfOrderExecution: false,
}),
}

const destSelector = networkInfo('ethereum-testnet-sepolia').chainSelector

Chain-Specific Notes

EVM

  • Full log filtering by topic and address
  • Supports block range pagination
  • Transaction receipts include gas used
TypeScript
import { EVMChain } from '@chainlink/ccip-sdk'

const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')

for await (const log of chain.getLogs({
topics: ['CCIPMessageSent'],
startBlock: 1000000,
endBlock: 1001000,
})) {
console.log('Log at block:', log.blockNumber)
}

Solana

  • Requires program address for log filtering
  • Uses signatures instead of transaction hashes
  • Account-based model affects message structure
TypeScript
import { SolanaChain } from '@chainlink/ccip-sdk'

const chain = await SolanaChain.fromUrl('https://api.devnet.solana.com')

for await (const log of chain.getLogs({
address: 'CCIPProgramAddress...',
topics: ['CCIPMessageSent'],
})) {
console.log('Found message')
}

Solana Transaction Flow

For Solana, use getTransaction() to fetch transaction details before extracting messages:

TypeScript
import { SolanaChain } from '@chainlink/ccip-sdk'

const chain = await SolanaChain.fromUrl('https://api.devnet.solana.com')

// Fetch transaction details from signature
const tx = await chain.getTransaction(signature)

// Extract messages from the transaction
const messages = await chain.getMessagesInTx(tx)
const messageId = messages[0]?.message.messageId

Solana Wallet Integration

Modern wallets like Phantom automatically add compute budget and priority fee instructions:

TypeScript
import { SolanaChain } from '@chainlink/ccip-sdk'
import { TransactionMessage, VersionedTransaction } from '@solana/web3.js'

const chain = await SolanaChain.fromUrl('https://api.devnet.solana.com')

// Generate unsigned transaction
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)

// Wallet automatically adds compute budget and priority fees
const signature = await sendTransaction(transaction, connection)

Aptos

  • Move-based smart contracts
  • Different event structure
  • Sequence numbers for ordering
TypeScript
import { AptosChain } from '@chainlink/ccip-sdk'

const chain = await AptosChain.fromUrl('https://fullnode.testnet.aptoslabs.com/v1')

const requests = await chain.getMessagesInTx('0xAptosTransactionHash...')

Network Information

Use networkInfo to get chain details:

TypeScript
import { networkInfo } from '@chainlink/ccip-sdk'

// Get by network name
const sepolia = networkInfo('ethereum-testnet-sepolia')
console.log('Chain selector:', sepolia.chainSelector)
console.log('Family:', sepolia.family) // 'EVM'

// Get by chain selector
const fuji = networkInfo(14767482510784806043n)
console.log('Network name:', fuji.name) // 'avalanche-testnet-fuji'

// Check chain family
const solana = networkInfo('solana-devnet')
console.log('Is EVM:', solana.family === 'EVM') // false
console.log('Is Solana:', solana.family === 'SVM') // true

Chain-Agnostic Code

Use the base Chain type for maximum flexibility:

TypeScript
import {
Chain,
EVMChain,
SolanaChain,
networkInfo,
ChainFamily
} from '@chainlink/ccip-sdk'

async function getChain(networkName: string): Promise<Chain> {
const network = networkInfo(networkName)

switch (network.family) {
case ChainFamily.EVM:
return EVMChain.fromUrl(getRpcUrl(networkName))
case ChainFamily.Solana:
return SolanaChain.fromUrl(getRpcUrl(networkName))
default:
throw new Error(`Unsupported chain family: ${network.family}`)
}
}

async function trackAnyMessage(networkName: string, txHash: string) {
const chain = await getChain(networkName)
const requests = await chain.getMessagesInTx(txHash)

console.log('Found', requests.length, 'messages on', networkName)
return requests
}

Complete Example

Track a cross-chain message from any source to any destination:

TypeScript
import {
EVMChain,
SolanaChain,
discoverOffRamp,
networkInfo,
ChainFamily,
type Chain,
} from '@chainlink/ccip-sdk'

async function createChain(networkName: string, rpcUrl: string): Promise<Chain> {
const { family } = networkInfo(networkName)
switch (family) {
case ChainFamily.EVM:
return EVMChain.fromUrl(rpcUrl)
case ChainFamily.Solana:
return SolanaChain.fromUrl(rpcUrl)
default:
throw new Error(`Unsupported: ${family}`)
}
}

async function trackCrossChainMessage(
sourceNetwork: string,
sourceRpc: string,
destNetwork: string,
destRpc: string,
sourceTxHash: string
) {
const source = await createChain(sourceNetwork, sourceRpc)
const dest = await createChain(destNetwork, destRpc)

console.log(`Tracking ${sourceNetwork} -> ${destNetwork}`)

const requests = await source.getMessagesInTx(sourceTxHash)
const request = requests[0]
console.log('Message ID:', request.message.messageId)

const offRamp = await discoverOffRamp(source, dest, request.lane.onRamp)
console.log('OffRamp:', offRamp)

for await (const execution of dest.getExecutionReceipts({
offRamp,
messageId: request.message.messageId,
})) {
console.log('Execution state:', execution.receipt.state)
return execution
}

console.log('Not yet executed')
return null
}

// Track EVM to EVM
await trackCrossChainMessage(
'ethereum-testnet-sepolia',
'https://rpc.sepolia.org',
'avalanche-testnet-fuji',
'https://rpc.fuji.avax.network',
'0x1234...'
)

// Track EVM to Solana
await trackCrossChainMessage(
'ethereum-testnet-sepolia',
'https://rpc.sepolia.org',
'solana-devnet',
'https://api.devnet.solana.com',
'0x5678...'
)