Turnkey

Upgrade existing Turnkey wallets to Smart Wallets to enable gasless transactions, batching, and more in under 10 minutes. Keep Turnkey for key management, no wallet migration needed. Add battle-tested transaction infrastructure using EIP-7702 to upgrade EOAs to Smart Wallets:

Don’t have a wallet set up yet? Start with the Smart Wallet Quickstart to create one in minutes.

Setup

Use Turnkey’s @turnkey/sdk-server package with Smart Wallets infrastructure in a server environment.

Installation

$npm install @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
># or
>yarn add @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
># or
>pnpm add @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem

Prerequisites: Get your keys

  • Alchemy API Key:
    • Go to the dashboard
    • Create or select an app and copy the API key
  • Gas sponsorship Policy ID:
    • Create a gas sponsorship policy in the dashboard and copy its Policy ID
  • Turnkey Credentials:
    • Go to the Turnkey Dashboard
    • Create or select an organization and copy the Organization ID
    • Create an API key pair and copy the API Public Key and Private Key
    • Create or import a private key and copy the Private Key ID (UUID)
    • Note the Ethereum address associated with that private key

Both the Smart Wallets configuration and the gas sponsorship policy must be linked to the application behind your API key for sponsorship to work.

Create Smart Wallet Client from Turnkey

Create a helper function that converts a Turnkey wallet into an Alchemy Smart Wallet client:

1import { WalletClientSigner } from "@aa-sdk/core";
2import { alchemy, baseSepolia } from "@account-kit/infra";
3import { createSmartWalletClient } from "@account-kit/wallet-client";
4import { Turnkey } from "@turnkey/sdk-server";
5import { createAccount } from "@turnkey/viem";
6import { createWalletClient } from "viem";
7
8const ALCHEMY_API_KEY = "your-alchemy-api-key";
9const ALCHEMY_GAS_POLICY_ID = "your-gas-policy-id";
10const TURNKEY_ORGANIZATION_ID = "your-turnkey-org-id";
11const TURNKEY_API_PRIVATE_KEY = "your-turnkey-api-private-key";
12const TURNKEY_API_PUBLIC_KEY = "your-turnkey-api-public-key";
13
14async function createTurnkeySmartWalletClient({
15 privateKeyId,
16 walletAddress,
17}) {
18 // Initialize Turnkey SDK
19 const turnkey = new Turnkey({
20 defaultOrganizationId: TURNKEY_ORGANIZATION_ID,
21 apiBaseUrl: "https://api.turnkey.com",
22 apiPrivateKey: TURNKEY_API_PRIVATE_KEY,
23 apiPublicKey: TURNKEY_API_PUBLIC_KEY,
24 });
25
26 // Create a viem account using Turnkey's signing capabilities
27 const baseTurnkeyAccount = await createAccount({
28 client: turnkey.apiClient(),
29 organizationId: TURNKEY_ORGANIZATION_ID,
30 signWith: privateKeyId,
31 ethereumAddress: walletAddress,
32 });
33
34 // Wrap the account to fix the double 0x prefix bug in @turnkey/viem 0.14.8
35 const turnkeyAccount = {
36 ...baseTurnkeyAccount,
37 signAuthorization: async (authorization) => {
38 if (!baseTurnkeyAccount.signAuthorization) {
39 throw new Error("signAuthorization not available on Turnkey account");
40 }
41 const result = await baseTurnkeyAccount.signAuthorization(authorization);
42
43 // Strip the extra 0x prefix if present
44 return {
45 ...result,
46 r: result.r.replace(/^0x0x/, "0x"),
47 s: result.s.replace(/^0x0x/, "0x"),
48 };
49 },
50 };
51
52 // Create wallet client with Turnkey account
53 const walletClient = createWalletClient({
54 account: turnkeyAccount,
55 chain: baseSepolia,
56 transport: alchemy({ apiKey: ALCHEMY_API_KEY }),
57 });
58
59 // Create signer from wallet client
60 const signer = new WalletClientSigner(walletClient, "turnkey-custom");
61
62 // Create and return the Smart Wallet client
63 return createSmartWalletClient({
64 chain: baseSepolia,
65 transport: alchemy({ apiKey: ALCHEMY_API_KEY }),
66 signer,
67 policyId: ALCHEMY_GAS_POLICY_ID,
68 });
69}

