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.
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.
Overview
- Set up Privy and replace Alchemy auth by swapping
@account-kit/react for @privy-io/react-auth.
- Reconnect sending and gas sponsorship by wiring Privy wallets to Alchemy transaction infrastructure.
- Install the migration SDK to migrate wallet keys when users log in.
- 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.
- Set up your organization in Privy.
- Follow React SDK setup instructions.
- 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
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.
Ensure you set createOnLogin to “user-without-wallet”, and that the plugin for wallet creation is
set as follows.
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.
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:
More on Alchemy SDK v5:
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. 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.
Step 2a: Install transaction SDK
npm install @alchemy/wallet-apis
Step 2b: Get the Privy signer
Use toViemAccount to convert a Privy embedded wallet into a viem LocalAccount.
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
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:
- Detects migration need by checking Alchemy migration metadata and missing embedded wallets.
- Shows a migration modal that prompts re-authentication with the original Alchemy method.
- Migrates wallets by exporting keys from Alchemy TEE and importing into Privy TEE.
- 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
npm install @privy-io/alchemy-migration @account-kit/react @account-kit/infra
Step 3b: Import migration styles
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.
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.
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.
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.
Recommended sequence: Deploy → Export → Import.
- Pause new signups briefly.
- Deploy your updated app with Privy auth.
- Immediately export users from Alchemy.
- Import users into Privy.
- Resume normal operations.
Step 4a: Export users from Alchemy
- Open the Alchemy dashboard and choose your app.
- Go to Wallets → Export.
- Select Export users and download the JSON file.
Step 4b: Import users into Privy
- Open the Privy dashboard and choose your target app.
- Go to User management → Users → Import users.
- 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:
- Users were not imported from Alchemy before first Privy login.
- Wallet auto-creation ran before migration checks.
- The user already has all required wallets in Privy.