> 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/23.-revoke-a-wallet.md).

# 23. Revoke a wallet

Revoking a wallet ends its binding to an identity. The Identity Owner does it when a wallet is compromised, lost or retired. From that moment the wallet can no longer receive any asset. What it holds stays attributed to the identity, so nothing is lost. A revocation is terminal: the wallet can never be linked again, to this identity or to any other.

## At a glance

|                   |                                                                                                                                                                                                                                      |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Triggered by**  | Identity Owner                                                                                                                                                                                                                       |
| **Actors**        | Identity Owner, Investor, Token Agent, Platform                                                                                                                                                                                      |
| **Contracts**     | `IdentityFactory`, ONCHAINID, `Token` (for the tokens left in the wallet)                                                                                                                                                            |
| **Preconditions** | The wallet is linked to the identity with the status `Active` (see [07](/guides/identity/07.-link-a-wallet-to-an-onchainid.md)); the owner controls a `MANAGEMENT` key of the identity; the identity is not of a single-binding type |
| **Result**        | The wallet has the status `Revoked`: it is refused as a recipient by every asset, and it still resolves to the identity for attribution                                                                                              |

## Actors and roles

| Actor          | Role in this flow                                                                                              | What they hold                                                               |
| -------------- | -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Identity Owner | Revokes the wallet and moves its tokens to an address it controls.                                             | A `MANAGEMENT` key of the identity to revoke, an `ACTION` key to move tokens |
| Investor       | The Identity Owner in its capacity as holder. Keeps its whole position.                                        | The identity's claims and its other addresses, unchanged                     |
| Token Agent    | Recovers the wallet when the owner asks, or when part of the balance is frozen. Freezes the address if needed. | `AGENT_RECOVERY_ADDRESS` or `AGENT_ADDRESS_FREEZER` on the asset             |
| Platform       | Runs the reads, relays the calls and verifies the result.                                                      | Whatever keys the owner has authorized                                       |

## Sequence

```mermaid
sequenceDiagram
    participant Platform
    participant Owner as Identity Owner
    participant Identity as ONCHAINID
    participant Factory as IdentityFactory
    participant Token

    Platform->>Factory: getAccountStatus(account) (read)
    Platform->>Token: balanceOf(wallet) (read), for each asset
    Owner->>Identity: instruct with a MANAGEMENT key
    Identity->>Factory: revokeAccount(account)
    Factory->>Factory: caller is a signing identity, wallet is its own and Active
    Factory-->>Platform: AccountRevoked(account, identity, caller)
    opt the wallet still holds tokens
        Owner->>Identity: instruct with an ACTION key
        Identity->>Token: identityTransfer(wallet, destination, amount)
        Token-->>Platform: Transfer, IdentityTransfer
    end
```

## Step by step

### Step 1. Check the wallet and what it holds

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

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

**Before the call:** The owner has decided to retire the wallet.

**What happens:** The Platform reads `IdentityFactory.getAccountStatus(account)`, which must be `Active`, and `IdentityFactory.getIdentity(account)`, which must return the owner's identity. For each asset the owner holds, it reads `Token.balanceOf(wallet)`, to know what will have to be moved, and it identifies a destination: the identity itself, or another active wallet of the identity. For a compromised wallet, the Platform prepares step 2 and step 3 to be sent without delay.

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

### Step 2. Revoke the wallet

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

**Call:** `IdentityFactory.revokeAccount(account)`, called by the identity

**Before the call:** Step 1 is done. A `MANAGEMENT` key instructs the identity. An `ACTION` key cannot: the factory is a management-grade target.

**What the contract checks:**

1. The caller is an identity of a type that can link and revoke. Otherwise `CannotRevokeFromNonSigningIdentity`.
2. `account` is a well-formed interoperable address in canonical form. Otherwise the call reverts on the encoding, or with `NonCanonicalAccount`.
3. The wallet is linked to the calling identity. Otherwise `WalletNotLinkedToIdentity`.
4. The wallet is `Active`. Otherwise `WalletNotActive`.

**What changes:** The factory sets the wallet's status to `Revoked` and removes it from the identity's list of active wallets. The record of the binding stays. The factory emits `AccountRevoked(account, identity, caller)`. From the next eligibility check, every asset refuses the wallet as a recipient.

### Step 3. Move the tokens out of the wallet

Only when the wallet holds tokens on the reference chain.

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

**Call:** `Token.identityTransfer(wallet, destination, amount)`, called by the identity, once per asset

**Before the call:** `destination` is an address that is verified for the asset: the identity itself, or another active wallet of the identity.

**What the contract checks:**

1. The caller is the identity the wallet resolves to. A revoked wallet still resolves to its identity. Otherwise `NotLinkedIdentity`.
2. The token is not paused, and neither address is frozen. Otherwise the call reverts.
3. The amount is within the wallet's free balance. Otherwise the call reverts.
4. The destination is verified. Otherwise the call reverts.
5. The compliance allows the movement. A movement between two addresses of one identity changes no position.

**What changes:** The tokens move from the revoked wallet to the destination. The token emits `Transfer(wallet, destination, amount)` and `IdentityTransfer(identity, wallet, destination, amount)`. The position of the identity does not change.

The alternative is a recovery by an agent of the asset, with `recoveryAddress` ([15](/guides/agents/15.-recover-a-position-from-a-lost-wallet.md)). For free tokens the result is the same. For frozen tokens it is not: `identityTransfer` moves the free balance only, and a recovery moves the whole balance and freezes the same amount again at the new address. A wallet with a frozen amount is therefore emptied by an agent.

## State after the flow

