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
| Field | Type | Description |
|---|---|---|
execId | uint192 | Execution ID assigned by the OffRamp. Must be included in the Router_CCIPReceiveConfirm response. |
message | Any2TVMMessage | The 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:
- Accept
Receiver_CCIPReceiveonly from the authorized CCIP Router — verifyin.senderAddress == st.router. - Verify sufficient value is attached — the Router needs at least 0.02 TON to forward the confirmation. Set
MIN_VALUEabove your execution costs. - Send
Router_CCIPReceiveConfirmback to the Router — includeexecIdfrom the received message. UseSEND_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.