# Migrate from Turnkey

This guide walks you through migrating from Turnkey to Openfort. The migration involves updating your SDK dependencies, provider configuration, authentication flows, and wallet interactions.

:::warning
This migration generates new wallet addresses for your users. Users need to transfer any assets from their Turnkey wallets to their new Openfort wallets after migration.
:::

## Before you begin

Before starting the migration:

1. Complete the [Openfort quickstart](/docs/products/embedded-wallet/react) to understand the SDK structure.
2. Obtain your [API keys](/docs/configuration/api-keys) from the Openfort dashboard.
3. Set up a [recovery endpoint](/docs/products/embedded-wallet/server/automatic-recovery-session) (for automatic recovery).

### Feature mapping

| Turnkey feature | Openfort equivalent | Notes |
|-----------------|---------------------|-------|
| Email OTP authentication | `AuthProvider.EMAIL_OTP` | OTP-based authentication |
| Social login (Google, Apple) | `AuthProvider.GOOGLE`, `AuthProvider.APPLE` | Configure in dashboard |
| External wallets | `AuthProvider.WALLET` | WalletConnect support |
| `handleLogin` modal | `OpenfortButton` | Pre-built UI component |

::::steps

### Install Openfort dependencies

Remove Turnkey packages and install Openfort with its peer dependencies:

:::code-group

```sh [npm]
npm uninstall @turnkey/react-wallet-kit @turnkey/sdk-react @turnkey/viem @turnkey/ethers @turnkey/core
npm install @openfort/react wagmi viem@^2.22.0 @tanstack/react-query
```

```sh [yarn]
yarn remove @turnkey/react-wallet-kit @turnkey/sdk-react @turnkey/viem @turnkey/ethers @turnkey/core
yarn add @openfort/react wagmi viem@^2.22.0 @tanstack/react-query
```

```sh [pnpm]
pnpm remove @turnkey/react-wallet-kit @turnkey/sdk-react @turnkey/viem @turnkey/ethers @turnkey/core
pnpm add @openfort/react wagmi viem@^2.22.0 @tanstack/react-query
```

:::

### Update provider configuration

Replace the Turnkey provider with Openfort's layered provider structure.

**Before (Turnkey):**

```tsx [Providers.tsx (Turnkey)]
import { TurnkeyProvider } from "@turnkey/react-wallet-kit";
import "@turnkey/react-wallet-kit/styles.css";

const turnkeyConfig = {
  organizationId: process.env.NEXT_PUBLIC_ORGANIZATION_ID!,
  authProxyConfigId: process.env.NEXT_PUBLIC_AUTH_PROXY_CONFIG_ID!,
  auth: {
    oauthConfig: {
      googleClientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
      appleClientId: process.env.NEXT_PUBLIC_APPLE_CLIENT_ID,
    },
  },
};

function App() {
  return (
    <TurnkeyProvider
      config={turnkeyConfig}
      callbacks={{
        onAuthenticationSuccess: ({ session }) => {
          console.log("Authenticated:", session);
        },
      }}
    >
      <YourApp />
    </TurnkeyProvider>
  );
}
```

**After (Openfort):**

```tsx [Providers.tsx (Openfort)]
import {
  AuthProvider,
  OpenfortProvider,
  RecoveryMethod,
} from "@openfort/react";
import { getDefaultConfig, OpenfortWagmiBridge } from "@openfort/react/wagmi";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { WagmiProvider, createConfig } from "wagmi";
import { mainnet, sepolia } from "viem/chains";

const config = createConfig(
  getDefaultConfig({
    appName: "Your App Name",
    chains: [mainnet, sepolia],  // [!code highlight]
    ssr: true,
  })
);

const queryClient = new QueryClient();

function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      <WagmiProvider config={config}>
        <OpenfortWagmiBridge>
        <OpenfortProvider
          publishableKey="YOUR_OPENFORT_PUBLISHABLE_KEY"  // [!code highlight]
          walletConfig={{
            shieldPublishableKey: "YOUR_SHIELD_PUBLISHABLE_KEY",  // [!code highlight]
            createEncryptedSessionEndpoint: "YOUR_RECOVERY_ENDPOINT",  // [!code highlight]
          }}
          uiConfig={{
            authProviders: [
              AuthProvider.EMAIL_OTP,
              AuthProvider.GOOGLE,
              AuthProvider.WALLET,
            ],
            walletRecovery: {
              defaultMethod: RecoveryMethod.AUTOMATIC,  // [!code highlight]
            },
          }}
        >
          {children}
        </OpenfortProvider>
        </OpenfortWagmiBridge>
      </WagmiProvider>
    </QueryClientProvider>
  );
}
```

### Update authentication code

Replace Turnkey authentication hooks with Openfort equivalents.

**Before (Turnkey):**

