Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.startale.com/llms.txt

Use this file to discover all available pages before exploring further.

Social recovery lets a configurable set of guardians cooperate to rotate the signing key on a smart account when the user loses access. It is implemented as an ERC-7579 validator module (the Rhinestone Social Recovery validator) that you install on top of the Startale Smart Account.
Source: StartaleGroup/scs-aa-sdk. This page uses helpers from @rhinestone/module-sdk which is already a peer of the AA SDK; install it explicitly if your project does not have it yet (npm install @rhinestone/module-sdk).

How it works

A recovery is a UserOperation validated by the Social Recovery module instead of the default validator. The module checks that the bundle of guardian signatures meets the configured threshold; if it does, the UserOperation is executed and rotates the account’s owner key.

1. Pick guardians and a threshold

The configuration is intentionally small:
ParameterTypeNotes
guardiansAddress[]Onchain addresses you trust to cooperate. They can be EOAs, multisigs, or other smart accounts.
thresholdnumberHow many guardian signatures are required for a recovery. Common choices are 2-of-3 and 3-of-5.
hookAddress (optional)An ERC-7579 hook to gate recovery further (rate limits, cooldowns).
Validate the configuration before installing the module: ensure addresses are deduplicated, all guardians pass isAddress, and 1 <= threshold <= guardians.length.

2. Install the module

import { getSocialRecoveryValidator } from "@rhinestone/module-sdk"
import type { StartaleAccountClient } from "@startale-scs/aa-sdk"
import type { Address } from "viem"

async function installSocialRecovery(
  client: StartaleAccountClient,
  guardians: Address[],
  threshold: number,
) {
  const recoveryModule = getSocialRecoveryValidator({ guardians, threshold })

  const userOpHash = await client.installModule({ module: recoveryModule })
  return client.waitForUserOperationReceipt({ hash: userOpHash })
}
SymbolSourceRole
getSocialRecoveryValidator@rhinestone/module-sdkBuilds the validator descriptor and init data (encodes guardian addresses + threshold).
installModuleStartaleAccountClient (ERC-7579 actions)Installs the module via a UserOperation.
waitForUserOperationReceiptStartaleAccountClientPolls the bundler for confirmation.

3. Manage guardians at runtime

After installation you can add or remove guardians and adjust the threshold without re-installing the module. The helpers return an Execution ({ to, data, value }), which you wrap in a UserOperation with sendUserOperation.
import {
  getAddSocialRecoveryGuardianAction,
  getRemoveSocialRecoveryGuardianAction,
  getSetSocialRecoveryThresholdAction,
} from "@rhinestone/module-sdk"
import type { Address } from "viem"

async function addGuardian(client: StartaleAccountClient, guardian: Address) {
  const exec = getAddSocialRecoveryGuardianAction({ guardian })
  const hash = await client.sendUserOperation({ calls: [exec] })
  return client.waitForUserOperationReceipt({ hash })
}

async function setThreshold(client: StartaleAccountClient, threshold: number) {
  const exec = getSetSocialRecoveryThresholdAction({ threshold })
  const hash = await client.sendUserOperation({ calls: [exec] })
  return client.waitForUserOperationReceipt({ hash })
}

async function removeGuardian(
  client: StartaleAccountClient,
  publicClient: import("viem").PublicClient,
  account: Parameters<typeof getRemoveSocialRecoveryGuardianAction>[0]["account"],
  guardian: Address,
) {
  const exec = await getRemoveSocialRecoveryGuardianAction({
    client: publicClient,
    account,
    guardian,
  })
  const hash = await client.sendUserOperation({ calls: [exec] })
  return client.waitForUserOperationReceipt({ hash })
}
SymbolSourceRole
getAddSocialRecoveryGuardianAction@rhinestone/module-sdkReturns the Execution for adding one guardian.
getRemoveSocialRecoveryGuardianAction@rhinestone/module-sdkReturns the Execution for removing a guardian (reads onchain state, hence the client parameter).
getSetSocialRecoveryThresholdAction@rhinestone/module-sdkReturns the Execution for updating the threshold.

4. Read the current guardian set

import { getSocialRecoveryGuardians } from "@rhinestone/module-sdk"

const guardians = await getSocialRecoveryGuardians({
  client: publicClient,
  account: smartAccountClient.account,
})
guardians is an Address[] of every guardian currently registered on the account.

5. Execute a recovery

A recovery is a UserOperation that:
  1. Targets the smart account itself.
  2. Calls the account’s owner-rotation method (for example, swapping the active ECDSA validator key).
  3. Is signed by enough guardians to meet the threshold.
The exact signing UX is up to you (each guardian signs typed data offchain, your backend aggregates the signatures, and you submit a single UserOperation). The module’s onchain logic verifies that the aggregated signature represents at least threshold distinct guardians from the registered set before the UserOperation is allowed to execute.
Recovery rotates the active validator key, not the smart account address. The address stays the same; the user just gains a new signer they control.

Operational guidance

ConcernRecommendation
Guardian selectionDiversify across people, devices, or services. Avoid choosing guardians who can collude.
Threshold2-of-3 is the most common starting point. Higher thresholds increase safety at the cost of recovery friction.
CommunicationDocument the recovery process for your guardians in advance so they recognise a legitimate request.
DrillsRun a dry-run recovery on a test account before relying on the configuration in production.

Next steps

Smart sessions

Pair recovery with scoped session keys for daily UX.

Sponsored paymaster

Sponsor recovery UserOperations so guardians do not need ETH.

Smart account setup

Refresh the underlying account and client setup.

Contracts and audits

Look up validator and account addresses on each network.