> 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/15.-recover-a-position-from-a-lost-wallet.md).

# 15. Recover a position from a lost wallet

A recovery restores a position to a holder who can no longer use the wallet that holds it. An agent moves the whole balance of the lost wallet to another address of the same identity, with its freezes. It is the path for a holder who cannot act, and for a wallet that holds frozen tokens: a holder who still controls its ONCHAINID moves its free tokens itself, with a transfer through the identity.

## At a glance

|                   |                                                                                                                                                                                                                                                                                                                                                                                              |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Triggered by**  | Token Agent, at the request of the Investor                                                                                                                                                                                                                                                                                                                                                  |
| **Actors**        | Investor, Issuer, Token Agent, Platform                                                                                                                                                                                                                                                                                                                                                      |
| **Contracts**     | `Token`, `TREXRegistry`, `IdentityRegistryStorage`, `ModularCompliance`, compliance modules                                                                                                                                                                                                                                                                                                  |
| **Preconditions** | The caller holds `AGENT_RECOVERY_ADDRESS` in the asset's domain (see [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)); the lost wallet holds tokens on the reference chain; the lost wallet or the new address resolves to an identity in the asset's registry; the token is not paused; the Issuer has verified, under its own process, that the person asking is the holder |
| **Result**        | The whole balance of the lost wallet, with its freezes, is at the new address, attributed to the same identity                                                                                                                                                                                                                                                                               |

## Actors and roles

| Actor       | Role in this flow                                                                       | What they hold                                                      |
| ----------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Investor    | Asks for the recovery and proves who it is, off-chain. Does not transact.               | An ONCHAINID. A new address: the ONCHAINID itself or another wallet |
| Issuer      | Verifies the request under its own process and decides. Does not transact in this flow. | Its evidence of the holder's identity                               |
| Token Agent | Submits the recovery.                                                                   | `AGENT_RECOVERY_ADDRESS` on the `Token`                             |
| 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 Investor
    participant Platform
    participant Agent as Token Agent
    participant Token
    participant Registry as TREXRegistry
    participant MC as ModularCompliance

    Investor->>Agent: request, with proof of identity, off-chain
    Platform->>Registry: identity(lostWallet), identity(newWallet) (read)
    Platform->>Token: balanceOf(lostWallet), paused() (read)
    Platform->>Agent: checks passed, submit
    Agent->>Token: recoveryAddress(lostWallet, newWallet, identity)
    Token->>Token: caller holds AGENT_RECOVERY_ADDRESS
    Token->>Token: wallets differ, lost wallet holds tokens
    Token->>Registry: contains(lostWallet), contains(newWallet), identity(newWallet)
    Token->>Token: token not paused
    opt the new address resolves to no identity
        Token->>Registry: registerIdentity(newWallet, identity, 0)
    end
    Token->>Token: move the whole balance, carry the freezes over
    Token->>MC: transferred(lostWallet, newWallet, balance)
    opt the lost wallet has a local registration
        Token->>Registry: deleteIdentity(lostWallet)
    end
    Token-->>Platform: RecoverySuccess(lostWallet, newWallet, identity)
