> 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/guides/cross-chain/31.-transfer-on-a-satellite.md).

# 31. Transfer on a satellite

A transfer on a satellite moves tokens out of a wallet on a satellite chain to a recipient on the same chain, or to a recipient on the reference chain. It takes three steps: the reference chain authorizes the transfer by issuing a compliance validation, T-REX Lite executes it against that validation, and the satellite reports the execution so that the register records the change of ownership. A transfer the rules refuse receives no validation and never executes.

A transfer to a recipient on another satellite follows the same steps with two reports. It is covered in [34](/guides/cross-chain/34.-transfer-between-two-satellites.md).

## At a glance

|                   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Triggered by**  | Investor (sender), through its ONCHAINID. Or a Token Agent on its behalf                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| **Actors**        | Investor (sender), Identity Owner, Investor (recipient), Venue or application (as spender), Platform                                                                                                                                                                                                                                                                                                                                                                                                         |
| **Contracts**     | `ModularCompliance`, compliance modules, `Token`, `TREXRegistry`, the ERC-7786 gateway of the route, T-REX Lite on the satellite and its compliance contract                                                                                                                                                                                                                                                                                                                                                 |
| **Preconditions** | The asset is open on the satellite, with its windows set (see [33](/guides/cross-chain/33.-open-an-asset-on-a-satellite.md)); the sending wallet holds a bridged position (see [30](/guides/cross-chain/30.-delegate-a-position-out-to-a-satellite.md)); the recipient's address is linked to an identity that is eligible for the asset; issuance is not paused for the chain; for a transfer through a spender, the asset's spender policy accepts it (see [26](/guides/issuer/26.-authorize-spenders.md)) |
| **Result**        | The executed amount has moved from the sender's bridged position to the recipient, and from the position of the sender's identity to the position of the recipient's identity                                                                                                                                                                                                                                                                                                                                |

## Actors and roles

| Actor                | Role in this flow                                                                                                | What they hold                                                  |
| -------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| Investor (sender)    | Holds the position on the satellite and executes the transfer there, unless a spender does.                      | A balance on T-REX Lite. The key of its wallet on the satellite |
| Identity Owner       | The sender in its capacity as controller of its ONCHAINID, which requests the validation on the reference chain. | An `ACTION` key of the identity the sending wallet is linked to |
| Investor (recipient) | Receives the tokens. Does not transact.                                                                          | An address linked to an eligible identity                       |
| Venue or application | Executes the transfer on the satellite for the sender, when the validation names it as spender.                  | Acceptance by the asset's spender policy                        |
| Platform             | Runs the pre-checks, relays the request, and follows the validation to its settlement.                           | Whatever keys the sender has authorized                         |

## Sequence

```mermaid
sequenceDiagram
    participant Identity as Sender ONCHAINID
    participant MC as ModularCompliance
    participant Token
    participant Gateway as ERC-7786 gateway
    participant Lite as T-REX Lite
    participant Sender as Sender wallet or spender

    Identity->>MC: requestTransferValidation(from, to, min, max, spender)
    MC->>MC: route, caller, deadlines, amount range, spender
    MC->>MC: record the validation, reserve amountMax for both identities
    MC->>Token: reserveForValidation(from, amountMax)
    MC-->>Identity: TransferValidationIssued(validationId, ...)
    MC->>Token: dispatchComplianceValidation(chainKey, validationId, body)
    Token->>Gateway: compliance validation, to the peer
    Gateway->>Lite: deliver
    Sender->>Lite: transfer(to, amount), or transferFrom by the spender
    Lite->>Lite: a stored validation matches: parties, amount in range, before expiry. Consume it
    Lite->>Gateway: settlement notification
    Gateway->>Token: receiveMessage
    Token->>Token: gateway trusted and pinned, author is the peer, token not paused
    Token->>MC: handleSettlement(chainKey, notification)
    MC->>MC: validation issued, leg matches, amount in range, not already applied
    MC->>MC: release the reservation, move the positions
    MC->>Token: settleValidation(from, to, amount, validationId)
    Token->>Token: sender's bridged position down, recipient up
    MC-->>Identity: ValidationSettled(validationId, chainKey, amount)
```

