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); 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
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
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:
The caller is an identity of a type that can link and revoke. Otherwise
CannotRevokeFromNonSigningIdentity.accountis a well-formed interoperable address in canonical form. Otherwise the call reverts on the encoding, or withNonCanonicalAccount.The wallet is linked to the calling identity. Otherwise
WalletNotLinkedToIdentity.The wallet is
Active. OtherwiseWalletNotActive.
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:
The caller is the identity the wallet resolves to. A revoked wallet still resolves to its identity. Otherwise
NotLinkedIdentity.The token is not paused, and neither address is frozen. Otherwise the call reverts.
The amount is within the wallet's free balance. Otherwise the call reverts.
The destination is verified. Otherwise the call reverts.
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). 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)returnsRevoked.IdentityFactory.getIdentity(account)returns zero.IdentityFactory.getIdentityIncludingRevoked(account)still returns the identity, with the statusRevoked.IdentityFactory.getAccounts(identity, start, end)no longer includes the wallet.For every asset,
TREXRegistry.isVerifiedis false for the wallet, andTREXRegistry.identitystill 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
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
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
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
MANAGEMENTkey. 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 withTREXRegistry.isVerified(address). At the factory,getIdentityIncludingRevoked(account)answers for wallet bindings, revoked ones included, andgetIdentity(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 : linking a replacement
22 Add and remove keys on an ONCHAINID : when a key, not a wallet, is compromised
24 Transfer through the identity : the call used in step 3
15 Recover a position from a lost wallet : the agent's way to empty the wallet, frozen tokens included
13 Freeze and unfreeze an address or a partial balance : immobilizing a compromised address
Last updated