API Version: v1.6.0

CCIP v1.6.0 TON Router API Reference

Router

The CCIP Router is the single entry point for sending cross-chain messages from TON. Each TON network has exactly one immutable Router contract.


Sending a Message

Router_CCIPSend

Opcode: 0x31768d95

Send this internal message to the Router to initiate a cross-chain message. See the Messages reference for the complete field descriptions.

Minimum value to attach

The Router requires a minimum value attached to the message:

  • Router_Costs.CCIPSend() covers the Router's own execution (checked onchain)
  • Additionally, you must cover the CCIP fee (query from FeeQuoter) and a gas reserve (~0.5 TON) for the execution chain

Responses

MessageOpcodeDescription
Router_MessageSent (opcode 0x6513f8e1)ACKThe message was accepted. messageId is the unique cross-chain message identifier.
Router_MessageRejected (opcode 0x8ae25114)NACKThe message was rejected. error contains the exit code. Both the fee and gas reserve are returned.
struct (0x6513f8e1) Router_MessageSent {
    queryID: uint64;
    messageId: uint256;
    destChainSelector: uint64;
    sender: address;
}

struct (0x8ae25114) Router_MessageRejected {
    queryID: uint64;
    destChainSelector: uint64;
    sender: address;
    error: uint256;
}

Example (sending directly from a wallet)

See the TON Starter Kit scripts/ton2evm/sendMessage.ts for a complete example. The core send call looks like:

const ccipSendCell = buildCCIPMessageForEVM(
  BigInt(seqno), // queryID
  destChainSelector,
  encodeEVMAddress(evmReceiver),
  data,
  feeToken,
  extraArgs
)

await walletContract.sendTransfer({
  seqno,
  secretKey: keyPair.secretKey,
  messages: [
    createInternal({
      to: routerAddress,
      value: feeWithBuffer + gasReserve,
      body: ccipSendCell,
    }),
  ],
})

Example (via a Sender contract)

To send from an onchain contract, use CCIPSender_RelayCCIPSend:

const relayMsg = beginCell()
  .storeUint(0x00000001, 32) // CCIPSender_RelayCCIPSend opcode
  .storeAddress(routerAddress) // CCIP Router address
  .storeCoins(valueToAttach) // CCIP fee + gas reserve
  .storeRef(ccipSendCell) // pre-built Router_CCIPSend cell
  .endCell()

Fee Estimation

Because TON contracts cannot call getters synchronously, there are two ways to estimate the CCIP fee before sending:

Call the validatedFeeCell getter on the FeeQuoter contract directly. This is a read-only getter and costs no gas.

Address resolution path: Router.onRamp(destChainSelector)OnRamp.feeQuoter(destChainSelector) → call validatedFeeCell

// FeeQuoter getter
get fun validatedFeeCell(msg: Cell): coins

Parameters

NameTypeDescription
msgCell<Router_CCIPSend>The fully constructed Router_CCIPSend cell you intend to send.

Returns

TypeDescription
coinsThe fee in nanoTON.

TypeScript example (from the Starter Kit)

// Resolve FeeQuoter address
const onRampAddr = (
  await client.runMethod(routerAddress, "onRamp", [{ type: "int", value: destChainSelector }])
).stack.readAddress()

const feeQuoterAddr = (
  await client.runMethod(onRampAddr, "feeQuoter", [{ type: "int", value: destChainSelector }])
).stack.readAddress()

// Query fee
const feeRes = await client.runMethod(feeQuoterAddr, "validatedFeeCell", [{ type: "cell", cell: ccipSendCell }])
const fee = feeRes.stack.readBigNumber()

Option 2: Router_GetValidatedFee message (costs gas)

Send a Router_GetValidatedFee internal message to the Router. The Router forwards the request to the OnRamp and FeeQuoter, then returns the fee via a callback message. This costs TON for the on-chain execution.

// Sent TO the Router
struct (0x4dd6aa82) Router_GetValidatedFee {
    ccipSend: Cell;
    context: T;
}

This option is primarily useful for contracts that need to query fees from within a transaction. For most off-chain scripts, use Option 1 instead.


Getters

The following read-only getters are available on the Router contract:

onRamp

Returns the OnRamp contract address for a given destination chain selector.

get fun onRamp(destChainSelector: uint64): address
NameTypeDescription
destChainSelectoruint64The CCIP chain selector of the destination chain.

Returns

The OnRamp contract address. Used to resolve the FeeQuoter for fee estimation.


offRamp

Returns the OffRamp contract address for a given source chain selector.

get fun offRamp(sourceChainSelector: uint64): address
NameTypeDescription
sourceChainSelectoruint64The CCIP chain selector of the source chain.

Returns

The OffRamp contract address.


Sender Pattern

CCIPSender_RelayCCIPSend

Opcode: 0x00000001

Use this message to trigger a CCIP send from a TON smart contract (instead of sending directly from a wallet). Deploy a Sender contract, then send it this message with the pre-built Router_CCIPSend cell.

struct (0x00000001) CCIPSender_RelayCCIPSend {
    routerAddress: address;   // The CCIP Router to forward to
    valueToAttach: coins;     // CCIP fee + gas reserve to forward to the Router
    message: Cell; // Pre-built Router_CCIPSend cell
}

Fields

FieldTypeDescription
routerAddressaddressThe CCIP Router contract address.
valueToAttachcoinsThe nanoTON value to forward to the Router. Must cover the CCIP fee plus execution gas reserve. Query validatedFeeCell beforehand and add at least a 10% buffer and ~0.5 TON gas reserve.
messageCell<Router_CCIPSend>The pre-built Router_CCIPSend message cell.

Total value to send to the Sender contract

valueToAttach + ~0.1 TON (to cover the Sender contract's own execution). Any surplus is returned via ACK.

Responses

MessageOpcodeMeaning
Router_MessageSent0x6513f8e1Message accepted. messageId is the cross-chain message ID. Any surplus TON is returned.
Router_MessageRejected0x8ae25114Message rejected. Both the fee and surplus are returned. error contains the exit code.

Get the latest Chainlink content straight to your inbox.