Privy now allows you to natively use smart wallet for a better developer experience. Check out the
docs here.
- ZeroDev
- Safe
- Pimlico
- Biconomy
- AccountKit
Account abstraction with ZeroDev
ZeroDev is a toolkit for creating ERC-4337-compatible smart wallets for your users, using the user’s EOA as the smart wallet’s signer. This allows you to easily add Account Abstraction features into your app.You can easily integrate ZeroDev alongside Privy to create smart wallets from your user’s embedded or external wallets, allowing you to enhance your app with gas sponsorship, batched transactions, and more!Read below to learn how to configure your app to create smart wallets for all your users!1. Install the required dependencies from Privy and ZeroDev
In your app’s repository, install the required dependencies from Privy and ZeroDev, as well as thepermissionless, and viem libraries:Report incorrect code
Copy
Ask AI
npm i @privy-io/react-auth @zerodev/sdk @zerodev/ecdsa-validator permissionless viem
2. Sign up for a ZeroDev account and get your project ID
Visit the ZeroDev dashboard and sign up for a new account if you do not have one already. Set up a new project for your required chain(s) and retrieve your ZeroDev project ID, as well as your paymaster and bundler URLs for the project.Within this Dashboard, you can also configure settings for gas sponsorship and other ZeroDev features!2. Configure your app’s Privy settings
First, follow the instructions in the Privy Quickstart to get your app set up with a basic Privy integration.Next, set Add confirmation modals to “off” in your app’s [Embedded wallets] page in the Privy dashboard. This will configure Privy to not show its default UIs when your user must sign messages or send transactions. Instead, we recommend you use your own custom UIs for showing users the user operationss they sign.Lastly, update theconfig.embeddedWallets.createOnLogin property of your PrivyProvider to 'users-without-wallets'.This will configure Privy to create an embedded wallet for users logging in via a web2 method (email, phone, socials), ensuring that all of your users have a wallet that can be used as an EOA.Your PrivyProvider should then look like:Report incorrect code
Copy
Ask AI
<PrivyProvider
appId='insert-your-privy-app-id'
config={{
embeddedWallets: {
createOnLogin: 'users-without-wallets',
}
...insertTheRestOfYourPrivyProviderConfig
}}
>
{/* Your app's components */}
</PrivyProvider>
3. Create a smart account for your user
You’ll now create a smart account for your user, using the Privy embedded wallet (an EOA) as the signer.To do so, when the user logs in, first find the user’s embedded wallet from Privy’suseWallets hook, and get its EIP1193 provider. You can find embedded wallet by finding the only entry in the useWallets array with a walletClientType of 'privy'.Report incorrect code
Copy
Ask AI
import {useWallets} from '@privy-io/react-auth';
import {sepolia} from 'viem/chains'; // Replace this with the chain used by your application
import {createWalletClient, custom} from 'viem';
...
// Find the embedded wallet and get its EIP1193 provider
const {wallets} = useWallets();
const embeddedWallet = wallets.find((wallet) => (wallet.walletClientType === 'privy'));
const provider = await embeddedWallet.getEthereumProvider();
provider to the toSimpleSmartAccount method from permissionless to create a SmartAccount. This signer corresponds to the user’s embedded wallet and authorizes actions for the user’s smart account.Report incorrect code
Copy
Ask AI
import {createSmartAccountClient} from 'permissionless';
import {toSimpleSmartAccount} from 'permissionless/accounts';
import {createPublicClient, http, zeroAddress} from 'viem';
import {sepolia} from 'viem/chains';
import {createPimlicoClient} from 'permissionless/clients/pimlico';
import {entryPoint07Address} from 'viem/account-abstraction';
const publicClient = createPublicClient({
chain: sepolia, // or whatever chain you are using
transport: http()
});
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=<PIMLICO_API_KEY>`;
const pimlicoClient = createPimlicoClient({
transport: http(pimlicoUrl),
entryPoint: {
address: entryPoint07Address,
version: '0.7'
}
});
// Use the EIP1193 `provider` from Privy to create a `SmartAccount`
const kernelSmartAccount = await toKernelSmartAccount({
owners: [provider],
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: '0.7'
}
});
SmartAccount from above, initialize a smart account client for the user like so:Report incorrect code
Copy
Ask AI
import {sepolia} from 'viem/chains'; // Replace this with the chain used by your application
import {createPublicClient, http} from 'viem';
import {ENTRYPOINT_ADDRESS_V07} from 'permissionless';
import {createZeroDevPaymasterClient, createKernelAccount, createKernelAccountClient} from "@zerodev/sdk";
import {signerToEcdsaValidator} from "@zerodev/ecdsa-validator";
...
// Initialize a viem public client on your app's desired network
const publicClient = createPublicClient({
transport: http(sepolia.rpcUrls.default.http[0]),
})
// Create a ZeroDev ECDSA validator from the `smartAccountSigner` from above and your `publicClient`
const ecdsaValidator = await signerToEcdsaValidator(publicClient, {
signer: kernelSmartAccount,
entryPoint: ENTRYPOINT_ADDRESS_V07,
})
// Create a Kernel account from the ECDSA validator
const account = await createKernelAccount(publicClient, {
plugins: {
sudo: ecdsaValidator,
},
entryPoint: ENTRYPOINT_ADDRESS_V07,
});
// Create a Kernel account client to send user operations from the smart account
const kernelClient = createKernelAccountClient({
account,
chain: sepolia,
entryPoint: ENTRYPOINT_ADDRESS_V07,
bundlerTransport: http('insert-your-bundler-RPC-from-the-dashboard'),
middleware: {
sponsorUserOperation: async ({ userOperation }) => {
const zerodevPaymaster = createZeroDevPaymasterClient({
chain: sepolia,
entryPoint: ENTRYPOINT_ADDRESS_V07,
transport: http('insert-your-paymaster-RPC-to-the-dashboard'),
})
return zerodevPaymaster.sponsorUserOperation({
userOperation,
entryPoint: ENTRYPOINT_ADDRESS_V07,
})
}
}
})
kernelClient is a drop-in replacement for a viem Wallet Client, and requests to the smart account can be made using viem’s API.You can also store the user’s smart account address on Privy’s user object. See this
guide for more.
4. Send user operations (transactions) from the smart account
Now that your users have Kernel (ZeroDev) smart accounts, they can now send UserOperations from their smart account. This is the AA analog to sending a transaction.To send a user operation from a user’s smart account, use the Kernel client’ssendTransaction method.Report incorrect code
Copy
Ask AI
const txHash = await kernelClient.sendTransaction({
to: 'TO_ADDRESS',
value: VALUE, // default to 0
data: '0xDATA' // default to 0x
});
sendTransaction method, and will automatically apply any smart account configurations (e.g. gas sponsorship) you configure in the middleware before sending the transaction.That’s it! You’ve configured your app to create smart wallets for all of your users, and can seamlessly add in AA features like gas sponsorship, batched transactions, and more. 🎉Account Abstraction with Safe
Safe Smart Accounts is a product by Safe for creating ERC-4337-compatible smart accounts for your users, using the user’s EOA as the smart account’s signer. The product builds off of the smart contract infrastructure powering the widely-used Safe wallet and allows you to easily add Account Abstraction and other Safe features into your app.To create Safe smart accounts for your users, simply follow our Pimlico integration guide. Safe does not operate its own paymaster and bundler infrastructure, and developers generally compose the Safe smart account with paymasters or bundlers from Pimlico.When integrating Safe alongside Pimlico, the only change from the default Pimlico setup is to replace thetoSimpleSmartAccount method with toSafeSmartAccount. This modifies the setup to deploy a Safe smart account for the user instead of a simple smart account.For example, when initializing the smart account from a viem wallet client for the user’s Privy embedded wallet, you should update your code as follows:Report incorrect code
Copy
Ask AI
import {createSmartAccountClient} from 'permissionless';
import {toSimpleSmartAccount} from 'permissionless/accounts';
import {toSafeSmartAccount} from 'permissionless/accounts';
import {createPimlicoClient} from 'permissionless/clients/pimlico';
import {createPublicClient, http} from 'viem';
import {entryPoint07Address} from 'viem/account-abstraction';
// Create a viem public client for RPC calls
const publicClient = createPublicClient({
chain: sepolia, // Replace this with the chain of your app
transport: http()
});
// Initialize the smart account for the user
const simpleSmartAccount = await toSimpleSmartAccount({
client: publicClient,
owner: privyClient.account,
factoryAddress: '0x9406Cc6185a346906296840746125a0E44976454'
});
const safeSmartAccount = await toSafeSmartAccount({
owners: [privyClient.account],
safeVersion: '1.4.1',
entryPoint: {
address: ENTRYPOINT_ADDRESS_V07,
version: '0.7'
}
});
// Create the Paymaster for gas sponsorship using the API key from your Pimlico dashboard
const pimlicoPaymaster = createPimlicoClient({
transport: http('https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY')
});
// Create the SmartAccountClient for requesting signatures and transactions (RPCs)
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
account: safeSmartAccount,
chain: sepolia, // Replace this with the chain for your app
bundlerTransport: http('https://api.pimlico.io/v1/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY'),
paymaster: pimlicoPaymaster // If your app uses a paymaster for gas sponsorship
});
You can also store the user’s smart account address on Privy’s user object. See this guide for more.
Account Abstraction with permissionless.js and Pimlico
permissionless.js is a modular and extensible TypeScript library originally created by Pimlico for deploying and managing ERC-4337 smart accounts. You can use this library for all major smart account implementations, including Safe, Kernel, Biconomy, SimpleAccount, and more.You can easily integrate permissionless.js alongside Privy to create smart wallets from your user’s embedded or external wallets, allowing you to enhance your app with gas sponsorship, batched transactions, and more.Just follow the steps below!Want to see an end-to-end integration of Privy with
permissionless.js? Check out our example
app!1. Install Privy and permissionless.js
In your project, install the necessary dependencies from Privy, Pimlico, and viem:Report incorrect code
Copy
Ask AI
npm i @privy-io/react-auth permissionless viem
2. Sign up for a Pimlico account and create an API key.
To send transactions from smart accounts, you will need access to a bundler. We also recommend using paymaster to sponsor your user’s transactions.To get a bundler and paymaster for your application, sign up for a Pimlico account and copy down your API key for the rest of this guide!3. Configure your app’s PrivyProvider
First, follow the instructions in the Privy Quickstart to get your app set up with Privy.Next, set Add confirmation modals to “off” in your app’s Embedded wallets page in the Privy dashboard. This will configure Privy to not show its default UIs when your user must sign messages or send transactions. Instead, we recommend you use your own custom UIs for showing users the UserOperations they sign.Then, update the config.embeddedWallets.createOnLogin property of your PrivyProvider to 'users-without-wallets'.This will configure Privy to create an embedded wallet for users logging in via a web2 method (email, phone, socials), ensuring that all of your users have a wallet that can be used as an EOA.Your PrivyProvider should then look like:Report incorrect code
Copy
Ask AI
<PrivyProvider
appId='insert-your-privy-app-id'
config={{
/* Replace this with your desired login methods */
loginMethods: ['email', 'wallet'],
/* Replace this with your desired appearance configuration */
appearance: {
theme: 'light',
accentColor: '#676FFF',
logo: 'your-logo-url'
}
embeddedWallets: {
createOnLogin: 'users-without-wallets',
showWalletUIs: false
}
}}
>
{/* Your app's components */}
</PrivyProvider>
4. Create a smart account for your user
You’ll now create a smart account for your user, using the Privy embedded wallet (an EOA) as the signer.To do so, when the user logs in, find the user’s embedded wallet from Privy’suseWallets hook, and create a viem WalletClient for it. You can find embedded wallet by finding the only entry in the useWallets array with a walletClientType of 'privy'.Report incorrect code
Copy
Ask AI
import {useWallets} from '@privy-io/react-auth';
import {sepolia} from 'viem/chains'; // Replace this with the chain used by your application
import {createWalletClient, custom} from 'viem';
...
// Find the embedded wallet and get its EIP1193 provider
const {wallets} = useWallets();
const embeddedWallet = wallets.find((wallet) => (wallet.walletClientType === 'privy'));
const eip1193provider = await embeddedWallet.getEthereumProvider();
// Create a viem WalletClient from the embedded wallet's EIP1193 provider
// This will be used as the signer for the user's smart account
const privyClient = createWalletClient({
account: embeddedWallet.address,
chain: sepolia, // Replace this with the chain used by your application
transport: custom(eip1193provider)
});
privyClient from above, create a SmartAccountClient which represents the user’s smart account. In creating the smart account, you can also specify which smart account implementation you’d like to use. Possible options include: Safe, Kernel, Biconomy, and SimpleAccount (the original smart account implementation).If your app also uses a paymaster to sponsor gas on behalf of users, you can also specify which paymaster to use by calling the createPimlicoClient method from permissionless with the RPC URL in your Pimlico Dashboard.Report incorrect code
Copy
Ask AI
import {createSmartAccountClient} from 'permissionless';
import {toSimpleSmartAccount} from 'permissionless/accounts';
import {createPimlicoClient} from 'permissionless/clients/pimlico';
import {createPublicClient, http} from 'viem';
import {sepolia} from 'viem/chains';
import {entryPoint07Address} from 'viem/account-abstraction';
import {useWallets} from '@privy-io/react-auth';
const {wallets} = useWallets();
const embeddedWallet = wallets.find((wallet) => wallet.walletClientType === 'privy');
const provider = await embeddedWallet.getEthereumProvider();
// Create a viem public client for RPC calls
const publicClient = createPublicClient({
chain: sepolia, // Replace this with the chain of your app
transport: http()
});
// Initialize the smart account for the user
const simpleSmartAccount = await toSimpleSmartAccount({
client: publicClient,
owner: {request: provider.request},
entryPoint: {
address: entryPoint07Address,
version: '0.7'
}
});
// Create the Paymaster for gas sponsorship using the API key from your Pimlico dashboard
const pimlicoPaymaster = createPimlicoClient({
transport: http('https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY')
});
// Create the SmartAccountClient for requesting signatures and transactions (RPCs)
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: sepolia, // Replace this with the chain for your app
bundlerTransport: http('https://api.pimlico.io/v1/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY'),
paymaster: pimlicoPaymaster // If your app uses a paymaster for gas sponsorship
});
YOUR_PIMLICO_API_KEY with your Pimlico API key that you created in step 2!You can also store the user’s smart account address on Privy’s user object. See this
guide for more.
5. Send transactions from the smart account
You can now send transactions using thesendTransaction method on the SmartAccountClient object, like so:Report incorrect code
Copy
Ask AI
const txHash = await smartAccountClient.sendTransaction({
account: smartAccountClient.account,
to: 'zero-address',
data: '0x',
value: BigInt(0)
});
SmartAccountClient functions as a drop-in replacement for viem’s wallet client - you can use the same interfaces with the SmartAccountClient object!That’s it! Once you’ve created smart accounts for your users, you can easily add AA features into your application like gas sponsorship, batched transactions, and more. 🎉 To learn more about what you can do with smart accounts, check out the permissionless.js guide.Account abstraction with Biconomy
Biconomy has an updated guide for using the new Biconomy
Nexus smart accounts. Please refer to
the Biconomy guide for the most up-to-date
information.
1. Install Privy and Biconomy
In your app’s repository, install the@privy-io/react-auth SDK from Privy and the @biconomy/account SDK from Biconomy:Report incorrect code
Copy
Ask AI
npm i @privy-io/react-auth @biconomy/account
2. Configure your app’s PrivyProvider
First, follow the instructions in the Privy Quickstart to get your app set up with Privy.Next, set Add confirmation modals to “off” in your app’s Embedded wallets page in the Privy dashboard. This will configure Privy to not show its default UIs when your user must sign messages or send transactions. Instead, we recommend you use your own custom UIs for showing users the UserOperations they sign.Then, update the config.embeddedWallets.createOnLogin property of your PrivyProvider to 'users-without-wallets'.This will configure Privy to create an embedded wallet for users logging in via a web2 method (email, phone, socials), ensuring that all of your users have a wallet that can be used as an EOA.Your PrivyProvider should then look like:Report incorrect code
Copy
Ask AI
<PrivyProvider
appId='insert-your-privy-app-id'
config={{
embeddedWallets: {
createOnLogin: 'users-without-wallets',
}
...insertTheRestOfYourPrivyProviderConfig
}}
>
{/* Your app's components */}
</PrivyProvider>
3. Configure your Biconomy bundler and paymaster
Go to the Biconomy Dashboard and configure a Paymaster and a Bundler for your app. Make sure these correspond to the desired network for your user’s smart accounts.Once you’ve configured a Paymaster, you can also deposit funds into your app’s gas tank and configure specific policies for gas sponsorship.Save the bundler URL and paymaster API key for your project, as you will need those values later.4. Initialize your users’ smart accounts
When users log into your app, Privy provisions each user an embedded wallet, which is an EOA. In order to leverage the features of Biconomy’s account abstraction, each user also needs a Biconomy smart account. You can provision Biconomy smart accounts for each user by assigning their embedded wallet as a signer for their smart account.To start, after a user logs in, find the user’s embedded wallet from Privy’suseWallets hook, and switch its network to your app’s target network. You can find embedded wallet by finding the only entry in the useWallets array with a walletClientType of 'privy'.Report incorrect code
Copy
Ask AI
import {useWallets} from '@privy-io/react-auth';
...
// Find the embedded wallet
const {wallets} = useWallets();
const embeddedWallet = wallets.find((wallet) => (wallet.walletClientType === 'privy'));
// Switch the embedded wallet to your target network
// Replace '80001' with your desired chain ID.
await embeddedWallet.switchChain(80001);
createSmartAccountClient method:Report incorrect code
Copy
Ask AI
import { createSmartAccountClient } from "@biconomy/account";
...
// Get an ethers provider and signer for the user's embedded wallet
const provider = await embeddedWallet.getEthereumProvider();
const ethersProvider = new ethers.providers.Web3Provider(provider);
const ethersSigner = ethersProvider.getSigner()
const smartAccount = await createSmartAccountClient({
signer: ethersSigner,
bundlerUrl: 'your-bundler-url-from-the-biconomy-dashboard',
biconomyPaymasterApiKey: 'your-paymaster-api-key-from-the-biconomy-dashboard'
});
You can also store the user’s smart account address on Privy’s user object. See this
guide for more.
5. Send transactions from the smart account
Now that your users have Biconomy smart accounts, they can now send transaction from their smart account.To send a transaction from a user’s smart account, use Biconomy’ssendTransaction method. An example of sending a transaction to mint an NFT gaslessly is below:Report incorrect code
Copy
Ask AI
// Initialize an ethers JsonRpcProvider for your network
const provider = new ethers.providers.JsonRpcProvider(`insert-rpc-url-for-your-network`);
// Initialize an ethers contract instance for your NFT
const nft = new ethers.Contract('insert-your-NFT-address', insertYourNftAbi, provider);
// Construct a Transaction for the minting transaction
const mintTransaction = await nft.populateTransaction.mint!('insert-the-smart-account-address');
// `smartAccount` is the Biconomy smart account we initialized above
const mintTx = {
to: 'insert-your-NFT-address',
data: mintTransaction.data
};
// Send transaction to mempool, to mint NFT gaslessly
const userOpResponse = await smartAccount.sendTransaction(mintTx, {
paymasterServiceData: {mode: PaymasterMode.SPONSORED}
});
const {transactionHash} = await userOpResponse.waitForTxHash();
console.log('Transaction Hash', transactionHash);
const userOpReceipt = await userOpResponse.wait();
if (userOpReceipt.success == 'true') {
console.log('UserOp receipt', userOpReceipt);
console.log('Transaction receipt', userOpReceipt.receipt);
}
Account Abstraction with AccountKit
AccountKit is a toolkit by Alchemy for creating ERC-4337-compatible smart accounts for your users, using the user’s EOA as the smart account’s signer. This allows you to easily add Account Abstraction features into your app.You can easily integrate AccountKit alongside Privy to create smart wallets from your user’s embedded or external wallets, allowing you to enhance your app with gas sponsorship, batched transactions, and more!Read below to learn how to configure your app to create smart wallets for all your users!1. Install Privy and AccountKit
Install the@privy-io/react-auth SDK from Privy, the @account-kit/privy-integration SDKs from Alchemy, and viem:Report incorrect code
Copy
Ask AI
npm i @privy-io/react-auth @account-kit/privy-integration viem
2. Configure your app’s PrivyProvider
First, follow the instructions in the Privy Quickstart to get your app set up with Privy.Next, set Add confirmation modals to “off” in your app’s Embedded wallets page in the Privy dashboard. This will configure Privy to not show its default UIs when your user must sign messages or send transactions. Instead, we recommend you use your own custom UIs for showing users the UserOperations they sign.Then, update the config.embeddedWallets.createOnLogin property of your PrivyProvider to 'users-without-wallets'.This will configure Privy to create an embedded wallet for users logging in via a web2 method (email, phone, socials), ensuring that all of your users have a wallet that can be used as an EOA.Lastly, add the AlchemyProvider as a child of your PrivyProvider, passing in your Alchemy API key and gas policy ID.Your PrivyProvider should then look like:Report incorrect code
Copy
Ask AI
<PrivyProvider
appId='insert-your-privy-app-id'
config={{
/* Replace this with your desired login methods */
loginMethods: ['email', 'wallet'],
/* Replace this with your desired appearance configuration */
appearance: {
theme: 'light',
accentColor: '#676FFF',
logo: 'your-logo-url'
}
embeddedWallets: {
createOnLogin: 'users-without-wallets',
showWalletUIs: false
},
// Import your desired chain from `viem/chains` and pass it to `defaultChain`
defaultChain: sepolia,
}}
>
<AlchemyProvider apiKey="your-alchemy-api-key" policyId="your-gas-policy-id">
{/* Your app's components */}
</AlchemyProvider>
</PrivyProvider>
3. Send gasless transactions with Alchemy’s AccountKit SDK
Now that your users have smart accounts created for them automatically, you can use Alchemy’s AccountKit SDK to send gasless transactions from their smart accounts.Report incorrect code
Copy
Ask AI
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>
</>
);
}

