Complete Recovery Flow Guide
Learn how to implement a complete account recovery flow using guardians and the Safe Recovery Service. This guide assumes you have a Safe account with the recovery module enabled and guardians configured.
The Recovery Service requires access credentials. Request access here.
To set up the recovery module, see Enable Recovery Module and Add Guardians.
Overview
The recovery flow consists of four key steps:
- Recovery Request: Create and submit a recovery request
- Guardian Approval: Collect required guardian signatures
- Execution: Execute recovery after collecting all signatures
- Finalization: Complete ownership transfer after the grace period expires
Prerequisites
Installation
- npm
- yarn
npm i abstractionkit safe-recovery-service-sdk viem
yarn add abstractionkit safe-recovery-service-sdk
abstractionkitprovides the core functionality for interacting with social recovery module smart contracts and constructing calldata.safe-recovery-service-sdkadds optional API services including alerts, recovery via email/SMS, and additional recovery features.viemprovides the ethereum library utils to generate private keys and sign transactions. You can also useethers.
Environment Setup
CHAIN_ID=11155111
BUNDLER_URL=https://api.candide.dev/public/v3/sepolia
NODE_URL=https://ethereum-sepolia-rpc.publicnode.com
RECOVERY_SERVICE_URL= #optional
GUARDIAN_1_PRIVATE_KEY=
GUARDIAN_2_PRIVATE_KEY=
Recovery Steps
Fork the recovery example to follow along.
Step 1: Initialize Services
Initialize the recovery service and guardian references.
About the Recovery Service
The Candide Recovery Service is an optional hosted service that streamlines recovery by automatically executing recovery after collecting required guardian signatures and finalizing it after the grace period expires. Request access to the recovery service here.
Alternative: Anyone can execute and finalize recovery transactions directly using Social Recovery Module methods, which are public and callable once guardian signatures are collected and grace period requirements are met.
import {
SafeAccountV0_3_0 as SafeAccount,
SocialRecoveryModule,
SocialRecoveryModuleGracePeriodSelector,
} from "abstractionkit";
import { RecoveryByGuardian } from "safe-recovery-service-sdk";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
const recoveryServiceURL = process.env.RECOVERY_SERVICE_URL as string;
const chainId = BigInt(process.env.CHAIN_ID as string);
const smartAccountAddress = "0x...";
const newOwnerAddress = "0x...";
const guardian1PrivateKey = process.env.GUARDIAN_1_PRIVATE_KEY as `0x${string}`;
const guardian1Account = privateKeyToAccount(guardian1PrivateKey);
const guardian2PrivateKey = process.env.GUARDIAN_2_PRIVATE_KEY as `0x${string}`;
const guardian2Account = privateKeyToAccount(guardian2PrivateKey);
const srm = new SocialRecoveryModule(SocialRecoveryModuleGracePeriodSelector.After3Minutes);
const recoveryService = new RecoveryByGuardian(
recoveryServiceURL,
chainId,
SocialRecoveryModuleGracePeriodSelector.After3Minutes
);
Step 2: Create Recovery Request
- First guardian signs EIP-712 recovery data
- Guardian 1 submits the recovery request with their signature (on-chain or through the service)
- Service generates a unique emoji sequence for this recovery request
import { TypedDataDomain } from 'viem';
import { EXECUTE_RECOVERY_PRIMARY_TYPE } from "abstractionkit"
const nodeUrl = process.env.NODE_URL as string;
const recoveryRequestEip712Data = await srm.getRecoveryRequestEip712Data(
nodeUrl,
chainId,
smartAccountAddress,
[newOwnerAddress],
1n
);
const guardian1Signature = await guardian1Account.signTypedData({
primaryType: EXECUTE_RECOVERY_PRIMARY_TYPE,
domain: recoveryRequestEip712Data.domain as TypedDataDomain,
types: recoveryRequestEip712Data.types,
message: recoveryRequestEip712Data.messageValue
});
const recoveryRequest = await recoveryService.createRecoveryRequest(
smartAccountAddress,
[newOwnerAddress],
1n,
guardian1Account.address,
guardian1Signature
);
Step 3: Second Guardian Signature
The second guardian and each consecutive guardian must sign the recovery request using EIP-712 signatures:
const guardian2Signature = await guardian2Account.signTypedData({
primaryType: EXECUTE_RECOVERY_PRIMARY_TYPE,
domain: recoveryRequestEip712Data.domain as TypedDataDomain,
types: recoveryRequestEip712Data.types,
message: recoveryRequestEip712Data.messageValue
});
await recoveryService.submitGuardianSignatureForRecoveryRequest(
recoveryRequest.id,
guardian2Account.address,
guardian2Signature
);
Step 4: Execute Recovery
Once all guardian signatures are collected or the threshold is met, the service executes the recovery to initiate the grace period:
await recoveryService.executeRecoveryRequest(recoveryRequest.id);
await new Promise(resolve => setTimeout(resolve, 1 * 30 * 1000)); // wait for tx to be included
const executedRequest = await recoveryService.getExecutedRecoveryRequestForLatestNonce(
nodeUrl,
smartAccountAddress
);
if (executedRequest && executedRequest.status === "EXECUTED") {
console.log("Transaction hash:", executedRequest.executeData.transactionHash);
}
Step 5: Finalize Recovery
After the grace period has elapsed, anyone can finalize the recovery. Here's how to use the recovery service:
const finalizationResult = await recoveryService.finalizeRecoveryRequest(
recoveryRequest.id
);
const pendingRequests = await recoveryService.getPendingRecoveryRequestsForLatestNonce(
nodeUrl,
smartAccountAddress
);
Complete Working Example
Full Recovery Flow Example
loading...
What's Next
- Recovery Alerts Guide: set up email and SMS notifications so account owners are alerted if a recovery is ever initiated
- Enable Email / SMS Recovery: use Candide's managed guardian service instead of self-managed guardian keys
Security Considerations
Emoji Verification
Candide Recovery Service provides an emoji-based communication system enabling guardians to verify and approve legitimate recovery requests.
- Unique Verification: Each recovery request generates a unique emoji sequence
- Anti-Phishing: Prevents malicious recovery attempts through social engineering
- Guardian Verification: Guardians must verify the emoji with the account owner before signing
- Secure Communication: Share emojis only through trusted, encrypted channels
Signatures
- EIP-712: All signatures use typed data for replay protection
- Domain Separation: Signatures are chain-specific
- Nonce Protection: Each recovery request includes a unique nonce
Grace Period
- Sufficient Duration: Allow adequate time for legitimate owners to cancel unauthorized recovery attempts
- Monitoring: Implement notification systems to alert owners of recovery attempts, such as Candide's Alert System
Utility Methods
Common utilities you might find helpful:
// Check guardian status
const isGuardian = await srm.isGuardian(
nodeUrl,
smartAccountAddress,
guardianAddress
);
if (!isGuardian) {
throw new Error("Address is not a configured guardian");
}
// Verify threshold
const threshold = await srm.threshold(
nodeUrl,
smartAccountAddress
);
console.log(`Required guardian signatures: ${threshold}`);