Skip to main content

Enable Email / SMS Recovery

Integrate Candide Guardian as a trusted recovery service for user Safe accounts. Candide Guardian provides secure account recovery through email and SMS verification.

info

The Recovery Service requires access credentials. Request access here.

To set up the recovery module, see Enable Recovery Module and Add Guardians.

What is Candide Guardian?

Candide Guardian is a managed recovery service acting as a trusted guardian for user Smart Accounts. Unlike personal guardians, Candide Guardian provides:

  • Familiar Security: Email, SMS, or both for recovery verification
  • No Key Management: Users don't manage guardian private keys
  • Automated Signing: Service automatically provides guardian signatures after verification
  • Grace Period Protection: Built-in security delay before recovery execution
  • Gas Sponsorship: Sponsored recovery transactions remove gas barriers

Quickstart

You can also fork the complete code and follow along.

Installation

npm i abstractionkit safe-recovery-service-sdk viem

Configure Environment

# 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 Service

import { RecoveryByCustodialGuardian } from "safe-recovery-service-sdk";

const chainId = BigInt(process.env.CHAIN_ID as string);
const serviceUrl = process.env.RECOVERY_SERVICE_URL as string;

const custodialGuardianService = new RecoveryByCustodialGuardian(serviceUrl, chainId);

Step 2: Create Registration Statement

Generate a SIWE (Sign-In with Ethereum) statement for guardian registration. Make sure the Safe account is already deployed.

const siweStatementToSign = await custodialGuardianService.createRegistrationToEmailRecoverySiweStatementToSign(
smartAccount.accountAddress,
"user@example.com"
);

Step 3: Sign and Submit Registration

Sign the SIWE statement with the Smart Account owner key and submit registration:

import { 
SafeAccountV0_3_0,
getSafeMessageEip712Data,
SAFE_MESSAGE_PRIMARY_TYPE
} from "abstractionkit";
import { TypedDataDomain } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

const ownerAccount = privateKeyToAccount(ownerPrivateKey);

const emailSafeTypedData = getSafeMessageEip712Data(
smartAccount.accountAddress,
chainId,
emailRegistrationSiweMessage
);

const emailOwnerSignature = await ownerAccount.signTypedData({
domain: emailSafeTypedData.domain as TypedDataDomain,
types: emailSafeTypedData.types,
primaryType: SAFE_MESSAGE_PRIMARY_TYPE,
message: emailSafeTypedData.messageValue
});

const emailRegistrationSignature = SafeAccountV0_3_0.buildSignaturesFromSingerSignaturePairs([
{ signer: ownerAccount.address, signature: emailOwnerSignature }
]);

const emailRegistrationChallengeId = await custodialGuardianService.createRegistrationToEmailRecovery(
smartAccount.accountAddress,
userEmail,
emailRegistrationSiweMessage,
emailRegistrationSignature
);

Step 4: Verify OTP Code

Submit the OTP code received via email or SMS:

// User enters OTP from email
const otpCode = "123456";

const verificationResponse = await custodialGuardianService.submitRegistrationChallenge(
emailRegistrationChallengeId, // returned by createRegistrationToEmailRecovery()
otpCode
);

const candideGuardianAddress = verificationResponse.guardianAddress;

Step 5: Enable Recovery Module and Add Guardian

Add Candide Guardian to your Safe account. See the complete flow of adding a guardian in the How to Add a Guardian guide

import { SocialRecoveryModule, SocialRecoveryModuleGracePeriodSelector } from "abstractionkit";

const srm = new SocialRecoveryModule(SocialRecoveryModuleGracePeriodSelector.After3Minutes);

// Create transactions to enable module
const enableModuleTx = srm.createEnableModuleMetaTransaction(
smartAccount.accountAddress
);

// Add Candide Guardian
const addGuardianTx = srm.createAddGuardianWithThresholdMetaTransaction(
candideGuardianAddress,
1n // threshold
);

// Create UserOp with both transactions
let userOperation = await smartAccount.createUserOperation(
[enableModuleTx, addGuardianTx],
process.env.NODE_URL,
process.env.BUNDLER_URL
);

// sponsor gas, sign, and submit userop
Recovery Strategy Recommendation

For maximum security, recommend Candide Guardian alongside personal guardians in a multi-guardian setup (e.g., 2 of 3: Candide Guardian + 2 personal guardians).

That's it! Your Safe account is now protected by Candide Guardian. In case of key loss, you can use it as one of the Guardians setup using your registered email/phone verification to recover your account.

Complete Working Example

Full Working Example
examples/01-enable-email-sms-recovery/index.ts
loading...

What's Next

  • Recovery Flow: walk through the full recovery flow using your registered email/SMS to regain access to a lost account