Onchain Architecture - Overview (TON)

TON as Source Chain

On the TON source blockchain, a user or another contract initiates a cross-chain message by sending a CCIPSend internal message to the CCIP Router contract. The Router validates that sufficient TON is attached for gas and forwards the message to the appropriate OnRamp contract. The OnRamp assigns a unique message ID, deploys an ephemeral SendExecutor{id} contract to isolate fee validation, and instructs it to call the FeeQuoter for a price check. Once the FeeQuoter confirms the fee, the SendExecutor reports back to the OnRamp, which then assigns a sequence number and emits a chain log (ExtOutLogMessage) containing the full CCIPSend payload. This external log is observed offchain by the Committing DON, which relays the message to the destination blockchain.

TON as Destination Chain

On the TON destination blockchain, the OffRamp contract receives two types of OCR3 reports from the offchain DONs. The Committing DON submits a Commit report containing a Merkle root of batched messages. The OffRamp verifies the OCR3 signatures, deploys an ephemeral MerkleRoot{id} contract to track per-message execution state for that root, and emits a CommitReportAccepted log. The Executing DON observes this log along with the original source chain messages, and for each message submits an Execute report. The OffRamp sends a Validate message to the relevant MerkleRoot{id} contract to verify the Merkle proof and mark the message as in-progress. The MerkleRoot{id} replies with ExecuteValidated, after which the OffRamp deploys (or reuses) an ephemeral ReceiveExecutor{id} to track the delivery state. The ReceiveExecutor{id} dispatches the message back to the OffRamp, which routes it via the Router to the Receiver contract. The Receiver processes the data and sends a CCIPReceiveConfirm back through the Router and OffRamp, which forwards a Confirm to the ReceiveExecutor{id}. On success, the ReceiveExecutor{id} reports NotifySuccess to the OffRamp, which emits an ExecutionStateChanged log marking the message as Success.

Key Components

Source Chain:

Destination Chain:

ComponentOwnershipRole
SenderExternal (User/Contract)A user wallet or a custom contract that initiates a cross-chain message on the source chain by sending CCIPSend to the Router.
ReceiverExternal (User/Contract)A custom contract on the destination chain that receives CCIPReceive from the Router, sends back a CCIPReceiveConfirm, and processes the message data.
RouterCCIPThe primary entry point for both sending and receiving. On the source chain it validates the attached TON, checks for sufficient gas, and forwards to the OnRamp. On the destination chain it routes executed messages from the OffRamp to the Receiver and relays the confirmation back. The Router also acts as the RMN Remote: it stores the set of cursed subjects, accepts Router_RMNRemoteCurse and Router_RMNRemoteUncurse messages from the authorized RMN admin, and propagates the updated curse state to all registered OffRamp contracts via OffRamp_UpdateCursedSubjects.
OnRampCCIPProcesses outbound messages. Assigns a message ID, deploys a SendExecutor{id} for fee validation, assigns a sequence number on success, and emits a chain log (CCIPSend) observed by the offchain DONs.
SendExecutor{id}CCIPAn ephemeral contract deployed by the OnRamp per outgoing message. It calls FeeQuoter for fee validation, verifies the sender attached enough TON to cover the CCIP fee, and reports the result back to the OnRamp. It is destroyed after reporting.
FeeQuoterCCIPStores destination-chain fee configuration, fee token prices, and premium multipliers. Validates the CCIPSend request and returns the computed fee or a validation error to the SendExecutor{id}.
OffRampCCIPReceives OCR3 Commit and Execute reports from the DONs. On commit, deploys a MerkleRoot{id} and emits CommitReportAccepted. On execute, validates each message via the MerkleRoot{id}, orchestrates delivery through a ReceiveExecutor{id} and the Router, and emits ExecutionStateChanged. Maintains a local cache of cursed subjects, updated by the Router whenever the RMN admin curses or un-curses a subject. This cache is checked synchronously during every Commit and Execute call.
MerkleRoot{id}CCIPAn ephemeral contract deployed once per committed Merkle root. Stores the two-bit execution state for every message in the root, verifies Merkle proofs, and gates retries. Destroyed when all messages in the root reach Success.
ReceiveExecutor{id}CCIPAn ephemeral contract deployed (or reused on retry) per incoming message. Holds the message content and its delivery state (Untouched, Execute, ExecuteFailed, Success). Dispatches delivery back to the OffRamp and tracks the confirmation or bounce result.

Typical Lifecycle of a Message

Source Blockchain (TON)

