Arbitrary Messaging: EVM to Aptos

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.

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.

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

Token Transfers:

  • Simply moves assets between chains.
  • No custom logic execution on the destination.

Arbitrary Messaging:

  • Sends a data payload to be processed by a module.
  • Triggers module execution on the destination.
  • Can update the module's on-chain state or perform complex logic.

The ccip_message_receiver Module

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

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:

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:

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

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.

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.

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

Command:

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:

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:

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.

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 and checking the Events tab.

Get the latest Chainlink content straight to your inbox.