Turnkey Integration Guide
Turnkey is secure, non-custodial wallet infrastructure that allows users to generate wallets scoped to your application via Email or WebAuthn. Turnkey leverages secure enclaves and a proprietary policy engine; this novel security architecture ensures that key material is only decrypted within an enclave and any signing is governed by your application's policies. This is great for enabling a secure, flexible experience for your users that can be powerfully enhanced by the benefits of Account Abstraction (gas sponsorship, batching, etc).
Combining Turnkey with Account Kit allows you to create a magical UX for your users. Use Turnkey via the aa-signers
package to generate embedded wallets at scale, and then leverage aa-alchemy
to create smart accounts for your users!
Integration
Create a Turnkey Account
Create an account and API keys on Turnkey's Dashboard.
Install the SDK
Using TurnkeySigner
in the aa-signers
package requires installation of the @turnkey/http
and @turnkey/viem
dependencies. aa-signers
lists them as optional dependencies.
Every request to Turnkey must be signed using a stamper. Turnkey supports multiple stampers including @turnkey/webauthn-stamper
to sign requests with Passkeys or WebAuthn devices, @turnkey/iframe-stamper
with Email, and @turnkey/api-key-stamper
with API keys.
npm i -s @turnkey/http
npm i -s @turnkey/viem
yarn add @turnkey/http
yarn add @turnkey/viem
Create a TurnkeySigner
Next, setup the Turnkey SDK and create an authenticated TurnkeySigner
using the aa-signers
package:
import {
TurnkeySigner,
TurnkeySubOrganization,
} from "@alchemy/aa-signers/turnkey";
import { WebauthnStamper } from "@turnkey/webauthn-stamper";
import { http } from "viem";
const TURNKEY_BASE_URL = "https://api.turnkey.com";
export const createTurnkeySigner = async () => {
const turnkeySigner = new TurnkeySigner({
apiUrl: TURNKEY_BASE_URL,
// API Key, WebAuthn, or Email Auth [stampers](https://docs.turnkey.com/category/api-design)
// must sign all requests to Turnkey.
stamper: new WebauthnStamper({
rpId: "your.app.xyz",
}),
});
await turnkeySigner.authenticate({
resolveSubOrganization: async () => {
return new TurnkeySubOrganization({
subOrganizationId: "12345678-1234-1234-1234-123456789abc",
signWith: "0x1234567890123456789012345678901234567890",
});
},
transport: http("https://eth-sepolia.g.alchemy.com/v2/ALCHEMY_API_KEY"),
});
return turnkeySigner;
};
Use it with Light Account
Let's see it in action with aa-alchemy
and ModularAccount
from aa-accounts
:
import { createModularAccountAlchemyClient } from "@alchemy/aa-alchemy";
import { sepolia } from "@alchemy/aa-core";
import { createTurnkeySigner } from "./turnkey";
const chain = sepolia;
const provider = await createModularAccountAlchemyClient({
apiKey: "ALCHEMY_API_KEY",
chain,
signer: await createTurnkeySigner(),
});
import {
TurnkeySigner,
TurnkeySubOrganization,
} from "@alchemy/aa-signers/turnkey";
import { WebauthnStamper } from "@turnkey/webauthn-stamper";
import { http } from "viem";
const TURNKEY_BASE_URL = "https://api.turnkey.com";
export const createTurnkeySigner = async () => {
const turnkeySigner = new TurnkeySigner({
apiUrl: TURNKEY_BASE_URL,
// API Key, WebAuthn, or Email Auth [stampers](https://docs.turnkey.com/category/api-design)
// must sign all requests to Turnkey.
stamper: new WebauthnStamper({
rpId: "your.app.xyz",
}),
});
await turnkeySigner.authenticate({
resolveSubOrganization: async () => {
return new TurnkeySubOrganization({
subOrganizationId: "12345678-1234-1234-1234-123456789abc",
signWith: "0x1234567890123456789012345678901234567890",
});
},
transport: http("https://eth-sepolia.g.alchemy.com/v2/ALCHEMY_API_KEY"),
});
return turnkeySigner;
};