Skip to main content
Version: 1.14.0

Querying Data

The SDK provides methods to query network information, token metadata, lane capabilities, balances, and fees. Most methods are RPC-based and work without the CCIP API.

Network Information​

Look Up Chains​

networkInfo resolves chain selectors, chain IDs, and chain names to a NetworkInfo object. The result is memoized.

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

// By name
const sepolia = networkInfo('ethereum-testnet-sepolia')

// By chain ID (number)
const mainnet = networkInfo(1)

// By CCIP chain selector (bigint)
const fuji = networkInfo(14767482510784806043n)

console.log('Name:', sepolia.name) // 'ethereum-testnet-sepolia'
console.log('Chain ID:', sepolia.chainId) // 11155111
console.log('Selector:', sepolia.chainSelector) // 16015286601757825753n
console.log('Family:', sepolia.family) // 'EVM'

Throws CCIPChainNotFoundError if the chain is not supported by CCIP.

Chain Selectors vs Chain IDs​

CCIP uses chain selectors (not chain IDs) to identify networks. Always use selectors for destChainSelector, lane configuration, and fee estimation:

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

// Wrong: chain ID
const destChain = 84532 // Base Sepolia chain ID — DON'T USE

// Correct: CCIP chain selector
const destSelector = networkInfo('ethereum-testnet-sepolia-base-1').chainSelector
// => 10344971235874465080n

Token Queries​

Token Metadata​

getTokenInfo returns the symbol, decimals, and optional name for any token. RPC-only.

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

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

const tokenInfo = await chain.getTokenInfo('0xTokenAddress...')

console.log('Symbol:', tokenInfo.symbol) // e.g. 'LINK'
console.log('Decimals:', tokenInfo.decimals) // e.g. 18
console.log('Name:', tokenInfo.name) // e.g. 'ChainLink Token' (optional)

Use getTokenInfo whenever you need to parse or format token amounts:

TypeScript
import { parseUnits, formatUnits } from 'viem'

const tokenInfo = await chain.getTokenInfo(tokenAddress)

// Parse user input with correct decimals
const amount = parseUnits('10.5', tokenInfo.decimals)

// Format on-chain amounts for display
const display = formatUnits(balance, tokenInfo.decimals)
console.log(`${display} ${tokenInfo.symbol}`)

Supported Tokens​

Query which tokens are configured for cross-chain transfers on a given router:

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

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

// Step 1: Get the TokenAdminRegistry address
const registry = await chain.getTokenAdminRegistryFor(router)

// Step 2: Get all configured tokens
const tokens = await chain.getSupportedTokens(registry)

for (const token of tokens) {
const info = await chain.getTokenInfo(token)
console.log(`${info.symbol} (${info.decimals} decimals): ${token}`)
}

getTokenAdminRegistryFor accepts a Router, OnRamp, or OffRamp address — it auto-detects the contract type and resolves the registry.

Fee Tokens​

getFeeTokens returns the tokens accepted for paying CCIP fees on a given router, along with their metadata:

TypeScript
const router = '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59'
const feeTokens = await chain.getFeeTokens(router)

// Record<string, TokenInfo> — token address → { symbol, decimals, name? }
for (const [address, info] of Object.entries(feeTokens)) {
console.log(`${info.symbol}: ${address}`)
}

Lane Queries​

Lane Capabilities​

getLaneFeatures checks what features are available on a specific lane, including Faster-Than-Finality (FTF) support and rate limits:

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

const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
const router = '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59'
const destSelector = networkInfo('avalanche-testnet-fuji').chainSelector

const features = await chain.getLaneFeatures({
router,
destChainSelector: destSelector,
token: '0xTokenAddress...', // pass to get token-specific FTF and rate limit info
})

Returns Partial<LaneFeatures> — not all fields are guaranteed:

FieldTypeMeaning
FINALITY_FASTnumber | undefinedundefined = pre-v2.0 lane, 0 = v2.0+ but FTF not enabled for this token, >0 = FTF enabled
FINALITY_SAFEtrue | undefinedtrue = FCR/safe finality supported on this lane
RATE_LIMITSRateLimiterState | nullStandard rate limiter state (null if disabled)
FAST_RATE_LIMITSRateLimiterState | nullFTF/FCR-specific rate limiter (only when FINALITY_FAST > 0 or FINALITY_SAFE)
note

getLaneFeatures is implemented for EVM chains only.

For the full FTF workflow — checking availability, sending with FTF, estimating latency and fees, and rate limits — see Faster-Than-Finality.

