Configuration
RPC Endpoints
All commands require RPC endpoints for the networks involved. The CLI accepts both http[s] and ws[s] URLs.
Where to Get RPCs
- EVM
- Solana
- Aptos
| Provider | Description |
|---|---|
| QuickNode | Multi-chain support with free tier |
| Alchemy | Enterprise-grade with free tier |
| Chainlist.org | Free public RPCs for EVM networks |
| Infura | Reliable EVM endpoints with free tier |
| Nodereal | Aptos and EVM support with free tier |
| Provider | Description |
|---|---|
| QuickNode | Multi-chain support with free tier |
| Alchemy | Enterprise-grade with free tier |
| Helius | Popular Solana RPC with generous free tier |
| Solana Official | Free but rate-limited official endpoints |
| Provider | Description |
|---|---|
| QuickNode | Multi-chain support with free tier |
| Aptos Labs | Official Aptos endpoints |
| Nodereal | Aptos and EVM support with free tier |
Providing RPCs
Command line: Use --rpc or --rpcs to specify endpoints directly:
ccip-cli show 0x123... \
--rpc https://eth-sepolia.example.com \
--rpc https://arb-sepolia.example.com
You can also pass comma-separated values:
ccip-cli show 0x123... \
--rpcs "https://eth-sepolia.example.com,https://arb-sepolia.example.com"
Environment variables: Export variables prefixed with RPC_. See Environment Variables for details.
Configuration file: Use --rpcs-file to load from a file (default: ./.env):
ccip-cli show 0x123... --rpcs-file ./my-rpcs.txt
File Format
The parser extracts URLs from any format. Lines can contain prefixes, suffixes, or comments:
https://eth-sepolia.g.alchemy.com/v2/demo
ARB_SEPOLIA_RPC: https://arbitrum-sepolia.drpc.org
RPC_AVALANCHE_TESTNET=https://avalanche-fuji-c-chain-rpc.publicnode.com
https://api.devnet.solana.com # solana devnet
https://api.testnet.aptoslabs.com/v1
The CLI connects to all endpoints in parallel and uses the fastest responding RPC for each network.
Environment Variables
The CLI supports environment variables for configuration. Command-line flags override environment variables.
RPC Configuration
| Variable | Description | Example |
|---|---|---|
RPC_* | RPC endpoints (any suffix) | RPC_SEPOLIA=https://... |
Wallet Configuration
The CLI checks these environment variables in order: PRIVATE_KEY, USER_KEY, OWNER_KEY.
| Variable | Description |
|---|---|
PRIVATE_KEY | Private key (EVM: hex, Solana: base58) |
USER_KEY | Alias for PRIVATE_KEY |
OWNER_KEY | Alias for PRIVATE_KEY |
USER_KEY_PASSWORD | Password for encrypted JSON keystore file or keystore fallback |
FOUNDRY_KEYSTORE_PASSWORD | Password for Foundry keystore (takes priority over USER_KEY_PASSWORD) |
HARDHAT_KEYSTORE_PASSWORD | Password for Hardhat keystore (takes priority over USER_KEY_PASSWORD) |
FOUNDRY_DIR | Override Foundry home directory (default: ~/.foundry) |
Output Preferences
| Variable | Description | Default |
|---|---|---|
CCIP_FORMAT | Output format (pretty, log, json) | pretty |
CCIP_VERBOSE | Enable debug logging (true/false) | false |
CCIP_PAGE | Pagination size for getLogs queries | - |
CCIP_API | CCIP API endpoint URL, or false/no to disable API calls (decentralized mode). true/yes to enable with default URL. | Enabled (https://api.ccip.chain.link) |
Example .env file:
# RPC Endpoints
RPC_SEPOLIA=https://eth-sepolia.example.com
RPC_ARB_SEPOLIA=https://arb-sepolia.example.com
RPC_AVALANCHE_FUJI=https://avalanche-fuji.example.com
RPC_SOLANA_DEVNET=https://api.devnet.solana.com
RPC_APTOS_TESTNET=https://api.testnet.aptoslabs.com/v1
# Wallet (for send/manual-exec commands)
USER_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
# Or use a named keystore (pass --wallet foundry:<name> or --wallet hardhat:<name>)
# FOUNDRY_KEYSTORE_PASSWORD=yourFoundryKeystorePassword
# HARDHAT_KEYSTORE_PASSWORD=yourHardhatKeystorePassword # hardhat type requires running from inside a Hardhat project
# Output preferences
CCIP_FORMAT=json
CCIP_VERBOSE=false
Use --rpcs-file to load a different file: ccip-cli show 0x... --rpcs-file ./prod.env
Wallet Configuration
Commands that send transactions require a wallet. The CLI checks these sources in order:
Auto-Detection
If --wallet is omitted, the CLI checks environment variables first (PRIVATE_KEY, USER_KEY, OWNER_KEY in that order), then falls back to scanning your --rpcs-file (default: ./.env):
# .env file
RPC_SEPOLIA=https://eth-sepolia.example.com
USER_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
# CLI will use USER_KEY automatically
ccip-cli send ethereum-testnet-sepolia 0x... dest --receiver 0x...
--wallet Option
Pass directly or specify a file path:
| Chain | Accepted Formats |
|---|---|
| EVM | Hex private key, path to encrypted JSON keystore, ledger[:index], foundry:<name>, or hardhat:<name> |
| Solana | Base58 private key, or path to id.json file (default: ~/.config/solana/id.json) |
| Aptos | Hex private key, or path to text file containing it |
| Sui | Hex or base64 private key |
| TON | 64-byte hex private key (0x-prefixed), mnemonic phrase (space-separated), path to key file, or ledger[:index] |
| Canton | Not required — party ID comes from --canton-config; transactions submit via JWT |
Foundry Cast Keystore
If you manage keys with Foundry Cast, use foundry:<name>:
# Import a key into the Foundry keystore (one-time setup)
cast wallet import sender --interactive
# Use it with the CLI
ccip-cli send ... --wallet foundry:sender
Password resolution order:
$FOUNDRY_KEYSTORE_PASSWORDenv var$USER_KEY_PASSWORDenv var- Interactive prompt
The keystore directory defaults to ~/.foundry/keystores/ and can be overridden with $FOUNDRY_DIR.
Hardhat Keystore
If you manage keys with Hardhat's built-in keystore, use hardhat:<name>. The CLI runs node_modules/.bin/hardhat keystore get directly, so Hardhat must be installed as a dev dependency in your project:
# Import a key into the Hardhat keystore (one-time setup)
npx hardhat keystore set sender
# Use it with the CLI (run from inside your Hardhat project)
ccip-cli send ... --wallet hardhat:sender
Password resolution order:
$HARDHAT_KEYSTORE_PASSWORDenv var (piped silently to Hardhat)$USER_KEY_PASSWORDenv var (piped silently to Hardhat)- Interactive prompt (ccip-cli prompts, answer is piped to Hardhat)
Ledger Hardware Wallet
Connect a Ledger device:
ccip-cli send ... --wallet ledger
Use a specific derivation index:
ccip-cli send ... --wallet ledger:1 # Uses m/44'/60'/1'/0/0 for EVM
Global Options
These options are available on all commands:
| Option | Alias | Type | Default | Description |
|---|---|---|---|---|
--rpcs | --rpc | string[] | - | RPC endpoint URLs |
--rpcs-file | - | string | ./.env | File containing RPC endpoints |
--format | -f | string | pretty | Output format: pretty, log, or json |
--verbose | -v | boolean | - | Enable debug logging |
--page | - | number | - | Pagination size for getLogs queries |
--api | - | string | https://api.ccip.chain.link | CCIP API endpoint URL. Enabled by default. Pass a URL for a custom endpoint. Use --no-api to disable (decentralized RPC-only mode) |
--canton-config | - | string | - | Path to Canton config JSON file (required for Canton operations) |
--indexer | - | string[] | - | CCIP v2 indexer URLs for CCV verifications (used when lane involves Canton) |
--help | -h | boolean | - | Show help |
--version | -V | boolean | - | Show version |
Output formats:
| Format | Use Case |
|---|---|
pretty | Human-readable tables (default) |
log | Console output with additional details |
json | Machine-readable, suitable for scripting — see Reading JSON Output |
Network Identifiers
Networks can be specified by name, chain ID, or CCIP chain selector. The CLI uses chain-selectors for resolution.
| Chain Family | Identifier Format | Example |
|---|---|---|
| All | CCIP chain selector | 16015286601757825753 (Sepolia) |
| All | Network name | ethereum-mainnet, ethereum-testnet-sepolia |
| EVM | Numeric chain ID | 1 (Ethereum), 11155111 (Sepolia) |
| Solana | Genesis hash | 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d |
| Aptos | Prefixed chain ID | aptos:1 (mainnet), aptos:2 (testnet) |
| Sui | Prefixed chain ID | sui:1 |
| Canton | Prefixed chain ID | canton:TestNet, canton:DevNet, canton:MainNet |
Canton Configuration
Canton operations require a JSON config file passed via --canton-config <path>. The Canton Ledger API URL is provided through the normal --rpc / --rpcs mechanism (detected by /api/json, /api/ledger, or port 7575).
Config file format:
{
"party": "u_7c1f39da042a::1220c250c23c...",
"ccipParty": "ccipOwner::1220e382f4e5...",
"auth": {
"type": "clientCredentials",
"authUrl": "https://auth.example.com/oauth2/default"
},
"edsUrl": "https://eds.example.com",
"transferInstructionUrl": "https://transfer-instruction.example.com",
"indexerUrl": "https://indexer.example.com",
"chainId": "canton:TestNet",
"packages": {
"perPartyRouter": "ccip-runtime-v2",
"ccipReceiver": "ccip-receiver-v2",
"ccipSender": "ccip-sender-v2"
},
"senderInstanceId": "prod-ccipsender",
"ccvs": ["0x5b92820da106..."]
}
Set CANTON_CLIENT_ID and CANTON_CLIENT_SECRET env vars for the clientCredentials flow, or use "jwt": "eyJ..." in place of the auth block for a pre-obtained token. See Canton Authentication for all flows.
| Field | Required | Description |
|---|---|---|
party | Yes | User ledger party for actAs and transaction visibility |
ccipParty | Yes | CCIP operator party (CCIPSender signatory / fee recipient) |
jwt | No* | JSON Web Token for Canton Ledger API authentication |
auth | No* | OIDC auth config (alternative to jwt — see below) |
edsUrl | Yes | Base URL for the Explicit Disclosure Service (EDS) |
transferInstructionUrl | Yes | Base URL for the Transfer Instruction API |
externalEdsUrlsByOwner | No | Map of owner party → external EDS URL |
indexerUrl | No | CCIP v2 indexer URL for CCV verifications (required for Canton manual execution) |
chainId | No | CCIP Canton chain ID (e.g. canton:TestNet); skips auto-detection |
senderInstanceId | No | CCIPSender instance id used as router on Canton source (CLI -r overrides) |
defaultSendGasLimit | No | Default gas limit for Canton → destination sends |
feeTransferFactoryAmount | No | Transfer-factory preview amount for fee payments (default: "1.0") |
ccvs | No | CCV instance addresses for execute disclosures and send defaults |
packages | No | DAR package names for ACS template filters |
* Either jwt or auth is required. If both are present, jwt takes precedence.
[!NOTE] The top-level
jwtfield is shorthand forauth: { type: "static", jwt }— both are equivalent. If both are present,jwttakes precedence.
Canton Authentication
The auth object supports three flows. The static flow wraps a pre-obtained JWT (equivalent to the top-level jwt field); clientCredentials and authorizationCode obtain a JWT automatically via OpenID Connect (OIDC):
auth.type | Use case | Required auth fields |
|---|---|---|
static | Pre-obtained JWT | jwt |
clientCredentials | Machine-to-machine (CI/CD) | authUrl, clientId†, clientSecret† |
authorizationCode | Interactive browser login (PKCE) | authUrl, clientId† |
† clientId and clientSecret may be omitted from the config file and resolved from CANTON_CLIENT_ID / CANTON_CLIENT_SECRET env vars instead. Keep secrets in env vars, not in config files.
Client credentials example (CI/CD, machine-to-machine):
{
"auth": {
"type": "clientCredentials",
"authUrl": "https://auth.example.com"
}
}
export CANTON_CLIENT_ID="my-client-id"
export CANTON_CLIENT_SECRET="my-client-secret"
Authorization code example (interactive browser login for human users):
{
"auth": {
"type": "authorizationCode",
"authUrl": "https://auth.example.com",
"callbackUrl": "http://localhost:8400/callback"
}
}
export CANTON_CLIENT_ID="my-client-id"
The authorizationCode flow opens a browser for login and starts a local callback server to receive the authorization code. The callbackUrl defaults to http://localhost:8400/callback — override it if port 8400 is in use or your OIDC provider requires a different redirect URI. PKCE with S256 is required.
When auth is set, the CLI resolves a JWT upfront (before connecting to the ledger) via the SDK's runtime-agnostic OAuth 2.0 protocol helpers (backed by oauth4webapi). The SDK itself never orchestrates an OAuth flow — it only consumes what it's given (jwt or a tokenGetter). For clientCredentials and authorizationCode, the CLI injects a tokenGetter so tokens are refreshed automatically per request. Optional auth fields: audience (Auth0-specific), scopes (defaults to daml_ledger_api for client credentials, openid daml_ledger_api for authorization code), callbackUrl (authorization code only, defaults to http://localhost:8400/callback).
[!NOTE] The
authorizationCodeflow is orchestrated by the CLI: it starts a local callback server (node:http) and opens the default browser (open/xdg-open). These Node-specific steps live in the CLI, not the SDK, so the SDK stays runtime-agnostic (nonode:*imports) and can be embedded in web/Electron apps. Web embedders compose the SDK's protocol helpers (buildAuthorizationRequest,validateAuthorizationCallback,exchangeAuthorizationCode) with their own redirect/callback handling.
Canton Wallet
On Canton, the party ID comes from config and transactions are submitted directly using the JWT from config (either the static jwt field or a token resolved from auth). The --wallet flag is not required for Canton operations.
ccip-cli send \
-s canton-testnet \
-d ethereum-testnet-sepolia \
-r prod-ccipsender \
--canton-config ./canton-config.json \
--rpc https://ledger.example.com/api/json \
--rpc https://ethereum-sepolia-rpc.example.com \
--to 0xReceiverContract \
-t link-token=1.0 \
--no-estimate-gas-limit
Shell Completion
Enable tab-completion for commands and options by adding the completion script to your shell profile:
# bash
ccip-cli completion >> ~/.bashrc
# zsh
ccip-cli completion >> ~/.zshrc
Restart your shell or run source ~/.bashrc (or ~/.zshrc) to activate. Once enabled, press Tab to autocomplete commands, subcommands, and flags.