## Step by step

### Step 1. Confirm the transfer can be authorized

**Who:** Platform, on behalf of the sender

**Call:** Off-chain, using read calls on the reference chain

**Before the call:** The sender has chosen the recipient and the amount. The Platform encodes the sending wallet and the recipient's address as ERC-7930 interoperable addresses in canonical form, `from` and `to`. It chooses a range `[requestedMin, requestedMax]`: the intended amount, with a tolerance when the executed amount may differ, as on a venue.

**What happens:** The Platform reads `Token.availableOf(from)`, the bridged position of the wallet less what earlier validations reserve, which must cover `requestedMin`. It reads `TREXRegistry.isWalletVerified(to)`, which must be true. It reads `ModularCompliance.isIssuancePaused(chainKey)`, which must be false, and `Token.isChainOpen(chainKey)`, which must be true.

**What changes:** Nothing on-chain.

### Step 2. Request the validation

**Who:** Identity Owner (`ACTION` key), through the ONCHAINID the sending wallet is linked to. Or a Token Agent (`AGENT`)

**Call:** `ModularCompliance.requestTransferValidation(from, to, requestedMin, requestedMax, spender)`, called by the identity. `spender` is empty when the sender executes itself

**Before the call:** Step 1 passed.

**What the contract checks:**

1. `requestedMin` is not above `requestedMax`. Otherwise the call reverts with `InvalidRequestedRange`.
2. `from`, `to` and `spender` are canonical interoperable addresses, and `from` is on a satellite. Otherwise `NonCanonicalInteroperableAddress` or `SenderNotOnSatellite`.
3. `from` resolves to an identity. A wallet whose link was revoked still resolves. Otherwise `UnverifiedWallet`.
4. The caller is that identity, or holds `AGENT` in the asset's domain. Otherwise `NotAuthorizedForWallet`.
5. The validity window is set, issuance is not paused for the chain, and the chain has a reconciliation window. Otherwise `ValidityWindowNotSet`, `ValidationIssuancePaused` or `ReconciliationWindowNotSet`.
6. `to` is eligible: it is actively linked to an identity that holds a valid claim from a trusted Claim Issuer for every required topic. Otherwise `UnverifiedWallet`.
7. The range is capped at what the wallet can still send, `availableOf(from)`. `requestedMin` must still fit. Otherwise `EmptyValidationRange`.
8. The range is capped at the smallest amount the `RULE` modules allow for this movement. `requestedMin` must still fit. Otherwise `EmptyValidationRange`. This check is skipped when both addresses belong to one identity.
9. The final maximum is not zero. Otherwise `ZeroValue`.
10. If a spender is named, every `SPENDER` module accepts it. Otherwise `ValidationSpenderRefused`.
11. The chain is open on the token. Otherwise `ChainNotOpen`.

**What changes:** The compliance takes the next `validationId` and records the validation with its final range, its `expiry` (now plus the validity window), its release time (`expiry` plus the reconciliation window) and the status `Pending`. It adds `amountMax` to the pending amounts of the two identities, which every rule counts from now on. The token reserves `amountMax` against the sending wallet and emits `ReservedForValidation`. The compliance emits `TransferValidationIssued(validationId, from, to, spender, amountMin, amountMax, expiry, reconciliationWindow)`. The token records the gateway the validation goes out through, emits `ValidationRoutePinned`, sends the validation to its peer and emits `ProtocolMessageSent`. Nothing has moved yet.

### Step 3. Execute on the satellite

**Who:** Investor (sender), from its wallet on the satellite. Or the spender named in the validation

