> 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/agents/14.-execute-a-forced-transfer.md).

# 14. Execute a forced transfer

A forced transfer moves tokens from one address to another without the holder. An agent executes it when a decision overrides the holder: a court order, a succession, the correction of an error, the enforcement of a pledge. It goes through freezes and does not ask the compliance rules, and it keeps one gate: the recipient must be eligible.

## At a glance

|                   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Triggered by**  | Token Agent                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| **Actors**        | Issuer, Token Agent, Investor (current holder), Investor (recipient), Platform                                                                                                                                                                                                                                                                                                                                                                                           |
| **Contracts**     | `Token`, `TREXRegistry`, `ModularCompliance`, compliance modules                                                                                                                                                                                                                                                                                                                                                                                                         |
| **Preconditions** | The caller holds `AGENT_FORCED_TRANSFER` in the asset's domain (see [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)); the source address holds at least `amount` on the reference chain; 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 token is not paused; the decision is taken and documented under the Issuer's process |
| **Result**        | The tokens have moved from the source address to the recipient, and the position has moved between their identities                                                                                                                                                                                                                                                                                                                                                      |

## Actors and roles

| Actor                     | Role in this flow                                                                                     | What they hold                                            |
| ------------------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| Issuer                    | Decides the forced transfer and keeps the evidence that justifies it. Does not transact in this flow. | The roles it delegated to its agents                      |
| Token Agent               | Submits the forced transfer.                                                                          | `AGENT_FORCED_TRANSFER` on the `Token`                    |
| Investor (current holder) | Holds the tokens that are moved. Does not transact and does not consent.                              | A balance at the source address                           |
| Investor (recipient)      | Receives the tokens. Does not transact.                                                               | An ONCHAINID with valid claims                            |
| Platform                  | Runs the reads and submits with the Token Agent's key.                                                | The Token Agent's signing key, or a key authorized for it |

## Sequence