Lane Latency​

getLaneLatency estimates the delivery time for a lane. Requires the CCIP API (enabled by default).

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

const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
const destSelector = networkInfo('avalanche-testnet-fuji').chainSelector

const latency = await chain.getLaneLatency(destSelector)
console.log(`Estimated delivery: ~${Math.round(latency.totalMs / 60000)} minutes`)

Throws CCIPApiClientNotAvailableError if you created the chain with apiClient: null. Throws CCIPLaneNotFoundError if the lane doesn't exist.

Fee Estimation​

getFee returns the fee amount in the fee token's smallest units. RPC-only — calls the Router contract directly.

TypeScript
import { EVMChain, networkInfo } from '@chainlink/ccip-sdk'
import { parseUnits, formatUnits } from 'viem'

const chain = await EVMChain.fromUrl('https://rpc.sepolia.org')
const router = '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59'
const destSelector = networkInfo('avalanche-testnet-fuji').chainSelector

const tokenAddress = '0xTokenAddress...'
const tokenInfo = await chain.getTokenInfo(tokenAddress)
const amount = parseUnits('10', tokenInfo.decimals)

const fee = await chain.getFee({
router,
destChainSelector: destSelector,
message: {
receiver: '0xReceiverAddress...',
data: '0x',
tokenAmounts: [{ token: tokenAddress, amount }],
},
})

// Fee is in native token units (wei) when paying with native, or fee token units
console.log('Fee:', formatUnits(fee, 18), 'ETH')

To pay with a specific fee token (e.g., LINK), set feeToken in the message:

TypeScript
const fee = await chain.getFee({
router,
destChainSelector: destSelector,
message: {
receiver: '0xReceiverAddress...',
data: '0x',
tokenAmounts: [],
feeToken: '0xLINKAddress...', // Pay fees in LINK
},
})

Balance Queries​

getBalance returns balances in the token's smallest units. RPC-only.

TypeScript
import { EVMChain } from '@chainlink/ccip-sdk'
import { formatEther, formatUnits } from 'viem'

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

// Native token balance (ETH)
const nativeBalance = await chain.getBalance({ holder })
console.log('ETH:', formatEther(nativeBalance))

// ERC-20 token balance
const tokenAddress = '0xTokenAddress...'
const tokenBalance = await chain.getBalance({ holder, token: tokenAddress })
const tokenInfo = await chain.getTokenInfo(tokenAddress)
console.log(`${tokenInfo.symbol}:`, formatUnits(tokenBalance, tokenInfo.decimals))

Works across all chain families:

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

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

// SOL balance
const solBalance = await chain.getBalance({ holder: 'YourSolanaAddress' })

// SPL Token balance (auto-detects Token-2022)
const splBalance = await chain.getBalance({
holder: 'YourSolanaAddress',
token: 'TokenMintAddress',
})

CCIP API Client​

For standalone API queries without creating a chain instance, use CCIPAPIClient directly. This is useful for backend services that don't need RPC connections.

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

const api = CCIPAPIClient.fromUrl() // defaults to https://api.ccip.chain.link

fromUrl is memoized — calling it with the same URL returns the same instance.

Get Message by ID​

TypeScript
const request = await api.getMessageById('0xMessageId...')

console.log('Status:', request.metadata.status)
console.log('Source:', request.metadata.sourceNetworkInfo.name)
console.log('Dest:', request.metadata.destNetworkInfo.name)

if (request.metadata.deliveryTime) {
console.log('Delivery time:', request.metadata.deliveryTime, 'ms')
}

Find Messages in Transaction​

TypeScript
const messageIds = await api.getMessageIdsInTx('0xTxHash...')

for (const id of messageIds) {
const request = await api.getMessageById(id)
console.log(`${id}: ${request.metadata.status}`)
}

Get Execution Input​

Fetch execution inputs (proof, off-chain token data) for manual execution:

TypeScript
const execInput = await api.getExecutionInput('0xMessageId...')

console.log('OffRamp:', execInput.offRamp)
console.log('Version:', execInput.version)

// Use with dest.execute()
const { offRamp, ...input } = execInput
await dest.execute({ offRamp, input, wallet })

Lane Latency (Standalone)​

TypeScript
const latency = await api.getLaneLatency(
5009297550715157269n, // Ethereum mainnet selector
4949039107694359620n, // Arbitrum mainnet selector
)

console.log(`Estimated delivery: ~${Math.round(latency.totalMs / 60000)} minutes`)

Unlike chain.getLaneLatency(destSelector), the standalone version requires both source and destination selectors.

