> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating embedded wallets from Alchemy to Privy

This guide walks through migrating your app and users' embedded wallets from Alchemy Account Kit to Privy. The migration SDK handles re-authenticating users with Alchemy, securely exporting their private keys, and importing them into Privy.

After migration, users log in with Privy while keeping the same wallet addresses and assets. Transactions and sponsorship continue through Alchemy infrastructure.

<Warning>
  June 1 is the cutoff for new signups and sends through the Alchemy React package. After June 1,
  Alchemy only allows logins to support migration.
</Warning>

## Overview

1. Set up Privy and replace Alchemy auth by swapping `@account-kit/react` for `@privy-io/react-auth`.
2. Reconnect sending and gas sponsorship by wiring Privy wallets to Alchemy transaction infrastructure.
3. Install the migration SDK to migrate wallet keys when users log in.
4. Deploy the updated app, export users from Alchemy, and import users into Privy.

## Step 1: Set up Privy and replace Alchemy auth

Replace Alchemy auth SDKs with Privy to support login and signup.

1. [Set up your organization in Privy](https://docs.privy.io/basics/get-started/organization).
2. Follow [React SDK setup instructions](https://docs.privy.io/basics/react/installation).
3. Enable the same login methods your users used with Alchemy, such as email, Google, and passkeys.

### Step 1a: Install Privy and remove Alchemy auth packages

```bash theme={"system"}
npm install @privy-io/react-auth
npm uninstall @account-kit/react
```

### Step 1b: Replace the Alchemy provider with `PrivyProvider`

Remove `AlchemyAccountProvider` (and `cookieToInitialState` for SSR), then wrap your app with `PrivyProvider`.

<Warning>
  Ensure you set createOnLogin to "user-without-wallet", and that the plugin for wallet creation is
  set as follows.
</Warning>

```tsx theme={"system"}
import {PrivyProvider, createWalletCreationOnLoginPlugin, User} from '@privy-io/react-auth';

function Providers({children}) {
  // Add custom logic to only create a new embedded wallet
  const walletCreationPluginOptions = {
    shouldCreateWallet: ({user}: {user: User}) =>
      user.customMetadata?.['alchemy_org_id'] === undefined
  };

  return (
    <PrivyProvider
      appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID}
      config={{
        plugins: [createWalletCreationOnLoginPlugin(walletCreationPluginOptions)],
        embeddedWallets: {
          ethereum: {
            createOnLogin: 'users-without-wallets'
          }
        }
      }}
    >
      {children}
    </PrivyProvider>
  );
}
```

### Step 1c: Replace Alchemy hooks with Privy equivalents

Update all imports from `@account-kit/react`.

| Before (Alchemy)                 | After (Privy)                                           |
| -------------------------------- | ------------------------------------------------------- |
| `useSignerStatus().isConnected`  | `usePrivy().authenticated`                              |
| `useAuthModal().openAuthModal()` | `usePrivy().login()`                                    |
| `useLogout()`                    | `usePrivy().logout` (function, not hook)                |
| `useUser()`                      | `usePrivy().user` (`email` is at `user.email?.address`) |
| `useSmartAccountClient()`        | Use `@alchemy/wallet-apis` in step 2                    |
| `useSendUserOperation()`         | `client.sendCalls()` in step 2                          |
| `cookieToInitialState` (SSR)     | Remove                                                  |

At this point, your app should compile, and users can log in with Privy. Transaction sending is not yet wired.

## Step 2: Reconnect sending and gas sponsorship

Wire Privy wallets to Alchemy transaction infrastructure so gasless transactions, batching, and existing send flows continue to work. This will now use Alchemy SDK v5 with Wallet APIs.

Full guide for Privy + Alchemy:

* [Alchemy: Privy signer integration guide](https://www.alchemy.com/docs/wallets/third-party/signers/privy)

More on Alchemy SDK v5:

* [Alchemy Wallets documentation](https://www.alchemy.com/docs/wallets)

Notes:

* The client defaults to EIP-7702 and delegates the Privy wallet at send time.
* For ERC-4337 mode, request an account before sending and include the account address in `sendCalls`.
* If your app previously used ERC-4337 (with assets directly in smart accounts), follow the EIP-7702 guide for non-7702 mode. Add an extra call to `wallet_requestAccount` before sending.

### Prerequisites

* **Alchemy API key**: From the [Alchemy Dashboard](https://dashboard.alchemy.com/apps). This must be the same app that has your Smart Wallets configuration and gas policy linked.
* **Gas sponsorship policy ID**: From the [Gas Manager dashboard](https://dashboard.alchemy.com/gas-manager).

### Step 2a: Install transaction SDK

```bash theme={"system"}
npm install @alchemy/wallet-apis
```

### Step 2b: Get the Privy signer

Use `toViemAccount` to convert a Privy embedded wallet into a viem `LocalAccount`.

```tsx theme={"system"}
import {toViemAccount, useWallets} from '@privy-io/react-auth';
import {useEffect, useState} from 'react';
import type {LocalAccount} from 'viem';

const usePrivySigner = () => {
  const {
    wallets: [wallet]
  } = useWallets();
  const [signer, setSigner] = useState<LocalAccount>();

  useEffect(() => {
    if (!wallet || signer) return;
    toViemAccount({wallet}).then(setSigner);
  }, [wallet, signer]);

  return signer;
};
```

What's happening: `useWallets()` returns the user's Privy wallets. `toViemAccount` converts the Privy wallet into a standard viem `LocalAccount`, which is what the Alchemy wallet client needs as a signer. The signer is `undefined` until the wallet is ready, so your UI should handle that loading state.

### Step 2c: Send gasless transactions

```tsx theme={"system"}
import {useMemo, useCallback} from 'react';
import {zeroAddress} from 'viem';
import {createSmartWalletClient, alchemyWalletTransport} from '@alchemy/wallet-apis';
import {arbitrumSepolia} from 'viem/chains';
import type {LocalAccount} from 'viem';

function SendTransaction({signer}: {signer: LocalAccount}) {
  const client = useMemo(
    () =>
      createSmartWalletClient({
        signer,
        transport: alchemyWalletTransport({
          apiKey: 'YOUR_ALCHEMY_API_KEY'
        }),
        chain: arbitrumSepolia,
        paymaster: {
          policyId: 'YOUR_GAS_MANAGER_POLICY_ID'
        }
      }),
    [signer]
  );

  const handleSend = useCallback(async () => {
    // If using 4337 accounts and not 7702, add a call to request account
    // const { address } = await client.requestAccount({ creationHint: { accountType: "sma-b" } });

    const {id} = await client.sendCalls({
      // If non 7702, you'll need to add the address to the from: field in sendCalls
      calls: [{to: zeroAddress, value: BigInt(0), data: '0x'}]
    });

    const result = await client.waitForCallsStatus({id});
    console.log(`Transaction hash: ${result.receipts?.[0]?.transactionHash}`);
  }, [client]);

  return <button onClick={handleSend}>Send transaction</button>;
}
```

At this point, your app should fully work with Privy auth and Alchemy gas sponsorship. Users can log in, sign, and send gasless transactions. Next, migrate existing users' wallet keys.

## Step 3: Install the React Migration SDK

The migration SDK is a drop-in React component that detects users who need wallet migration, prompts re-authentication through Alchemy, and transfers key material from Alchemy TEE to Privy TEE with end-to-end encryption.

### How it works

When a user logs in, the SDK:

1. Detects migration need by checking Alchemy migration metadata and missing embedded wallets.
2. Shows a migration modal that prompts re-authentication with the original Alchemy method.
3. Migrates wallets by exporting keys from Alchemy TEE and importing into Privy TEE.
4. Confirms completion with a success view and auto-close.

Users keep the same wallet addresses. No seed phrases and no manual steps are required.

### Supported authentication methods

* Email (OTP)
* Google
* Twitter/X
* GitHub
* Discord
* Passkey (non-anonymous, for example passkeys linked to email)

### Step 3a: Install migration packages

```bash theme={"system"}
npm install @privy-io/alchemy-migration @account-kit/react @account-kit/infra
```

### Step 3b: Import migration styles

```tsx theme={"system"}
import '@privy-io/alchemy-migration/styles.css';
```

### Step 3c: Create the Alchemy config

Use `createMigrationConfig` and include all auth methods your users used with Alchemy.

The `auth.sections` field should include all login methods originally used with Alchemy. The SDK automatically detects which method each user needs and shows the right sign-in flow.

This remains true even if your app used custom UI with `useAuthenticate`. Use the same config pattern below and include all auth methods your users used. Only pre-built components are supported for the migration flow.

```tsx theme={"system"}
import {createMigrationConfig} from '@privy-io/alchemy-migration';
import {alchemy} from '@account-kit/infra';
import {sepolia} from '@account-kit/infra';

const alchemyConfig = createMigrationConfig(
  {
    transport: alchemy({apiKey: 'YOUR_ALCHEMY_API_KEY'}),
    chain: sepolia, // Use your app's chain
    ssr: true
  },
  {
    auth: {
      sections: [
        [{type: 'email'}],
        [
          {type: 'passkey'},
          {type: 'social', authProviderId: 'google', mode: 'popup'}
          // Add all other providers your users signed up with
        ]
      ]
    }
  }
);
```

### Step 3d: Set up `MigrationProvider`

Wrap `MigrationProvider` inside `PrivyProvider`.

```tsx theme={"system"}
import {PrivyProvider} from '@privy-io/react-auth';
import {MigrationProvider} from '@privy-io/alchemy-migration';

function App({children}) {
  return (
    <PrivyProvider appId="YOUR_PRIVY_APP_ID">
      <MigrationProvider
        alchemyConfig={alchemyConfig}
        privyAppId="YOUR_PRIVY_APP_ID"
        privyClientId="YOUR_PRIVY_APP_CLIENT_ID"
      >
        {children}
      </MigrationProvider>
    </PrivyProvider>
  );
}
```

### `MigrationProvider` props

| Prop                  | Type                          | Required | Description                                 |
| --------------------- | ----------------------------- | -------- | ------------------------------------------- |
| `alchemyConfig`       | `AlchemyAccountsConfigWithUI` | Yes      | Config created with `createMigrationConfig` |
| `privyAppId`          | `string`                      | Yes      | Privy app ID                                |
| `privyClientId`       | `string`                      | Yes      | Privy app client ID                         |
| `showDebugButton`     | `boolean`                     | No       | Show debug buttons for testing (dev only)   |
| `skipOrgVerification` | `boolean`                     | No       | Skip Alchemy org ID verification (dev only) |

### After migration is complete

After users have migrated wallets (for example, after at least one login):

* Remove `@privy-io/alchemy-migration`, `@account-kit/react`, and `@account-kit/infra`.
* Keep `createOnLogin: 'users-without-wallets'` for standard new-user wallet creation.
* Remove `MigrationProvider`.

## Step 4: Deploy the updated app, export users, and import into Privy

To detect users who need key migration, Privy needs imported user data and linked auth methods from Alchemy. This import does not move keys.

<Warning>
  Critical sequencing: user export from Alchemy must happen at deployment time or immediately after
  deployment. If export runs first, users who sign up before deployment can be missing from Privy.
</Warning>

Recommended sequence: **Deploy → Export → Import**.

1. Pause new signups briefly.
2. Deploy your updated app with Privy auth.
3. Immediately export users from Alchemy.
4. Import users into Privy.
5. Resume normal operations.

### Step 4a: Export users from Alchemy

1. Open the Alchemy dashboard and choose your app.
2. Go to **Wallets** → **Export**.
3. Select **Export users** and download the JSON file.

### Step 4b: Import users into Privy

1. Open the Privy dashboard and choose your target app.
2. Go to **User management** → **Users** → **Import users**.
3. Upload the JSON exported from Alchemy.

Privy creates accounts with linked auth methods and migration metadata. The migration SDK then detects which users need key transfer.

## FAQ

### Do users need to do anything?

Users only need to sign in once with their original Alchemy login method.

### Will wallet addresses change?

No. Wallet addresses remain the same.

### Are private keys ever exposed?

No. Keys move from Alchemy TEE to Privy TEE through end-to-end encryption.

### What if a user has both Ethereum and Solana wallets?

Both are migrated automatically and independently.

### What if a user already has a Privy wallet?

Only missing wallets are migrated. If a user already has an ETH wallet but not SOL, only SOL is migrated.

### Can migration be tested before launch?

Yes. Set `showDebugButton: true` on `MigrationProvider` and validate with a test Alchemy app.

### What if deployment and export cannot happen at exactly the same time?

Deploy first, then export. Once your app is on Privy, no new users are being created in Alchemy, so the export captures everyone. If export happens before deploy and there is a gap, run a second export after deploying to capture users who signed up in between.

### The migration modal never appears

Common causes:

1. Users were not imported from Alchemy before first Privy login.
2. Wallet auto-creation ran before migration checks.
3. The user already has all required wallets in Privy.
