> 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/identity/24.-transfer-through-the-identity.md).

# 24. Transfer through the identity

An ONCHAINID can move the tokens held in any wallet linked to it, on the reference chain, without a signature from that wallet and without an allowance. The Identity Owner uses this to command a position spread over several wallets from one place, to consolidate it, or to empty a wallet it no longer trusts. The movement is an ordinary transfer: it passes the same gates as any other.

## At a glance

|                   |                                                                                                                                                                                                                                                                                                   |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Triggered by**  | Identity Owner                                                                                                                                                                                                                                                                                    |
| **Actors**        | Identity Owner, Investor, Platform                                                                                                                                                                                                                                                                |
| **Contracts**     | ONCHAINID, `Token`, `TREXRegistry`, `ModularCompliance`                                                                                                                                                                                                                                           |
| **Preconditions** | The source wallet resolves to the identity (see [07](/guides/identity/07.-link-a-wallet-to-an-onchainid.md)); the owner controls an `ACTION` key of the identity; the source wallet holds a free balance on the reference chain; the recipient is eligible for the asset; the token is not paused |
| **Result**        | The tokens have moved from the linked wallet to the recipient, on the identity's order                                                                                                                                                                                                            |

## Actors and roles

| Actor          | Role in this flow                                                         | What they hold                               |
| -------------- | ------------------------------------------------------------------------- | -------------------------------------------- |
| Identity Owner | Orders the transfer through its identity.                                 | An `ACTION` key of the identity              |
| Investor       | The Identity Owner in its capacity as holder of the position being moved. | A balance in a wallet linked to the identity |
| Platform       | Runs the pre-checks and relays the order.                                 | Whatever keys the owner has authorized       |

## Sequence

```mermaid
sequenceDiagram
    participant Platform
    participant Owner as Identity Owner
    participant Identity as ONCHAINID
    participant Token
    participant Registry as TREXRegistry
    participant MC as ModularCompliance

    Platform->>Registry: identity(from), isVerified(to) (read)
    Platform->>Token: freeBalanceOf(from) (read)
    Platform->>MC: canTransfer(from, to, amount) (read)
    Platform->>Owner: pre-checks passed, sign
    Owner->>Identity: instruct with an ACTION key
    Identity->>Token: identityTransfer(from, to, amount)
    Token->>Registry: identity(from)
    Registry-->>Token: the calling identity
    Token->>Token: not paused, not frozen, free balance sufficient
    Token->>Registry: isVerified(to)
    Token->>MC: canTransfer(from, to, amount)
    Token->>Token: move the balance
    Token->>MC: transferred(from, to, amount)
    Token-->>Platform: Transfer, IdentityTransfer
```

## Step by step

### Step 1. Confirm the transfer can execute

**Who:** Platform, on behalf of the Identity Owner

**Call:** Off-chain, using read calls

**Before the call:** The owner has chosen the source wallet `from`, the recipient `to` and the amount.

**What happens:** The Platform reads `TREXRegistry.identity(from)`, which must return the owner's identity, and `Token.freeBalanceOf(from)`, which must cover the amount. It reads `TREXRegistry.isVerified(to)` and `ModularCompliance.canTransfer(from, to, amount)`, which must both be true. When `to` is another address of the same identity, no position changes, and a rule on distribution such as `MaxBalancePerIdentityModule` does not limit the amount.

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

### Step 2. Order the transfer from the identity

**Who:** Identity Owner (`ACTION` key), through the identity

**Call:** `Token.identityTransfer(from, to, amount)`, called by the identity

**Before the call:** Step 1 passed. An `ACTION` key instructs the identity to make the call, in a user operation or through the identity's execution queue. The token is an external target, so `ACTION` is sufficient.

**What the contract checks:**

1. `from` resolves to an identity, and the caller is that identity. Otherwise the call reverts with `NotLinkedIdentity`.
2. The token is not paused. Otherwise `EnforcedPause`.
3. Neither `from` nor `to` is frozen. Otherwise `FrozenWallet`.
4. The amount is within the free balance of `from`. Otherwise `ERC20InsufficientBalance`.
5. `TREXRegistry.isVerified(to)` returns true: `to` resolves to an identity that holds a valid claim from a trusted Claim Issuer for every required topic. Otherwise `UnverifiedIdentity`.
6. `ModularCompliance.canTransfer(from, to, amount)` returns true. Otherwise `ComplianceNotFollowed`.
7. The amount is not zero. The compliance does not record a movement of zero. Otherwise `ZeroValue`.

These reverts are the token's. When the instruction goes through the identity's execution queue, the queue catches the revert: the owner's transaction succeeds, the queue emits `ExecutionFailed`, and nothing moves. A successful instruction emits `Executed`.

**What changes:** The token debits `from` and credits `to`, and emits `Transfer(from, to, amount)`. It calls `ModularCompliance.transferred(from, to, amount)`: the compliance moves the amount from the position of the sender's identity to the position of the recipient's identity, unless they are the same, and tells its tracker modules. The token then emits `IdentityTransfer(identity, from, to, amount)`.

## State after the flow

