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
Result
The identity has a new MANAGEMENT key, controlled by the owner
Actors and roles
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:
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.The weights of the signers reach the threshold. Otherwise
ERC7579SocialRecoveryExecutorInvalidSchedule.The module is installed on the identity. Otherwise
ERC7579ExecutorModuleNotInstalled.No operation with the same identity, salt, mode and
recoveryDatawas ever scheduled. OtherwiseERC7579ExecutorUnexpectedOperationState.
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:
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 withERC7579ExecutorUnexpectedOperationState(operationId, currentState, allowedStates), which names the state the operation is in:Unknown,Scheduled,Expired,ExecutedorCanceled.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 withExecutorPurposeNotAuthorized.
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
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
MANAGEMENTkey. 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
startRecoveryandexecuteRecoveryrequires 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 withgetSchedule(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
ERC7579ExecutorOperationScheduledon 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.
Related flows
22 Add and remove keys on an ONCHAINID : removing the lost keys afterward
02 Deploy an ONCHAINID : when to install guardians
23 Revoke a wallet : a lost wallet is not a lost identity
15 Recover a position from a lost wallet : the agent's operation on an asset
14 Execute a forced transfer : when an identity cannot be recovered
28 Install the recovery module and manage guardians : the precondition of this flow
Last updated