Recover Account using Candide Guardian
Recover your Safe account using Candide Guardian when you've lost access to your owner keys. This guide covers the complete recovery process using email and SMS verification.
If you haven't set up Candide Guardian yet, see the Add Candide Guardian guide first.
What You'll Need
Before starting recovery, ensure you have:
- Access to registered recovery methods (email or SMS)
- Safe account address to recover
- New private key to replace the lost key
- Recovery service access (Candide Guardian enabled)
Recovery Overview
The recovery process consists of five key steps:
- Generate New Owner Key - Create a replacement for the lost key
- Request Guardian Signature - Initiate recovery through the Guardian service
- Verify All Channels - Confirm identity through ALL registered methods
- Execute Recovery - Submit the recovery transaction
- Finalize Recovery - Complete ownership transfer after the grace period
Quickstart
You can also fork the complete code and follow along.
Installation
- npm
- yarn
npm i abstractionkit safe-recovery-service-sdk viem
yarn add abstractionkit safe-recovery-service-sdk viem
Configure Environment
- .env
# Network Configuration
CHAIN_ID=11155111
BUNDLER_URL=https://api.candide.dev/public/v3/11155111
NODE_URL=https://ethereum-sepolia-rpc.publicnode.com
# Recovery Service URL
# Get access here: https://app.formbricks.com/s/brdzlw0t897cz3mxl3ausfb5
RECOVERY_SERVICE_URL=
Step 1: Initialize Recovery Service
import { RecoveryByCustodialGuardian } from "safe-recovery-service-sdk";
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
const chainId = BigInt(process.env.CHAIN_ID as string);
const serviceUrl = process.env.RECOVERY_SERVICE_URL as string;
// Initialize recovery service
const custodialGuardianService = new RecoveryByCustodialGuardian(serviceUrl, chainId);
// Generate new owner key to replace the lost one
const newOwnerPrivateKey = generatePrivateKey();
const newOwner = privateKeyToAccount(newOwnerPrivateKey);
Step 2: Request Guardian Signature Challenge
Request a recovery signature from Candide Guardian:
// Request Candide Guardian signature for recovery
const signatureRequest = await custodialGuardianService.requestCustodialGuardianSignatureChallenge(
safeAccountAddress,
[newOwner.address], // New owner array
1 // New threshold
);
Step 3: Verify All Recovery Channels
You must verify ALL registered channels to complete recovery. Submit the OTP codes received:
- Email Verification
- SMS Verification
- Verify All Channels
// Find the email authentication challenge
const emailAuth = signatureRequest.auths.find(auth => auth.channel === 'email');
// Enter OTP from email
const emailOtp = "123456"; // Replace with actual OTP
const verificationResult = await custodialGuardianService.submitCustodialGuardianSignatureChallenge(
signatureRequest.requestId,
emailAuth.challengeId,
emailOtp
);
// Find the SMS authentication challenge
const smsAuth = signatureRequest.auths.find(auth => auth.channel === 'sms');
// Enter OTP from SMS
const smsOtp = "654321"; // Replace with actual OTP
const verificationResult = await custodialGuardianService.submitCustodialGuardianSignatureChallenge(
signatureRequest.requestId,
smsAuth.challengeId,
smsOtp
);
// Verify ALL registered channels (required for recovery)
let verificationResult;
for (const auth of signatureRequest.auths) {
// Enter OTP for current channel
const recoveryOtpCode = "123456"; // Replace with actual OTP from respective channel
verificationResult = await custodialGuardianService.submitCustodialGuardianSignatureChallenge(
signatureRequest.requestId,
auth.challengeId,
recoveryOtpCode
);
}
Step 4: Execute Recovery Transaction
After verifying all channels, execute the recovery with the obtained guardian signature:
// Execute recovery using the service
const recoveryRequest = await custodialGuardianService.createAndExecuteRecoveryRequest(
safeAccountAddress,
[newOwner.address], // New owner array
1, // New threshold
verificationResult.custodianGuardianAddress as string,
verificationResult.custodianGuardianSignature as string
);
Step 5: Finalize Recovery After Grace Period
Wait for the grace period to expire, then finalize the recovery:
import { SocialRecoveryModuleGracePeriodSelector } from "abstractionkit";
import { RecoveryByGuardian } from "safe-recovery-service-sdk";
// Wait for grace period (3 minutes)
await new Promise(resolve => setTimeout(resolve, 3 * 60 * 1000));
// Initialize recovery service for finalization
const recoveryService = new RecoveryByGuardian(
serviceUrl,
chainId,
SocialRecoveryModuleGracePeriodSelector.After3Minutes
);
// Finalize the recovery
const finalizationResult = await recoveryService.finalizeRecoveryRequest(recoveryRequest.id);