> For the complete documentation index, see [llms.txt](https://docs.t-rex.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.t-rex.network/build/implement-t-rex-lite-on-a-new-chain.md).

# Implement T-REX Lite on a new chain

This page is for a team that brings T-REX assets to a chain, EVM or not, by implementing T-REX Lite there: the token that represents an asset on a satellite chain. It gives the wire format, the five message types, every check the reference chain applies to what a satellite sends, and what the implementer still has to obtain.

> **Scope.** T-REX Lite has no code in the protocol repositories and no published specification. This page is derived from the contracts of the reference chain, which define every message they send to a satellite and accept from one, and from [T-REX Lite](/cross-chain-the-distributed-register/t-rex-lite.md). What neither defines is listed under "What is not specified".

## What T-REX Lite is

On an EVM satellite, T-REX Lite is a plain ERC-20 with a linked compliance contract that stores the validations it received. It holds no identity, no eligibility configuration and no rule, and it decides nothing.

* **Supply.** It mints and burns on messages that arrive through the ERC-7786 gateway.
* **Transfer.** `transfer`, or `transferFrom` by a named spender, executes only against a stored validation, consumes it and sends a settlement notification.
* **Recall.** It burns locally and sends a burn proof.
* **Deployment.** The T-REX Lite factory of the satellite deploys it, on a message through the gateway.

Five obligations are the specification of any implementation, on any chain.

| Obligation                                                                                                                                                                                                                                                            | What it guarantees                                                                       |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| **Accept messages only through the trusted gateway, from its peers.** A validation or an instruction counts only if its author is the asset's token on the reference chain. A proof of burn counts only if its author is the asset's T-REX Lite on another satellite. | Nobody else can authorize a transfer or create tokens on the satellite.                  |
| **Execute a transfer only against a validation, and consume it.** The sender, the recipient and the spender match the validation. The amount is inside its range. The execution happens strictly before its expiry. A validation is used once.                        | No transfer happens that the reference chain has not authorized, and none happens twice. |
| **Report every execution.** After a transfer, it sends a settlement notification naming the validation and the exact amount.                                                                                                                                          | The register records the change of ownership.                                            |
| **Create and destroy only on instruction, or with proof.** It mints on a mint instruction from the reference chain, or on a proof of burn from another satellite. When it burns, it sends the proof of it.                                                            | The balances on the satellite always equal what the register records as bridged there.   |
| **Refuse everything else.** A transfer with no matching validation does not execute.                                                                                                                                                                                  | A holder cannot move tokens outside the rules by acting on the satellite.                |

## The messages

`MessageTypesLib` is the wire specification. Every message is an ABI-encoded envelope around a body.

```solidity
uint8 internal constant VERSION = 1;

function encode(Message messageType, bytes memory body) internal pure returns (bytes memory) {
    return abi.encode(messageType, VERSION, body);
}
```

* **Envelope.** Three 32-byte head words (the type code, the version `1`, the offset of the body), then the length of the body and its bytes. `decode` reads it with `abi.decode(payload, (Message, uint8, bytes))` and refuses another version with `UnsupportedMessageVersion`.
* **Body.** For types 0, 3 and 4, `abi.encode` of one struct. The three structs have dynamic fields, so a body starts with the 32-byte offset `0x20`, followed by the tuple. It has no type prefix and no version of its own. For types 1 and 2, the token forwards the bytes the agent passes, and the code defines no encoding for them.
* **Transport.** The token calls `sendMessage` on its ERC-7786 gateway with its peer as recipient, an empty list of attributes and no value.

| Code | Type                      | Direction                    | Body                     | Sent                                                     |
| ---- | ------------------------- | ---------------------------- | ------------------------ | -------------------------------------------------------- |
| 0    | `COMPLIANCE_VALIDATION`   | Reference chain to satellite | `ComplianceValidation`   | At issuance, once per satellite chain involved           |
| 1    | `MINT_INSTRUCTION`        | Reference chain to satellite | No struct in the code    | For a delegation-out. One-way                            |
| 2    | `RECALL_INSTRUCTION`      | Reference chain to satellite | No struct in the code    | When an agent orders a recall                            |
| 3    | `SETTLEMENT_NOTIFICATION` | Satellite to reference chain | `SettlementNotification` | After each execution, by each chain that executed a half |
| 4    | `BURN_PROOF`              | Satellite to reference chain | `BurnProof`              | After a burn for a recall                                |

No type is defined for a message from one satellite to another. A new type is appended to the enum, so these codes never change.

### `COMPLIANCE_VALIDATION` (0)

```solidity
struct ComplianceValidation {
    uint256 validationId;
    bytes from;
    bytes to;
    bytes spender;
    uint256 amountMin;
    uint256 amountMax;
    address token;
    uint64 expiry;
    uint64 reconciliationWindow;
}
```

**Sent by:** `ModularCompliance.requestTransferValidation`. It encodes the struct with `abi.encode` and calls `Token.dispatchComplianceValidation(chainKey, validationId, body)`, which only the bound compliance can call. The token wraps the body in the envelope and sends it to its peer. For a transfer between two satellites, the same body goes to both chains.

**The receiver** decodes it with `abi.decode(body, (ComplianceValidation))`, as `decodeValidation` does, and stores it until it is consumed or expires. `reconciliationWindow` is in seconds: the time the reference chain keeps the reservation after `expiry`.

### `MINT_INSTRUCTION` (1) and `RECALL_INSTRUCTION` (2)

**Sent by:** `Token.dispatchMintInstruction(chainKey, body)` and `Token.dispatchRecallInstruction(chainKey, body)`, both restricted to `AGENT`. The token forwards `body` as given and keeps no record of the instruction.

**The receiver** of a mint instruction mints a position that the register already records as bridged to a wallet on its chain, and sends nothing back. On a recall instruction it burns the amount from the named wallet and answers with a `BURN_PROOF`. If the wallet no longer holds the amount, it reports the failure.

### `SETTLEMENT_NOTIFICATION` (3)

```solidity
struct SettlementNotification {
    uint256 validationId;
    bytes from;
    bytes to;
    uint256 amount;
}
```

**Sent by:** T-REX Lite, after it executed against a validation. The payload is what `encodeSettlement` returns: `abi.encode(Message.SETTLEMENT_NOTIFICATION, VERSION, abi.encode(notification))`.

| Execution                                                           | `from`                  | `to`                  | Sent from           |
| ------------------------------------------------------------------- | ----------------------- | --------------------- | ------------------- |
| Transfer on one satellite, or to a recipient on the reference chain | The validation's `from` | The validation's `to` | The chain of `from` |
| Burn half of a transfer between two satellites                      | The validation's `from` | Empty                 | The chain of `from` |
| Mint half of a transfer between two satellites                      | Empty                   | The validation's `to` | The chain of `to`   |

`from` and `to` are the bytes of the validation, unchanged: the reference chain compares their `keccak256` with the hashes it stored at issuance. `amount` is the exact amount executed.

### `BURN_PROOF` (4)

```solidity
struct BurnProof {
    bytes burnedWallet;
    uint256 amount;
    address nativeWallet;
}
```

**Sent by:** T-REX Lite, after a burn for a recall, started by the holder or by a recall instruction. The payload is what `encodeBurnProof` returns. `burnedWallet` is the wallet that was burned, as an interoperable address. `nativeWallet` is the address to credit on the reference chain, of the same identity.

## What the reference chain checks on delivery

The gateway calls `Token.receiveMessage(receiveId, sender, payload)`. The checks run in this order. A revert undoes everything, including the record of the delivery, so the same message can be delivered again.

| # | Check                                                                   | If it fails                                                                                                      |
| - | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| 1 | The gateway is in the network's `TrustedGatewayRegistry`.               | `ERC7786RecipientUnauthorizedGateway`                                                                            |
| 2 | `sender` is, byte for byte, `peerFor(chainKey)` for the chain it names. | `InteroperableAddressParsingError` when `sender` is not an ERC-7930 version 1 address. Otherwise `SenderNotPeer` |
| 3 | The envelope decodes, with a known type and version `1`.                | A revert, or `UnsupportedMessageVersion`                                                                         |
| 4 | This gateway has not delivered this `receiveId` before.                 | `MessageAlreadyReceived`                                                                                         |
| 5 | The type is 3 or 4.                                                     | `MessageTypeNotInbound`                                                                                          |

Then, for a settlement notification:

| #  | Check                                                                                                                                                                       | If it fails                                                  |
| -- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| 6  | The body decodes as a `SettlementNotification`.                                                                                                                             | A revert                                                     |
| 7  | The gateway is the one this validation went out through toward that chain: `pinnedRouteFor(validationId, chainKey)`. With no pinned route, it is the chain's current route. | `GatewayNotPinned`, or `GatewayNotRouted`                    |
| 8  | The token is not paused.                                                                                                                                                    | `EnforcedPause`                                              |
| 9  | The `validationId` was issued.                                                                                                                                              | No revert: `ReplayedSettlement`, and the token pauses itself |
| 10 | The wallets and the origin chain match one row of the table above. For a transfer between two satellites, a report with both wallets matches none.                          | `SettlementLegMismatch`                                      |
| 11 | `amountMin <= amount <= amountMax`.                                                                                                                                         | `SettlementOutOfBounds`                                      |
| 12 | This half was not already applied, and the validation is not `Resolved`.                                                                                                    | No revert: `ReplayedSettlement`, and the token pauses itself |
| 13 | For the second of two halves: `amount` equals the amount of the first.                                                                                                      | `SettlementAmountMismatch`                                   |
| 14 | The bridged position recorded for `from` covers the amount.                                                                                                                 | `InsufficientBridgedBalance`                                 |

A notification for a validation that the Validation Keeper discarded passes the same checks and is applied. The compliance emits `LateReconciliation`, and pauses issuance for the origin chain when the executed amount exceeds what the rules allow at that moment.

Or, for a burn proof:

| # | Check                                                              | If it fails                        |
| - | ------------------------------------------------------------------ | ---------------------------------- |
| 6 | The gateway is the chain's current route.                          | `GatewayNotRouted`                 |
| 7 | The body decodes as a `BurnProof`, and `nativeWallet` is not zero. | A revert, or `ZeroAddress`         |
| 8 | `nativeWallet` belongs to the same identity as `burnedWallet`.     | `[[TBC: the error of this check]]` |
| 9 | The bridged position of `burnedWallet` covers the amount.          | `InsufficientBridgedBalance`       |

> **A halt stops the asset.** On checks 9 and 12 of a settlement, nothing is applied and the token pauses every movement between holders until an agent has investigated: see [36 Handle a late reconciliation or a halt](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md). A satellite that reports one execution twice stops the asset.

## Consuming a validation

A validation authorizes one transfer. `expiry` is a timestamp in seconds: the reference chain's block time at issuance plus the validity window. The reference chain verifies part of what consuming means and trusts T-REX Lite for the rest.

| On the satellite                                                                                  | The reference chain                                                                                                                                |
| ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| The sender is `from` and the recipient is `to`.                                                   | Verifies: check 10.                                                                                                                                |
| The amount is inside `[amountMin, amountMax]`, bounds included.                                   | Verifies: check 11.                                                                                                                                |
| The validation is used once.                                                                      | Verifies, as an emergency: a second report halts the token, check 12.                                                                              |
| The execution is strictly before `expiry`.                                                        | Trusts the satellite. A notification carries no time, and a settlement is applied whatever the clock says.                                         |
| The caller is `from`, or `spender` through `transferFrom`. An empty `spender` means `from` alone. | Trusts the satellite. A notification names no caller, and no module runs on the satellite: the `SPENDER` modules accepted the spender at issuance. |
| The sender holds the amount.                                                                      | Verifies on its own ledger: check 14. At issuance, `amountMax` is capped at what the register records as available for `from`.                     |

The compliance stores `MessageTypesLib.hashValidation(validation)`, an ERC-712 struct hash without a domain, in `validationOf(validationId).hash`. No message carries it. The NatSpec of the stored hash calls it the identifier the satellite consumes: a satellite that uses it recomputes it from the body.

## A transfer between two satellites

The compliance expects two reports when `to` is on another satellite than `from`. The flow is in [34 Transfer between two satellites](/guides/cross-chain/34.-transfer-between-two-satellites.md).

* **The sending chain** consumes the validation, burns the sender's balance, and sends a proof of burn to the asset's T-REX Lite on the receiving chain. It reports the burn.
* **The receiving chain** mints the same amount to the recipient against that proof. It does not check the validation again. It reports the mint.

The burn always comes first. The reference chain accepts the two reports in either order. The first report is recorded with `ValidationLegConfirmed`: a burn puts the amount in transit on the register (status `AwaitingMint`), a mint moves nothing (status `AwaitingBurn`). The second report completes the transfer if it repeats the amount, with `ValidationSettled`.

When one report never arrives, the Validation Keeper calls `resolveStuckValidation`, two reconciliation windows after `expiry` at the earliest. If only the burn was reported, the amount in transit returns to the sender's wallet on the register. If only the mint was reported, issuance is paused for the sending chain. A report that arrives afterward halts the token.

## Addresses and identity

* **Wallets are ERC-7930 interoperable addresses, version 1, in binary form:** a 2-byte version, a 2-byte chain type, the length of the chain reference on 1 byte, the chain reference, the length of the address on 1 byte, the address. An EVM address on chain id 137 is `0x00010000018914` followed by its 20 bytes.
* **Only the canonical form is accepted.** `WalletKeyLib.parse` refuses bytes after the address and, on an EVM chain, a chain reference of several bytes that starts with a zero byte: `NonCanonicalInteroperableAddress`. The register keys a wallet by the `keccak256` of these bytes.
* **On an EVM satellite, T-REX Lite sits at the token's own address.** With no registered peer, `peerFor` returns that address for a registered chain of type `0x0000`. See [Deterministic deployment (CREATE3)](/governance-and-infrastructure/deterministic-deployment-create3.md).
* **On another kind of chain, the Issuer registers the peer** with `Token.setPeer(chainKey, peer)`. Until then the chain is not open. The gateway presents exactly the registered bytes as `sender`.
* **Identity stays on the reference chain.** A wallet of the satellite is linked to an ONCHAINID through the `IdentityFactory`: see [07 Link a wallet to an identity](/guides/identity/07.-link-a-wallet-to-an-onchainid.md). The wallet sends a link proposal from its own chain, through a gateway that the `IdentityFactory` trusts for that chain. This list is separate from the `TrustedGatewayRegistry`. The gateway presents the wallet itself as `sender`, and the payload is `abi.encode(wallet, identity, expiry)` with the types `(bytes, address, uint256)`. A validation that names a wallet with no link reverts with `UnverifiedWallet`, so a chain has no usable wallet until this path works. `[[TBC: the contract and the function the wallet calls on its own chain]]`

## On a non-EVM chain

The reference chain speaks one format, ABI encoding, and an implementation on another kind of chain reproduces it. See [Non-EVM satellites](/cross-chain-the-distributed-register/non-evm-satellites.md).

* Decode the envelope and the `ComplianceValidation` tuple: 32-byte big-endian words, offsets for the `bytes` fields, content padded on the right.
* Encode `SettlementNotification` and `BurnProof` the same way, in the same envelope, with version `1`.
* Keep `token` and `nativeWallet` as 20-byte EVM addresses: they are on the reference chain.

## What is not specified

The implementer obtains these from the protocol team.

* `[[TBC: the names and the signatures of T-REX Lite's own functions, the T-REX Lite factory and the interface of the linked compliance contract]]`
* `[[TBC: the content of the mint instruction, and the function of T-REX Lite that receives it]]`
* `[[TBC: the content of a recall instruction]]`
* `[[TBC: the recall function of T-REX Lite, its name and its arguments]]`
* `[[TBC: the message that reports a failed recall]]`
* `[[TBC: the format of the proof of burn between two satellites, and how T-REX Lite is told its gateway and its peers on other satellites]]`
* `[[TBC: how a transfer on T-REX Lite names a recipient that is on another satellite, or on the reference chain]]`
* `[[TBC: the call on the reference chain that has T-REX Lite deployed on a satellite]]`
* `[[TBC: who pays for a message that T-REX Lite sends while a delivery is executed]]`
* `[[TBC: what the receiving satellite does with its copy of the validation, and whether its mint report takes the validationId and the bytes of to from that copy or from the proof of burn]]`

## Test against the reference chain

The interop tests of the T-REX repository simulate a satellite, in `test/integration/interop` and `test/integration/helpers/InteropSuiteTest.sol`. `ERC7786GatewayMock` stands in for the gateway of one satellite chain: a send only queues, and the test relays, repeats or drops each message. The token's own address plays T-REX Lite and sends payloads built with `MessageTypesLib.encodeSettlement` and `encodeBurnProof`. `_decodeQueuedValidation` decodes an outbound validation as a satellite does. Feed the bytes your implementation produces through the same mock. The mock attributes every message to an EVM chain id and to the address that queued it: it tests the payload bytes of any implementation, under an EVM chain id.

## Conformance checklist

* A message is accepted only through the trusted gateway and from a peer.
* The envelope is read as `(uint8, uint8, bytes)`, and every message sent carries version `1`.
* A transfer executes only against a stored validation: `from`, `to`, the caller, the inclusive range, strictly before `expiry`. Any other transfer reverts.
* A validation is consumed once.
* Every execution is reported once, with the `validationId`, the exact amount and the wallet bytes of the validation.
* Between two satellites, the burn comes first, each chain reports its own half with the other wallet empty, and both amounts are equal.
* Tokens are minted only on a mint instruction or on a proof of burn, and every burn is proven.
* A burn proof carries the `nativeWallet` to credit on the reference chain, which is not zero. The reference chain checks that it belongs to the identity of `burnedWallet`.
* Every wallet is written in canonical ERC-7930 form.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.t-rex.network/build/implement-t-rex-lite-on-a-new-chain.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