Custom Configuration​

TypeScript
const api = CCIPAPIClient.fromUrl('https://custom-api.example.com', {
timeoutMs: 60000, // Request timeout (default: 30000)
logger: customLogger, // Custom logger
fetch: customFetch, // Custom fetch function
})

Generate links to the CCIP Explorer — no API or RPC needed:

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

const messageUrl = getCCIPExplorerUrl('msg', messageId)
// => 'https://ccip.chain.link/msg/0x...'

const txUrl = getCCIPExplorerUrl('tx', txHash)
// => 'https://ccip.chain.link/tx/0x...'

const addressUrl = getCCIPExplorerUrl('address', senderAddress)
// => 'https://ccip.chain.link/address/0x...'

Complete Example​

Build a transfer form that queries all necessary data before sending:

TypeScript
import { EVMChain, networkInfo } from '@chainlink/ccip-sdk'
import { formatUnits, parseUnits } from 'viem'

async function getTransferInfo(
sourceRpc: string,
destNetworkName: string,
walletAddress: string,
tokenAddress: string,
amountString: string
) {
const chain = await EVMChain.fromUrl(sourceRpc)
const router = '0x0BF3dE8c5D3e8A2B34D2BEeB17ABfCeBaf363A59' // Sepolia Router
const destSelector = networkInfo(destNetworkName).chainSelector

// Get registry for token queries
const registry = await chain.getTokenAdminRegistryFor(router)

// Parallel queries — all RPC-based except latency
const [tokenInfo, balance, nativeBalance, supportedTokens, laneFeatures] =
await Promise.all([
chain.getTokenInfo(tokenAddress),
chain.getBalance({ holder: walletAddress, token: tokenAddress }),
chain.getBalance({ holder: walletAddress }),
chain.getSupportedTokens(registry),
chain.getLaneFeatures({ router, destChainSelector: destSelector, token: tokenAddress }),
])

// Check if token is supported
const isSupported = supportedTokens.some(
(t) => t.toLowerCase() === tokenAddress.toLowerCase()
)
if (!isSupported) {
throw new Error(`Token ${tokenInfo.symbol} not supported on this lane`)
}

// Parse amount and check balance
const amount = parseUnits(amountString, tokenInfo.decimals)
if (amount > balance) {
throw new Error('Insufficient token balance')
}

// Estimate fee
const fee = await chain.getFee({
router,
destChainSelector: destSelector,
message: {
receiver: walletAddress,
data: '0x',
tokenAmounts: [{ token: tokenAddress, amount }],
},
})

if (fee > nativeBalance) {
throw new Error('Insufficient native balance for fee')
}

return {
token: tokenInfo,
balance: formatUnits(balance, tokenInfo.decimals),
amount: amountString,
fee: formatUnits(fee, 18),
ftfEnabled:
laneFeatures.FINALITY_FAST != null &&
laneFeatures.FINALITY_FAST > 0,
rateLimited: laneFeatures.RATE_LIMITS != null,
}
}

Method Reference​

MethodCalled onData sourcePurpose
networkInfo(selectorOrIdOrName)— (standalone)Built-in registryResolve chain names, IDs, and selectors
getTokenInfo(token)ChainRPCGet token symbol, decimals, and name
getSupportedTokens(registry)ChainRPCList tokens configured in TokenAdminRegistry
getTokenAdminRegistryFor(address)ChainRPCResolve TokenAdminRegistry from Router/OnRamp/OffRamp
getFeeTokens(router)ChainRPCList accepted fee tokens with metadata
getLaneFeatures({ router, destChainSelector, token? })ChainRPCQuery lane capabilities (FTF, rate limits)
getLaneLatency(destSelector)ChainCCIP APIEstimated delivery time
getFee({ router, destChainSelector, message })ChainRPCFee estimation in fee token units
getBalance({ holder, token? })ChainRPCNative or token balance
getCCIPExplorerUrl(type, value)— (standalone)NoneGenerate CCIP Explorer URLs
CCIPAPIClient.fromUrl(url?)— (static)—Create standalone API client
api.getMessageById(id)CCIPAPIClientCCIP APIFull message with lifecycle metadata
api.getMessageIdsInTx(txHash)CCIPAPIClientCCIP APIMessage IDs in a transaction
api.getExecutionInput(messageId)CCIPAPIClientCCIP APIProof and off-chain data for manual execution
api.getLaneLatency(source, dest)CCIPAPIClientCCIP APIEstimated delivery time (standalone)