> 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/reference/token-suite/transfer-validation.md).

# Transfer validation

A transfer on a satellite executes only against a validation issued on the reference chain. The functions that issue, track, settle and close validations are part of `ModularCompliance`. This page lists them. The model is in [Authorization before execution](/cross-chain-the-distributed-register/authorization-before-execution.md) and [The dual-state lifecycle](/cross-chain-the-distributed-register/the-dual-state-lifecycle.md).

### At a glance

|                |                                                                                                                   |
| -------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Source**     | `contracts/compliance/modular/TransferValidation.sol`, inherited by `ModularCompliance`                           |
| **Interfaces** | `ITransferValidation`, `ISettlementHandler`                                                                       |
| **Works with** | The `Token`, which reserves the amount and sends the message, and the `TREXRegistry`, which resolves wallets      |
| **Roles**      | `AGENT` to request for a holder, `COMPLIANCE_MANAGER` for the windows, `VALIDATION_KEEPER` to discard and resolve |

### Request a validation

```solidity
function requestTransferValidation(
    bytes calldata from,
    bytes calldata to,
    uint256 requestedMin,
    uint256 requestedMax,
    bytes calldata spender
) external returns (uint256 validationId);
```

`from`, `to` and `spender` are ERC-7930 interoperable addresses in canonical form. `spender` is empty when the sender executes itself. The function checks the range, answers five questions in this order, then writes.

| Question                  | Checks                                                                                                                                                                                                         | Reverts with                                                                                                           |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Is the range valid?       | `requestedMin` is at most `requestedMax`                                                                                                                                                                       | `InvalidRequestedRange`                                                                                                |
| Where does it go?         | `from` is on a satellite. The transfer has two legs when `to` is on another satellite                                                                                                                          | `SenderNotOnSatellite`, `NonCanonicalInteroperableAddress`                                                             |
| May the caller ask?       | `from` resolves to an identity. The caller is that identity, or holds `AGENT`                                                                                                                                  | `UnverifiedWallet`, `NotAuthorizedForWallet`                                                                           |
| By when?                  | A validity window is set. For each satellite involved, issuance is not paused and a reconciliation window is set. The reservation lasts for the larger of the two windows                                      | `ValidityWindowNotSet`, `ValidationIssuancePaused`, `ReconciliationWindowNotSet`                                       |
| How much?                 | `to` is eligible (`isWalletVerified`). The maximum is capped at what the sending wallet has available, then at what the `RULE` modules allow. The rules are not asked when both wallets belong to one identity | `UnverifiedWallet`, `ZeroValue` when nothing is available, `EmptyValidationRange` when the cap falls below the minimum |
| May this spender execute? | Every `SPENDER` module accepts a named spender                                                                                                                                                                 | `ValidationSpenderRefused`                                                                                             |

On success the compliance stores the validation, reserves `amountMax` on the sending wallet (`Token.reserveForValidation`), counts it in the pending amounts of both identities, emits `TransferValidationIssued`, and has the token send one `COMPLIANCE_VALIDATION` message to each satellite involved. Guide: [31 Transfer on a satellite](/guides/cross-chain/31.-transfer-on-a-satellite.md).

A request is allowed while the token is paused.

### Status of a validation

`statusOf(validationId)` returns one of ten values. It reverts with `UnknownValidation` for an id that was never issued.

| Status                                           | Meaning                                                                                                              |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| `Pending`                                        | Issued, amount reserved, no report received                                                                          |
| `AwaitingMint`                                   | Two legs: the burn was reported, the mint is owed. The amount is in transit on the token                             |
| `AwaitingBurn`                                   | Two legs: the mint was reported, the burn is owed                                                                    |
| `Settled`                                        | Every expected report arrived. Reservation released, positions updated                                               |
| `Expired`                                        | Computed, never stored: `Pending` and past `releaseAt`                                                               |
| `Discarded`                                      | The Validation Keeper released the reservation of an expired validation                                              |
| `DiscardedAwaitingMint`, `DiscardedAwaitingBurn` | Discarded, then one leg arrived late. The other is owed                                                              |
| `LateReconciled`                                 | Every report arrived after the discard and was applied                                                               |
| `Resolved`                                       | The Validation Keeper gave up on a pair whose other leg never arrived. A leg that arrives afterwards halts the token |

`validationOf(validationId)` returns the stored record.

| Field                        | Content                                                                               |
| ---------------------------- | ------------------------------------------------------------------------------------- |
| `hash`                       | The ERC-712 struct hash of the `ComplianceValidation` that was sent                   |
| `amountMin`, `amountMax`     | The final bounds, inclusive                                                           |
| `expiry`                     | The deadline of the satellite: no execution at or after it                            |
| `releaseAt`                  | `expiry` plus the reconciliation window: from then on the validation can be discarded |
| `fromChainKey`, `toChainKey` | The chain of each wallet                                                              |
| `fromKey`, `toKey`           | The `keccak256` of each wallet, which a report is matched against                     |
| `fromWallet`                 | The sending wallet, as `bytes`                                                        |
| `twoLegs`                    | True when the two wallets are on two different satellites                             |
| `fromIdentity`, `toIdentity` | The identities resolved at issuance                                                   |
| `relocation`                 | True when both wallets belong to one identity                                         |
| `status`                     | The stored status                                                                     |
| `executedAmount`             | The amount of the first report, which the second must repeat                          |
| `legWallet`                  | For two legs, the wallet the first report carried                                     |

