Social Recovery with Smart Accounts
Social recovery provides a secure way to recover your smart account if you lose access to your primary wallet. Instead of relying on a single seed phrase, you can designate trusted guardians who can help recover your account.
Overview
Social recovery leverages the Rhinestone Social Recovery Module (ERC-7579 compatible) to:
- Guardian Setup: Designate trusted friends, family, or services as guardians
- Threshold Recovery: Require multiple guardians to approve recovery (e.g., 2 of 3)
- Secure Process: Multi-step recovery process prevents unauthorized access
- Module-Based: Easily install/uninstall without affecting other account features
Installation & Setup
The social recovery module is automatically available through the Rhinestone Module SDK:
hooks/useSocialRecovery.ts
import { useState, useEffect } from 'react'
import { getModule } from '@rhinestone/module-sdk'
import { useStartale } from '../providers/StartaleAccountProvider'
// Social Recovery Module configuration
const SOCIAL_RECOVERY_MODULE = getModule({
module: 'social-recovery',
initData: [], // Will be set when installing
type: 'validator',
})
export function useSocialRecovery() {
const { startaleAccount, startaleClient } = useStartale()
const [isModuleInstalled, setIsModuleInstalled] = useState(false)
const [guardians, setGuardians] = useState<string[]>([])
const [threshold, setThreshold] = useState(2)
// Check if module is installed
useEffect(() => {
checkModuleInstallation()
}, [startaleAccount])
async function checkModuleInstallation() {
if (!startaleAccount) return
try {
// Note: This would typically check the account's installed modules
// Implementation depends on the specific account contract
console.log('Checking social recovery module installation...')
// For demo purposes, we'll assume it's not installed initially
setIsModuleInstalled(false)
} catch (error) {
console.error('Error checking module installation:', error)
}
}
return {
isModuleInstalled,
guardians,
threshold,
setGuardians,
setThreshold,
}
}
Social Recovery Component
Let's build a comprehensive social recovery interface:
components/SocialRecovery.tsx
import { useState } from 'react'
import { encodeFunctionData, isAddress } from 'viem'
import { getInstallModule, getUninstallModule } from '@rhinestone/module-sdk'
import { useStartale } from '../providers/StartaleAccountProvider'
import { useSocialRecovery } from '../hooks/useSocialRecovery'
type RecoveryStep = 'setup' | 'installed' | 'recovery'
export function SocialRecovery() {
const { startaleAccount, startaleClient } = useStartale()
const {
isModuleInstalled,
guardians,
threshold,
setGuardians,
setThreshold
} = useSocialRecovery()
const [currentStep, setCurrentStep] = useState<RecoveryStep>('setup')
const [newGuardian, setNewGuardian] = useState('')
const [isLoading, setIsLoading] = useState(false)
const [recoveryInProgress, setRecoveryInProgress] = useState(false)
// Determine current step based on module status
const step: RecoveryStep = isModuleInstalled ? 'installed' : 'setup'
return (
<div className="social-recovery">
<h3>Social Recovery Setup</h3>
<p>Secure your account with trusted guardians who can help you recover access.</p>
{step === 'setup' && (
<SetupPhase
guardians={guardians}
threshold={threshold}
newGuardian={newGuardian}
setNewGuardian={setNewGuardian}
setGuardians={setGuardians}
setThreshold={setThreshold}
onInstall={installSocialRecovery}
isLoading={isLoading}
/>
)}
{step === 'installed' && (
<InstalledPhase
guardians={guardians}
threshold={threshold}
onUninstall={uninstallSocialRecovery}
onInitiateRecovery={() => setRecoveryInProgress(true)}
isLoading={isLoading}
/>
)}
{recoveryInProgress && (
<RecoveryPhase
guardians={guardians}
threshold={threshold}
onCancel={() => setRecoveryInProgress(false)}
/>
)}
</div>
)
async function installSocialRecovery() {
if (!startaleClient || guardians.length === 0 || threshold > guardians.length) {
alert('Please configure guardians and threshold first')
return
}
setIsLoading(true)
try {
console.log('Installing social recovery module...', {
guardians,
threshold,
})
// Get module installation data
const installModule = getInstallModule({
module: 'social-recovery',
initData: encodeFunctionData({
abi: [
{
name: 'initSocialRecovery',
type: 'function',
inputs: [
{ name: 'guardians', type: 'address[]' },
{ name: 'threshold', type: 'uint256' },
],
outputs: [],
},
],
functionName: 'initSocialRecovery',
args: [guardians as `0x${string}`[], BigInt(threshold)],
}),
type: 'validator',
})
// Install the module
const hash = await startaleClient.sendUserOperation({
calls: [installModule],
})
console.log('Social recovery module installed:', hash)
alert('Social recovery module installed successfully!')
// Refresh module status
// checkModuleInstallation()
} catch (error) {
console.error('Failed to install social recovery module:', error)
alert(`Installation failed: ${(error as Error).message}`)
} finally {
setIsLoading(false)
}
}
async function uninstallSocialRecovery() {
if (!startaleClient) return
const confirm = window.confirm('Are you sure you want to remove social recovery? This will disable account recovery via guardians.')
if (!confirm) return
setIsLoading(true)
try {
console.log('Uninstalling social recovery module...')
// Get module uninstallation data
const uninstallModule = getUninstallModule({
module: 'social-recovery',
type: 'validator',
})
// Uninstall the module
const hash = await startaleClient.sendUserOperation({
calls: [uninstallModule],
})
console.log('Social recovery module uninstalled:', hash)
alert('Social recovery module removed successfully!')
} catch (error) {
console.error('Failed to uninstall social recovery module:', error)
alert(`Uninstallation failed: ${(error as Error).message}`)
} finally {
setIsLoading(false)
}
}
}
Guardian Management
Advanced Guardian Features
utils/guardianUtils.ts
export interface Guardian {
address: string
name?: string
email?: string
addedAt: number
isActive: boolean
}
export function validateGuardianConfiguration(
guardians: Guardian[],
threshold: number
): { isValid: boolean; errors: string[] } {
const errors: string[] = []
if (guardians.length === 0) {
errors.push('At least one guardian is required')
}
if (threshold < 1) {
errors.push('Threshold must be at least 1')
}
if (threshold > guardians.length) {
errors.push('Threshold cannot exceed number of guardians')
}
// Check for duplicate addresses
const addresses = guardians.map(g => g.address.toLowerCase())
const uniqueAddresses = new Set(addresses)
if (addresses.length !== uniqueAddresses.size) {
errors.push('Duplicate guardian addresses detected')
}
// Validate Ethereum addresses
guardians.forEach((guardian, index) => {
if (!isAddress(guardian.address)) {
errors.push(`Guardian ${index + 1} has invalid address format`)
}
})
return {
isValid: errors.length === 0,
errors,
}
}
export function generateRecoveryMessage(
accountAddress: string,
newOwner: string,
nonce: number
): string {
return `Account Recovery Request:
Account: ${accountAddress}
New Owner: ${newOwner}
Nonce: ${nonce}
I confirm this recovery request is authorized.`
}
Recovery Signature Process
utils/recoverySignatures.ts
import { hashMessage, recoverAddress } from 'viem'
export interface RecoverySignature {
guardian: string
signature: string
timestamp: number
}
export async function verifyRecoverySignature(
message: string,
signature: string,
expectedGuardian: string
): Promise<boolean> {
try {
const messageHash = hashMessage(message)
const recoveredAddress = await recoverAddress({
hash: messageHash,
signature: signature as `0x${string}`,
})
return recoveredAddress.toLowerCase() === expectedGuardian.toLowerCase()
} catch (error) {
console.error('Signature verification failed:', error)
return false
}
}
export function hasThresholdMet(
signatures: RecoverySignature[],
guardians: string[],
threshold: number
): boolean {
const validGuardians = signatures.filter(sig =>
guardians.includes(sig.guardian.toLowerCase())
)
return validGuardians.length >= threshold
}
Security Considerations
Best Practices
-
Guardian Selection:
- Choose trustworthy individuals who understand the responsibility
- Diversify guardians geographically and technologically
- Avoid choosing guardians who might collude
-
Threshold Configuration:
- 2-of-3 is most common for personal accounts
- Higher thresholds provide more security but less convenience
- Consider backup guardians for critical accounts
-
Communication Protocol:
- Establish secure communication channels with guardians
- Document the recovery process clearly
- Regular check-ins to ensure guardians' addresses are still valid
Common Pitfalls
- Single Point of Failure: Don't rely on one type of guardian (e.g., all family members)
- Lost Guardians: Plan for guardians becoming unavailable
- Threshold Too High: Ensure you can always reach enough guardians
- Insufficient Verification: Guardians should verify identity before signing
Testing & Validation
Recovery Simulation
utils/recoverySimulation.ts
export class RecoverySimulator {
private guardians: string[]
private threshold: number
constructor(guardians: string[], threshold: number) {
this.guardians = guardians
this.threshold = threshold
}
simulateRecovery(availableGuardians: string[]): {
canRecover: boolean
requiredSignatures: number
availableSignatures: number
missingGuardians: string[]
} {
const availableCount = availableGuardians.filter(guardian =>
this.guardians.includes(guardian)
).length
const missingGuardians = this.guardians.filter(guardian =>
!availableGuardians.includes(guardian)
)
return {
canRecover: availableCount >= this.threshold,
requiredSignatures: this.threshold,
availableSignatures: availableCount,
missingGuardians,
}
}
testScenarios(): Array<{
scenario: string
unavailableGuardians: number
canRecover: boolean
}> {
const scenarios = []
for (let unavailable = 0; unavailable <= this.guardians.length; unavailable++) {
const available = this.guardians.length - unavailable
scenarios.push({
scenario: `${unavailable} guardian(s) unavailable`,
unavailableGuardians: unavailable,
canRecover: available >= this.threshold,
})
}
return scenarios
}
}
Integration with Other Features
Social recovery works seamlessly with other smart account features:
- Multi-signature operations: Guardians can approve emergency actions
- Session keys: Recover access to session-enabled applications
- Upgrade mechanisms: Update account logic through guardian consensus
Next Steps
- Test the Setup: Configure guardians and test the recovery flow
- Smart Sessions: Learn about reducing signature friction
Troubleshooting
Module installation fails:
- Ensure sufficient gas or token balance for transaction
- Verify all guardian addresses are valid
- Check that threshold is within valid range
Recovery process stuck:
- Verify guardian signatures are valid
- Ensure threshold requirements are met
- Check for expired signatures or replay attacks
Guardians can't sign:
- Confirm guardians have access to their wallets
- Verify the recovery message format is correct
- Check guardian addresses match exactly