* `Token.balanceOf(from)` has decreased by `amount`, and `Token.balanceOf(to)` has increased by `amount`.
* `Token.totalSupply()` is unchanged.
* No allowance was read or written.
* When `to` belongs to another identity, `ModularCompliance.positionOf` has moved by `amount` between the two identities. When `to` belongs to the same identity, no position has changed.
* An indexer has seen `Transfer` followed by `IdentityTransfer`.

## Failure modes

| Symptom                                                                   | Cause                                                                                                                                                            | Fix                                                                                                         |
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Reverts with `NotLinkedIdentity`                                          | The caller is not the identity `from` resolves to: the call came from a key directly, from another identity, or `from` resolves to no identity (step 2, check 1) | Have the identity make the call. Read `TREXRegistry.identity(from)`                                         |
| The identity's instruction is refused                                     | The instructing key holds neither `ACTION` nor `MANAGEMENT`                                                                                                      | Use an `ACTION` key                                                                                         |
| The transaction succeeds, `ExecutionFailed` is emitted and no token moved | The instruction went through the identity's execution queue and the token refused the transfer (step 2, checks 1 to 7)                                           | Run the reads of step 1 again to find the check that fails                                                  |
| Reverts with `EnforcedPause`                                              | The token is paused (step 2, check 2)                                                                                                                            | Wait for the Issuer's agents to lift the pause                                                              |
| Reverts with `FrozenWallet`                                               | `from` or `to` is frozen (step 2, check 3)                                                                                                                       | Ask the asset's agents                                                                                      |
| Reverts with `ERC20InsufficientBalance`                                   | The amount exceeds the free balance of `from`: part of it is frozen, or it is not there (step 2, check 4)                                                        | Read `freeBalanceOf(from)` and reduce the amount                                                            |
| Reverts with `UnverifiedIdentity`                                         | `to` is not eligible: it resolves to no identity, its link was revoked, or its identity lacks a valid claim for a required topic (step 2, check 5)               | Send to an eligible address. For claims, see [05](/guides/claim-issuer/05.-issue-a-claim-to-an-investor.md) |
| Reverts with `ComplianceNotFollowed`                                      | A rule refuses this amount for this recipient (step 2, check 6)                                                                                                  | Reduce the amount, or see [11](/guides/issuer/11.-add-a-compliance-rule.md) for where rules come from       |
| Reverts with `ZeroValue`                                                  | The amount is zero (step 2, check 7)                                                                                                                             | Send a positive amount                                                                                      |
| The tokens are on a satellite and cannot be moved this way                | The identity's control covers the reference chain only                                                                                                           | Transfer on the satellite, or recall the position first                                                     |

## Common misunderstandings

> **Wrong:** "The identity has an allowance over its wallets."
>
> **Right:** No allowance exists. `identityTransfer` checks that the caller is the identity the wallet resolves to, and moves the tokens. The ERC-20 allowances of the wallet are neither read nor changed, and `approve`, `transferFrom` and `permit` keep their standard meaning.

> **Wrong:** "A key of the identity can call `identityTransfer`."
>
> **Right:** The caller must be the identity itself. A key holder that calls the token directly is just another address, and is refused. The key instructs the identity, and the identity calls the token.

> **Wrong:** "A transfer ordered by the identity skips the checks."
>
> **Right:** It passes every check of a transfer: the pause, freezes, the eligibility of the recipient and the compliance rules. What it skips is the spender policy, because the holder's own identity is not a third party.

> **Wrong:** "Moving tokens between my own wallets is a transfer of ownership."
>
> **Right:** Ownership belongs to the identity. A movement between two addresses of one identity changes where the tokens sit, not who owns them. The identity's position is unchanged, and a rule on distribution such as `MaxBalancePerIdentityModule` does not limit it.

## Integration notes

* Run `isVerified(to)` and `canTransfer(from, to, amount)` as reads first. They are the checks the token runs.
* To consolidate, send to the identity's own address. The identity holds tokens itself.
* Several transfers can go in one user operation as a batch, each from a different linked wallet.
* Index `IdentityTransfer` to distinguish a transfer ordered by the identity from one signed by the wallet. It always follows the `Transfer` it describes.
* A wallet whose link was revoked still resolves to its identity for this call. The owner can empty it. See [23](/guides/identity/23.-revoke-a-wallet.md).
* For tokens the identity holds at its own address, the identity calls `Token.transfer`. `identityTransfer` is for linked wallets.

## Related flows

* [07 Link a wallet to an ONCHAINID](/guides/identity/07.-link-a-wallet-to-an-onchainid.md) : what gives the identity this authority
* [23 Revoke a wallet](/guides/identity/23.-revoke-a-wallet.md) : emptying a wallet that is no longer trusted
* [10 Transfer between investors](/guides/positions/10.-transfer-between-investors.md) : the same gates, signed by the holding address
* [26 Authorize spenders](/guides/issuer/26.-authorize-spenders.md) : third parties, which this flow does not involve
* [32 Recall a position to the reference chain](/guides/cross-chain/32.-recall-a-position-to-the-reference-chain.md) : bringing a satellite position within the identity's reach


---

# 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/identity/24.-transfer-through-the-identity.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.