Other reads: `lastValidationId()`, `defaultValidityWindow()`, `reconciliationWindowOf(chainKey)`, `isIssuancePaused(chainKey)`.

### Settlement

`handleSettlement(originChainKey, notification)` is called by the token when a `SETTLEMENT_NOTIFICATION` passes the delivery checks. It reverts with `AddressNotATokenBoundToComplianceContract` for another caller.

| Case                                                                                    | Result                                                                                                                                  |
| --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| The validation is unknown, the leg was already applied, or the validation is `Resolved` | Nothing is applied. `ReplayedSettlement` is emitted and the function tells the token to pause itself                                    |
| The wallets or the origin chain do not match the validation                             | Reverts with `SettlementLegMismatch`                                                                                                    |
| The amount is outside `[amountMin, amountMax]`                                          | Reverts with `SettlementOutOfBounds`                                                                                                    |
| Second leg with another amount than the first                                           | Reverts with `SettlementAmountMismatch`                                                                                                 |
| First of two legs                                                                       | Recorded with `ValidationLegConfirmed`. A burn puts the amount in transit on the token                                                  |
| Last expected report                                                                    | Positions and token ledger updated, `ValidationSettled`                                                                                 |
| Report for a discarded validation                                                       | Applied, with `LateReconciliation`. Issuance is paused for the origin chain when the amount exceeds what the rules allow at that moment |

Every report that reaches the compliance emits `SettlementNotified`.

### Windows and issuance

| Function                                      | Access               | Reverts with                                                                                     |
| --------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------ |
| `setDefaultValidityWindow(duration)`          | `COMPLIANCE_MANAGER` | `ZeroDuration`. Emits `DefaultValidityWindowSet`                                                 |
| `setReconciliationWindow(chainKey, duration)` | `COMPLIANCE_MANAGER` | `ZeroDuration`. Emits `ReconciliationWindowSet`                                                  |
| `setIssuancePaused(chainKey, paused)`         | `COMPLIANCE_MANAGER` | Nothing. Emits `ValidationIssuancePaused` or `ValidationIssuanceUnpaused` when the state changes |

### The Validation Keeper

| Function                                   | Access              | Reverts with                                                                                                                                                                                                                                |
| ------------------------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `discardExpiredValidations(validationIds)` | `VALIDATION_KEEPER` | `UnknownValidation`, `ValidationNotDiscardable` unless the validation is `Pending`, `ValidationNotReleasable` before `releaseAt`. Releases the reservation and emits `ValidationDiscarded`                                                  |
| `resolveStuckValidation(validationId)`     | `VALIDATION_KEEPER` | `UnknownValidation`, `ValidationNotStuck` unless one leg of two arrived, `ValidationNotYetResolvable` before the date the error carries. Returns an amount held in transit to the wallet it was burned from, and emits `ValidationResolved` |

Guides: [35 Discard or resolve a validation](/guides/cross-chain/35.-discard-or-resolve-a-validation.md), [36 Handle a late reconciliation or a halt](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md).

### Events

| Event                                                                                                                                                                           | Emitted when                                               |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `TransferValidationIssued(uint256 indexed validationId, bytes from, bytes to, bytes spender, uint256 amountMin, uint256 amountMax, uint64 expiry, uint64 reconciliationWindow)` | A validation is issued                                     |
| `SettlementNotified(bytes32 indexed originChainKey, uint256 indexed validationId, bytes from, bytes to, uint256 amount)`                                                        | A report from a satellite reaches the compliance           |
| `ValidationLegConfirmed(uint256 indexed validationId, bytes32 indexed chainKey, uint256 amount)`, `ValidationSettled` (same arguments)                                          | The first of two reports is recorded. Every report arrived |
| `LateReconciliation(uint256 indexed validationId, bytes32 indexed chainKey)`                                                                                                    | A report is applied after the discard                      |
| `ReplayedSettlement(uint256 indexed validationId, bytes32 indexed chainKey)`                                                                                                    | A report is refused and the token halts                    |
| `ValidationDiscarded(uint256 indexed validationId)`                                                                                                                             | An expired validation is discarded                         |
| `ValidationResolved(uint256 indexed validationId, uint256 returnedAmount)`                                                                                                      | A half-reported transfer is closed                         |
| `ValidationIssuancePaused(bytes32 indexed chainKey)`, `ValidationIssuanceUnpaused` (same argument)                                                                              | Issuance for a chain stops, resumes                        |
| `DefaultValidityWindowSet(uint64 duration)`, `ReconciliationWindowSet(bytes32 indexed chainKey, uint64 duration)`                                                               | A window is set                                            |


---

# 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/reference/token-suite/transfer-validation.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.