Send Transactions with EIP-7702

Send gasless transactions using EIP-7702 to upgrade the Turnkey wallet to a smart wallet:

1const TURNKEY_PRIVATE_KEY_ID = "your-private-key-uuid";
2const TURNKEY_WALLET_ADDRESS = "0x...";
3
4async function sendTransaction() {
5 console.log("Wallet address:", TURNKEY_WALLET_ADDRESS);
6
7 // Create Smart Wallet client from Turnkey wallet
8 const client = await createTurnkeySmartWalletClient({
9 privateKeyId: TURNKEY_PRIVATE_KEY_ID,
10 walletAddress: TURNKEY_WALLET_ADDRESS,
11 });
12
13 // Send a gasless transaction with EIP-7702
14 const result = await client.sendCalls({
15 from: TURNKEY_WALLET_ADDRESS,
16 calls: [
17 {
18 to: "0x0000000000000000000000000000000000000000",
19 value: "0x0",
20 data: "0x",
21 },
22 ],
23 capabilities: {
24 eip7702Auth: true,
25 paymasterService: {
26 policyId: ALCHEMY_GAS_POLICY_ID,
27 },
28 },
29 });
30
31 console.log("Transaction sent:", result.preparedCallIds[0]);
32}

Wait for Transaction Confirmation

Wait for the transaction to be confirmed onchain:

1async function sendAndWaitForTransaction() {
2 const client = await createTurnkeySmartWalletClient({
3 privateKeyId: TURNKEY_PRIVATE_KEY_ID,
4 walletAddress: TURNKEY_WALLET_ADDRESS,
5 });
6
7 const result = await client.sendCalls({
8 from: TURNKEY_WALLET_ADDRESS,
9 calls: [
10 {
11 to: "0x0000000000000000000000000000000000000000",
12 value: "0x0",
13 data: "0x",
14 },
15 ],
16 capabilities: {
17 eip7702Auth: true,
18 paymasterService: {
19 policyId: ALCHEMY_GAS_POLICY_ID,
20 },
21 },
22 });
23
24 console.log("Waiting for confirmation...");
25
26 // Wait for the transaction to be confirmed
27 const txStatus = await client.waitForCallsStatus({
28 id: result.preparedCallIds[0],
29 timeout: 60_000, // 60 seconds
30 });
31
32 const txnHash = txStatus.receipts?.[0]?.transactionHash;
33
34 console.log("Transaction confirmed!");
35 console.log("Transaction hash:", txnHash);
36}

Batch Multiple Transactions

Send multiple transactions in a single batch:

1const result = await client.sendCalls({
2 from: TURNKEY_WALLET_ADDRESS,
3 calls: [
4 { to: "0x...", data: "0x...", value: "0x0" },
5 { to: "0x...", data: "0x...", value: "0x0" },
6 { to: "0x...", data: "0x...", value: "0x0" },
7 ],
8 capabilities: {
9 eip7702Auth: true,
10 paymasterService: {
11 policyId: ALCHEMY_GAS_POLICY_ID,
12 },
13 },
14});

Notes

  • EIP-7702: Upgrades the Turnkey wallet to a smart wallet at transaction time without migration
  • Gas Sponsorship: Use paymasterService with your policy ID for gasless transactions
  • Server-Side: This example uses Turnkey’s @turnkey/sdk-server package which is designed for server environments. However, the @account-kit packages (@account-kit/wallet-client, @account-kit/infra, etc.) can be used in any JavaScript environment including browsers, React Native, and Node.js