Skip to content
Alchemy Logo

Privy

@alchemy/wallet-apis (v5.x.x) is currently in beta but is the recommended replacement for @account-kit/wallet-client (v4.x.x). If you run into any issues, please reach out.

Upgrade existing Privy wallets to Smart Wallets to enable gasless transactions, batching, 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:

Follow these steps to use Privy signers with the Wallet Client SDK.

npm install @alchemy/wallet-apis @privy-io/react-auth viem

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

The gas sponsorship policy must be linked to the application behind your Alchemy API key for sponsorship to work.

Wrap your app with PrivyProvider from @privy-io/react-auth:

import { PrivyProvider } from "@privy-io/react-auth";
 
export function App() {
  return (
    <PrivyProvider
      appId="your-privy-app-id"
      config={{
        embeddedWallets: {
          ethereum: {
            createOnLogin: "all-users",
          },
          showWalletUIs: false,
        },
      }}
    >
      <YourApp />
    </PrivyProvider>
  );
}

Use toViemAccount and useWallets from @privy-io/react-auth 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;
};

Use usePrivy to manage authentication state and conditionally render your wallet UI once the user is logged in:

import { usePrivy } from "@privy-io/react-auth";
 
function PrivyWallet() {
  const { ready, authenticated, login, logout } = usePrivy();
  const signer = usePrivySigner();
 
  if (!ready) return <p>Loading...</p>;
 
  if (!authenticated) {
    return <button onClick={() => login()}>Login with Privy</button>;
  }
 
  return (
    <div>
      <button onClick={() => logout()}>Logout</button>
      {signer ? <SendTransaction signer={signer} /> : <p>Loading signer...</p>}
    </div>
  );
}

Pass the Privy signer to createSmartWalletClient and use it to send transactions:

import { useMemo, useCallback } from "react";
import { zeroAddress } from "viem";
import { createSmartWalletClient, alchemyWalletTransport } from "@alchemy/wallet-apis";
import { arbitrumSepolia } from "viem/chains";
 
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 () => {
    // Send the transaction
    const { id } = await client.sendCalls({
      calls: [{ to: zeroAddress, value: BigInt(0), data: "0x" }],
    });
 
    // Wait for the transaction to be confirmed
    const result = await client.waitForCallsStatus({ id });
    console.log(`Transaction hash: ${result.receipts?.[0]?.transactionHash}`);
  }, [client]);
 
  return <button onClick={handleSend}>Send transaction</button>;
}

  • The client defaults to EIP-7702, which delegates the Privy wallet to a smart account at send time. No deployment or wallet migration needed. See the EIP-7702 guide for non-7702 mode.
  • See the Sponsor gas guide for more on gas sponsorship configuration.
Was this page helpful?