# Arbitrary Messaging: EVM to Aptos
Source: https://docs.chain.link/ccip/tutorials/aptos/destination/arbitrary-messaging
Last Updated: 2025-09-03

> For the complete documentation index, see [llms.txt](/llms.txt).

This tutorial demonstrates how to send arbitrary data from an Ethereum Virtual Machine (EVM) chain to a Move module on the Aptos blockchain using Chainlink's Cross-Chain Interoperability Protocol (CCIP). You will learn how to configure a CCIP message that triggers a module execution on the destination chain.

> \*\*NOTE: Prerequisites\*\*
>
>
>
> Make sure you've completed the [prerequisites for EVM to Aptos
> tutorials](/ccip/tutorials/aptos/destination/prerequisites) and understand how to [build CCIP messages from EVM to
> Aptos](/ccip/tutorials/aptos/destination/build-messages) before beginning this tutorial.

## Introduction

This tutorial shows you how to send a data-only message from the Ethereum Sepolia testnet to a receiver module on the Aptos testnet.

## What You will Build

In this tutorial, you will:

- Publish a CCIP receiver module to the Aptos Testnet.
- Configure a CCIP message for arbitrary data messaging.
- Send data from Ethereum Sepolia to your Aptos module.
- Pay for CCIP transaction fees using LINK tokens.
- Verify that the data was received and processed by the module on Aptos.

## Understanding Arbitrary Messaging to Aptos

This tutorial focuses on arbitrary messaging from EVM chains to Aptos Move modules. For detailed information about CCIP message structure and parameters, refer to the [guide on building CCIP messages from EVM to Aptos](/ccip/tutorials/aptos/destination/build-messages).

### Key Points Specific to Arbitrary Messaging

- **Module Execution**: The message is sent to a specific module on Aptos, triggering the execution of its `ccip_receive` function.
- **Mandatory Settings**:
  - The `receiver` field of the CCIP message must be the account address of your destination Aptos module.
  - The `tokenAmounts` array must be empty (`[]`).
  - The `extraArgs` field should be encoded with a `gasLimit` and a `allowOutOfOrderExecution` flag.
  - The `gasLimit` must be a tested value and sufficient for the execution of the `ccip_receive` function of the receiver module (i.e., the destination Aptos module).
  - The `allowOutOfOrderExecution` flag must be set to `true` when Aptos is the destination chain.

### Key Differences from Token Transfers

### The `ccip_message_receiver` Module

This tutorial uses the `ccip_message_receiver` module from the `aptos-starter-kit` as the destination.

> \*\*NOTE: Scope Clarification\*\*
>
>
>
> This guide focuses on sending messages to an existing, pre-written module. To learn how to write your own receiver
> from scratch, please refer to the **[Implementing CCIP Receivers on Aptos](/ccip/tutorials/aptos/receivers)** guide.

The `ccip_message_receiver` is a simple module designed to receive arbitrary data. When its `ccip_receive` function is called, it decodes the data as a string and emits a `ReceivedMessage` event, providing a clear on-chain record that the message was processed.

## Implementing Arbitrary Messaging

In this section, you'll first publish the receiver module to Aptos and then use a script to send a message to it.

### Publish the Receiver Module

Before you can send a message, the destination module must exist on the Aptos Testnet. The starter kit provides a script to create a **Resource Account** and publish the `ccip_message_receiver` module to it.

Run the following command:

```bash
npx ts-node scripts/deploy/aptos/createResourceAccountAndPublishReceiver.ts
```

This command will output the address of the newly created resource account. **Copy this address**, as you will need it for the next step.

### Configure and Send the Message

The `evm2aptos/ccipSendMsgRouter.ts` script handles the configuration and sending of the CCIP message. The core of the script builds the message payload:

```typescript
// From scripts/evm2aptos/ccipSendMsgRouter.ts
const ccipMessage = buildCCIPMessage(
  recipient, // The address of your deployed Aptos receiver module
  hexlify(toUtf8Bytes("Hello Aptos from EVM")), // Your data, hex-encoded
  networkConfig.sepolia.linkTokenAddress, // Fee token
  encodeExtraArgsV2(0n, true) // gasLimit is 0, allowOutOfOrderExecution is true
)
```

