CCIP v1.6.0 SVM Messages API Reference
Messages
SVMTokenAmount
The SVMTokenAmount
struct captures essential information about a specific token and the transfer amount.
pub struct SVMTokenAmount {
pub token: Pubkey,
pub amount: u64,
}
Parameters
Field | Type | Description |
---|---|---|
token | Pubkey | The mint address of the token on Solana. For example, the Mint Pubkey for an SPL token. |
amount | u64 | The number of tokens to transfer is in the token's smallest unit (often nine decimals for SPL). |
Where it's used:
- CCIP Sender: As part of
SVM2AnyMessage.token_amounts
- CCIP Receiver: As part of
Any2SVMMessage.token_amounts
Extra Args
Below is a developer-focused API reference for the extra arguments used when sending SVM → EVM or SVM → SVM messages. These extra arguments get serialized into the extra_args
field inside the SVM2AnyMessage
struct. We will show:
- The constants (tags), identifying each extra-args type.
- The struct layouts (
EVMExtraArgsV2
andSVMExtraArgsV1
). - How to serialize (and deserialize) these extra-args so they can be appended to the extra_args in a CCIP message.
Tags
You prepend a 4-byte "tag" inside your message, identifying which extra args to use. These tags are just constants, computed as 32-bit from a known hash:
Constant | Value (Hex) | Purpose |
---|---|---|
EVM_EXTRA_ARGS_V2_TAG | 0x181dcf10 | Denotes EVMExtraArgsV2 (SVM → EVM extra args) |
SVM_EXTRA_ARGS_V1_TAG | 0x1f3b3aba | Denotes SVMExtraArgsV1 (SVM → SVM extra args) |
EVMExtraArgsV2
(SVM → EVM)
When sending a message from SVM-based blockchains (e.g., Solana) to EVM-based blockchains (e.g., Ethereum), you can supply extra parameters such as a per-message gas_limit
. This is particularly relevant for an EVM-based destination that requires enough gas to call the receiver contract.
/// `bytes4(keccak256("CCIP EVMExtraArgsV2"))` = `0x181dcf10`
pub const EVM_EXTRA_ARGS_V2_TAG: u32 = 0x181dcf10;
#[derive(Clone, AnchorSerialize, AnchorDeserialize, Default)]
pub struct EVMExtraArgsV2 {
pub gas_limit: u128, // message gas limit for EVM execution
pub allow_out_of_order_execution: bool, // user-configurable OOO execution
}
Parameters
Field | Type | Description |
---|---|---|
gas_limit | u128 | The exact gas limit to call the ccipReceive function of the Receiver contract. If your Receiver contract needs 50,000 gas to run, set gas_limit = 50000 . |
allow_out_of_order_execution | bool | If true , you let CCIP process the message in a potentially out-of-order manner. |
Below is a Rust function showing how to serialize an EVMExtraArgsV2
:
fn create_evm_extra_args(gas_limit: u128, ooo: bool) -> Vec<u8> {
use anchor_lang::AnchorSerialize;
let data = EVMExtraArgsV2 {
gas_limit,
allow_out_of_order_execution: ooo,
};
// 1) Start with the 4-byte tag, big-endian
let mut buffer = EVM_EXTRA_ARGS_V2_TAG.to_be_bytes().to_vec();
// 2) Serialize the struct via Borsh
let mut serialized = data.try_to_vec().unwrap();
// 3) Append the serialized payload
buffer.append(&mut serialized);
buffer
}
SVMExtraArgsV1
(SVM → SVM)
If your destination is another SVM-based blockchain, the extra args can define compute_units
, an accounts list, and whether you allow out-of-order execution, among other fields.
/// `bytes4(keccak256("CCIP SVMExtraArgsV1"))` = `0x1f3b3aba`
pub const SVM_EXTRA_ARGS_V1_TAG: u32 = 0x1f3b3aba;
#[derive(Clone, AnchorSerialize, AnchorDeserialize, Default)]
pub struct SVMExtraArgsV1 {
pub compute_units: u32, // Offchain usage for compute budget
pub account_is_writable_bitmap: u64, // Bitmask describing which accounts are writable
pub allow_out_of_order_execution: bool, // Must match the receiving chain config if OOO
pub token_receiver: [u8; 32], // If tokens are sent, cannot be the 0-address
pub accounts: Vec<[u8; 32]>, // Additional accounts for SVM cross-program calls
}
Parameters
Field | Type | Description |
---|---|---|
compute_units | u32 | An optional override determines how many compute units the destination OffRamp should set. |
account_is_writable_bitmap | u64 | A bitmask marking which accounts entries are writable (bit = 1) vs. read-only (bit = 0). For example, if the 2nd bit is set, the underlying on-chain program treats accounts[1] as writable. |
allow_out_of_order_execution | bool | Whether this cross-chain message may be executed out of order (OOO). If true , the normal strict sequence requirement can be bypassed, allowing the chain to process it earlier/later. |
token_receiver | [u8; 32] | A Solana Pubkey (32 bytes) that receives tokens if the cross-chain message includes token transfers. If so, this field must be nonzero. Typically, the Associated Token Account (ATA) of the receiver. Set this to [0; 32] if no tokens are sent. |
accounts | Vec<[u8; 32]> | A list of additional accounts (each a 32-byte Solana Pubkey ) that the transaction needs to reference on the destination chain. These might be program IDs to invoke, PDAs storing state, or other required accounts. |
Below is a Rust function showing how to serialize an SVMExtraArgsV1
:
fn create_svm_extra_args(
compute_units: u32,
account_is_writable_bitmap: u64,
allow_ooo: bool,
token_receiver: [u8; 32],
accounts: Vec<[u8; 32]>,
) -> Vec<u8> {
use anchor_lang::AnchorSerialize;
let data = SVMExtraArgsV1 {
compute_units,
account_is_writable_bitmap,
allow_out_of_order_execution: allow_ooo,
token_receiver,
accounts,
};
// 1) Start with the 4-byte tag, big-endian
let mut buffer = SVM_EXTRA_ARGS_V1_TAG.to_be_bytes().to_vec();
// 2) Serialize the struct via Borsh (AnchorSerialize)
let mut serialized = data.try_to_vec().unwrap();
// 3) Append the serialized payload
buffer.append(&mut serialized);
buffer
}
Message Structures
SVM2AnyMessage
When you send a cross-chain message from SVM-based blockchains to another blockchain, the SVM2AnyMessage
struct packages all necessary data: destination address, payload, tokens, fees, and any extra arguments.
pub struct SVM2AnyMessage {
pub receiver: Vec<u8>,
pub data: Vec<u8>,
pub token_amounts: Vec<SVMTokenAmount>,
pub fee_token: Pubkey,
pub extra_args: Vec<u8>,
}
Parameters
Field | Type | Description |
---|---|---|
receiver | Vec<u8> | The destination address in raw byte format (since different blockchains have different address lengths). |
data | Vec<u8> | Arbitrary payload to be parsed by the receiver on the destination blockchain. |
token_amounts | Vec<SVMTokenAmount> | List of tokens to transfer. |
fee_token | Pubkey | Mint of the fee token used to pay cross-chain fees. Use Pubkey::default() if paying with native SOL. |
extra_args | Vec<u8> | Additional arguments or metadata (Read Extra Args for more details). |
Any2SVMMessage
When the OffRamp executes a cross-chain message into an SVM-based blockchain (e.g., Solana), it provides an Any2SVMMessage
to the receiving program. This struct lets you verify the source, handle tokens, and parse the payload.
pub struct Any2SVMMessage {
pub message_id: [u8; 32],
pub source_chain_selector: u64,
pub sender: Vec<u8>,
pub data: Vec<u8>,
pub token_amounts: Vec<SVMTokenAmount>,
}
Parameters
Field | Type | Description |
---|---|---|
message_id | [u8; 32] | Unique 32-byte ID for this cross-chain message. |
source_chain_selector | u64 | Indicates which blockchain the message originated from. |
sender | Vec<u8> | The sender's address on the source blockchain, in raw byte format. |
data | Vec<u8> | Arbitrary payload to be parsed by the receiver on the destination blockchain. |
token_amounts | Vec<SVMTokenAmount> | If tokens are bridged, they appear here, along with how many. |
SVM2AnyRampMessage
When sending a cross-chain message from a Solana-compatible (SVM) blockchain to a destination chain (EVM, SVM, etc.), the SVM2AnyRampMessage
struct describes all necessary data and token information. It’s typically constructed inside the ccip_send call.
#[derive(Clone, AnchorSerialize, AnchorDeserialize)]
pub struct SVM2AnyRampMessage {
pub header: RampMessageHeader,
pub sender: Pubkey,
pub data: Vec<u8>,
pub receiver: Vec<u8>,
pub extra_args: Vec<u8>,
pub fee_token: Pubkey,
pub token_amounts: Vec<SVM2AnyTokenTransfer>,
pub fee_token_amount: CrossChainAmount,
pub fee_value_juels: CrossChainAmount,
}
Parameters
Field | Type | Description |
---|---|---|
header | RampMessageHeader | Metadata about the message: message_id , source_chain_selector , dest_chain_selector , sequence_number , nonce . |
sender | Pubkey | The sender's Solana Pubkey (32 bytes) on the source chain, representing the wallet or program that invoked ccip_send . |
data | Vec<u8> | Arbitrary payload data. |
receiver | Vec<u8> | The destination address in raw bytes. For example, if it's an EVM chain, this might be 20 bytes (the EVM contract address). If it's an SVM chain, it might be 32 bytes (a Solana Pubkey). |
extra_args | Vec<u8> | Serialized "extra arguments" relevant to the destination. |
fee_token | Pubkey | Mint of the token used to pay the cross-chain fees. |
token_amounts | Vec<SVM2AnyTokenTransfer> | List of tokens locked or burned as part of this cross-chain message. Each SVM2AnyTokenTransfer in the array has: source_pool_address (the source pool address that locked/burned these tokens), dest_token_address (the byte address for the destination token contract), extra_data (optional data attached to this token transfer), amount (how many tokens are locked on Solana, denominated in the source token's decimals), and dest_exec_data (destination-chain execution parameters). |
fee_token_amount | CrossChainAmount | The billing token amount (in local chain decimals) was ultimately charged as a fee. |
fee_value_juels | CrossChainAmount | The fees are converted to Juels. |