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 agasLimit
and aallowOutOfOrderExecution
flag. - The
gasLimit
must be a tested value and sufficient for the execution of theccip_receive
function of the receiver module (i.e., the destination Aptos module). - The
allowOutOfOrderExecution
flag must be set totrue
when Aptos is the destination chain.
- The
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.
Token Transfers:
- Tokens arrive in a user's primary fungible store.
Arbitrary Messaging:
- Data arrives at a specific Move module.
- The module executes logic to process the data.
Token Transfers:
- Check the token balance in the user's account.
- Use the Aptos Explorer to verify balances.
Arbitrary Messaging:
- Verify the module's state has changed.
- Query the module's events or view functions to retrieve the data.
Token Transfers:
receiver
is the end-user's Aptos account address.data
is empty (0x
).
Arbitrary Messaging:
receiver
is the Aptos module's account address.data
contains the encoded payload.tokenAmounts
is an empty array.
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.