Upgrade existing Privy wallets to Wallet APIs to enable gasless transactions, batching, and more in under 10 minutes. Keep Privy for authentication, no wallet migration needed. Add battle-tested transaction infrastructure using EIP-7702 to upgrade your wallets to Wallet APIs:
- #1 gas abstraction infrastructure on the market
- 370M+ sponsored transactions
- 99.9% SLAs
- Trusted by Worldcoin, JP Morgan, Gensyn, and more
Follow these steps to use Privy signers with the Wallet Client SDK. EVM and Solana share the same setup; each chain family has its own send-transaction flow below.
npm install @alchemy/wallet-apis @privy-io/react-auth viemFor Solana support, see Send Solana transactions below for the additional peer dependencies.
- Alchemy API key:
- Go to the Alchemy Dashboard
- Create or select an app and copy the API key
- Gas sponsorship Policy ID (Gas Manager):
- Create a gas sponsorship policy in the dashboard and copy its Policy ID
- Privy App ID:
- Go to the Privy Dashboard
- Create or select an app and copy the 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 wallet 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.
The example below uses @solana/kit and @solana-program/system to build the transfer call:
npm install @solana/kit @solana-program/systemEnable Solana embedded wallets in your PrivyProvider config:
<PrivyProvider
appId="your-privy-app-id"
config={{
embeddedWallets: {
ethereum: { createOnLogin: "all-users" },
solana: { createOnLogin: "all-users" },
showWalletUIs: false,
},
}}
>
<YourApp />
</PrivyProvider>Use useWallets from @privy-io/react-auth/solana to get the connected Solana wallet. The returned wallet can be passed directly as the Wallet APIs Solana signer.
import {
alchemyWalletTransport,
createSmartWalletClient,
} from "@alchemy/wallet-apis";
import { useWallets } from "@privy-io/react-auth/solana";
import { useCallback, useMemo } from "react";
import { transferSolCall } from "./utils";
export function SendSponsoredSolanaTransaction() {
const { wallets } = useWallets();
const signer = wallets[0];
const client = useMemo(() => {
if (!signer) return undefined;
return createSmartWalletClient({
transport: alchemyWalletTransport({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
chain: "solana:devnet",
signer,
paymaster: { policyId: "YOUR_SOLANA_POLICY_ID" },
});
}, [signer]);
const handleSend = useCallback(async () => {
if (!client) return;
const { id } = await client.sendCalls({
calls: [
transferSolCall({
from: client.solanaAccount,
to: client.solanaAccount,
lamports: 0n,
}),
],
});
const result = await client.waitForCallsStatus({ id });
console.log("Sponsored Solana transaction status:", result.status);
}, [client]);
return (
<button onClick={handleSend} disabled={!client}>
Send sponsored Solana transaction
</button>
);
}- Solana sponsorship requires a Solana gas sponsorship policy. See Solana sponsorship.
- For non-Privy Solana signer sources (Phantom,
@solana/wallet-adapter-react, raw keypairs, Wallet Standard), see Solana signer adapters.