# Building CCIP Messages from EVM to SVM
Source: https://docs.chain.link/ccip/tutorials/svm/destination/build-messages
Last Updated: 2025-07-25


## Introduction

This guide explains how to construct CCIP Messages from Ethereum Virtual Machine (EVM) chains (e.g. Ethereum) to SVM chains (e.g. Solana). We'll cover the message structure, required parameters, and implementation details for different message types including token transfers, arbitrary data messaging, and programmable token transfers (data and tokens).

## CCIP Message Structure

CCIP messages are built using the [`EVM2AnyMessage`](/ccip/api-reference/evm/v1.6.1/client#evm2anymessage) struct from the [`Client.sol`](/ccip/api-reference/evm/v1.6.1/client) library. The `EVM2AnyMessage` struct is defined as follows:

```solidity
struct EVM2AnyMessage {
    bytes receiver;
    bytes data;
    EVMTokenAmount[] tokenAmounts;
    address feeToken;
    bytes extraArgs;
}
```

### receiver

- **Definition**: The receiver field specifies which program on the destination chain will process the incoming CCIP message.
- **Token-only transfers**:
  - **Use**: `0x0000000000000000000000000000000000000000000000000000000000000000` (32-byte zero address)
  - **Why**: No program execution needed for token-only transfers.
- **Arbitrary Messaging** or **Programmable Token Transfers**:
  - **Use**: The program ID of the SVM program that implements the `ccip_receive` instruction, converted to a 32-byte hex format.
  - **Why**: The program will process the incoming CCIP message and execute the `ccip_receive` instruction.

> \*\*NOTE: Program ID Format\*\*
>
>
>
> When providing a SVM program ID as the receiver, it must be converted from [Solana's base58 format](https://solana.com/developers/guides/advanced/exchange#basic-verification) to a 32-byte hex format for CCIP messages.
>
> Below is a JavaScript example using the [bs58](https://www.npmjs.com/package/bs58) library:
>
> 1. Decode the base58 Solana address to its raw bytes (Solana addresses are already 32 bytes when decoded)
> 2. Convert those bytes to a hex string with "0x" prefix
>
> For example:
>
> ```javascript
> function encodeSolanaAddressToBytes32(solanaAddress) {
>   const addressBytes = bs58.decode(solanaAddress);
>   return `0x${Buffer.from(addressBytes).toString("hex")}`;
> }
> ```

### data

- **Definition**: Contains the payload that will be passed to the receiving program
- **For token-only transfers**: Empty (`0x`)
- **For arbitrary messaging** or **programmable token transfers**: Contains the data the receiver program will process
- **Encoding requirement**: Must be encoded as a hex string with `0x`· prefix

> **TIP: Data Encoding**
>
> Data must be converted to bytes, then to a hex string with `0x` prefix. For example:

```javascript
const messageData = `0x${Buffer.from("Hello World").toString("hex")}`
```

**Note**: Ensure the SVM program on the destination chain can properly decode this format.

### tokenAmounts

- **Definition**: An array of token addresses and amounts to transfer
- **For data-only messages**: Must be an empty array
- **For token transfers** or **programmable token transfers**: Each entry specifies a token address and amount. **Note**: Check the [CCIP Directory](/ccip/directory) for the list of supported tokens on each lane

### feeToken

- **Definition**: Specifies which token to use for paying CCIP fees
- **For native gas token**: Use `address(0)` to specify the source chain's native gas token (e.g. ETH on Ethereum)
- **For ERC-20 tokens**: Can specify an ERC-20 token address for fee payment. **Note**: Check the [CCIP Directory](/ccip/directory) for the list of supported fee tokens on your source chain

## extraArgs

The most critical component for SVM-bound messages is the [`SVMExtraArgsV1`
](/ccip/api-reference/evm/v1.6.1/client#svmextraargsv1) structure:

```solidity
struct SVMExtraArgsV1 {
    uint32 computeUnits;
    uint64 accountIsWritableBitmap;
    bool allowOutOfOrderExecution;
    bytes32 tokenReceiver;
    bytes32[] accounts;
}

bytes4 public constant SVM_EXTRA_ARGS_V1_TAG = 0x1f3b3aba;
```

Let's examine each field in detail:

### computeUnits

Specifies the amount of compute units allowed for calling the `ccip_receive` instruction of the receiver program on SVM (similar to gas limit on EVM chains).

> \*\*NOTE: Setting Compute Units\*\*
>
>
>
> - **For token transfers only**: **MUST** be set to 0
>
> - **For arbitrary messaging** or **programmable token transfers**: Must be
>   determined through comprehensive testing of the receiver program under different conditions

### Understanding SVM Account Model

Unlike EVM chains where smart contracts manage their own storage, SVM blockchains (e.g. Solana) use an account-based architecture where:

- **All data is stored in accounts**: There's no dedicated storage for programs
- **Programs are stateless**: They can only read from and write to accounts passed to them
- **Explicit account access**: Every account a program needs to access must be explicitly provided
- **Access permissions**: Each account must be marked as either *readable* or *writable*

> **NOTE: Why Accounts Matter**
>
> In SVM blockchains, you must provide an accounts array because programs cannot access any data without explicitly
> receiving account references. This differs fundamentally from EVM chains where contracts can access their own storage
> implicitly.

#### accounts

An array of 32-byte Solana public keys representing additional accounts required for execution.

> **NOTE: Important Considerations**
>
> - Maximum of 64 accounts

- Must be in the exact order expected by the receiving program

- Must include all accounts needed by the receiver (state accounts, token accounts, etc.)

#### accountIsWritableBitmap

A 64-bit bitmap indicating which accounts in the `accounts` array should be marked as writable.

### allowOutOfOrderExecution

**MUST** be set to `true` for SVM as a destination chain.

### tokenReceiver

The Solana account that will initially receive tokens, represented as a 32-byte hex-encoded Solana public key.

<Aside title="Encoding tokenReceiver">
  Similar to the receiver field, the tokenReceiver must be properly encoded as a 32-byte hex format:

  - **For token transfers and programmatic transfers**: Convert the Solana wallet address or PDA from [base58](https://solana.com/developers/guides/advanced/exchange#basic-verification) to a 32-byte hex string.
  - **For arbitrary messaging (data-only)**: Use `0x0000000000000000000000000000000000000000000000000000000000000000` (32-byte zero address).

  Below is a JavaScript example using the [bs58](https://www.npmjs.com/package/bs58) library:

  ```javascript
  function encodeSolanaAddressToBytes32(solanaAddress) {
    const addressBytes = bs58.decode(solanaAddress)
    return `0x${Buffer.from(addressBytes).toString("hex")}`
  }
  ```
</Aside>

## Message Encoding Requirements

When implementing CCIP from EVM to SVM, proper encoding of various elements is crucial for successful message delivery and processing.

> \*\*NOTE: Encoding Considerations\*\*
>
>
>
> Before sending a CCIP message to Solana, several elements must be correctly encoded:
>
> 1. **SVM Addresses**: All Solana addresses (program IDs, PDAs, wallet addresses) must be properly encoded to bytes32 format
> 2. **ExtraArgs Structure**: The SVMExtraArgsV1 structure must be ABI-encoded with the correct type tag
> 3. **Message Data**: Any payload data must be properly hex-encoded with the `0x` prefix
> 4. **Accounts Array**: All accounts must be in the exact order expected by the receiving program

## Implementation by Message Type

### Token Transfer

Use this configuration when sending only tokens from EVM to Solana:

> **NOTE: Key Requirements**
>
> - `computeUnits` **MUST** be 0

- `receiver` should be `0x0000000000000000000000000000000000000000000000000000000000000000` for token-only transfers
- `tokenReceiver`:
  - For user transfers, `tokenReceiver` should be the user's wallet address, converted to 32-byte hex format
  - For transfers to programs, `tokenReceiver` must be a PDA that the program has authority over, converted to 32-byte hex format
- `allowOutOfOrderExecution` **MUST** be set to `true`

### Arbitrary Messaging

Use this configuration when sending only data messages to SVM:

> **NOTE: Key Requirements**
>
> - `computeUnits` must be determined through testing of the receiver program

- `accounts` must include all accounts required by the receiver program
- `accountIsWritableBitmap` must correctly identify writable accounts
- `allowOutOfOrderExecution` **MUST** be set to `true`

### Programmable Token Transfer (Data and Tokens)

> **CAUTION: USDC from EVM to Solana Not Available**
>
> Programmable token transfers are not currently available for USDC from EVM to Solana. Due to limitations on Solana, transaction heap allocation limits (32KB) will be hit when attempting to invoke a receiver with the USDC transfer, which could lead to stuck transactions.

Use this configuration when sending both tokens and data in a single message:

> **NOTE: Key Requirements**
>
> - **Token Security**: The `tokenReceiver` must be an Associated Token Account (ATA) that the receiver program has authority over. Since the program cannot verify that tokens were sent to a specific address, it should validate it received the expected tokens at its own ATA, then forward them to the final destination.

- **Account References**: The `accounts` array must include:
  - The program's own ATA (for validation that tokens were received)
  - The final token destination ATA (to forward tokens to)
  - Any other accounts required by the receiver program's `ccip_receive` instruction

- **Validation Pattern**: The receiver program should:
  1. Check that it received the expected tokens at its controlled ATA
  2. Forward the tokens to the final destination

- `allowOutOfOrderExecution` **MUST** be set to `true`

## Related Tutorials

To see these concepts in action with step-by-step implementation guides, check out the following tutorials:

- [Token Transfers: EVM to SVM](/ccip/tutorials/svm/destination/token-transfers) - Learn how to implement token-only transfers from EVM chains to Solana wallets
- [Arbitrary Messaging: EVM to SVM](/ccip/tutorials/svm/destination/arbitrary-messaging) - Learn how to send data messages from EVM chains to Solana programs

These tutorials provide complete, working examples using the concepts covered in this guide.

> **CAUTION: Educational Example Disclaimer**
>
> This page includes an educational example to use a Chainlink system, product, or service and is provided to
> demonstrate how to interact with Chainlink's systems, products, and services to integrate them into your own. This
> template is provided "AS IS" and "AS AVAILABLE" without warranties of any kind, it has not been audited, and it may be
> missing key checks or error handling to make the usage of the system, product or service more clear. Do not use the
> code in this example in a production environment without completing your own audits and application of best practices.
> Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for unintended outputs
> that are generated due to errors in code.