This outlines the process when initiating a CCIP transaction from the TON blockchain.

  1. Preparation

    • The Sender prepares the CCIP message, including:
      • Receiver: An encoded destination address (e.g., a 20-byte EVM address).
      • Data payload: Arbitrary bytes to be delivered to the receiver contract.
      • Destination chain selector: Identifies the target blockchain.
      • Extra arguments: Destination-specific parameters such as a gas limit for EVM chains.
    • The fee can be queried in two ways:
      • Onchain: Send a Router_GetValidatedFee message to the Router, which relays to the FeeQuoter via the OnRamp. The Router responds with Router_MessageValidated (or Router_MessageValidationFailed) containing the computed fee.
      • Offchain: Call the validatedFee getter directly on the FeeQuoter contract.
    • The fee is paid in native TON. The Sender must attach the CCIP fee plus additional TON to cover internal message forwarding gas costs.
  2. Sending

    • The Sender sends a CCIPSend internal message to the Router, attaching the required TON.
    • The Router validates that sufficient TON is attached and forwards the message to the OnRamp.
    • The OnRamp assigns a message ID, then deploys a SendExecutor{id} contract with a deterministic address derived from the OnRamp address and a randomized id. It sends an Execute message to the SendExecutor{id} containing the full CCIPSend payload.
    • The SendExecutor{id} sends GetValidatedFee to the FeeQuoter. If the fee is valid and the sender attached sufficient TON, the FeeQuoter replies with MessageValidated. The SendExecutor{id} then destroys itself and reports ExecutorFinishedSuccessfully to the OnRamp, returning any remaining balance.
    • If the fee is insufficient or validation fails, the FeeQuoter replies with MessageValidationFailed and the SendExecutor{id} reports ExecutorFinishedWithError, triggering a rejection path back through the OnRamp and Router to the Sender (CCIPSendNACK).
    • On success, the OnRamp assigns a sequence number and emits a chain log (ExtOutLogMessage) containing the sequenced CCIPSend message. The Sender receives a CCIPSendACK with the unused TON.
  3. Initial Offchain Processing

    • The Committing DON observes the CCIPSend chain log emitted by the OnRamp and begins batching messages offchain to prepare a Merkle root for commitment on the destination chain.

Destination Blockchain (TON)

This outlines the process when TON is the receiving chain for a CCIP message.

  1. Commit Phase

    • The Committing DON submits a Commit OCR3 report to the OffRamp, containing a Merkle root covering a batch of sequenced messages and any price updates.
    • The OffRamp verifies the source chain is enabled and checks its locally cached cursed subjects. If the source chain is cursed, the report is rejected.
    • The OffRamp deploys a MerkleRoot{id} contract initialized with the Merkle root and the expected message sequence range, and advances the next expected sequence number.
    • If the report includes price updates that are newer than the latest recorded OCR sequence number, the OffRamp forwards them to the FeeQuoter.
    • The OffRamp verifies the Committing DON's OCR3 signatures and emits an OCR3Base_Transmitted log. Signature verification is enabled for the Commit plugin and required. RMN BLS signature verification over blessed Merkle roots is not performed on TON; curse protection is provided exclusively through the Router's curse mechanism.
    • The OffRamp emits a CommitReportAccepted log, signaling that the root is stored and messages are ready for execution.
  2. Secondary Offchain Processing

    • The Executing DON observes the CommitReportAccepted log on the destination chain and the original source chain logs. For each message in the committed batch, it computes the Merkle proof and submits a separate Execute OCR3 report.
  3. Execution Phase

    • The OffRamp receives the Execute report and immediately checks its locally cached cursed subjects. If the source chain is cursed, execution is rejected before any further processing.
    • The OffRamp verifies the source chain is enabled and computes a metadata hash from the source chain selector, destination chain selector, and the known OnRamp address. This hash is used together with the Merkle proof to reconstruct and verify the committed root, ensuring the message has not been tampered with. The Execute plugin does not perform OCR3 signature verification; signature verification is disabled for the Execute plugin by configuration.
    • The OffRamp sends a Validate message to the MerkleRoot{id} contract corresponding to this root.
    • The MerkleRoot{id} verifies the Merkle proof, checks that the message has not already been executed (state must be Untouched), marks it as InProgress, and replies with ExecuteValidated. If this is the last message in the root, the MerkleRoot{id} destroys itself.
    • The OffRamp emits ExecutionStateChanged with state InProgress, then deploys (or reuses on retry) a ReceiveExecutor{id} for the message. It sends InitExecute to the ReceiveExecutor{id}.
    • The ReceiveExecutor{id} sends DispatchValidated back to the OffRamp and sets its state to Execute.
    • The OffRamp sends RouteMessage to the Router, which forwards a CCIPReceive internal message to the Receiver contract. The CCIPReceive payload includes the execId, messageId, source chain selector, sender address, and data.
    • The Receiver verifies that the sender is the Router, sends back CCIPReceiveConfirm{execId} to the Router, and processes the message data.
    • The Router forwards CCIPReceiveConfirm to the OffRamp, which sends Confirm to the ReceiveExecutor{id}.
    • The ReceiveExecutor{id} verifies the confirm came from the OffRamp and that the original sender matches the Receiver. It sets its state to Success, destroys itself, and replies NotifySuccess to the OffRamp.
    • The OffRamp emits ExecutionStateChanged with state Success.
  4. Failure and Retry

    • If the Receiver bounces the CCIPReceive message (fails to process it), the Router sends CCIPReceiveBounced to the OffRamp. The OffRamp forwards Bounced to the ReceiveExecutor{id}, which sets its state to ExecuteFailed and reports NotifyFailure. The OffRamp emits ExecutionStateChanged with state Failure and calls MarkState(Failure) on the MerkleRoot{id}.
    • Failed messages can be retried. The MerkleRoot{id} allows a transition from Failure back to InProgress on a subsequent Validate call, and the existing ReceiveExecutor{id} is reused for the retry attempt. Manual execution is also supported permissionlessly after a configured time delay. For more information, read the manual execution page.

Get the latest Chainlink content straight to your inbox.