Add an Social Account as a Recovery Method using Magic
Magic is a popular hosted wallet provider for EOAs. It enable users to login using various authentication methods like Social Logins and Email OTP.
To leverage the full potential of Account Abstraction, you can combine Magic with AbstractionKit to enable email / social recovery experience, while using a Smart Account as the smart wallet to sponsor gas for users, batch transactions, and more.
Below are some relevant links that provide additional information and resources which you might find helpful as you go through this guide.
- How on-chain guardian recovery works
- Guardian Recovery SDK Reference
- Simple code example on GitHub
- Magic Documentation Website
Installation
Install required dependencies
- ethers
- viem
npm i abstractionkit && magic-sdk
npm i abstractionkit && magic-sdk && viem
Create a Magic account
Create an account and get the API key on Magic's Dashboard. You will need the PUBLISHABLE API KEY.
Configure .env file
Configure the values you created from Magic dashboard in an .env file
// magic
PUBLISHABLE_API_KEY=
// candide
BUNDLER_URL=
JSON_RPC_NODE_PROVIDER=
OWNER_PUBLIC_ADDRESS=
NEW_OWNER_PUBLIC_ADDRESS=
Setup Guardian
- ethers
- viem
import {
SafeAccountV0_3_0 as SafeAccount,
SocialRecoveryModule,
} from "abstractionkit";
import { Magic } from "magic-sdk";
import { BrowserProvider } from 'ethers';
const magic = new Magic(process.env.PUBLISHABLE_API_KEY);
const provider = new BrowserProvider(magic.rpcProvider);
const guardianSigner = await provider.getSigner();
const smartAccount = SafeAccount.initializeNewAccount([process.env.OWNER_PUBLIC_ADDRESS]);
const srm = new SocialRecoveryModule();
const enableModuleTx = srm.createEnableModuleMetaTransaction(
smartAccount.accountAddress
);
const addGuardianTx = srm.createAddGuardianWithThresholdMetaTransaction(
smartAccount.accountAddress,
guardianSigner.address, // Magic Guardian Address
1n //threshold
);
let userOperation = await smartAccount.createUserOperation(
[enableModuleTx, addGuardianTx],
process.env.JSON_RPC_NODE_PROVIDER,
process.env.BUNDLER_URL,
);
import { SafeAccountV0_3_0 as SafeAccount } from "abstractionkit";
import { Magic } from "magic-sdk";
import { createWalletClient, custom } from "viem";
const magic = new Magic(process.env.PUBLISHABLE_API_KEY);
const guardianSigner = createWalletClient({
transport: custom(magic.rpcProvider),
});
const guardianAddresses = await signer.getAddresses();
const srm = new SocialRecoveryModule();
const enableModuleTx = srm.createEnableModuleMetaTransaction(
smartAccount.accountAddress
);
const addGuardianTx = srm.createAddGuardianWithThresholdMetaTransaction(
smartAccount.accountAddress,
guardianAddresses[0], // Magic Guardian Address
1n //threshold
);
let userOperation = await smartAccount.createUserOperation(
[enableModuleTx, addGuardianTx],
process.env.JSON_RPC_NODE_PROVIDER,
process.env.BUNDLER_URL,
);
To use MagicSigner in your app's client, you must ensure the window
object is defined. For example, use magic in webapps and not node scripts.
Initiate Recovery
- ethers
- viem
import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit";
const initiateRecoveryMetaTx = createConfirmRecoveryMetaTransaction(
smartAccount.address,
[process.env.NEW_OWNER_PUBLIC_ADDRESS],
1, // new threshold
true, // whether to auto-start execution of recovery
);
// make sure to fund the guardian address on magic
// otherwise, you can make the guardian a smart account from the start
// so you are able to sponsor the gas!
const sendTx1 = guardianSigner.sendTransaction({
to: initiateRecoveryMetaTx.to,
data: initiateRecoveryMetaTx.data,
value: 0,
});
import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit";
import { createWalletClient, http } from "viem";
const initiateRecoveryMetaTx = createConfirmRecoveryMetaTransaction(
smartAccount.address,
[process.env.NEW_ONWER_PUBLIC_ADDRESS],
1, // new threshold
true, // whether to auto-start execution of recovery
)
// make sure to fund the guardian address on magic
const sendTx1 = await guardianSigner.sendTransaction({
to: initiateRecoveryMetaTx.to,
data: initiateRecoveryMetaTx.data,
});
Finilize Recovery
Wait for recovery period to pass before finilizing
- ethers
- viem
const finalizeRecoveryMetaTx = createFinalizeRecoveryMetaTransaction(smartAccount.accountAddress)
// Anyone can call the finilize function after the grace period is over
const sendTx2 = await guardianSigner.sendTransaction({
to: finalizeRecoveryMetaTx.to,
data: finalizeRecoveryMetaTx.data,
})
const finalizeRecoveryMetaTx = createFinalizeRecoveryMetaTransaction(smartAccount.accountAddress)
// Anyone can call the finilize function after the grace period is over
const sendTx2 = await guardianSigner.sendTransaction({
to: finalizeRecoveryMetaTx.to,
data: finalizeRecoveryMetaTx.data,
})