```mermaid
sequenceDiagram
    participant Platform
    participant Agent as Token Agent
    participant Token
    participant Registry as TREXRegistry
    participant MC as ModularCompliance
    participant Module as Compliance modules

    Platform->>Token: balanceOf(from), getFrozenTokens(from), paused() (read)
    Platform->>Registry: isVerified(to) (read)
    Platform->>MC: canTransfer(from, to, amount) (read, for information)
    Platform->>Agent: decision recorded, submit
    Agent->>Token: forcedTransfer(from, to, amount)
    Token->>Token: caller holds AGENT_FORCED_TRANSFER, token not paused
    Token->>Registry: isVerified(to)
    Registry-->>Token: true
    Token->>Token: balance sufficient, unfreeze what the transfer needs
    Token->>Token: move the balance
    Token-->>Platform: Transfer(from, to, amount), ForcedTransfer(agent)
    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. Assess the transfer

**Who:** Platform, on behalf of the Token Agent

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

**Before the call:** The Issuer has decided the transfer and holds the document that justifies it. The Platform knows the source address `from`, the recipient address `to` and the amount.

**What happens:** The Platform reads `Token.balanceOf(from)`, which must cover the amount, and `Token.getFrozenTokens(from)`, to know whether the transfer will release frozen tokens. It reads `Token.paused()`, which must be false, and `TREXRegistry.isVerified(to)`, which must be true. It reads `ModularCompliance.canTransfer(from, to, amount)` for information: the contract does not ask the rules, so a false answer tells the Issuer that the transfer will leave the asset in a state its rules would have refused. If part of the holder's position is active on a satellite, that part is not in `balanceOf` and is recalled first.

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

### Step 2. Force the transfer

**Who:** Token Agent (`AGENT_FORCED_TRANSFER`)

**Call:** `Token.forcedTransfer(from, to, amount)`

**Before the call:** Step 1 is done.

**What the contract checks:**

1. The caller holds `AGENT_FORCED_TRANSFER` in the asset's domain. Otherwise the call reverts with `AccessManagedUnauthorized`.
2. The token is not paused. Otherwise `EnforcedPause`.
3. `TREXRegistry.isVerified(to)` returns true: `to` resolves to an identity that holds a valid claim from a trusted Claim Issuer for every required topic, and its link is not revoked. Otherwise `UnverifiedIdentity`.
4. The amount is within the balance of `from`, frozen tokens included. Otherwise `ERC20InsufficientBalance`.
5. The amount is not zero. The compliance does not record a movement of zero. Otherwise `ZeroValue`.

A freeze on `from` or on `to` is not checked. The compliance rules are not asked. The eligibility of the current holder is not evaluated.

**What changes:** If the amount exceeds the free balance of `from`, the token releases the frozen tokens it needs and emits `TokensUnfrozen(from, releasedAmount)`. It debits `from`, credits `to`, and emits `Transfer(from, to, amount)`, then `ForcedTransfer(agent)`. It calls `ModularCompliance.transferred(from, to, amount)`. The compliance moves `amount` from the position of the holder's identity to the position of the recipient's identity and calls `afterTransfer` on every `TRACKER` module.

## State after the flow

* `Token.balanceOf(from)` has decreased by `amount` and `Token.balanceOf(to)` has increased by `amount`.
* `Token.totalSupply()` is unchanged.
* `Token.getFrozenTokens(from)` has decreased by what the transfer had to release. The tokens arrive free at `to`.
* `ModularCompliance.positionOf` has moved by `amount` from the holder's identity to the recipient's identity. When both addresses belong to one identity, no position has changed.
* No allowance was read or written.
* An indexer has seen `Transfer` followed by `ForcedTransfer`, which names the agent.

## Failure modes

| Symptom                                                        | Cause                                                                                                                                                       | Fix                                                                                                                                                        |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reverts with `AccessManagedUnauthorized`                       | The caller does not hold `AGENT_FORCED_TRANSFER` in the asset's domain (step 2, check 1)                                                                    | Grant the role: [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md). Holding another agent role is not enough                                   |
| Reverts with `EnforcedPause`                                   | The token is paused. A forced transfer does not go through the pause (step 2, check 2)                                                                      | Lift the pause first: [16](/guides/agents/16.-pause-and-unpause-a-token.md)                                                                                |
| Reverts with `UnverifiedIdentity`                              | The recipient 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 3) | Deliver to an eligible address. For claims, see [05](/guides/claim-issuer/05.-issue-a-claim-to-an-investor.md)                                             |
| Reverts with `ERC20InsufficientBalance`                        | `from` holds less than `amount` on the reference chain (step 2, check 4)                                                                                    | Read `balanceOf(from)`. For a position on a satellite, recall it first                                                                                     |
| Reverts with `ZeroValue`                                       | The amount is zero (step 2, check 5)                                                                                                                        | Pass a positive amount                                                                                                                                     |
| The transfer succeeded and a rule of the asset is now breached | A forced transfer does not ask the rules                                                                                                                    | Anticipate it in step 1 with `canTransfer`. The Issuer regularizes, or accepts the exception and documents it                                              |
| The transfer succeeded to a frozen address                     | A freeze on the recipient does not block a forced transfer                                                                                                  | Intended. The tokens stay immobilized there for the holder                                                                                                 |
| An unexpected `TokensUnfrozen` event appears                   | The transfer exceeded the free balance and released frozen tokens                                                                                           | Intended. To keep them frozen at the destination, freeze the amount there: [13](/guides/agents/13.-freeze-and-unfreeze-an-address-or-a-partial-balance.md) |

## Common misunderstandings

> **Wrong:** "A forced transfer bypasses everything."
>
> **Right:** It bypasses the holder's consent, the freezes and the compliance rules. It does not bypass eligibility: the recipient must be verified, and the call reverts otherwise. It does not bypass the pause either. The register never records a holding that should not exist, even on an agent's instruction.

> **Wrong:** "Forced transfers are invisible to the compliance."
>
> **Right:** The token tells the compliance after the move. Positions follow the tokens and tracker modules record the movement. What is skipped is the question, not the bookkeeping.

> **Wrong:** "The agent has to unfreeze before forcing a transfer."
>
> **Right:** The transfer releases exactly the frozen amount it lacks, and emits `TokensUnfrozen` for it. The rest of the freeze stays in place on the source address.

> **Wrong:** "Any agent can force a transfer."
>
> **Right:** The function is gated by `AGENT_FORCED_TRANSFER`, a role of its own. An agent that mints, freezes or pauses cannot move a holder's tokens unless this role is granted to it.

## Integration notes

* Read `isVerified(to)` before submitting. It is the one gate the contract applies.
* Read `canTransfer(from, to, amount)` and record the answer with the decision. The contract will not stop a transfer the rules refuse, so the assessment belongs to the Issuer's file.
* Use `Token.batchForcedTransfer(fromList, toList, amounts)` for several transfers. It is gated by the same role, the three lists have the same length, and the batch reverts as a whole if one entry fails.
* Index `ForcedTransfer(agent)`. It is emitted in the same transaction as the `Transfer` it qualifies, right after it, and it is what distinguishes a forced transfer from a holder's transfer.
* Store the legal basis of each forced transfer off-chain, keyed by the transaction hash. The chain records who moved what and when, not why.
* To move a whole position from a wallet the holder lost to another address of the same holder, use a recovery. It carries the freezes over, which a forced transfer does not.

## Related flows

* [13 Freeze and unfreeze an address or a partial balance](/guides/agents/13.-freeze-and-unfreeze-an-address-or-a-partial-balance.md) : immobilizing instead of moving
* [15 Recover a position from a lost wallet](/guides/agents/15.-recover-a-position-from-a-lost-wallet.md) : moving a whole position for the same holder
* [16 Pause and unpause a token](/guides/agents/16.-pause-and-unpause-a-token.md) : a forced transfer waits for the pause to be lifted
* [10 Transfer between investors](/guides/positions/10.-transfer-between-investors.md) : the same movement, decided by the holder
* [09 Burn a position](/guides/positions/09.-burn-a-position.md) : destroying a position instead of moving it
* [32 Recall a position to the reference chain](/guides/cross-chain/32.-recall-a-position-to-the-reference-chain.md) : before acting on a position active on a satellite


---

# 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/agents/14.-execute-a-forced-transfer.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.