**Call:** The ERC-20 `transfer(to, amount)` on T-REX Lite, by the sender. Or `transferFrom(from, to, amount)`, by the spender. `[[TBC: how the call names a recipient that is on the reference chain]]`

**Before the call:** The validation has been delivered to T-REX Lite through the gateway and is stored in the compliance contract linked to it.

**What the contract checks:** T-REX Lite is a plain ERC-20 whose transfers are conditional. A transfer executes only if a stored validation matches it: the sender and the recipient are those of the validation, the caller is the sender or the named spender, the amount is inside `[amountMin, amountMax]`, and the execution is strictly before `expiry`. The validation is consumed by the transfer and cannot be used again. A transfer with no matching validation reverts.

**What changes:** On the satellite, for a recipient on the same chain, the sender's balance decreases and the recipient's increases by the executed amount. For a recipient on the reference chain, the sender's balance is burned. T-REX Lite sends a settlement notification carrying the `validationId`, both addresses and the exact amount. The register is unchanged until that notification arrives.

### Step 4. Settle on the reference chain

**Who:** The ERC-7786 gateway delivers the notification to the `Token`. No actor transacts

**Call:** `Token.receiveMessage(receiveId, sender, payload)`, called by the gateway

**Before the call:** Step 3 executed, and the satellite's transaction is final for the messaging provider.

**What the contract checks:**

1. The delivering gateway is trusted by the network. Otherwise `ERC7786RecipientUnauthorizedGateway`.
2. The author of the message is the asset's peer on that chain. Otherwise `SenderNotPeer`.
3. This gateway has not already delivered this message. Otherwise `MessageAlreadyReceived`.
4. The gateway is the one the validation went out through. Otherwise `GatewayNotPinned`.
5. The token is not paused. Otherwise `EnforcedPause`: the delivery reverts and can be made again.
6. The validation was issued. Otherwise nothing is applied, the compliance emits `ReplayedSettlement` and the token pauses itself.
7. The notification carries both addresses of the validation and comes from the sender's chain. Otherwise `SettlementLegMismatch`.
8. The amount is inside the issued range. Otherwise `SettlementOutOfBounds`.
9. The report of this validation was not already applied. Otherwise nothing is applied, the compliance emits `ReplayedSettlement` and the token pauses itself.

**What changes:** The compliance emits `SettlementNotified`. It releases the pending amounts of the two identities, and the token releases the reservation on the sending wallet with `ReleasedFromValidation`. The compliance moves the executed amount from the position of the sender's identity to the position of the recipient's identity, unless they are the same. The token updates its ledger:

| The recipient is       | On the token                                                                                              | Event                                                                                            |
| ---------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| On the same satellite  | The bridged position of `from` decreases and the bridged position of `to` increases.                      | `BridgedTransfer(fromKey, toKey, validationId, from, to, amount)`                                |
| On the reference chain | The bridged position of `from` and the total bridged amount decrease. The free balance of `to` increases. | `Transfer(0x0, to, amount)` and `SettledToNative(fromKey, to, validationId, fromWallet, amount)` |

The validation becomes `Settled` and the compliance emits `ValidationSettled(validationId, chainKey, amount)`. The `TRACKER` modules are told. The token emits `ProtocolMessageReceived`.

## State after the flow

* `ModularCompliance.statusOf(validationId)` is `Settled`, and `validationOf(validationId).executedAmount` is the amount that moved.
* `Token.bridgedBalanceOf(from)` has decreased by the executed amount. For a recipient on the same satellite, `Token.bridgedBalanceOf(to)` has increased by it. For a recipient on the reference chain, `Token.balanceOf(to)` has increased by it and `Token.totalBridged()` has decreased by it.
* `ModularCompliance.positionOf` has moved by the executed amount between the two identities, unless they are the same.
* `ModularCompliance.pendingOutOf` and `pendingInOf` no longer count this validation, and `Token.reservedOf(from)` no longer includes it.
* `Token.totalSupply()` is unchanged.
* The balances on T-REX Lite match the bridged positions on the register.

