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:

  1. The SDK fetches explicit disclosures from Global CCIP EDS and creates or reuses CCIPSender and PerPartyRouter on Canton.
  2. The message is submitted via PerPartyRouter_CCIPSend with CommitteeVerifier tickets.
  3. CommitteeVerifier operators validate the message and aggregate proofs to the indexer.
  4. 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:data to 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 a data payload is delivered to the Sepolia receiver contract's ccipReceive function.
  • Fee payment: CCIP fees are paid on Canton in Amulet by default. Pass --feeToken link to pay in CCIP LINK instead.
  • Auto-execution: By default, the starter kit allows automatic execution on Sepolia when an executor is configured. Pass --no-exec to skip auto-execution and run canton2any:manual-exec after CCV verification.
  • 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:

  1. Context initialization: Loads canton-config.json, obtains a JWT, and connects to the Canton Ledger API.
  2. Argument parsing: Reads --dataString, --feeToken, --evmReceiver, --gasLimit (default 50000), and --no-exec.
  3. Message construction: Builds a CCIP message with encoded data, fee token, and extraArgs (gas limit and optional executorMode: 'none').
  4. Ledger submit: Submits the send via JWT-authenticated direct ledger submit — no local Canton signing key required.
  5. Output: Prints the CCIP Message ID, CCIP Explorer URL, and Canton Lighthouse transaction link.

Running the Arbitrary Message

Prerequisites Check

Before running the script:

  1. Complete prerequisites setup (npm install, .env, canton-config.json).

  2. Confirm Amulet balance for fees:

    Terminal
    npm run check-balance -- --chain canton --token amulet
    
  3. Ensure ETHEREUM_SEPOLIA_RPC_URL and EVM_PRIVATE_KEY are set if you plan to run canton2any:manual-exec.

Execute the Script

Send a data-only message from Canton to Sepolia:

Terminal
npm run canton2any:data -- --dataString "Hello Sepolia"

Pay the CCIP fee in LINK instead of Amulet:

Terminal
npm run canton2any:data -- --dataString "Hello Sepolia" --feeToken link

Skip automatic execution on Sepolia and execute manually after CCV verification:

Terminal
npm run canton2any:data -- --dataString "Hello Sepolia" --no-exec

Override the Sepolia receiver contract:

Terminal
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 JWT obtained via 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 CCV verification completes (check the CCIP Explorer), then run:

Terminal
npm run canton2any:manual-exec -- <cantonUpdateIdOrMessageId>

Replace <cantonUpdateIdOrMessageId> with the Canton update ID or 32-byte CCIP message ID from the send output.

On success:

⚙️  Executing on Sepolia OffRamp 0x…
✅ Execution transaction: https://sepolia.etherscan.io/tx/0x…

Get the latest Chainlink content straight to your inbox.