For the complete documentation index, see llms.txt. This page is also available as Markdown.

21. Recover access to an ONCHAINID

Recovery restores control of an identity whose MANAGEMENT keys are lost. The guardians the owner chose in advance sign an operation that gives the identity a new MANAGEMENT key. The operation is scheduled, waits for a delay during which it can be cancelled, then executes. The Identity Owner triggers the flow by asking its guardians, and it only works if the recovery module was installed while the owner still had its keys.

At a glance

Triggered by

Identity Owner, through its guardians

Actors

Identity Owner, Guardian, Platform

Contracts

RecoveryModule, ONCHAINID, ERC734Validator

Preconditions

The identity exists (see 02); the RecoveryModule is installed on the identity with its guardians, weights, threshold, delay and expiration, and holds the MANAGEMENT purpose (see 28); enough guardians to reach the threshold are available; the owner has a new signer

Result

The identity has a new MANAGEMENT key, controlled by the owner

Actors and roles

Actor
Role in this flow
What they hold

Identity Owner

Has lost its MANAGEMENT keys. Provides the new signer and asks the guardians.

A new signer, not yet a key of the identity

Guardian

Verifies the owner's request off-chain and signs the recovery.

A signer listed as guardian in the identity's recovery settings

Platform

Builds the operation, collects the signatures, submits the transactions and monitors the delay. Anyone can play this part.

No role and no key of the identity

Sequence

Step by step

Step 1. Build the recovery operation

Who: Platform, on behalf of the Identity Owner

Call: Off-chain, using read calls

Before the call: The owner has generated a new signer and has given its signer data to the Platform.

What happens: The Platform reads the identity's recovery settings from the RecoveryModule: its guardians with getGuardians(identity, start, end) and getGuardianCount(identity), the threshold with threshold(identity), the delay with getDelay(identity) and the expiration with getExpiration(identity). It builds the operation the recovery performs, recoveryData: a call from the identity to itself, addKeyWithData(keyHash, MANAGEMENT, keyType, signerData, clientData) for the new signer. It chooses a salt that identifies this recovery, and the execution mode for a single call.

What changes: Nothing on-chain.

Step 2. Collect the guardians' signatures

Who: Guardians

Call: Off-chain

Before the call: Each guardian has verified, by its own means, that the request comes from the owner. This verification is the whole security of the recovery.

What happens: Each guardian signs the ERC-712 typed message for scheduling a recovery, ScheduleRecover(address executor, bytes32 salt, bytes32 mode, bytes recoveryCalldata), in the signing domain of the identity being recovered. executor is the address of the RecoveryModule. The Platform gathers signatures until the weights of the signers reach the threshold, and assembles the signers and their signatures in one bundle.

What changes: Nothing on-chain.

Step 3. Schedule the recovery

Who: Platform, or anyone holding the bundle

Call: RecoveryModule.startRecovery(identity, salt, mode, recoveryData, multisignature), with multisignature the bundle of signers and signatures

Before the call: Step 2 is done.

What the contract checks:

  1. The bundle decodes, each signer is a guardian of this identity, and each signature is valid for the scheduling message in this identity's domain. Otherwise the call reverts with ERC7579SocialRecoveryExecutorInvalidSchedule.

  2. The weights of the signers reach the threshold. Otherwise ERC7579SocialRecoveryExecutorInvalidSchedule.

  3. The module is installed on the identity. Otherwise ERC7579ExecutorModuleNotInstalled.

  4. No operation with the same identity, salt, mode and recoveryData was ever scheduled. Otherwise ERC7579ExecutorUnexpectedOperationState.

What changes: The module records the operation as scheduled, executable once the delay has elapsed and until the expiration. It emits ERC7579ExecutorOperationScheduled(account, operationId, salt, mode, executionCalldata, schedule), where schedule is the time from which the operation can execute. Nothing changes on the identity.

Step 4. Wait for the delay

Who: Identity Owner and Guardians

Call: Off-chain. Optionally RecoveryModule.cancelRecovery(identity, salt, mode, recoveryData) by the identity, or RecoveryModule.cancelRecovery(identity, salt, mode, recoveryData, multisignature) by anyone holding a bundle of guardian signatures over the cancellation message, CancelRecover with the same fields as the scheduling message.

Before the call: The recovery is scheduled.

What happens: Nothing, if the recovery is legitimate. The delay exists for the opposite case: an owner who still holds a MANAGEMENT key and did not ask for a recovery cancels it from the identity. Guardians who reach the threshold can cancel as well. A signature made to schedule a recovery cannot be used to cancel one, and the reverse. A cancellation that comes neither from the identity nor with valid guardian signatures reverts with ERC7579SocialRecoveryExecutorInvalidCancel.

What changes: The module emits ERC7579ExecutorOperationCanceled(account, operationId). A cancelled recovery can no longer be executed, and the same operation cannot be scheduled again: a new attempt uses another salt.

Step 5. Execute the recovery

Who: Platform, or anyone

Call: RecoveryModule.executeRecovery(identity, salt, mode, recoveryData)

Before the call: The delay has elapsed and the expiration has not passed.

What the contract checks:

  1. The operation is in the state Ready: it was scheduled with these exact parameters, it was not cancelled or already executed, its delay has elapsed and it has not expired. Otherwise the call reverts with ERC7579ExecutorUnexpectedOperationState(operationId, currentState, allowedStates), which names the state the operation is in: Unknown, Scheduled, Expired, Executed or Canceled.

  2. The module holds the purpose required for the target of the operation on the identity. A call to the identity itself requires MANAGEMENT. Otherwise the identity reverts with ExecutorPurposeNotAuthorized.

