> 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/positions/10.-transfer-between-investors.md).

# 10. Transfer between investors

A transfer moves units of the asset from one holder to another on the reference chain. It is the secondary movement of a regulated asset: peer to peer, with no intermediary deciding, and under the rules of the asset. The holder triggers it with a standard ERC-20 call, or a spender the holder approved triggers it on the holder's behalf. The token records the transfer only if the recipient is eligible and the movement complies.

## At a glance

|                   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Triggered by**  | Investor (sender), or a spender it approved                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| **Actors**        | Investor (sender), Investor (recipient), Venue or application (as spender), Platform                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| **Contracts**     | `Token`, `TREXRegistry`, `ModularCompliance`, compliance modules, the ONCHAINID of the recipient and its Claim Issuers (read only, during verification)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| **Preconditions** | The sender holds a free balance on the reference chain (see [08](/guides/positions/08.-mint-a-position-to-an-investor.md)); the recipient's identity holds a valid claim from a trusted Claim Issuer for every required topic (see [05](/guides/claim-issuer/05.-issue-a-claim-to-an-investor.md)); the receiving address is the recipient's ONCHAINID, a wallet linked to it, or an address registered to it for the asset (see [06](/guides/agents/06.-register-an-investor-for-an-asset.md)); the token is not paused; for a transfer through a spender, the sender has approved the spender and the asset's spender policy accepts it (see [26](/guides/issuer/26.-authorize-spenders.md)) |
| **Result**        | The sender's balance has decreased and the recipient's has increased by `amount`, and the position has moved from the sender's identity to the recipient's                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

## Actors and roles

| Actor                | Role in this flow                                                                    | What they hold                                                         |
| -------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| Investor (sender)    | Sends the tokens, or approves a spender to do it.                                    | A free balance at one of its addresses, and the key of that address    |
| Investor (recipient) | Receives the tokens. Does not transact.                                              | An ONCHAINID with valid claims                                         |
| Venue or application | Executes the transfer for the sender, when the transfer goes through `transferFrom`. | An allowance from the sender. Acceptance by the asset's spender policy |
| Platform             | Runs the pre-checks and submits.                                                     | The key of the party it acts for                                       |

## Sequence

```mermaid
sequenceDiagram
    participant Platform
    participant Sender as Investor or spender
    participant Token
    participant Registry as TREXRegistry
    participant MC as ModularCompliance
    participant Module as Compliance modules

    Platform->>Registry: isVerified(to) (read)
    Platform->>MC: canTransfer(from, to, amount) (read)
    Platform->>Sender: pre-checks passed, submit
    alt the holder sends
        Sender->>Token: transfer(to, amount)
    else a spender sends
        Sender->>Token: transferFrom(from, to, amount)
        Token->>MC: canSpenderCall(spender, from, to, amount)
        MC->>Module: moduleCheckSpender(ctx), for each SPENDER module
        Token->>Token: allowance sufficient, then spent
    end
    Token->>Token: not paused, neither address frozen, free balance sufficient
    Token->>Registry: isVerified(to)
    Token->>MC: canTransfer(from, to, amount)
    MC->>Module: allowedAmount(ctx), for each RULE module
    Token->>Token: move the balance
    Token-->>Platform: Transfer(from, to, amount)
    Token->>MC: transferred(from, to, amount)
    MC->>MC: positions of both identities updated
    MC->>Module: afterTransfer(ctx), for each TRACKER module
```

## Step by step

### Step 1. Confirm the transfer can execute

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

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

**Before the call:** The sender has chosen the recipient address `to` and the amount.

**What happens:** The Platform reads `Token.freeBalanceOf(from)`, which must cover the amount, `TREXRegistry.isVerified(to)` and `ModularCompliance.canTransfer(from, to, amount)`, which must both be true, and `Token.paused()`, which must be false. For a transfer through a spender, it also reads `Token.allowance(from, spender)` and `ModularCompliance.canSpenderCall(spender, from, to, amount)`.

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

### Step 2. Approve a spender

Only for a transfer through a spender.

**Who:** Investor (sender)