## Running the Arbitrary Messaging Script

### Execute the Script

Run the script from your terminal. You will need to provide the `--aptosReceiver` address you copied from the deployment step. This example sends from **Ethereum Sepolia**.

```bash
npx ts-node scripts/evm2aptos/ccipSendMsgRouter.ts --sourceChain sepolia --feeToken link --aptosReceiver <YOUR_RECEIVER_MODULE_ADDRESS> --msgString "Hello Aptos from EVM"
```

### Expected Output

The script will output the progress of the transaction, including approvals and fee calculations, and finish by providing the transaction hash and the CCIP Message ID.

```text
Base Fee (in LINK JUELS): ...
Fee with 20% buffer (in LINK JUELS): ...
Current Allowance of LINK token: 0
Approval tx sent: 0x...
Approval transaction confirmed in block ... after 3 confirmations.
Router contract approved to spend ... of LINK token from your account.
Proceeding with the message transfer...
Transaction sent: 0x...
Waiting for transaction confirmation...
Transaction confirmed in block 8803642 after 3 confirmations.
✅ Transaction successful: https://sepolia.etherscan.io/tx/0x...
🆔 CCIP Message ID: 0x...
🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/0x...
```

## Verification: Retrieving the Message

After sending the message, you can verify its delivery and processing on Aptos.

### Check Message Execution

#### Use the CCIP Explorer to check the message status

Use the CCIP Explorer link provided in the transaction output to track your message status across chains. The explorer gives an overview of the entire cross-chain transaction life cycle.

```text
🔗 CCIP Explorer URL: https://ccip.chain.link/#/side-drawer/msg/<YOUR_CCIP_MESSAGE_ID>
```

#### Programmatically check the message status

After you receive a CCIP Message ID, you can programmatically check if the CCIP message has been successfully executed on the Aptos network. This is done by querying the [`ExecutionStateChanged`](/ccip/api-reference/aptos/v1.6.0/events#execute_single_report) event emitted by the CCIP OffRamp module. The `evm2aptos/checkMsgExecutionStateOnAptos.ts` script is designed for this purpose.

After 15-20 minutes, run the script using the CCIP Message ID you received from the previous step.

> **NOTE: Note**
>
> Since end-to-end transaction time depends primarily on the time to finality on the source blockchain (Ethereum Sepolia
> in this case), it's recommended to wait 15-20 minutes before running the script. For more details, refer to the
> [Finality by Blockchain](/ccip/ccip-execution-latency#finality-by-blockchain).

**Command**:

```bash
npx ts-node scripts/evm2aptos/checkMsgExecutionStateOnAptos.ts --msgId <YOUR_CCIP_MESSAGE_ID>
```

*Replace `<YOUR_CCIP_MESSAGE_ID>` with the actual CCIP Message ID from the log output.*

**Output**:
When the message has been successfully delivered, you will see the following output:

```text
Execution state for CCIP message <YOUR_CCIP_MESSAGE_ID> is SUCCESS
```

### Query the Receiver Module

Once the message is successfully executed, you can verify that your receiver module processed it. The `getLatestMessageOnAptos.ts` script queries the `ReceivedMessage` event that your module emitted.

Run the verification script, passing the address of your receiver module:

```bash
npx ts-node scripts/evm2aptos/getLatestMessageOnAptos.ts --aptosReceiver <YOUR_RECEIVER_MODULE_ADDRESS>
```

### Expected Verification Output

When you run the verification script, you should see the decoded message that was stored by your module, confirming the end-to-end flow was successful.

```text
Latest message received on Aptos at <YOUR_RECEIVER_MODULE_ADDRESS>: Hello Aptos from EVM
```

You can also manually verify this by finding the `offramp::execute` transaction for your module's address in the [Aptos Explorer](https://explorer.aptoslabs.com/?network=testnet) and checking the `Events` tab.

> **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.