* `IdentityFactory.getAccountStatus(account)` returns `Revoked`.
* `IdentityFactory.getIdentity(account)` returns zero. `IdentityFactory.getIdentityIncludingRevoked(account)` still returns the identity, with the status `Revoked`.
* `IdentityFactory.getAccounts(identity, start, end)` no longer includes the wallet.
* For every asset, `TREXRegistry.isVerified` is false for the wallet, and `TREXRegistry.identity` still returns the identity.
* The identity's keys, claims, other wallets and eligibility are unchanged.
* The identity's position in each asset is unchanged. After step 3, it sits at the destination.
* The wallet can never be linked again.

## Failure modes

| Symptom                                                                                                 | Cause                                                                                                | Fix                                                                                                                                   |
| ------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| Reverts with `NonCanonicalAccount`, or on the address encoding                                          | The interoperable address is malformed or not canonical (step 2, check 2)                            | Encode the wallet exactly as it was linked                                                                                            |
| Reverts with `CannotRevokeFromNonSigningIdentity`                                                       | The identity is of a single-binding type, such as the identity of a token (step 2, check 1)          | Not possible for this identity                                                                                                        |
| Reverts with `WalletNotLinkedToIdentity`                                                                | The wallet belongs to another identity, or to none (step 2, check 3)                                 | Read `getIdentityIncludingRevoked(account)`                                                                                           |
| Reverts with `WalletNotActive`                                                                          | The wallet is already revoked (step 2, check 4)                                                      | Nothing to do                                                                                                                         |
| The identity's instruction is refused                                                                   | The key that instructed the identity holds `ACTION` only                                             | Use a `MANAGEMENT` key                                                                                                                |
| `identityTransfer` reverts with `NotLinkedIdentity`                                                     | The caller is not the identity the wallet resolves to (step 3, check 1)                              | Have the identity make the call                                                                                                       |
| `identityTransfer` reverts on the destination                                                           | The destination is not verified for the asset (step 3, check 4)                                      | Send to the identity itself or to an active wallet of an eligible identity                                                            |
| `identityTransfer` reverts with `ERC20InsufficientBalance`, or leaves part of the balance in the wallet | Part of the balance is frozen. A transfer moves free tokens only (step 3, check 3)                   | An agent recovers the wallet, which carries the frozen amount over: [15](/guides/agents/15.-recover-a-position-from-a-lost-wallet.md) |
| `identityTransfer` reverts because of a freeze or the pause                                             | The wallet or the destination is frozen, or the token is paused (step 3, check 2)                    | Ask the asset's agents                                                                                                                |
| The tokens left the wallet before the owner moved them                                                  | The wallet's key was compromised and the attacker transferred first. A revoked wallet can still send | Revoke and move the tokens at once. Ask the asset's agents to freeze the address if in doubt                                          |
| The owner wants the wallet back                                                                         | A revocation is terminal                                                                             | Link a new wallet: [07](/guides/identity/07.-link-a-wallet-to-an-onchainid.md)                                                        |

## Common misunderstandings

> **Wrong:** "Revoking a wallet freezes the tokens in it."
>
> **Right:** A revoked wallet can no longer receive. It can still send. Revoking protects the holder against tokens being sent to an address it no longer trusts. To immobilize what the wallet holds, the holder moves the tokens out through its identity, or the asset's agents freeze the address.

> **Wrong:** "Revoking a wallet loses the tokens in it."
>
> **Right:** The wallet keeps resolving to its identity for attribution. The position still belongs to the identity. The owner moves the free tokens out with `identityTransfer`, or an agent recovers the wallet, frozen tokens included.

> **Wrong:** "A revoked wallet can be linked again later."
>
> **Right:** A revocation is terminal. The record of the binding stays on-chain with the status `Revoked`, and the wallet is refused by every later attempt to link it, to any identity.

> **Wrong:** "Revoking a wallet revokes the investor."
>
> **Right:** Eligibility belongs to the identity, not to a wallet. The identity keeps its claims, its keys and its other addresses, and stays eligible.

## Integration notes

* For a compromised wallet, send the revocation and the transfers out as fast as possible, ideally in one user operation signed by a `MANAGEMENT` key. The attacker holds the wallet's key and can still send.
* Moving the tokens before revoking is equally valid, and is the safer order when the owner has time.
* For an asset, attribute a balance with `TREXRegistry.identity(address)` and test admission with `TREXRegistry.isVerified(address)`. At the factory, `getIdentityIncludingRevoked(account)` answers for wallet bindings, revoked ones included, and `getIdentity(account)` for active bindings only. Both return the identity itself for an identity's own address.
* Index `AccountRevoked`. An application that lists a holder's addresses removes the wallet from the active list and keeps it for history.
* Revoking the last wallet of an identity does not disable the identity. Its keys still work, it can hold tokens at its own address, and it can link a new wallet.
* A wallet on a satellite is revoked with the same call, with its interoperable address. Its bridged position stays recorded for the identity.

## Related flows

* [07 Link a wallet to an ONCHAINID](/guides/identity/07.-link-a-wallet-to-an-onchainid.md) : linking a replacement
* [22 Add and remove keys on an ONCHAINID](/guides/identity/22.-add-and-remove-keys-on-an-onchainid.md) : when a key, not a wallet, is compromised
* [24 Transfer through the identity](/guides/identity/24.-transfer-through-the-identity.md) : the call used in step 3
* [15 Recover a position from a lost wallet](/guides/agents/15.-recover-a-position-from-a-lost-wallet.md) : the agent's way to empty the wallet, frozen tokens included
* [13 Freeze and unfreeze an address or a partial balance](/guides/agents/13.-freeze-and-unfreeze-an-address-or-a-partial-balance.md) : immobilizing a compromised address


---

# 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/23.-revoke-a-wallet.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.
