Skip to main content

Use Turnkey with AbstractionKit

Turnkey offers a flexible private key management solution leveraging AWS Nitro Systems to secure user keys.

Fully leverage Account Abstraction by combining Turnkey with AbstractionKit to enable a trusted recoverer. Enabling the Account Recovery Module requires an on-chain transaction. For user convenience, batch recovery transactions and sponsor gas fees.

Relevant links for additional information during this guide:

Installation

Install required dependencies

npm i abstractionkit && @turnkey/http && @turnkey/api-key-stamper && @turnkey/ethers

Create a Turnkey account

Create an account and get the API keys on Turnkey's Dashboard. You will need:

  • Organization ID
  • Public API Key
  • Private API Key
  • Create a private/public key pair on Turnkey dashboard. This is Guardian Account.

Configure .env file

Configure the values you created from Turnkey dashboard in an .env file, along with a node endpoint.

// turnkey guardian
TURNKEY_PUBLIC_KEY=
TURNKEY_PRIVATE_KEY= // For this demo, we exported the private key of the guardian account. In production, use Turnkey api
TURNKEY_ORG_ID=
TURNKEY_WALLET_ADDRESS=

// candide
BUNDLER_URL=
JSON_RPC_NODE_PROVIDER=
OWNER_PUBLIC_ADDRESS=
NEW_ONWER_PUBLIC_ADDRESS=

Setup Guardian

This step shows how to contrust the calldata for creating the userop to add a guardian.

import { SocialRecoveryModule } from "abstractionkit";

const srm = new SocialRecoveryModule();

const enableModuleTx = srm.createEnableModuleMetaTransaction(
smartAccount.accountAddress
);
const addGuardianTx = srm.createAddGuardianWithThresholdMetaTransaction(
smartAccount.accountAddress,
process.env.TURNKEY_PUBLIC_KEY, // Turnkey Guardian Address
1n //threshold
);

let userOperation = await smartAccount.createUserOperation(
[enableModuleTx, addGuardianTx],
process.env.JSON_RPC_NODE_PROVIDER,
process.env.BUNDLER_URL,
)

Initiate Recovery

import { SafeAccountV0_3_0 as SafeAccount } from "abstractionkit";

import { TurnkeySigner } from "@turnkey/ethers";
import { TurnkeyClient } from "@turnkey/http";
import { ApiKeyStamper } from "@turnkey/api-key-stamper";

const turnkeyClient = new TurnkeyClient(
{ baseUrl: "https://api.turnkey.com" },
new ApiKeyStamper({
apiPublicKey: process.env.TURNKEY_PUBLIC_KEY as string,
apiPrivateKey: process.env.TURNKEY_PRIVATE_KEY as string,
})
);
const guardianSigner = new TurnkeySigner({
client: turnkeyClient,
organizationId: process.env.TURNKEY_ORG_ID as string,
signWith: process.env.TURNKEY_WALLET_ADDRESS as string, // For this demo, we manually generated the signer on turnkey dashboard
});

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 turnkey
const sendTx = guardianSigner.sendTransaction({
to: initiateRecoveryMetaTx.to,
data: initiateRecoveryMetaTx.data,
value: 0,
});

Finilize Recovery

Wait for recovery period to pass before finilizing

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,
})