**Call:** `Token.approve(spender, amount)`, or a signature consumed by `Token.permit(owner, spender, value, deadline, v, r, s)`

**Before the call:** The sender has decided to let the spender move up to `amount` of its tokens.

**What the contract checks:** The standard ERC-20 conditions. For `permit`: the signature is the owner's, over the current nonce, and the deadline has not passed.

**What changes:** The allowance of the spender over the sender's address is set. The token emits `Approval(owner, spender, value)`. The approval itself is not subject to the asset's spender policy. The policy applies when the spender uses it.

### Step 3. Transfer

**Who:** Investor (sender), from the address that holds the tokens. Or the spender.

**Call:** `Token.transfer(to, amount)` by the holder. `Token.transferFrom(from, to, amount)` by a spender.

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

**What the contract checks:**

1. For `transferFrom`: `ModularCompliance.canSpenderCall(spender, from, to, amount)` returns true. Every `SPENDER` module must accept the caller. With no `SPENDER` module, every spender is accepted. Otherwise the call reverts with `SpenderNotAllowed`, and the allowance is untouched.
2. For `transferFrom`: the allowance covers the amount. Otherwise `ERC20InsufficientAllowance`.
3. The token is not paused. Otherwise `EnforcedPause`.
4. Neither the sender's address nor the recipient's address is frozen. Otherwise `FrozenWallet`.
5. The amount is within the sender's free balance: its balance less its frozen tokens. Otherwise `ERC20InsufficientBalance`.
6. `TREXRegistry.isVerified(to)` returns true. The registry resolves `to` to an identity, refuses a wallet whose link was revoked, and requires a valid claim from a trusted Claim Issuer for every topic required of that identity's type. Otherwise `UnverifiedIdentity`.
7. `ModularCompliance.canTransfer(from, to, amount)` returns true. The compliance resolves the identity of both sides. Each `RULE` module answers the largest amount it allows, and `amount` must not exceed the smallest answer. Otherwise `ComplianceNotFollowed`.
8. The amount is not zero. The compliance does not record a movement of zero. Otherwise `ZeroValue`.

**What changes:** The token debits the sender and credits the recipient, and emits `Transfer(from, to, amount)`. It calls `ModularCompliance.transferred(from, to, amount)`. The compliance moves `amount` from the position of the sender's identity to the position of the recipient's identity and calls `afterTransfer` on every `TRACKER` module. When both addresses belong to the same identity, no position changes.

## State after the flow

* `Token.balanceOf(from)` has decreased by `amount` and `Token.balanceOf(to)` has increased by `amount`.
* `Token.totalSupply()` is unchanged.
* `ModularCompliance.positionOf` has moved by `amount` from the sender's identity to the recipient's identity.
* For `transferFrom`, the spender's allowance has decreased by `amount`. An allowance of `type(uint256).max` is not decreased.
* One `Transfer` event was emitted. The transfer is settled: the register has recorded the change of ownership in the same transaction.

## Failure modes

| Symptom                                     | Cause                                                                                                                                                     | Fix                                                                                                                                                                 |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reverts with `SpenderNotAllowed`            | The asset's spender policy refuses the caller of `transferFrom` (step 3, check 1)                                                                         | The Issuer authorizes the spender: [26](/guides/issuer/26.-authorize-spenders.md). Or the holder transfers directly                                                 |
| Reverts with `ERC20InsufficientAllowance`   | The spender has no allowance, or not enough (step 3, check 2)                                                                                             | The holder approves the spender (step 2)                                                                                                                            |
| Reverts with `EnforcedPause`                | The token is paused (step 3, check 3)                                                                                                                     | Wait for the Issuer's agents to lift the pause                                                                                                                      |
| Reverts with `FrozenWallet`                 | The sender's or the recipient's address is frozen (step 3, check 4)                                                                                       | The error names the address. Ask the asset's agents: [13](/guides/agents/13.-freeze-and-unfreeze-an-address-or-a-partial-balance.md)                                |
| Reverts with `ERC20InsufficientBalance`     | The amount exceeds the free balance: part of the balance is frozen, or the tokens are on a satellite (step 3, check 5)                                    | Read `freeBalanceOf(from)`. Reduce the amount                                                                                                                       |
| Reverts with `UnverifiedIdentity`           | The recipient is not eligible: no identity, a revoked link, a missing, expired or revoked claim, or an issuer not trusted for the topic (step 3, check 6) | The recipient completes its onboarding: [05](/guides/claim-issuer/05.-issue-a-claim-to-an-investor.md), [07](/guides/identity/07.-link-a-wallet-to-an-onchainid.md) |
| Reverts with `ComplianceNotFollowed`        | A rule refuses this amount for this pair of holders, or one side resolves to no identity while a rule is bound (step 3, check 7)                          | Reduce the amount. See [11](/guides/issuer/11.-add-a-compliance-rule.md) for where rules come from                                                                  |
| Reverts with `ZeroValue`                    | The amount is zero. Unlike a plain ERC-20, the token refuses a transfer of zero (step 3, check 8)                                                         | Send a positive amount                                                                                                                                              |
| A wallet shows a balance and cannot send it | The balance includes frozen tokens. `balanceOf` is free plus frozen                                                                                       | Show `freeBalanceOf` as the transferable amount                                                                                                                     |

