API Version: v1.6.0

CCIP v1.6.0 TON Receiver API Reference

Receiver

This section describes the interface a TON contract must implement to receive CCIP messages.


Receiving Messages

Receiver_CCIPReceive

Opcode: 0xb3126df1

This is the message the CCIP Router delivers to your receiver contract when a cross-chain message arrives. Your contract must handle this opcode in its onInternalMessage entry function.

struct (0xb3126df1) Receiver_CCIPReceive {
    execId: uint192;
    message: Any2TVMMessage;
}

Fields

FieldTypeDescription
execIduint192Execution ID assigned by the OffRamp. Must be included in the Router_CCIPReceiveConfirm response.
messageAny2TVMMessageThe CCIP message payload. See Any2TVMMessage.

Router_CCIPReceiveConfirm

Opcode: 0x1e55bbf6

After processing a Receiver_CCIPReceive, your contract must send this confirmation back to the Router. The Router uses it to finalize message delivery. Without this confirmation, the message is not considered delivered.

struct (0x1e55bbf6) Router_CCIPReceiveConfirm {
    execId: ReceiveExecutorId; // uint192
}

Send this with SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE so the remaining TON (the gas reserve forwarded by the OffRamp minus your execution costs) is returned through the protocol chain.


Implementation Requirements

Every TON CCIP receiver must:

  1. Accept Receiver_CCIPReceive only from the authorized CCIP Router — verify in.senderAddress == st.router.
  2. Verify sufficient value is attached — the Router needs at least 0.02 TON to forward the confirmation. Set MIN_VALUE above your execution costs.
  3. Send Router_CCIPReceiveConfirm back to the Router — include execId from the received message. Use SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE.

validateAndConfirm Helper

The receiver library exposes a validateAndConfirm helper that encapsulates the three required steps for simple receivers.

fun Receiver_CCIPReceive.validateAndConfirm(self, opts: Receiver_ValidateOptions)

Receiver_ValidateOptions

struct Receiver_ValidateOptions {
    router: address;           // Expected CCIP Router address
    minValue: coins;           // Minimum attached value required
    inMsg: Receiver_InMessagePartial; // The incoming message metadata
    confirmationMsg: Receiver_ConfirmOpts; // Options for the confirmation message
}

struct Receiver_ConfirmOpts {
    value: coins = 0;
    sendMode: uint8 = SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE;
}

Minimal Receiver Example

import "../chainlink-ton/contracts/contracts/lib/receiver/types"    // Any2TVMMessage
import "../chainlink-ton/contracts/contracts/lib/receiver/errors"   // Receiver_Error
import "../chainlink-ton/contracts/contracts/lib/receiver/messages" // Receiver_CCIPReceive
import "../chainlink-ton/contracts/contracts/ccip/router/messages"  // Router_CCIPReceiveConfirm

struct Storage {
    router: address; // The CCIP Router address
}

const MIN_VALUE: coins = ton("0.03"); // 0.02 TON for OffRamp + your execution costs

type MinimalReceiver_InMessage = Receiver_CCIPReceive;

fun onInternalMessage(in: InMessage) {
    val msg = lazy MinimalReceiver_InMessage.fromSlice(in.body);
    match (msg) {
        Receiver_CCIPReceive => {
            val st = lazy Storage.load();

            // 1. Accept messages only from the authorized CCIP Router
            assert(in.senderAddress == st.router) throw (Receiver_Error.Unauthorized as int);

            // 2. Verify enough value is attached
            assert(in.valueCoins >= MIN_VALUE) throw (Receiver_Error.LowValue as int);

            // 3. Send CCIPReceiveConfirm back to the Router
            val receiveConfirm = createMessage({
                bounce: true,
                value: 0,
                dest: in.senderAddress,
                body: Router_CCIPReceiveConfirm { execId: msg.execId },
            });
            receiveConfirm.send(SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE);

            // 4. Process the message — add your logic here
            processMessage(msg.message);
        }
        else => {
            assert(in.body.isEmpty()) throw 0xFFFF;
        }
    }
}

fun processMessage(message: Any2TVMMessage) {
    // message.data — your payload from the source chain
    // message.sourceChainSelector — verify the sender chain
    // message.sender — cross-chain address of the sender
}

For a complete walkthrough, see the Implementing CCIP Receivers tutorial.

Get the latest Chainlink content straight to your inbox.