Overview
Social Recovery allows users to set one or multiple accounts as guardians. Guardians can recover access to the account by approving a change to the validator configuration.
For example, if a user loses access to their key, guardians are able to rotate the signer to a new ECDSA key. Or if a smart account has a multisig configuration, guardians can be used to recover a signer of the multisig or change the threshold.
Guardians can only update the validator configuration, they are not permitted to make any other transactions.
Set guardians carefully! They can change the ownership of a smart account without any approval from the previous owner. There is no timelock for the recovery transaction. Prefer setting multiple trusted guardians with a higher signature threshold.
How it works
Once installed, the Social Recovery module gives the selected guardians access to change the configuration of the validator modules.
In other words, the guardian (or guardians) is allowed to make any transaction to any installed validator module, usually to add an owner, remove an owner, or update the signature threshold.
Making recovery transactions requires setting up a
bundler.
Account recovery may result in multiple account calls, each of them should be done separately (batching is not supported).
Initialization
To install a social recovery module during account deployment:
const rhinestoneAccount = await createRhinestoneAccount({
owners: {
type: 'ecdsa',
accounts: [oldAccount],
},
recovery: {
guardians: [guardianAccount],
},
})
To install the module separately (i.e., when the account is already deployed):
import { setUpRecovery } from '@rhinestone/sdk'
const setUpTransaction = await rhinestoneAccount.sendTransaction({
chain,
calls: setUpRecovery({
rhinestoneAccount,
guardians: [guardianAccount],
}),
tokenRequests: [],
})
await rhinestoneAccount.waitForExecution(setUpTransaction, true)
Multiple Guardians
You can also set multiple guardians for a single account, and use a custom signature threshold:
const rhinestoneAccount = await createRhinestoneAccount({
owners: {
type: 'ecdsa',
accounts: [oldAccount],
},
recovery: {
guardians: [guardianAccountA, guardianAccountB, guardianAccountC],
threshold: 2,
},
rhinestoneApiKey,
})
Usage
ECDSA Account
To recover access to the account:
import { recoverEcdsaOwnership } from '@rhinestone/sdk'
const recoveryCalls = await recoverEcdsaOwnership(
address,
{
type: 'ecdsa',
accounts: [newOwnerAccount],
},
chain,
)
for (const call of recoveryCalls) {
const transaction = await rhinestoneAccount.sendTransaction({
chain,
calls: [call],
tokenRequests: [],
signers: {
type: 'guardians',
guardians: [guardianAccountA],
},
})
await rhinestoneAccount.waitForExecution(transaction, true)
}
This will prompt a signature from the guardian account, and submit a transaction on its behalf to update the ownership.
Passkey Account
To recover access to the account:
import { recoverPasskeyOwnership } from '@rhinestone/sdk'
const recoveryCalls = await recoverPasskeyOwnership(
address,
// List of existing owners: those will be removed
[
{
pubKeyX: BigInt(slice(passkeyAccountA.publicKey, 0, 32)),
pubKeyY: BigInt(slice(passkeyAccountA.publicKey, 32, 64)),
},
],
// New owners: those will be added
{
type: "passkey",
accounts: [passkeyAccountB],
},
chain
);
for (const call of recoveryCalls) {
const transaction = await account.sendTransaction({
chain,
calls: [call],
tokenRequests: [],
signers: {
type: "guardians",
guardians: [guardianAccountA],
},
});
await account.waitForExecution(transaction, true);
}
This will prompt a signature from the guardian account, and submit a transaction on its behalf to update the ownership.