## Common misunderstandings

> **Wrong:** "A transfer is approved by the Issuer or by an agent."
>
> **Right:** Nobody approves a transfer. The holder sends, and the token evaluates the rules of the asset in the same transaction. The transfer settles if the recipient is eligible and the movement complies, and reverts otherwise.

> **Wrong:** "Both parties must be whitelisted."
>
> **Right:** There is no list of approved addresses. The token checks the eligibility of the recipient, from the claims on its identity, at the moment of the transfer. The sender is not re-verified, by design: a holder whose claim expired was eligible when it received the tokens and is still identified, so it keeps its position and can still send to an eligible recipient. An Issuer that wants to block it freezes its tokens or its address.

> **Wrong:** "The compliance check looks at the sender and the receiver."
>
> **Right:** It looks at their identities, at the amount, and at the distribution of the asset: a rule can count everything an identity owns across its addresses and chains. On `transferFrom` it also looks at the spender.

> **Wrong:** "A non-compliant transfer is flagged and reversed."
>
> **Right:** It is not recorded. The transaction reverts before any balance moves. There is nothing to reverse.

## Integration notes

* Run `isVerified(to)` and `canTransfer(from, to, amount)` as reads before submitting, and show the holder which gate refuses. Each revert names its cause.
* Show `freeBalanceOf` as the amount a holder can send. `balanceOf` includes frozen tokens, and does not include tokens active on satellites.
* A recipient can be an ONCHAINID. Do not assume that a holding address is a wallet.
* A transfer of zero reverts. An application that sends zero-value transfers to probe a token, as some do with ERC-20 tokens, uses the reads instead.
* `Token.batchTransfer(toList, amounts)` sends to several recipients in one transaction. Each entry passes every check. The batch reverts as a whole if one fails.
* A venue reads `canSpenderCall` before a `transferFrom`. Being approved by the holder is not sufficient when the asset has a spender policy.
* The transfer is final as a change of ownership when the transaction is. There is no pending state on the reference chain.
* A transfer involving a wallet on a satellite is a different flow, with an authorization issued first.

## Related flows

* [08 Mint a position to an investor](/guides/positions/08.-mint-a-position-to-an-investor.md) : the same gates, without a sender
* [24 Transfer through the identity](/guides/identity/24.-transfer-through-the-identity.md) : the identity moves tokens from a linked wallet
* [26 Authorize spenders](/guides/issuer/26.-authorize-spenders.md) : the spender policy of an asset
* [13 Freeze and unfreeze an address or a partial balance](/guides/agents/13.-freeze-and-unfreeze-an-address-or-a-partial-balance.md) : why a transfer can be refused for a freeze
* [14 Execute a forced transfer](/guides/agents/14.-execute-a-forced-transfer.md) : a transfer ordered by an agent
* [31 Transfer on a satellite](/guides/cross-chain/31.-transfer-on-a-satellite.md) : the cross-chain equivalent


---

# 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/positions/10.-transfer-between-investors.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.
