# Implementing CCIP Receivers
Source: https://docs.chain.link/ccip/tutorials/ton/receivers
Last Updated: 2026-03-29


> **NOTE: Prerequisites**
>
> This reference guide assumes familiarity with:

- The [Tolk language](https://docs.ton.org/tolk/overview) and [TON smart contract development](https://docs.ton.org/contract-dev/first-smart-contract)
- The [CCIP architecture](/ccip/concepts/architecture/overview)
- The [prerequisites](/ccip/tutorials/ton/source/prerequisites) for TON CCIP development

# Implementing CCIP Receivers

A **CCIP Receiver** is a TON smart contract (written in Tolk) that accepts incoming cross-chain messages delivered by the CCIP protocol. When a message is sent from an EVM chain to TON via CCIP, the CCIP Router on TON forwards it to your receiver contract as an internal message. Your contract must validate the delivery, acknowledge it to the protocol, and process the payload.

## How Message Delivery Works

When a cross-chain message arrives on TON:

1. The CCIP off-ramp verifies the message against a Merkle root and routes it through the CCIP Router on TON.
2. The Router sends a `Receiver_CCIPReceive` internal message to your receiver contract, with enough TON attached to cover the confirmation transaction.
3. Your contract performs three mandatory protocol steps, then executes your application logic.
4. Your contract sends `Router_CCIPReceiveConfirm` back to the Router, which marks the message as successfully delivered on-chain.

## Security Architecture

### Three Mandatory Protocol Steps

Every TON CCIP receiver **must** implement all three steps in order:

**Step 1 — Authorize the Router.** Accept `Receiver_CCIPReceive` messages only from the configured CCIP Router address. Any other sender must be rejected.

```tolk
assert(in.senderAddress == st.router) throw (Receiver_Error.Unauthorized as int);
```

**Step 2 — Check attached value.** The Router forwards TON with the message to cover the confirmation transaction. Verify the attached value meets `MIN_VALUE`. The Router needs at least **0.02 TON** to send `Router_CCIPReceiveConfirm` back through the protocol chain. Use **0.03 TON** as a baseline and increase it to cover your own execution costs.

```tolk
assert(in.valueCoins >= MIN_VALUE) throw (Receiver_Error.LowValue as int);
```

**Step 3 — Acknowledge delivery.** Send `Router_CCIPReceiveConfirm` back to the Router with enough TON attached to cover the protocol's confirmation chain. Using `SEND_MODE_CARRY_ALL_REMAINING_MESSAGE_VALUE` is the simplest approach — it forwards all remaining value automatically — but any send mode is valid as long as at least **0.02 TON** reaches the Router.

```tolk
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);
```

> **CAUTION: Setting MIN\_VALUE Too Low**
>
> If `MIN_VALUE` is too small, your contract may not forward enough TON for `Router_CCIPReceiveConfirm` to propagate back through the protocol chain, leaving the message in an undelivered state. The message cannot be retried and used gas is not refunded. Start at **0.03 TON** and benchmark your contract's execution costs to determine the correct value.

### Developer Responsibilities

Unlike EVM receivers, **source chain and sender validation are not enforced at the protocol level on TON** — only the Router address check (step 1) is a protocol requirement. Your contract is responsible for any application-layer checks:

- **Source chain validation**: Check `message.sourceChainSelector` against an allowlist of trusted chains.
- **Sender validation**: Check `message.sender` against trusted source-side addresses.

Without these checks, any address on any chain can send a CCIP message to your receiver and have it processed.

## Message Structure

The CCIP Router delivers an `Any2TVMMessage` struct inside each `Receiver_CCIPReceive` message:

```tolk filename="chainlink-ton/contracts/contracts/lib/receiver/types.tolk"
struct Any2TVMMessage {
    messageId: uint256;          // Unique message identifier
    sourceChainSelector: uint64; // CCIP chain selector of the originating chain
    sender: CrossChainAddress;   // Encoded sender address from the source chain
    data: cell;                  // Arbitrary payload (your application data)
    tokenAmounts: cell?;         // Reserved for future token support; currently unused
}
```

| Field                 | Type                | Description                                                                              |
| --------------------- | ------------------- | ---------------------------------------------------------------------------------------- |
| `messageId`           | `uint256`           | Unique identifier for the CCIP message                                                   |
| `sourceChainSelector` | `uint64`            | CCIP selector of the originating chain                                                   |
| `sender`              | `CrossChainAddress` | Encoded source-chain sender address; for EVM sources, these are the 20 EVM address bytes |
| `data`                | `cell`              | Application payload encoded as a TON Cell                                                |
| `tokenAmounts`        | `cell?`             | Reserved for future token-transfer support; currently `null`                             |

`CrossChainAddress` is a `slice` type. For EVM-to-TON messages, it contains the 20-byte EVM address of the sender.

> **NOTE: Receiver Library: Early Development**
>
> The `lib/receiver/` library (`messages.tolk`, `types.tolk`, `errors.tolk`) shipped with `chainlink-ton` is **audited but still under active development**. It may not cover every use case. You are responsible for correctly implementing the three mandatory protocol steps in your receiver. If `validateAndConfirm` does not meet your needs, implement the steps inline as shown in `MinimalReceiver` below.

## Receiver Implementations

The starter kit provides three receiver contracts at different complexity levels. Choose the one that fits your use case, or use one as a starting template.

## After Deployment

After deploying, send a test message from an EVM chain to verify delivery:

```bash filename="Terminal"
npm run evm2ton:send -- \
  --sourceChain <evm-chain-name> \
  --tonReceiver <your-deployed-receiver-address> \
  --msg "Hello TON from EVM" \
  --feeToken native
```

Then confirm the message was received on TON:

```bash filename="Terminal"
npm run utils:checkTON -- \
  --sourceChain <evm-chain-name> \
  --tonReceiver <your-deployed-receiver-address> \
  --msg "Hello TON from EVM"
```

> **CAUTION: Educational Example Disclaimer**
>
> This page includes an educational example to use a Chainlink system, product, or service and is provided to
> demonstrate how to interact with Chainlink's systems, products, and services to integrate them into your own. This
> template is provided "AS IS" and "AS AVAILABLE" without warranties of any kind, it has not been audited, and it may be
> missing key checks or error handling to make the usage of the system, product or service more clear. Do not use the
> code in this example in a production environment without completing your own audits and application of best practices.
> Neither Chainlink Labs, the Chainlink Foundation, nor Chainlink node operators are responsible for unintended outputs
> that are generated due to errors in code.