Privy

Upgrade existing Privy wallets to Smart Wallets to enable gasless transactions (EVM & Solana), batching, token swaps, and more in under 10 minutes. Keep Privy for authentication, no wallet migration needed. Add our battle-tested transaction infrastructure using EIP-7702 to upgrade your wallets to Smart Wallets:

Don’t have social login or a wallet set up yet? Start with the Smart Wallet Quickstart to create one in minutes.

Setup

Follow these steps to integrate Privy with Smart Wallets.

Installation

$npm install @account-kit/privy-integration
># or
>yarn add @account-kit/privy-integration
># or
>pnpm add @account-kit/privy-integration

Solana Support (Optional): To use useAlchemySolanaTransaction, install @solana/web3.js and import from @account-kit/privy-integration/solana. This dependency is optional—skip it if you only need EVM features.

Prerequisites: Get your keys (API key, Policy ID, Privy App ID)

  • Alchemy API Key:
  • Gas sponsorship Policy ID (Gas Manager):
    • Create a gas sponsorship policy in the dashboard and copy its Policy ID
  • Privy App ID:

Both the Smart Wallets configuration and the gas sponsorship policy must be linked to the application behind your Alchemy API key for sponsorship to work.

1. Add AlchemyProvider

1import { PrivyProvider } from "@privy-io/react-auth";
2import { AlchemyProvider } from "@account-kit/privy-integration";
3
4function App() {
5 return (
6 <PrivyProvider appId="your-privy-app-id" config={{}}>
7 <AlchemyProvider apiKey="your-alchemy-api-key" policyId="your-gas-policy-id">
8 <YourApp />
9 </AlchemyProvider>
10 </PrivyProvider>
11 );
12}

Props for AlchemyProvider:

  • apiKey | jwt | rpcUrl (pick a valid transport config)
  • policyId (optional) for EVM gas sponsorship
  • solanaPolicyId (optional) for Solana gas sponsorship
  • solanaRpcUrl (optional) for Solana RPC URL (separate from EVM)
  • disableSponsorship (optional) to opt-out per default
  • accountAuthMode (optional) - 'eip7702' (default, recommended) or 'owner' for a traditional smart account

2. Send Gasless Transactions

1import { useAlchemySendTransaction } from "@account-kit/privy-integration";
2
3function SendButtons() {
4 const { sendTransaction, isLoading } = useAlchemySendTransaction();
5
6 const single = async () =>
7 await sendTransaction({ to: "0x...", data: "0x...", value: "0x0" });
8
9 const batch = async () =>
10 await sendTransaction([
11 { to: "0x...", data: "0x..." },
12 { to: "0x...", data: "0x..." },
13 ]);
14
15 return (
16 <>
17 <button onClick={single} disabled={isLoading}>Send</button>
18 <button onClick={batch} disabled={isLoading}>Send Batch</button>
19 </>
20 );
21}

Control sponsorship per call:

1await sendTransaction({ to: "0x...", data: "0x..." });
2await sendTransaction({ to: "0x...", data: "0x..." }, { disableSponsorship: true });

3. Execute Token Swaps

1import { useAlchemyPrepareSwap, useAlchemySubmitSwap } from "@account-kit/privy-integration";
2
3function SwapButton() {
4 const { prepareSwap } = useAlchemyPrepareSwap();
5 const { submitSwap, isLoading } = useAlchemySubmitSwap();
6
7 const handleSwap = async () => {
8 const prepared = await prepareSwap({
9 fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
10 toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum mainnet
11 fromAmount: "0xde0b6b3a7640000",
12 });
13 await submitSwap(prepared);
14 };
15
16 return <button onClick={handleSwap} disabled={isLoading}>Swap</button>;
17}

4. Send Solana Transactions

1import { useAlchemySolanaTransaction } from "@account-kit/privy-integration/solana";
2
3function SolanaButton() {
4 const { sendTransactionAsync, isPending } = useAlchemySolanaTransaction({
5 rpcUrl: "https://solana-mainnet.g.alchemy.com/v2/your-api-key",
6 policyId: "your-solana-policy-id", // optional
7 });
8
9 const handleTransfer = async () => {
10 const result = await sendTransactionAsync({
11 transfer: {
12 amount: 1_000_000_000, // 1 SOL in lamports
13 toAddress: "recipient-base58-address",
14 },
15 });
16 console.log("Transaction hash:", result.hash);
17 };
18
19 return <button onClick={handleTransfer} disabled={isPending}>Send SOL</button>;
20}

Advanced: Access the Smart Wallet Client

1import { useAlchemyClient } from "@account-kit/privy-integration";
2
3function Advanced() {
4 const { getClient } = useAlchemyClient();
5
6 const doOp = async () => {
7 const { client, account } = await getClient();
8 await client.sendCalls({
9 from: account.address,
10 calls: [{ to: "0x...", data: "0x" }],
11 capabilities: { eip7702Auth: true, paymasterService: { policyId: "your-policy-id" } },
12 });
13 };
14
15 return <button onClick={doOp}>Advanced Op</button>;
16}

Authorization Modes

The integration supports two modes via accountAuthMode:

  • 'eip7702' (default, recommended): Uses EIP-7702 to delegate the Privy wallet to a smart account. No deployment needed, funds stay at the wallet address.
  • 'owner': Uses a traditional smart account with Privy wallet as owner. The smart account has a separate address. Use for environments without EIP-7702 support.
1// Default (EIP-7702) - recommended
2<AlchemyProvider apiKey="...">
3 <YourApp />
4</AlchemyProvider>
5
6// Traditional smart account mode
7<AlchemyProvider apiKey="..." accountAuthMode="owner">
8 <YourApp />
9</AlchemyProvider>

Getting the Smart Account Address (owner mode):

When using owner mode, access the smart account address (different from the Privy signer address):

1import { useAlchemyClient } from "@account-kit/privy-integration";
2
3function MyComponent() {
4 const { getClient } = useAlchemyClient();
5
6 const getAddress = async () => {
7 const { account } = await getClient();
8 console.log("Smart account:", account.address);
9 // Different from Privy signer address in owner mode
10 };
11}

Notes

  • EVM: EIP-7702 (default) delegates your wallet to a smart account at send time. Alternatively, use accountAuthMode="owner" for a traditional smart account.
  • Solana: Gas sponsorship via Alchemy’s fee payer service for Privy’s embedded Solana wallets.
  • The hooks API mirrors Privy’s transaction hooks for easy migration.