```tsx [LoginButton.tsx (Turnkey)]
import { useTurnkey, AuthState } from "@turnkey/react-wallet-kit";

function LoginButton() {
  const { handleLogin, authState, user } = useTurnkey();

  if (authState === AuthState.Authenticated) {
    return (
      <div>
        <p>Welcome, {user?.userName}</p>
        <button onClick={() => window.location.reload()}>Logout</button>
      </div>
    );
  }

  return <button onClick={handleLogin}>Login / Sign Up</button>;
}
```

**After (Openfort):**

```tsx [LoginButton.tsx (Openfort)]
import { useUser, useSignOut, OpenfortButton } from "@openfort/react";

function LoginButton() {
  const { user, isLoading, isAuthenticated } = useUser();  // [!code highlight]
  const { signOut } = useSignOut();

  if (isLoading) return <div>Loading...</div>;  // [!code highlight]

  if (isAuthenticated) {
    return (
      <div>
        <p>Welcome, {user?.email}</p>
        <button onClick={signOut}>Sign Out</button>
      </div>
    );
  }

  // Use the pre-built button component  // [!code highlight]
  return <OpenfortButton />;  // [!code highlight]
}
```

For custom authentication (email OTP or passkey):

**Before (Turnkey):**

```tsx [CustomAuth.tsx (Turnkey)]
import { useTurnkey } from "@turnkey/react-wallet-kit";

function CustomAuth() {
  const { loginWithPasskey, signUpWithPasskey } = useTurnkey();

  return (
    <div>
      <button onClick={loginWithPasskey}>Login with Passkey</button>
      <button onClick={signUpWithPasskey}>Sign Up with Passkey</button>
    </div>
  );
}
```

**After (Openfort):**

```tsx [CustomLogin.tsx (Openfort)]
import { useEmailOtpAuth, useOAuth, AuthProvider } from "@openfort/react";  // [!code highlight]
import { useState } from "react";

function CustomLogin() {
  const { requestEmailOtp, signInEmailOtp, isRequesting, isLoading } = useEmailOtpAuth();  // [!code highlight]
  const { initOAuth } = useOAuth();  // [!code highlight]
  const [email, setEmail] = useState("");
  const [otp, setOtp] = useState("");
  const [otpSent, setOtpSent] = useState(false);  // [!code highlight]

  const handleRequestOtp = async () => {
    await requestEmailOtp({ email });  // [!code highlight]
    setOtpSent(true);  // [!code highlight]
  };

  const handleSignIn = async () => {
    await signInEmailOtp({ email, otp });  // [!code highlight]
  };

  return (
    <div>
      <input
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      {otpSent && (
        <input
          value={otp}
          onChange={(e) => setOtp(e.target.value)}
          placeholder="Enter OTP"
        />
      )}
      {!otpSent ? (
        <button onClick={handleRequestOtp} disabled={isRequesting}>
          {isRequesting ? "Sending..." : "Send OTP"}
        </button>
      ) : (
        <button onClick={handleSignIn} disabled={isLoading}>
          {isLoading ? "Verifying..." : "Verify"}
        </button>
      )}
      <button onClick={() => initOAuth({ provider: AuthProvider.GOOGLE })}>  // [!code highlight]
        Sign in with Google
      </button>
    </div>
  );
}
```

### Update wallet access code

Replace Turnkey wallet access with Wagmi hooks.

**Before (Turnkey):**

```tsx [WalletInfo.tsx (Turnkey)]
import { useTurnkey } from "@turnkey/react-wallet-kit";

function WalletInfo() {
  const { wallets } = useTurnkey();
  const wallet = wallets?.[0];
  const address = wallet?.accounts?.[0]?.address;

  if (!address) return <p>No wallet</p>;

  return <p>Wallet: {address}</p>;
}
```

**After (Openfort):**

```tsx [WalletInfo.tsx (Openfort)]
import { useAccount } from "wagmi";  // [!code highlight]

function WalletInfo() {
  const { address, isConnected } = useAccount();  // [!code highlight]

  if (!isConnected) return <p>No wallet</p>;

  return <p>Wallet: {address}</p>;  // Smart wallet address
}
```

### Update transaction code

Replace Turnkey signing with Wagmi hooks.

**Before (Turnkey):**

```tsx [SendTransaction.tsx (Turnkey)]
import { useTurnkey } from "@turnkey/react-wallet-kit";

function SendTransaction() {
  const { wallets, signTransaction } = useTurnkey();

  const send = async () => {
    const walletAccount = wallets[0]?.accounts[0];
    if (!walletAccount) return;

    const signature = await signTransaction({
      walletAccount,
      unsignedTransaction: "0x...", // RLP-encoded unsigned transaction
      transactionType: "TRANSACTION_TYPE_ETHEREUM",
    });
    console.log("Transaction:", signature);
  };

  return <button onClick={send}>Send Transaction</button>;
}
```

**After (Openfort):**

