# Use Turnkey with AbstractionKit

[Turnkey](https://turnkey.com) 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:

* [High level explanation: How on-chain guardian recovery works](https://docs.candide.dev/wallet/plugins/recovery-with-guardians/)
* [SDK: Guardian Recovery References](https://docs.candide.dev/blog/making-accounts-recoverable/)
* [Turnkey Docs](https://docs.turnkey.com)
* [Code Example on GitHub](https://github.com/candidelabs/abstractionkit-examples/blob/main/recovery/recovery.ts)

## Installation[​](#installation "Direct link to Installation")

### Install required dependencies[​](#install-required-dependencies "Direct link to Install required dependencies")

* ethers
* viem

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

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

### Create a Turnkey account[​](#create-a-turnkey-account "Direct link to Create a Turnkey account")

Create an account and get the API keys on [Turnkey's Dashboard](https://app.turnkey.com). 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-env-file "Direct link to 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[​](#setup-guardian "Direct link to 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[​](#initiate-recovery "Direct link to Initiate Recovery")

* ethers
* viem

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

```
import { SafeAccountV0_3_0 as SafeAccount } from "abstractionkit";
import { createWalletClient, http } from "viem";

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

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 turnkeyAccount = await createAccount({
  client: turnkeyClient,
  organizationId: process.env.TURNKEY_ORG_ID as string,
  signWith: process.env.TURNKEY_WALLET_ADDRESS as string, // for this example, we manually generated the signer on turnkey dashboard
});

const guardianSigner = createWalletClient({
  account: turnkeyAccount,
  transport: http(process.env.JSON_RPC_NODE_PROVIDER as string),
});

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 sendTx1 = await guardianSigner.sendTransaction({
    to: initiateRecoveryMetaTx.to,
    data: initiateRecoveryMetaTx.data,
});
```

## Finilize Recovery[​](#finilize-recovery "Direct link to 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,
})
```