## Failure modes

| Symptom                                                                                          | Cause                                                                                                                                                                              | Fix                                                                                                                                                                 |
| ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reverts with `SenderNotOnSatellite`                                                              | `from` is an address on the reference chain (step 2, check 2)                                                                                                                      | A movement that starts on the reference chain does not use a validation. See [Movement types](/cross-chain-the-distributed-register/movement-types.md)              |
| Reverts with `NonCanonicalInteroperableAddress`                                                  | An address is malformed or not in canonical form (step 2, check 2)                                                                                                                 | Encode the chain and the address in canonical ERC-7930 form                                                                                                         |
| Reverts with `UnverifiedWallet`, naming `from`                                                   | The sending wallet is linked to no identity (step 2, check 3)                                                                                                                      | Link the wallet: [07](/guides/identity/07.-link-a-wallet-to-an-onchainid.md)                                                                                        |
| Reverts with `NotAuthorizedForWallet`                                                            | The caller is the wallet itself, a key of the identity, or another account. It is neither the identity nor an agent (step 2, check 4)                                              | Have the identity make the call                                                                                                                                     |
| Reverts with `ValidityWindowNotSet` or `ReconciliationWindowNotSet`                              | The Issuer has not set a window (step 2, check 5)                                                                                                                                  | [33](/guides/cross-chain/33.-open-an-asset-on-a-satellite.md)                                                                                                       |
| Reverts with `ValidationIssuancePaused`                                                          | Issuance is paused for the chain, by the Issuer or after a late reconciliation that breached a rule (step 2, check 5)                                                              | Wait for the Issuer to lift it: [36](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md)                                                             |
| Reverts with `UnverifiedWallet`, naming `to`                                                     | The recipient is not eligible: no link, a revoked link, or a missing, expired or revoked claim (step 2, check 6)                                                                   | The recipient completes its onboarding: [07](/guides/identity/07.-link-a-wallet-to-an-onchainid.md), [05](/guides/claim-issuer/05.-issue-a-claim-to-an-investor.md) |
| Reverts with `EmptyValidationRange`                                                              | The wallet cannot send `requestedMin`: its position is smaller, or earlier validations reserve it (step 2, check 7). Or the rules allow less than `requestedMin` (step 2, check 8) | Read `availableOf(from)`. Lower `requestedMin`, or wait for earlier validations to settle or be discarded                                                           |
| Reverts with `ZeroValue`                                                                         | Nothing is left to authorize (step 2, check 9)                                                                                                                                     | Same                                                                                                                                                                |
| Reverts with `ValidationSpenderRefused`                                                          | The asset's spender policy does not accept the named spender (step 2, check 10)                                                                                                    | The Issuer authorizes it: [26](/guides/issuer/26.-authorize-spenders.md). Or the sender executes itself                                                             |
| Reverts with `ChainNotOpen`                                                                      | The asset has no usable route to that chain (step 2, check 11)                                                                                                                     | [33](/guides/cross-chain/33.-open-an-asset-on-a-satellite.md)                                                                                                       |
| The validation was issued for less than `requestedMax`                                           | The compliance narrowed the range to what the wallet holds or the rules allow                                                                                                      | Read `amountMax` in `TransferValidationIssued`. Execute within it                                                                                                   |
| The transfer reverts on the satellite                                                            | No stored validation matches it: the validation is not delivered yet, has expired, was already consumed, or the amount is outside its range                                        | Wait for delivery. After `expiry`, request a new validation                                                                                                         |
| The validation expired without being executed and the wallet's available amount is still reduced | The reservation stays until the Validation Keeper discards the validation, after its release time                                                                                  | [35](/guides/cross-chain/35.-discard-or-resolve-a-validation.md)                                                                                                    |
| The transfer executed on the satellite and the register does not show it                         | The settlement notification is not delivered yet, or the token is paused (step 4, check 5)                                                                                         | Wait. A settlement is applied whenever it arrives                                                                                                                   |
| The delivery of the settlement reverts with `SettlementLegMismatch` or `SettlementOutOfBounds`   | The notification does not match the validation (step 4, checks 7 and 8)                                                                                                            | An anomaly of the satellite. The Issuer investigates: [36](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md)                                       |
| The token paused itself                                                                          | A settlement for an unknown validation, or one already applied, was delivered (step 4, checks 6 and 9)                                                                             | [36](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md)                                                                                             |