```tsx [SendTransaction.tsx (Openfort)]
import { useSendTransaction, useAccount } from "wagmi";  // [!code highlight]
import { parseEther } from "viem";

function SendTransaction() {
  const { address } = useAccount();
  const { sendTransaction, isPending } = useSendTransaction();  // [!code highlight]

  const send = () => {
    sendTransaction({
      to: "0xRecipientAddress",
      value: parseEther("0.001"),
    });
  };

  return (
    <button onClick={send} disabled={isPending || !address}>
      {isPending ? "Sending..." : "Send Transaction"}
    </button>
  );
}
```

### Update message signing

Replace Turnkey message signing with Wagmi hooks.

**Before (Turnkey):**

```tsx [SignMessage.tsx (Turnkey)]
import { useTurnkey } from "@turnkey/react-wallet-kit";

function SignMessage() {
  const { wallets, signMessage } = useTurnkey();

  const sign = async () => {
    const walletAccount = wallets[0]?.accounts[0];
    if (!walletAccount) return;

    const signature = await signMessage({
      walletAccount,
      message: "Hello World",
    });
    console.log("Signature:", signature);
  };

  return <button onClick={sign}>Sign Message</button>;
}
```

**After (Openfort):**

```tsx [SignMessage.tsx (Openfort)]
import { useSignMessage } from "wagmi";  // [!code highlight]

function SignMessage() {
  const { signMessage, isPending } = useSignMessage();  // [!code highlight]

  const sign = () => {
    signMessage({ message: "Hello World" });
  };

  return (
    <button onClick={sign} disabled={isPending}>
      {isPending ? "Signing..." : "Sign Message"}
    </button>
  );
}
```

### Remove remaining Turnkey dependencies

Verify no Turnkey packages remain in your `package.json`:

```sh
# Check for any remaining Turnkey packages
npm ls 2>/dev/null | grep turnkey || echo "All Turnkey packages removed"
```

If you skipped the uninstall in step 1, remove all Turnkey packages now:

:::code-group

```sh [npm]
npm uninstall @turnkey/react-wallet-kit @turnkey/sdk-react @turnkey/viem @turnkey/ethers @turnkey/core
```

```sh [yarn]
yarn remove @turnkey/react-wallet-kit @turnkey/sdk-react @turnkey/viem @turnkey/ethers @turnkey/core
```

```sh [pnpm]
pnpm remove @turnkey/react-wallet-kit @turnkey/sdk-react @turnkey/viem @turnkey/ethers @turnkey/core
```

:::

::::

## Hook mapping reference

| Turnkey hook/method | Openfort/Wagmi equivalent |
|---------------------|---------------------------|
| `useTurnkey()` | `useUser` + `useAccount` (wagmi) |
| `user` from `useTurnkey` | `useUser` |
| `handleLogin()` | `OpenfortButton` or individual auth hooks |
| `loginWithPasskey()` / `signUpWithPasskey()` | `OpenfortButton` or `useEmailOtpAuth`, `useOAuth` |
| `wallets` from `useTurnkey` | `useAccount` (wagmi) |
| `signMessage()` | `useSignMessage` (wagmi) |
| `signTransaction()` | `useSendTransaction` (wagmi) |
| `authState` | `useUser` (`isLoading`, `isAuthenticated`, `user`) |

## Considerations

### Session handling

Turnkey sessions are invalidated during migration. Users need to re-authenticate with Openfort.

### Wallet addresses

Users receive new smart wallet addresses. Communicate this change to users before migration so they can:

1. Export any assets from their Turnkey wallet.
2. Transfer assets to their new Openfort wallet after authentication.

### Key management differences

Turnkey uses a secure enclave infrastructure with Auth Proxy for key management. Openfort provides three recovery options:

| Recovery method | Description | Backend required |
|-----------------|-------------|------------------|
| Automatic | Seamless, backend-managed recovery | Yes |
| Password | User-set password for recovery | No |
| Passkey | Biometric/device authentication | No |

### Chain configuration

Both Turnkey and Openfort support multiple chains. Update your chain configuration in the Wagmi config:

```tsx [config.ts]
import { getDefaultConfig } from "@openfort/react/wagmi";
import { mainnet, polygon, arbitrum, base } from "viem/chains";
import { createConfig } from "wagmi";

const config = createConfig(
  getDefaultConfig({
    appName: "Your App",
    chains: [mainnet, polygon, arbitrum, base],
  })
);
```

## Next steps

<HoverCardLayout>
  <HoverCardLink description="Configure your embedded wallet settings." href="/products/embedded-wallet/react/wallet" title="Wallet configuration" subtitle="Wallet setup" icon={Wallet} color="#3B82F6" />

  <HoverCardLink description="Cover gas fees for your users." href="/configuration/gas-sponsorship" title="Gas sponsorship" subtitle="Paymaster setup" icon={Fuel} color="#10B981" />

  <HoverCardLink description="Delegate signing with session keys." href="/products/embedded-wallet/react/wallet/actions/session-keys" title="Session keys" subtitle="Delegated signing" icon={Key} color="#8B5CF6" />
</HoverCardLayout>
