Alchemy Logo

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.

Follow these steps to integrate Privy with Smart Wallets.

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.

  • 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.

import { PrivyProvider } from "@privy-io/react-auth";
import { AlchemyProvider } from "@account-kit/privy-integration";
 
function App() {
  return (
    <PrivyProvider appId="your-privy-app-id" config={{}}>
      <AlchemyProvider apiKey="your-alchemy-api-key" policyId="your-gas-policy-id">
        <YourApp />
      </AlchemyProvider>
    </PrivyProvider>
  );
}

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
  • walletAddress (optional) - specify which wallet address to use if user has multiple wallets (defaults to first wallet)

import { useAlchemySendTransaction } from "@account-kit/privy-integration";
 
function SendButtons() {
  const { sendTransaction, isLoading } = useAlchemySendTransaction();
 
  const single = async () =>
    await sendTransaction({ to: "0x...", data: "0x...", value: "0x0" });
 
  const batch = async () =>
    await sendTransaction([
      { to: "0x...", data: "0x..." },
      { to: "0x...", data: "0x..." },
    ]);
 
  return (
    <>
      <button onClick={single} disabled={isLoading}>Send</button>
      <button onClick={batch} disabled={isLoading}>Send Batch</button>
    </>
  );
}

Control sponsorship per call:

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

import { useAlchemyPrepareSwap, useAlchemySubmitSwap } from "@account-kit/privy-integration";
 
function SwapButton() {
  const { prepareSwap } = useAlchemyPrepareSwap();
  const { submitSwap, isLoading } = useAlchemySubmitSwap();
 
  const handleSwap = async () => {
    const prepared = await prepareSwap({
      fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
      toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum mainnet
      fromAmount: "0xde0b6b3a7640000",
    });
    await submitSwap(prepared);
  };
 
  return <button onClick={handleSwap} disabled={isLoading}>Swap</button>;
}

import { useAlchemySolanaTransaction } from "@account-kit/privy-integration/solana";
 
function SolanaButton() {
  const { sendTransactionAsync, isPending } = useAlchemySolanaTransaction({
    rpcUrl: "https://solana-mainnet.g.alchemy.com/v2/your-api-key",
    policyId: "your-solana-policy-id", // optional
  });
 
  const handleTransfer = async () => {
    const result = await sendTransactionAsync({
      transfer: {
        amount: 1_000_000_000, // 1 SOL in lamports
        toAddress: "recipient-base58-address",
      },
    });
    console.log("Transaction hash:", result.hash);
  };
 
  return <button onClick={handleTransfer} disabled={isPending}>Send SOL</button>;
}

import { useAlchemyClient } from "@account-kit/privy-integration";
 
function Advanced() {
  const { getClient } = useAlchemyClient();
 
  const doOp = async () => {
    const { client, account } = await getClient();
    await client.sendCalls({
      from: account.address,
      calls: [{ to: "0x...", data: "0x" }],
      capabilities: { eip7702Auth: true, paymasterService: { policyId: "your-policy-id" } },
    });
  };
 
  return <button onClick={doOp}>Advanced Op</button>;
}

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.
// Default (EIP-7702) - recommended
<AlchemyProvider apiKey="...">
  <YourApp />
</AlchemyProvider>
 
// Traditional smart account mode
<AlchemyProvider apiKey="..." accountAuthMode="owner">
  <YourApp />
</AlchemyProvider>

Getting the Smart Account Address (owner mode):

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

import { useAlchemyClient } from "@account-kit/privy-integration";
 
function MyComponent() {
  const { getClient } = useAlchemyClient();
 
  const getAddress = async () => {
    const { account } = await getClient();
    console.log("Smart account:", account.address);
    // Different from Privy signer address in owner mode
  };
}

  • 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.
Was this page helpful?