Arbitrary Messaging: Canton as Source
This tutorial demonstrates how to send a data-only CCIP message from Canton testnet to a receiver contract on Ethereum Sepolia using the ccip-starter-kit-canton.
Introduction
This tutorial covers sending arbitrary data from Canton to Ethereum Sepolia without any token transfer. When you send a message using CCIP:
- The SDK fetches explicit disclosures from Global CCIP EDS and creates or reuses
CCIPSenderandPerPartyRouteron Canton. - The message is submitted via
PerPartyRouter_CCIPSendwith CommitteeVerifier tickets. - CommitteeVerifier operators validate the message and aggregate proofs to the indexer.
- The message is executed on Sepolia โ automatically by default, or manually with
canton2any:manual-exec.
What You Will Build
In this tutorial, you will:
- Run
canton2any:datato send a UTF-8 data payload from Canton to Sepolia. - Pay CCIP fees in Amulet (default) or LINK.
- Track delivery in the CCIP Explorer.
- Optionally execute manually on Sepolia with
canton2any:manual-exec.
Understanding Arbitrary Messaging (canton2any)
Key points for data-only sends from Canton:
- Data only: No
tokenAmountsโ only adatapayload is delivered to the Sepolia receiver contract'sccipReceivefunction. - Fee payment: CCIP fees are paid on Canton in Amulet by default. Pass
--feeToken linkto pay in CCIP LINK instead. - Auto-execution: By default, the starter kit allows automatic execution on Sepolia when an executor is configured. Pass
--no-execto skip auto-execution and runcanton2any:manual-execafter Committee Verifier proofs are on the indexer. - Receiver: Defaults to the starter kit's demo receiver on Sepolia. Override with
--evmReceiver.
See the Canton as Source flow in the overview for the full on-ledger and off-ledger path.
How the Script Works
The scripts/canton2any/ccipSendData.ts script uses @chainlink/ccip-sdk CantonChain.sendMessage:
- Context initialization: Loads
canton-config.json, authenticates to the Ledger API, and connects. - Argument parsing: Reads
--dataString,--feeToken,--evmReceiver,--gasLimit(default200000), and--no-exec. - Message construction: Builds a CCIP message with encoded data, fee token, and
extraArgs(gas limit and optionalexecutorMode: 'none'). - Ledger submit: Submits the send via authenticated direct ledger submit โ no local Canton signing key required.
- Output: Prints the CCIP Message ID, CCIP Explorer URL, and Canton Lighthouse transaction link.
Running the Arbitrary Message
Prerequisites Check
Before running the script:
-
Complete prerequisites setup (
npm install,.env,canton-config.json). -
Confirm Amulet balance for fees:
Terminalnpm run check-balance -- --chain canton --token amulet -
Ensure
ETHEREUM_SEPOLIA_RPC_URLandEVM_PRIVATE_KEYare set if you plan to runcanton2any:manual-exec.
Execute the Script
Send a data-only message from Canton to Sepolia:
npm run canton2any:data -- --dataString "Hello Sepolia"
Pay the CCIP fee in LINK instead of Amulet:
npm run canton2any:data -- --dataString "Hello Sepolia" --feeToken link
Skip automatic execution on Sepolia and execute manually after Committee Verifier proofs are on the indexer:
npm run canton2any:data -- --dataString "Hello Sepolia" --no-exec
Override the Sepolia receiver contract:
npm run canton2any:data -- --dataString "Hello Sepolia" --evmReceiver 0xYourReceiverAddress
Understanding the Output
On success, you should see output similar to:
๐ง Sending data from Canton โ Sepolia: "Hello Sepolia"
Executor: default (auto-execution on Sepolia when an executor is configured)
Canton bearer token obtained via OIDC client credentials.
๐ CCIP Message ID: 0x1220e4133ede8f2daf2e665b53321da54d1bd0b8e00fb1cece20b63079c1d48d6a5b
๐ CCIP Explorer: https://ccip.chain.link/#/side-drawer/msg/0x1220โฆ
๐ Canton transaction: https://lighthouse.testnet.cantonloop.com/transactions/โฆ
If you used --no-exec, the script also prints a copy-pasteable canton2any:manual-exec command.
Verification and Monitoring
Track on CCIP Explorer
Open the CCIP Explorer URL from the script output to monitor message status from send through destination execution.
Manual execution on Sepolia
If you passed --no-exec, wait until proofs are on the indexer (check the CCIP Explorer), then run:
npm run canton2any:manual-exec -- <cantonUpdateId | messageId>
Pass either the Canton update ID from the send output (๐ Canton transaction / Lighthouse link) or the CCIP Message ID (๐ CCIP Message ID, 0xโฆ). canton2any:manual-exec accepts both โ resolving by message ID lets the SDK fetch the OffRamp and Committee Verifier proofs from the CCIP API automatically.
On success:
โ๏ธ Executing on Sepolia OffRamp 0xโฆ
โ
Execution transaction: https://sepolia.etherscan.io/tx/0xโฆ