Skip to main content

Migration Path

This guide aims to cover the process for migrating from lower-level Turnkey SDK (i.e. @turnkey/{ http, api-key-stamper, webauthn-stamper, iframe-stamper }) libraries, to our more recent abstractions: @turnkey/{ sdk-browser, sdk-server, sdk-react }

Why migrate?

Turnkey’s low-level libraries allow developers to get as close to the bare Turnkey metals as possible, allowing you to specify all request parameters. While some may desire this configurability, it does have a slight cost of convenience. Enter our higher-order libraries — @turnkey/{ sdk-browser, sdk-server, sdk-react } abstract away the details that most developers don’t need to configure, enabling you to focus more on business logic and less on configuration.

How to migrate?

In short, this depends on your use case. Here are some example paths:

Turnkey in the server

If you’re using Turnkey in a backend setting, you’re most likely using a combination of @turnkey/http and @turnkey/api-key-stamper. The transition to using @turnkey/sdk-server is fairly straightforward: just bring your API key details, and you’ll be able to reduce the amount of code you need to include.

Turnkey on the client

In a browser setting, you’re most likely using @turnkey/http and @turnkey/api-key-stamper and/or @turnkey/iframe-stamper. If you’re using NextJS or React in general, you’ll benefit from using @turnkey/sdk-react.

We’ve included some details on making these transitions below:

Examples

Turnkey in the server

To illustrate the difference, here’s an example creating a new Ethereum wallet via a combination of @turnkey/http and @turnkey/api-key-stamper:

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.API_PUBLIC_KEY!,
apiPrivateKey: process.env.API_PRIVATE_KEY!,
}),
);

const activityResponse = await turnkeyClient.createWallet({
type: "ACTIVITY_TYPE_CREATE_WALLET",
timestampMs: String(Date.now()),
organizationId: process.env.ORGANIZATION_ID!,
parameters: {
walletName: "ETH Wallet",
accounts: [
{
curve: "CURVE_SECP256K1",
pathFormat: "PATH_FORMAT_BIP32",
path: "m/44'/60'/0'/0/0",
addressFormat: "ADDRESS_FORMAT_ETHEREUM",
},
],
},
});

const newAddress =
activityResponse.activity.result.createWalletResult?.addresses[0];

And here’s how you might do the same with just @turnkey/sdk-server:

import { Turnkey, DEFAULT_ETHEREUM_ACCOUNTS } from "@turnkey/sdk-server";

const turnkeyClient = new Turnkey({
apiBaseUrl: "https://api.turnkey.com",
apiPublicKey: process.env.API_PUBLIC_KEY!,
apiPrivateKey: process.env.API_PRIVATE_KEY!,
defaultOrganizationId: process.env.ORGANIZATION_ID!,
});

const { walletId, addresses } = await turnkeyClient.apiClient().createWallet({
walletName: "ETH Wallet",
accounts: DEFAULT_ETHEREUM_ACCOUNTS,
});

const newAddress = addresses[0];

As you can see, much less boilerplate and dealing with low-level details such as activity types and nested results.

Turnkey on the client

Here’s a similar illustration, starting with an initial combination of @turnkey/http and @turnkey/webauthn-stamper

import { TurnkeyClient } from "@turnkey/http";
import { WebauthnStamper } from "@turnkey/webauthn-stamper";

const turnkeyClient = new TurnkeyClient(
{ baseUrl: "https://api.turnkey.com" },
new WebauthnStamper({
rpId: "localhost", // for testing locally
}),
);

const activityResponse = await turnkeyClient.createWallet({
type: "ACTIVITY_TYPE_CREATE_WALLET",
timestampMs: String(Date.now()),
organizationId: process.env.ORGANIZATION_ID!,
parameters: {
walletName: "ETH Wallet",
accounts: [
{
curve: "CURVE_SECP256K1",
pathFormat: "PATH_FORMAT_BIP32",
path: "m/44'/60'/0'/0/0",
addressFormat: "ADDRESS_FORMAT_ETHEREUM",
},
],
},
});

const newAddress =
activityResponse.activity.result.createWalletResult?.addresses[0];

And here’s how you might do the same with just @turnkey/sdk-react:

import { TurnkeyProvider } from "@turnkey/sdk-react";

// Configure once in the root of your app (e.g. _app.tsx)
const turnkeyConfig = {
apiBaseUrl: "https://api.turnkey.com",
defaultOrganizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID!,
rpId: process.env.NEXT_PUBLIC_RPID!,
iframeUrl: "https://auth.turnkey.com",
};

// Wrap your app in the provider
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
<TurnkeyProvider
config={turnkeyConfig}
>
{children}
</TurnkeyProvider>
</body>
</html>
);
}

// ... and now you can use PasskeyClient anywhere in your app (no need to re-initialize each time!)

const { passkeyClient } = useTurnkey();

const { walletId, addresses } = await passkeyClient!.createWallet({
walletName: "ETH Wallet",
accounts: DEFAULT_ETHEREUM_ACCOUNTS,
});

const newAddress = addresses[0];

For examples of the new SDK library paradigm in practice, please refer to our Demo Wallet (code can be found here). Another detailed example, specifically around Email Recovery, can be found here.