## Common misunderstandings

> **Wrong:** "The satellite checks compliance when the transfer executes."
>
> **Right:** The satellite checks nothing about the holder or the rules. It checks that a validation issued by the reference chain matches the transfer. Eligibility and compliance were evaluated on the reference chain, against the complete distribution of the asset, before the validation was issued.

> **Wrong:** "The wallet on the satellite requests its own validation."
>
> **Right:** The identity the wallet is linked to requests it, on the reference chain. A wallet on another chain cannot call the compliance, and authority over a holder's positions lives in its identity. An agent can also request a validation for a holder.

> **Wrong:** "Once the transfer executed on the satellite, the recipient owns the tokens on the register."
>
> **Right:** The register records the change of ownership when the settlement arrives. Between the two, the recipient holds a balance on T-REX Lite that it cannot move further: an onward transfer needs a new validation, and a validation is capped at the position the register records for the sending wallet.

> **Wrong:** "A validation is for an exact amount."
>
> **Right:** A validation carries a range. The satellite may execute any amount inside it, and the register treats the maximum as the amount that moves until the transfer settles. The settlement carries the exact amount.

## Integration notes

* Request the validation through the identity: a user operation or the execution queue, with an `ACTION` key. The compliance is an external target for the identity.
* Take `validationId`, the final range and `expiry` from `TransferValidationIssued`. The range may be narrower than requested.
* Execute well before `expiry`. The satellite refuses an execution at or after it.
* Follow a validation with `ModularCompliance.statusOf(validationId)`. `Pending` means not settled yet, `Expired` means past its release time and not discarded, `Settled` means recorded.
* A venue asks to be named as `spender` in the validation. Whether it is accepted is decided at issuance, by the asset's spender policy.
* Between two wallets of one identity, the rules about distribution are not asked and nothing is reserved against the identity. The wallet's available amount and the spender policy still apply.
* While a validation is `Pending`, its maximum counts against the recipient's room under any cap and against the sender's available amount. Request ranges no wider than needed.
* A validation can be issued while the token is paused. Its settlement waits for the pause to be lifted.
* Each party pays for the message its own transaction sends, directly or through a paymaster: the identity for the validation, on the reference chain, and the caller of the transfer for the settlement notification, on the satellite.
* A wallet on a satellite sees T-REX Lite as an ordinary ERC-20 token. An integration needs no special transfer call, only a validation in place before the transfer.

## Related flows

* [30 Delegate a position out to a satellite](/guides/cross-chain/30.-delegate-a-position-out-to-a-satellite.md) : how the position came to the satellite
* [34 Transfer between two satellites](/guides/cross-chain/34.-transfer-between-two-satellites.md) : the same flow with two reports
* [35 Discard or resolve a validation](/guides/cross-chain/35.-discard-or-resolve-a-validation.md) : when a validation is never executed
* [36 Handle a late reconciliation or a halt](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md) : when a settlement arrives late, or cannot be accounted for
* [26 Authorize spenders](/guides/issuer/26.-authorize-spenders.md) : the spender policy applied at issuance
* [10 Transfer between investors](/guides/positions/10.-transfer-between-investors.md) : the same movement on the reference chain


---

# 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/guides/cross-chain/31.-transfer-on-a-satellite.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.