What changes: The module has the identity execute the operation. The identity adds the new signer with the MANAGEMENT purpose, and the registry emits KeyAdded. The owner controls the identity again.

Step 6. Clean up

Who: Identity Owner (the new MANAGEMENT key)

Call: Identity.removeKey(keyHash, purpose) for each lost key (see 22)

Before the call: Step 5 is confirmed.

What the contract checks: As in 22. The last MANAGEMENT signer cannot be removed, and the new key now counts as one.

What changes: The lost keys no longer hold any purpose. The owner also reviews its guardians and its linked wallets.

State after the flow

  • Identity.keyHasPurpose(newKeyHash, 1) is true.

  • The identity's address, type, claims, linked wallets and positions are unchanged. Its eligibility for every asset is unchanged.

  • The recovery settings are unchanged: same guardians, threshold, delay and expiration.

  • The scheduled operation is consumed and cannot be executed again.

  • After step 6, the lost keys hold no purpose.

Failure modes

Symptom
Cause
Fix

There is no recovery module on the identity

It was never installed. It can only be installed by a MANAGEMENT key

No on-chain recovery is possible. The positions the identity holds can be moved by each asset's agents to a new identity: 14

startRecovery reverts with ERC7579SocialRecoveryExecutorInvalidSchedule

A signer is not a guardian of this identity, a signature was made over other parameters or in another domain, or the weights do not reach the threshold (step 3, checks 1 and 2)

Rebuild the message with the exact salt, mode and recoveryData, in the identity's own signing domain. Collect more signatures

startRecovery reverts with ERC7579ExecutorModuleNotInstalled

The module is not installed on this identity (step 3, check 3)

No on-chain recovery is possible for it

startRecovery reverts with ERC7579ExecutorUnexpectedOperationState

The same operation was scheduled before, with the same salt (step 3, check 4)

Use a new salt and collect signatures over it

executeRecovery reverts with ERC7579ExecutorUnexpectedOperationState and the state Scheduled

The delay has not elapsed (step 5, check 1)

Wait. Read getSchedule for the time

The same error with the state Expired

The operation was not executed before its expiration (step 5, check 1)

Schedule it again with a new salt and new signatures

The same error with the state Unknown

The salt, the mode or recoveryData differ from what was scheduled (step 5, check 1)

Use the exact scheduled parameters

The same error with the state Canceled or Executed

The recovery was cancelled, or has already run (step 5, check 1)

See the rows below for a cancellation. Nothing to do when it already ran

executeRecovery reverts with ExecutorPurposeNotAuthorized

The module is installed and does not hold MANAGEMENT on the identity (step 5, check 2)

The purpose must be granted while a MANAGEMENT key exists. Without it, the module cannot change the keys

The recovery was cancelled

The owner, or the guardians, cancelled during the delay

If the cancellation was hostile, the party that cancelled holds a MANAGEMENT key or a guardian quorum. Treat the identity as compromised

A recovery the owner did not ask for is scheduled

Guardians reaching the threshold signed it

Cancel from the identity with a MANAGEMENT key before the delay elapses, then replace the guardians

Common misunderstandings

Wrong: "The Issuer can recover an investor's identity."

Right: No Issuer and no agent has any authority over an identity. An identity is recovered by its guardians, chosen by its owner. What an asset's agents can do is move the positions of that asset, which is a different operation on a different contract.

Wrong: "Recovering the identity recovers the tokens."

Right: The tokens never left. Positions belong to the identity, at its own address and in its linked wallets. Recovery restores the ability to act through the identity. A wallet whose own key is lost is a separate matter: the identity moves its tokens with identityTransfer.

Wrong: "Guardians hold a copy of the key."

Right: Guardians hold no key of the identity. Together, at the threshold, they can authorize one specific operation, after a delay. They cannot act alone, at once, or for anything they did not sign.

Wrong: "Recovery can be set up when it is needed."

Right: Installing the module takes a MANAGEMENT key. Once the keys are lost, it is too late. Guardians are set up when the identity is created.

Integration notes

  • Offer the installation of the recovery module, with its guardians, in the same session as the creation of the identity.

  • Show each guardian exactly what it signs: the identity, and the new key that will control it.

  • Submitting startRecovery and executeRecovery requires no key of the identity. A Platform or a relayer can pay for both. Anyone can execute a recovery once its delay has elapsed.

  • Guardians can cancel only while the identity has a guardian configuration with a threshold above zero. The identity itself can always cancel.

  • Read the state of a recovery with RecoveryModule.state(identity, salt, mode, recoveryData) and its times with getSchedule(identity, salt, mode, recoveryData), which returns when it was scheduled, when it can execute and when it expires.

  • Monitor scheduled recoveries on every identity your application serves, by indexing ERC7579ExecutorOperationScheduled on the module, and notify the owner. The delay protects the owner only if the owner learns of the recovery in time.

  • Choose the delay with the owner. A short delay recovers faster. A long delay leaves more time to cancel a recovery that was not requested.

  • Guardians are signers of any supported type. Their signatures are bound to the identity being recovered and cannot be replayed on another identity that uses the same module.

  • After a recovery, remove the lost keys and consider replacing the guardians: the event that cost the keys may have exposed them too.

Last updated