--- title: Recover a Safe Account with Google using AbstractionKit (Guide 2/2) description: Learn how to offer a recovery option for your users using AbstractinKit image: /img/posters/abstractionkit-meta.png keywords: [mpc,hosted-wallets,social-login, abstractionkit, recovery-module] --- # Recover a Safe Account with Google using AbstractionKit (Guide 2/2) This is the second of two guides on how to recovery a Safe Account using Google. In the first guide, we show the steps on how to add the recovery method. We recommend starting with the first guide here. ## What is AbstractionKit? [AbstractionKit](https://docs.candide.dev/wallet/abstractionkit/introduction/) is a Typescript Library to easily build on Account Abstraction, with first class support for Safe Accounts. One of their usecases is to enable users to add recovery methods as a backup in case their use their main signer. To leverage the full potential of Account Abstraction, you can combine Lit 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. ## Relevant Links For additional information during this guide: - [How on-chain guardian recovery works](https://docs.candide.dev/wallet/plugins/recovery-with-guardians/) - [Guardian Recovery SDK Reference](https://docs.candide.dev/blog/making-accounts-recoverable/) - [Simple Recovery example on GitHub](https://github.com/candidelabs/abstractionkit/tree/experimental/examples/SafeAccountExamples/SocialRecovery) - [Lit Documentation Website](https://developer.litprotocol.com/) ## Installation ### Install required dependencies ```bash npm i abstractionkit@0.1.12 && @lit-protocol/lit-auth-client ``` ### Configure .env file Configure the values you created from Lit dashboard in an .env file ```ts // Lit LIT_API_KEY=Request Relay Server API Key from Lit at https://forms.gle/RNZYtGYTY9BcD9MEA // Candide BUNDLER_URL="https://sepolia.voltaire.candidewallet.com/rpc" // Other networks are found here: https://docs.candide.dev/wallet/bundler/rpc-endpoints PAYMASTER_URL="Request an API key from Candide on Discord" // Generate a Public/Private Key OWNER_PUBLIC_ADDRESS= OWNER_PRIVATE_KEY= NEW_OWNER_PUBLIC_ADDRESS= // Network Info VITE_CHAIN_ID= JSON_RPC_NODE_PROVIDER= // "Get an RPC from a Node provider" ``` ## Sign in with Google using Lit ### Initialize the Lit client and provider - Connect to the Lit Network using LitNodeClient. - Set up the LitAuthClient for authentication. - Initialize a GoogleProvider for Google sign-in. ```ts import { LitNodeClient } from "@lit-protocol/lit-node-client"; import { LitAuthClient, GoogleProvider } from "@lit-protocol/lit-auth-client"; import { ProviderType } from "@lit-protocol/constants"; const initalizeClientsAndProvider = async () => { const litNodeClient = new LitNodeClient({ litNetwork: "datil-dev", debug: true, }); await litNodeClient.connect(); const litAuthClient = new LitAuthClient({ litRelayConfig: { relayApiKey: process.env.LIT_API_KEY, }, litNodeClient, }); console.log("Connected to Lit Node and Lit Auth Clients ✔️"); const provider = litAuthClient.initProvider<GoogleProvider>( ProviderType.Google, { //redirectUri: VITE_REDIRECT_URI, } ); return { litNodeClient, litAuthClient, provider }; }; ``` ### Authentication with Gmail - Generate Authentication Method - Check if the user is already authenticated. If not, redirect to Google sign-in ```ts import { AuthMethod } from "@lit-protocol/types"; const generateAuthMethod = async () => { const url = new URL(window.location.href); if (!url.searchParams.get("provider")) { console.log("Signing in with Google..."); provider.signIn((url) => { window.location.href = url; }); } else if (url.searchParams.get("provider") === "google") { const authMethod = await provider.authenticate(); return authMethod; } }; const authMethod = await generateAuthMethod(); if (!authMethod) { return; } ``` ### Mint PKP (Programmable Key Pair) ```ts import { LitAuthClient } from "@lit-protocol/lit-auth-client"; const mintWithGoogle = async (authMethod) => { const pkp = await litAuthClient.mintPKPWithAuthMethods([authMethod], { addPkpEthAddressAsPermittedAddress: true }); console.log("Fetched PKP", pkp); return pkp; }; const pkp = await mintWithGoogle(authMethod); console.log("Minted PKP ✔️"); ``` ### Get the Google Guardian Signer ```ts import { PKPEthersWallet } from "@lit-protocol/pkp-ethers"; import { LitAbility, LitPKPResource } from "@lit-protocol/auth-helpers"; import { AuthCallbackParams } from "@lit-protocol/types"; const authNeededCallback = async (params: AuthCallbackParams) => { console.log(`auth needed callback params`, JSON.stringify(params, null, 2)); const response = await litNodeClient.signSessionKey({ statement: params.statement, authMethods: [authMethod], resourceAbilityRequests: [ { resource: new LitPKPResource("*"), ability: LitAbility.PKPSigning, }, ], expiration: params.expiration, resources: params.resources, chainId: 1, pkpPublicKey: pkp.pkpPublicKey, }); console.log("AUTHSIG", response); return response.authSig; }; const guardianSigner = new PKPEthersWallet({ litNodeClient, authContext: { getSessionSigsProps: { chain: "ethereum", expiration: new Date(Date.now() + 60_000 * 60).toISOString(), resourceAbilityRequests: [ { resource: new LitPKPResource("*"), ability: LitAbility.PKPSigning, }, ], authNeededCallback: authNeededCallback, }, }, pkpPubKey: pkp.pkpPublicKey, rpc: "https://yellowstone-rpc.litprotocol.com", }); console.log("Created PKPEthersWallet using the PKP ✔️"); ``` ## Start the Recovery Process ### Initilize the Safe Account Class ```ts import { SafeAccountV0_2_0 as SafeAccount } from "abstractionkit"; const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress]); console.log("Smart Account Address: ", smartAccount.accountAddress); ``` ### Repare The Recovery Transaction ```ts import { SocialRecoveryModule } from "abstractionkit"; const srm = new SocialRecoveryModule(); const initiateRecoveryMetaTx = srm.createConfirmRecoveryMetaTransaction( smartAccount.accountAddress, [newOwnerPublicAddress], 1, // new threshold true // whether to auto-start execution of recovery ); let userOperationRecovery = await guardianSmartAccount.createUserOperation( [initiateRecoveryMetaTx], process.env.JSON_RPC_NODE_PROVIDER, process.env.BUNDLER_URL ); ``` ### Sponsor the Gas ```ts import { CandidePaymaster } from "abstractionkit"; import ethers from "ethers" // Sponsor the recovery transaction using the paymaster const paymasterUrl = process.env.PAYMASTER_URL; const paymaster = new CandidePaymaster(paymasterUrl); userOperationRecovery = await paymaster.createSponsorPaymasterUserOperation( userOperationRecovery, process.env.BUNDLER_URL ); ``` ### Sign and Submit UserOperation ```ts // Sign const domain = { chainId: process.env.CHAIN_ID, verifyingContract: smartAccount.safe4337ModuleAddress, }; const types = SafeAccount.EIP712_SAFE_OPERATION_TYPE; // formate according to EIP712 Safe Operation Type const { sender, ...userOp } = userOperation; const safeUserOperation = { ...userOp, safe: userOperation.sender, validUntil: BigInt(0), validAfter: BigInt(0), entryPoint: smartAccount.entrypointAddress, }; const signature = await guardianSigner.signTypedData(domain, types, safeUserOperation); const formatedSig = SafeAccount.formatEip712SignaturesToUseroperationSignature([ownerPublicAddress], [signature]); userOperationRecovery.signature = signature; // Submit const sendUserOpResponseRecovery = await guardianSmartAccount.sendUserOperation( userOperationRecovery, process.env.BUNDLER_URL ); ``` ### Monitor UserOp ```ts // Wait for receipt const userOpReceiptResultRecovery = await sendUserOpResponseRecovery.included(); console.log(userOpReceiptResultRecovery); ``` ## Finalize the Recovery After the grace period is over, you can finilize the recovery ### Prepare the Finilization UserOp ```ts const finalizeRecoveryMetaTx = srm.createFinalizeRecoveryMetaTransaction( smartAccount.accountAddress ); let userOperationFinalizeRecovery = await guardianSmartAccount.createUserOperation( [finalizeRecoveryMetaTx], process.env.JSON_RPC_NODE_PROVIDER, process.env.BUNDLER_URL ); ``` ### Sponsor the Gas ```ts // Add gas sponsorship info using paymaster userOperationFinalizeRecovery = await paymaster.createSponsorPaymasterUserOperation( userOperationRecovery, process.env.BUNDLER_URL ); ``` ### Sign and Submit ```ts // Sign userOperation const domain = { chainId: process.env.CHAIN_ID, verifyingContract: smartAccount.safe4337ModuleAddress, }; const types = SafeAccount.EIP712_SAFE_OPERATION_TYPE; // formate according to EIP712 Safe Operation Type const { sender, ...userOp } = userOperation; const safeUserOperation = { ...userOp, safe: userOperation.sender, validUntil: BigInt(0), validAfter: BigInt(0), entryPoint: smartAccount.entrypointAddress, }; const signature = await guardianSigner.signTypedData(domain, types, safeUserOperation); const formatedSig = SafeAccount.formatEip712SignaturesToUseroperationSignature([ownerPublicAddress], [signature]); userOperationRecovery.signature = signature; // Submit userOperation const sendUserOperationResponseRecovery = await guardianSmartAccount.sendUserOperation( userOperationRecovery, process.env.BUNDLER_URL ); ``` ### Monitor UserOperation ```ts const userOperationReceiptResultRecovery = await sendUserOperationResponseRecovery.included(); console.log(userOperationReceiptResultRecovery); ``` --- That's it! You've successfully added a Guardian capable of recovering an account with a Google Account using Lit. Find here the complete [doc page for Account Recovery](https://docs.candide.dev/wallet/plugins/recovery-with-guardians). ---