```

## Step by step

### Step 1. Verify the request

**Who:** Issuer and Platform

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

**Before the call:** The Investor has asked for the recovery and given the new address.

**What happens:** The Issuer verifies, under its own process, that the person asking is the holder of the identity. The Platform reads `TREXRegistry.identity(lostWallet)`, which gives the identity that owns the position, and passes that identity in the call. It reads `TREXRegistry.identity(newWallet)`: the result is zero, or that same identity. It reads `Token.balanceOf(lostWallet)`, which must not be zero, and `Token.paused()`, which must be false. If part of the position is active on a satellite, the recovery does not reach it.

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

### Step 2. Recover the position

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

**Call:** `Token.recoveryAddress(lostWallet, newWallet, investorOnchainID)`

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

**What the contract checks:**

1. The caller holds `AGENT_RECOVERY_ADDRESS` in the asset's domain. Otherwise the call reverts with `AccessManagedUnauthorized`.
2. The two addresses are different. Otherwise `SameWalletRecovery`.
3. The lost wallet holds tokens. Otherwise `NoTokenToRecover`.
4. At least one of the two addresses resolves to an identity in the registry. Otherwise `RecoveryNotPossible`.
5. If the new address resolves to an identity, it is the identity named in the call. Otherwise `RecoveryNotPossible`.
6. The token is not paused. Otherwise `EnforcedPause`.

The contract does not check who is asking. It does not check that the identity named in the call is the one the lost wallet resolves to. A recovery that names another identity is accepted, when the new address already belongs to that identity or is not known to the registry. It does not evaluate the eligibility of the identity or the compliance rules.

**What changes:**

* When the new address resolves to no identity, the token registers it for the named identity in the asset's registry. The storage emits `IdentityStored` and the registry emits `IdentityRegistered`.
* The token moves the whole balance of the lost wallet to the new address and emits `Transfer(lostWallet, newWallet, balance)`.
* A frozen amount on the lost wallet is carried over: the token emits `TokensUnfrozen(lostWallet, amount)` and `TokensFrozen(newWallet, amount)`.
* An address freeze on the lost wallet is carried over: the token emits `AddressFrozen(lostWallet, false, token)` and, if the new address was not already frozen, `AddressFrozen(newWallet, true, token)`.
* The token calls `ModularCompliance.transferred(lostWallet, newWallet, balance)`. When both addresses belong to one identity, no position changes. When the new address belongs to another identity, the position moves to it. The `TRACKER` modules are told.
* When the lost wallet has a local registration, the token deletes it. The storage emits `IdentityUnstored` and the registry emits `IdentityRemoved`.
* The token emits `RecoverySuccess(lostWallet, newWallet, investorOnchainID)`.

### Step 3. Confirm the effect

**Who:** Platform

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

**Before the call:** Step 2 is confirmed.

**What happens:** The Platform reads `Token.balanceOf(newWallet)`, `Token.getFrozenTokens(newWallet)` and `Token.isFrozen(newWallet)`. It reads `TREXRegistry.identity(newWallet)`, which returns the identity, and `TREXRegistry.isVerified(newWallet)`. The Platform tells the Investor to revoke the lost wallet on its identity if it was linked there.

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

## State after the flow

* `Token.balanceOf(lostWallet)` is zero and `Token.balanceOf(newWallet)` has increased by the recovered balance.
* `Token.getFrozenTokens(newWallet)` has increased by what was frozen on the lost wallet. A recovery releases nothing.
* If the lost wallet was frozen as an address, it no longer is, and the new address is.
* `TREXRegistry.identity(newWallet)` returns the identity. If the new address resolved nowhere, it now has a local registration.
* The lost wallet has no local registration. If its owner linked it in the global identity registry, that link is unchanged.
* `ModularCompliance.positionOf(identity)` is unchanged, for a recovery within one identity.
* `Token.totalSupply()` is unchanged.

## Failure modes

| Symptom                                                                   | Cause                                                                                                                                                                                                                                                    | Fix                                                                                                                                                                                       |
| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reverts with `AccessManagedUnauthorized`                                  | The caller does not hold `AGENT_RECOVERY_ADDRESS` in the asset's domain (step 2, check 1)                                                                                                                                                                | Grant the role: [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)                                                                                                            |
| Reverts with `AccessManagedUnauthorized`, naming the token                | The token does not hold `AGENT` on the registry, which it needs to register the new address. The suite is not commissioned                                                                                                                               | Commission the suite: [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)                                                                                                      |
| Reverts with `SameWalletRecovery`                                         | The lost wallet and the new address are the same (step 2, check 2)                                                                                                                                                                                       | Pass another address of the holder                                                                                                                                                        |
| Reverts with `NoTokenToRecover`                                           | The lost wallet holds nothing on the reference chain (step 2, check 3)                                                                                                                                                                                   | Nothing to recover. For a position on a satellite, see the last row                                                                                                                       |
| Reverts with `RecoveryNotPossible`                                        | Neither address resolves to an identity (step 2, check 4), or the new address belongs to another identity than the one named (step 2, check 5)                                                                                                           | Read `identity(lostWallet)` and `identity(newWallet)`. Pass the identity that owns the position, and an address that is unknown or belongs to it                                          |
| Reverts with `EnforcedPause`                                              | The token is paused (step 2, check 6)                                                                                                                                                                                                                    | Lift the pause first: [16](/guides/agents/16.-pause-and-unpause-a-token.md)                                                                                                               |
| The recovery succeeded and the new address cannot receive further tokens  | The identity lacks a valid claim for a required topic. A recovery does not check eligibility                                                                                                                                                             | Have the claim renewed: [05](/guides/claim-issuer/05.-issue-a-claim-to-an-investor.md)                                                                                                    |
| The recovery succeeded and the position moved to another identity         | The identity named in the call is not the one the lost wallet resolves to. The new address already belonged to that identity, or was unknown and was registered under it. The function allows it: it is a change of ownership, with no eligibility check | To stay within one identity, pass the identity the lost wallet resolves to (step 1). A recovery across identities is the Issuer's decision, to be taken as carefully as a forced transfer |
| The lost wallet still resolves to the identity                            | Its owner linked it in the global identity registry, which an agent cannot change                                                                                                                                                                        | The owner revokes it: [23](/guides/identity/23.-revoke-a-wallet.md)                                                                                                                       |
| Part of the position is still at the lost wallet's address on a satellite | A recovery acts on the reference chain                                                                                                                                                                                                                   | An agent recalls the position, then recovers it: [32](/guides/cross-chain/32.-recall-a-position-to-the-reference-chain.md)                                                                |

## Common misunderstandings

> **Wrong:** "A recovery restores access to the wallet."
>
> **Right:** Nothing is restored on the lost wallet. Its tokens are moved to another address of the same holder. The wallet itself stays as it was, empty.

> **Wrong:** "A recovery is a way out of a freeze."
>
> **Right:** The freezes travel with the tokens. A frozen amount is frozen again at the new address, and a frozen address stays frozen. A forced transfer releases a freeze. A recovery does not.

> **Wrong:** "The contract checks that the request comes from the holder."
>
> **Right:** The contract checks the role of the caller and the consistency of the addresses. Verifying who is asking is the Issuer's procedure, done before the call. The `RecoverySuccess` event makes every recovery visible.

> **Wrong:** "A recovery is the answer to every loss."
>
> **Right:** It answers one case: the holder cannot move the tokens itself. A holder that still controls its ONCHAINID moves them with a transfer through the identity. A holder that lost the keys of its ONCHAINID recovers the identity through its guardians. An identity lost for good calls for a forced transfer to the holder's new identity.

## Integration notes

* Read `identity(lostWallet)` and pass exactly that identity. It is what keeps the recovery inside one identity.
* The new address can be the ONCHAINID itself. A holder that lost a wallet does not need another wallet.
* A recovery moves the whole balance. There is no amount argument.
* A recovery also empties a wallet its owner has revoked. It is the only way to move frozen tokens out of such a wallet, since a transfer through the identity moves the free balance only.
* Index `RecoverySuccess(lostWallet, newWallet, identity)`. The freeze events of a recovery name the token itself as the actor.
* A recovery is per asset. A holder that lost a wallet holding several assets is recovered on each token. Assets that share an identity storage share the registration of the new address.
* After the recovery, the holder revokes the lost wallet on its identity, so that the wallet can no longer receive any asset.

## Related flows

* [24 Transfer through the identity](/guides/identity/24.-transfer-through-the-identity.md) : what a holder does itself when it still controls its identity
* [23 Revoke a wallet](/guides/identity/23.-revoke-a-wallet.md) : ending the global link of the lost wallet
* [21 Recover access to an ONCHAINID](/guides/identity/21.-recover-access-to-an-onchainid.md) : when the keys of the identity are lost
* [14 Execute a forced transfer](/guides/agents/14.-execute-a-forced-transfer.md) : moving a position to another identity
* [13 Freeze and unfreeze an address or a partial balance](/guides/agents/13.-freeze-and-unfreeze-an-address-or-a-partial-balance.md) : the freezes a recovery carries over
* [16 Pause and unpause a token](/guides/agents/16.-pause-and-unpause-a-token.md) : a recovery waits for the pause to be lifted


---

# 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/15.-recover-a-position-from-a-lost-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.
