# Pimlico
> Pimlico is the world's most popular account abstraction infrastructure platform
## Migration from Stackup \[This guide is intended for users migrating from Stackup to Pimlico]
Welcome to Pimlico! We're excited to help you in your transition from Stackup. This guide will help you understand the migration process, the changes you'll need to make to your existing setup, and give a brief overview of new features you will have access to.
### Migration Process for Startup and Developer Tiers
If you were previously subscribed to Stackup's Startup or Developer tiers, your information has been pre-loaded onto our system for a smooth migration process.
Follow these steps to migrate to Pimlico:
:::steps
#### Go to the Pimlico dashboard
The Pimlico dashboard can be found at [https://dashboard.pimlico.io](https://dashboard.pimlico.io).

#### Sign up using the email address previously used on Stackup
We have used your email address you used for Stackup to pre-load your information for a seamless migration process.

#### Enter your card details
Once signed in, you will be prompted to enter your card details and confirm your new plan. Enter your card information to continue your subscription seamlessly.
:::
After completing these steps, your Pimlico account will be fully set up and ready to use.
### Pimlico Overview
With Pimlico, you'll have largely the same functionality as Stackup, with a few small differences and several new features.
#### Dashboard
The Pimlico dashboard offers a wide range of features to help streamline your smart account development.

Dashboard features include:
* [Rich analytics and metrics](https://dashboard.pimlico.io/metrics)
* [Logs and requests overview](https://dashboard.pimlico.io/logs)
* [Debugging tools](/guides/how-to/debugging/dropped-user-operations#how-to-debug-dropped-user-operations)
* [Gas grants](https://dashboard.pimlico.io/sponsorship-policies)
* [Sponsorship policies](/guides/how-to/sponsorship-policies)
#### API Credit system
Pimlico uses a transparent API credit system, ensuring fair billing based on your actual usage. The 'worth' of each API credit is different than it is for Stackup. The API cost of each RPC call is detailed on our [pricing page](/guides/pricing).
#### API Endpoint changes
There are several API changes you may need to implement for a successful migration.
##### pimlico\_getUserOperationGasPrice
The Pimlico bundler requires gasPrices to be fetched from the `pimlico_getUserOperationGasPrice` endpoint. This is necessary to ensure the UserOperation is sent with a sufficient gas price regardless of network conditions.
##### Separate RPC for node requests
The Stackup endpoint supports both standard RPC and ERC-4337 calls, but Pimlico only supports ERC-4337. During migration, you'll need a separate RPC provider for standard calls.
#### Multichain support
The Pimlico API supports seamless multichain access. This means that the same API key and sponsorship policies (if configured) can be used on all chains. The API uses a standardized endpoint structure for all networks:
```ts
const endpoint = `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${apiKey}`
```
where `chainId` specifies the target chainId. The Pimlico bundler is currently live on 90+ chains, the full breakdown can be found on the [supported-chains page](/guides/supported-chains).
#### ERC-20 Paymaster endpoint
If you are using Stackup's `pm_getERC20TokenQuotes` endpoint, you can migrate to Pimlico's `pimlico_getTokenQuotes`. The Pimlico endpoint does not take in a user operation, however you can follow this [how-to guide](/guides/how-to/erc20-paymaster/how-to/use-paymaster) to achieve the same functionality.
#### Origin and user agent whitelist
If you were previously using Stackup's origin whitelist or user agent whitelist, you can apply the same settings by navigating to apikeys and clicking the edit button.

### FAQs
#### Do I need to enter my billing details to start using Pimlico?
Yes, you'll be prompted to enter your billing details during the migration process. This ensures your account is fully configured and ready to use Pimlico seamlessly.
#### How do I reach out if I need help?
If you run into any issues or have any questions during the migration, feel free to reach out to us at [support@pimlico.io](mailto\:support@pimlico.io) or [directly through telegram](https://t.me/kristofgazso).
## Create API Key
To use Pimlico's services, you'll need an API key. We recommend using our CLI for the quickest setup, but you can also use the dashboard.
### Using the CLI (Recommended)
The fastest way to get started is with our CLI tool. Run the following command from your project's root directory:
```bash
pnpm dlx @pimlico/cli@latest
```
This command will:
* Interactively guide you through the setup process
* Create your API key automatically
* Add it to your `.env` file for immediate use
### Using the Dashboard
Alternatively, you can create an API key through our web dashboard:
1. Go to the [Pimlico Dashboard](https://dashboard.pimlico.io/)
2. Sign up or log in to your account
3. Click **API Keys** in the sidebar
4. Click **Create API key**
5. Copy your API key and add it to your project's `.env` file
:::tip
Save your API key securely as you'll need it for all API requests. You can always retrieve it later from the [Dashboard](https://dashboard.pimlico.io/apikeys).
:::
### Next Steps
Once you have your API key:
* Add it to your `.env` file as `PIMLICO_API_KEY=your_key_here`
* Start building with our [tutorials](/guides/tutorials/)
## Getting Started with Pimlico
Welcome to Pimlico, the world's most advanced ERC-4337 account abstraction infrastructure platform. This guide will help you understand Pimlico's ecosystem and get you started with building applications using our tools and services.
### What is Pimlico?
Pimlico provides a suite of tools and services to help you build, deploy, and manage smart accounts on Ethereum and other EVM-compatible chains. Our infrastructure powers the next generation of web3 applications by making account abstraction accessible and easy to implement.
### Core Products
Pimlico offers several key products that work together to provide a complete account abstraction solution:
#### permissionless.js
A TypeScript library built on viem for interacting with ERC-4337 bundlers, paymasters, and user operations. It provides:
* High-level smart account support for popular implementations (Safe, Kernel, Biconomy, TrustWallet, etc.)
* Bundler support for all ERC-4337 actions
* Gas sponsorship capabilities
* User Operation utility functions
* Modular and extensible architecture
[Learn more about permissionless.js →](/references/permissionless)
#### Bundler
Our Alto Bundler is a performant, type-safe ERC-4337 bundler written in TypeScript that powers smart accounts by relaying user operations for fast and reliable on-chain inclusion. Key features include:
* Full type safety
* Transaction inclusion reliability
* Fast transaction inclusion speed
* Support for 100+ EVM chains
[Learn more about our Bundler →](/references/bundler)
#### Paymasters
Pimlico offers two types of paymasters to abstract away gas fees for your users:
1. **Verifying Paymaster**: Load up your off-chain Pimlico balance through our dashboard and sponsor on-chain gas fees for your users across 100+ chains.
2. **ERC-20 Paymaster**: A permissionless on-chain smart contract that lets your users pay for their own gas fees using ERC-20 tokens.
[Learn more about our Paymasters →](/references/paymaster)
{/* ### Batua
A fully customizable smart account UI component built with shadcn/ui and secured with passkeys. Batua makes it easy to add account abstraction to your application with a beautiful, ready-to-use interface.
[Learn more about Batua →](https://batua.sh/) */}
### Quick Start
The fastest way to get started with Pimlico is to follow our step-by-step tutorials:
#### Tutorial 1: Send Your First Gasless Transaction
In this tutorial, you'll learn how to:
* Set up the necessary permissionless.js clients
* Create a user operation
* Use Pimlico's verifying paymaster to sponsor gas fees
* Submit the operation on-chain with Pimlico's bundler
[Start Tutorial 1 →](/references/permissionless/tutorial/tutorial-1)
#### Tutorial 2: Pay for Gas with ERC-20 Tokens
Learn how to use Pimlico's ERC-20 paymaster to allow users to pay for their transactions with tokens instead of ETH.
[Start Tutorial 2 →](/references/permissionless/tutorial/tutorial-2)
#### Getting a Pimlico API Key
To use Pimlico's services, you'll need an API key. [Learn how to create one](/guides/create-api-key)
### Understanding Account Abstraction
If you're new to account abstraction, we recommend starting with our conceptual guides:
* [About Account Abstraction](/guides/conceptual/account-abstraction)
* [ERC-4337 Explained](/guides/conceptual/erc4337)
* [EIP-7702 Explained](/guides/eip7702)
* [ERC-7579 Explained](/guides/conceptual/erc7579)
### Choosing a Smart Account
Pimlico supports various smart account implementations. Check out our comparison guide to choose the right one for your needs:
* [Smart Account Comparison](/guides/how-to/accounts/comparison)
Or jump directly to implementation guides for specific accounts:
* [Safe Account](/guides/how-to/accounts/use-safe-account)
* [Kernel Account](/guides/how-to/accounts/use-kernel-account)
* [Biconomy Account](/guides/how-to/accounts/use-biconomy-account)
* [Simple Account](/guides/how-to/accounts/use-simple-account)
* And more...
### Advanced Topics
Once you're familiar with the basics, explore these advanced topics:
* [Dapp Gas Sponsorship](/guides/how-to/dapp-gas-sponsorship)
* [Parallel Transactions](/guides/how-to/parallel-transactions)
* [Conditional Sponsoring](/guides/how-to/paymasters/conditional-sponsoring)
### Community and Support
Join our community to get help, share your projects, and stay updated:
* Follow [@pimlicoHQ](https://twitter.com/pimlicoHQ) on Twitter for project updates
* Join the [discussions on 4337 Mafia](https://t.me/+emlONDb-lXI4ZTFh)
* Star [permissionless.js](https://github.com/pimlicolabs/permissionless.js) and our [Alto Bundler](https://github.com/pimlicolabs/alto) on GitHub
* Browse the [awesome-account-abstraction](https://github.com/4337Mafia/awesome-account-abstraction) list of resources
### Next Steps
Ready to dive deeper? Check out our How-To Guides for step-by-step instructions on implementing specific features, or explore our API References for detailed technical documentation.
## Pricing
Below is a comparison of the different pricing plans available for Pimlico. With Pimlico, you're able to launch without any upfront costs, and only pay for what you use with our pay-as-you-go plan, even for production applications with significant volume. We can also offer enterprise plans for users wishing to have a custom plan tailored to their needs. If you have any questions, please don't hesitate to [contact us through telegram](https://t.me/pimlicoHQ) or [through email](mailto\:support@pimlico.io)
| Feature |
Free
|
Pay-as-you-go
|
Enterprise (contact us)
|
| ----------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Subscription Fee | $0/mo | $0/mo, card required | Custom |
| Networks | Testnets: ✅ (all 25+ testnet chains) Mainnets: ❌ | Testnets: ✅ (all 25+ testnet chains) Mainnets: ✅ (all 25+ mainnet chains) | Testnets: ✅ (all 25+ testnet chains) Mainnets: ✅ (all 25+ mainnet chains) |
| API Requests | 1,000,000 credits / month included | 10,000,000 credits / month included, then $1 per additional 100,000 credits | Custom (incl. lower pre-paid rate) |
| Approximate User Operations | \~1,300 user operations (\~950 if sponsored) / month included | \~13,000 user operations (\~9,500 if sponsored) / month included, then \~$0.0075 per additional user operation (\~$0.0105 if sponsored) | Custom (incl. lower pre-paid rate) |
| Rate Limit | 500 requests / min | 5000 requests / min | Custom / Unlimited |
| Bundler | ✅ | ✅ | ✅ |
| Verifying Paymaster Surcharge | Testnets: Free Mainnets: N/A | Testnets: Free Mainnets: 10% | Custom |
| Billing Usage Threshold | N/A | $1,000 / month (we bill immediately once accumulated fees reach the limit) | Custom / Unlimited |
| ERC-20 Paymasters | ❌ | USDC on Ethereum, Polygon, Base, Optimism, Arbitrum | Custom (up to 300+ tokens: ✅) Custom ERC-20 tokens: ✅ |
| ERC-20 Paymaster Surcharge | N/A | 10% | Custom |
| Support | Community Support: ✅ | Community Support: ✅ Private Slack/Telegram Chat: ✅ | Community Support: ✅ Private Slack/Telegram Chat: ✅ 24x7x365 Phone: ✅ |
| Uptime SLA | ❌ | ❌ | ✅ |
### Pay-as-you-go pricing simulation
:::warning
These numbers are approximate and may vary based on the actual number of API credits used. The simulation assumes 750 API credits per unsponsored user operation and 1,050 API credits per sponsored user operation.
:::
import Slider from "../../components/slider"
### API credit breakdown
The cost of individual API requests is measured in credits, with the breakdown of credits for each method provided below.
| method | credits |
| --------------------------------- | ------- |
| pm\_sponsorUserOperation | 500 |
| eth\_sendUserOperation | 500 |
| pm\_getPaymasterData | 300 |
| pm\_getPaymasterStubData | 200 |
| eth\_estimateUserOperationGas | 200 |
| pimlico\_getTokenQuotes | 100 |
| pimlico\_getSupportedTokens | 100 |
| pimlico\_getUserOperationGasPrice | 10 |
| eth\_getUserOperationReceipt | 10 |
| eth\_getUserOperationByHash | 10 |
| pm\_validateSponsorshipPolicies | 10 |
| pimlico\_getUserOperationStatus | 5 |
| eth\_supportedEntryPoints | 1 |
| eth\_chainId | 1 |
### FAQ
#### How do we calculate the paymaster surcharge?
Here's a simplified and more fluent version of your text:
When you request paymaster data using `pm_getPaymasterData`, you need to include the user operation with the gas fees value. We sign the gas fees you specify because it’s the bundler’s responsibility to reject user operations that underpay them. Once the transaction is included on-chain, we use the `actualGasCost` based on the details in the `UserOperationEvent`.
Here’s how `entryPoint` determines the `actualGasCost` and calculates the gas price:
```javascript
const gasPrice = min(userop.maxFeePerGas, baseFee + userop.maxPriorityFeePerGas);
const actualGasCost = actualGas * gasPrice;
```
At the end of the month, we add a 10% surcharge to the `actualGasCost` and bill you. The total billed amount is:
```javascript
const billedAmount = actualGasCost * 1.1; // 10% surcharge
```
Why don’t we charge 10% based on what the bundler pays? Because we might not be the bundler—you’re free to use any bundler available. Since the paymaster pays the `gasCost` to the bundler, and there’s no guarantee that we will be the bundler, we must charge the full `actualGasCost` plus the surcharge.
#### Where do I find a gas tank to top up my paymaster?
You do not need to top up you paymaster with Pimlico. We manage the paymaster for you automatically. We track your usage and generate an invoice for you at the end of the month which you can pay with your preferred payment method.
## Supported Chains
All API calls must use the JSON-RPC format and be sent to the following URL:
```
https://api.pimlico.io/v2/target_chain/rpc?apikey=your_api_key_here
```
Where chain is either the chainId or chain name.
For example to access Base, both of these endpoints will work:
* [https://api.pimlico.io/v2/8453/rpc?apikey=your\_api\_key\_here](https://api.pimlico.io/v2/8453/rpc?apikey=your_api_key_here)
* [https://api.pimlico.io/v2/base/rpc?apikey=your\_api\_key\_here](https://api.pimlico.io/v2/base/rpc?apikey=your_api_key_here)
You can also access the public endpoint without an API key ([more info](/references/bundler/public-endpoint)).
## Pimlico supports the following chains:
import SupportedChains from "../../../data/supported-chains.md"
import ChainDetails from "../../../data/chain-details.md";
### Chain Details
Below you can find detailed information about supported entrypoints and account implementations for each chain. For each chain, the table shows which account implementations support which entrypoint versions.
## Boosted Paymaster
The Boosted paymaster is an offchain paymaster that offers faster execution times and cheaper gas fees due to requiring fewer RPC calls and requiring no paymaster data.
:::info
The Boosted Paymaster is available upon request. To activate this feature, please contact us at [support@pimlico.io](mailto\:support@pimlico.io)
:::
### How does Boosted user operations work?
Boosted user operations are sponsored user operations without paymaster data. This allows you to save on extra RPC calls and optimize the latency.
These are the RPC calls that are skipped:
* `pimlico_getUserOperationGasPrice` - we set the gas price to zero, read below for why.
* `pm_getPaymasterStubData` - since we don't have paymaster data, we don't need to call this.
* `pm_getPaymasterData` - since we don't have paymaster data, we don't need to call this.
optional:
* `eth_estimateUserOperationGas` - If your `callData` is predictable, you can hard code the gas limits and skip this call as well.
When you set `maxFeePerGas` and `maxPriorityFeePerGas` to zero, no balance is deducted from user operation's sender on chain. While we charge you off-chain at the end of the month when your bill gets generated. The charge is based on how much fees the bundler has to pay to include the user operation plus the paymaster surcharge for that user operation.
### How to send a Boosted User Operation
To send a Boosted User Operation, you only need to:
* Set `maxFeePerGas` and `maxPriorityFeePerGas` to zero
* Remove the `paymaster` and `paymasterData` fields
You won't need to make any changes to your application logic as the Boosted User Operation is sent using the standard eth\_sendUserOperation endpoint.
In the background, Pimlico will calculate the User Operation cost offchain based on the User Operation's `gasUsed` and the bundler's `gasPrice`.
#### Code Example
```ts
import { createSmartAccountClient } from "permissionless"
import { toSimpleSmartAccount } from "permissionless/accounts"
import { http, createPublicClient, zeroAddress } from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { sepolia } from "viem/chains"
const apiKey = "YOUR_PIMLICO_API_KEY"
const pimlicoUrl = `https://api.pimlico.io/v2/${chain.id}/rpc?apikey=${apiKey}`
const publicClient = createPublicClient({
chain: sepolia,
transport: http()
})
const account = await toSimpleSmartAccount({
owner: privateKeyToAccount(generatePrivateKey()),
client: publicClient
})
const pimlicoClient = createPimlicoClient({ // [!code --]
chain: sepolia, // [!code --]
transport: http(pimlicoUrl), // [!code --]
entryPoint // [!code --]
}) // [!code --]
const smartAccount = createSmartAccountClient({
bundlerTransport: http(pimlicoUrl),
paymaster: pimlicoClient, // [!code --]
account,
chain: sepolia,
userOperation: { // [!code ++]
estimateFeesPerGas: async () => { // [!code ++]
return { // [!code ++]
maxFeePerGas: 0n, // [!code ++]
maxPriorityFeePerGas: 0n // [!code ++]
} // [!code ++]
} // [!code ++]
} // [!code ++]
})
// Send a mock transaction
const tx = await smartAccount.sendTransaction({
to: zeroAddress,
value: 0n
})
console.log(`Transaction included: https://sepolia.etherscan.io/tx/${tx}`)
```
### Further optimizations
For maximum performance, hardcode your gas limits if you know your User Operation's gas usage. This eliminates the gas estimation RPC call, reducing your entire transaction to a single `eth_sendUserOperation` request.
```ts
...
// Send a mock transaction with hardcoded gasLimits
const tx = await smartAccount.sendTransaction({
to: zeroAddress,
value: 0n,
callGasLimit: 250_000n,
verificationGasLimit: 250_000n,
preVerificationGas: 0n
})
```
:::info
* Set `preVerificationGas` to 0 as the bundler skips this check for Boosted User Operations
* Don't set gas limits too high - the bundler will reject operations that exceed the block gas limit
:::
### Benchmarks
| Chain | % faster | Avg. ms saved | Standard latency (ms) | Boosted latency (ms) |
| ---------------- | -------: | ------------: | --------------------: | -------------------: |
| OP Mainnet | 42.27% | 2,524.22 ms | 5,971.89 ms | 3,447.67 ms |
| Base Sepolia | 43.15% | 3,216.54 ms | 7,454.20 ms | 4,237.65 ms |
| Base | 39.23% | 2,662.73 ms | 6,786.65 ms | 4,123.93 ms |
| Arbitrum One | 33.93% | 2,799.35 ms | 8,250.12 ms | 5,450.78 ms |
| Arbitrum Sepolia | 41.88% | 3,881.81 ms | 9,267.81 ms | 5,386.01 ms |
| Sepolia | 34.74% | 5,396.30 ms | 15,535.38 ms | 10,139.09 ms |
*Benchmarks were collected by sending 100 simple ETH-transfer user operations per chain and comparing end-to-end latency between a standard sponsored flow and the Boosted flow. Real-world performance will vary depending on network conditions and callData complexity.*
## Account Abstraction
Account abstraction is a feature that allows users to customize their Ethereum accounts with smart contract logic. It simplifies the account system by reducing Ethereum's two types of accounts (Externally Owned Accounts and Contract Accounts) to one type - Contract Accounts. The resulting contract accounts can initiate transactions, pay transaction fees, and have more flexibility and security than regular accounts.
### Why is account abstraction necessary?
Account abstraction is essential for several reasons:
* It enables users to create **smart contract wallets** with features such as multi-sig, social recovery, meta-transactions, batched transactions, gas abstraction, etc.
* It allows users to **pay transaction fees with any token or asset** they want, not just ETH. This can lower the entry barriers for new users and increase the adoption of Ethereum.
* It gives developers more **freedom and creativity** to design their account logic and user experience **without being constrained by the protocol rules**. This can foster innovation and diversity in the Ethereum ecosystem.
### How does account abstraction work?
Account abstraction allows contract accounts to specify their validation logic instead of relying on the protocol's signature verification and nonce incrementing. This means that contract accounts can decide how to authorize transactions, how to pay gas fees, and how to prevent replay attacks using smart contract code.
The leading proposal for account abstraction is [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337). ERC-4337 is a specification that aims to use an entry point contract to achieve account abstraction without changing the consensus layer protocol of Ethereum. ERC-4337 simplifies the stack by modularizing the responsibilities of executing the transaction into several entities.
The different entities that make up ERC-4337 are:
* **Bundlers**: entities that collect UserOperations from users and submit them to an EntryPoint contract.
* **EntryPoint**: a contract that receives UserOperations from bundlers, validates them, pays for gas, and executes them.
* **UserOperations**: pseudo-transaction objects that are used to execute transactions with contract accounts. They contain information such as nonce, gas limit, gas price, initCode or target, data, signature, etc.
* **Wallets**: smart contract accounts that can execute transactions with UserOperations. They have logic to verify signatures, update nonces, and perform actions.
* **Paymasters**: contracts that pay for the gas fees of UserOperations on behalf of wallets. They can implement various payment schemes such as tokens, subscriptions, etc.
For a user-friendly explanation of account abstraction, ERC-4337, and smart contract wallets, we recommend the below video by Kristof Gazso, co-author of ERC-4337 and founder of Pimlico:
### How does Pimlico fit in?
While all the benefits of account abstraction are already *theoretically* available, more infrastructure is needed to support this new generation of wallets.
Pimlico's vision is to be the underlying infrastructure layer that will power Ethereum's transition to smart contract wallets through mass ERC-4337 adoption.
We will initially focus on providing comprehensive infrastructure for two entities, as mentioned earlier: **Bundlers and Paymasters**, two of the most critical pieces of the puzzle missing for wallets building on top of ERC-4337.
## ERC-4337
ERC-4337 is the leading proposal to achieve account abstraction on EVM chains. The standard works by creating a new type of transaction, called user operartion, that special relayers called bundlers can submit through a so-called EntryPoint contract that is able to prompt smart accounts compatible with the ERC-4337 interface to check whether the smart account considers the user operation to be valid, and then execute said user operation.
## ERC-7579
ERC-7579 is a proposal that aims to standardize modular smart accounts in a minimal way to allow for continued innovation while making module interoperability between different account implementations far more achievable.
## Conceptual Guides
High-level explanations of core Account Abstraction and Pimlico concepts. Most useful when you want to understand how Pimlico works under the hood.
## EIP-7702 Quickstart — Send a user operation from an EOA
This guide showcases a simple demo that uses ERC-4337 and EIP-7702 to send a sponsored user operation from a EOA. We will use SimpleSmartAccount as our smart account implementation of choice, other ERC-7702 compatible smart accounts will work as well.
For a high level overview of EIP-7702, checkout our [EIP-7702 conceptual guide](/guides/eip7702) and for a more technical overview, please refer to the [EIP-7702 proposal](https://eips.ethereum.org/EIPS/eip-7702).
### Installation
Make sure you have `viem^2.28.0` and `permissionless^0.2.24` installed.
:::code-group
```bash [npm]
npm install permissionless viem
```
```bash [yarn]
yarn add permissionless
```
```bash [pnpm]
pnpm install permissionless viem
```
```bash [bun]
bun install permissionless viem
```
:::
#### Steps
:::steps
#### Setup the smart account
The setup process follows the typical flow of sending a userOperation. The only difference is that when creating the Simple smart account instance, we set the sender address as our EOA's address.
```ts
// [!include ~/snippets/7702/embeddedSendTransaction.ts:createSmartAccount]
```
#### Setup paymaster (optional)
The paymaster client will be responsible for interacting with Pimlico's verifying paymaster endpoint and requesting sponsorship. Make sure to replace YOUR\_PIMLICO\_API\_KEY in the code below with your actual Pimlico API key.
```ts
// [!include ~/snippets/7702/embeddedSendTransaction.ts:setupPaymaster]
```
#### Create Smart Account Client
A Smart Account Client is an almost drop-in replacement for a standard viem walletClient but for managing smart accounts instead of EOA accounts. In addition, a Smart Account Client also contains, depending on the underlying smart account implementation used, a few extra actions that are specific to smart accounts.
To create a Smart Account Client, use the createSmartAccountClient function.
```ts
// [!include ~/snippets/7702/embeddedSendTransaction.ts:createSmartAccountClient]
```
#### Sending the transaction
Make sure you set the signed authorization for the first time when the EOA does not have the authorization code set.
```ts
// [!include ~/snippets/7702/embeddedSendTransaction.ts:sendTransaction]
```
#### Batch multiple transactions
The process is the same as sending a single transaction, the only difference is that we pass an array of calls to the sendTransaction function.
```ts
// [!include ~/snippets/7702/embeddedSendTransaction.ts:sendTransactions]
```
#### Review
Congratulations! You have successfully sent a sponsored userOperation from your EOA, if you review the transaction on the blockchain explorer, you will see that the userOperation's sender address is equal to your EOA's address.
:::
## ERC-4337 vs EIP-7702
This page outlines the key differences and design motivations behind ERC-4337 and EIP-7702 — two distinct approaches to improving smart accounts (also known as "account abstraction") on Ethereum.
### TL;DR
* **ERC-4337**: Defines a standard interface for smart accounts that allows them to plug into standard relayers (bundlers) and paymasters such as Pimlico. No protocol upgrades required.
* **EIP-7702**: Allows EOAs to become smart accounts. Requires a protocol upgrade.
Importantly, **ERC-4337 and EIP-7702 are not competing proposals**. You can use both together, allowing EOAs to become smart accounts while also plugging into standard relayers and paymasters.
Given that ERC-4337 is already live and has a large ecosystem, it is the de-facto standard for smart account relaying and gas sponsoring today and is expected to be the main way that most smart accounts with EIP-7702 will also be used.
### ERC-4337 Overview
* **Timeline**: Proposal finalized in March 2023, latest EntryPoint contract deployed in April 2025.
* **Requires a Protocol Upgrade**: No
* **Mechanism**: Standardizes the bundler and paymaster interface, introduces an `EntryPoint` contract to handle smart account transaction flows.
* **Key Concepts**: User Operations, bundlers, paymasters
#### Pros
* No hard fork needed
* Modular and extensible design (plug-in paymasters, validation logic, etc.)
* Ecosystem already building on it (e.g., Pimlico, Safe, Stackup)
#### Cons
* Complex architecture (bundlers, extra calldata)
### EIP-7702 Overview
* **Timeline**: Expected to be included in the Pectra hardfork, on May 7, 2025.
* **Requires a Protocol Upgrade**: Yes
* **Mechanism**: Turns an EOA into a contract account during via a new transaction type.
* **Key Concepts**: `authorization_list`
#### Pros
* Clean UX: complexity is in the protocol
* Reuses existing transaction flow (no separate mempool or entry point)
#### Cons
* Requires hard fork and client changes
* Not all chains will have it live at the same time — backwards compatibility will be required.
* Master EOA retains ability to control and change the code of the smart account, true multisig / social recovery is not possible
## Batch / sponsor transactions for an external wallets
This guide showcases a simple demo that sends a batch of transactions that are sponsored to an external EIP-7702 enabled wallet.
For a high level overview of EIP-7702, checkout our [EIP-7702 conceptual guide](/guides/eip7702) and for a more technical overview, please refer to the [EIP-7702 proposal](https://eips.ethereum.org/EIPS/eip-7702).
### Steps
:::steps
#### Check if external wallet can batch transactions
```tsx
import { useCapabilities, useChainId } from 'wagmi'
import { useMemo } from 'react'
export function useExternalWalletSupportedCapabilities() {
const chainId = useChainId()
const { data: capabilities } = useCapabilities()
const supportsBatchingTransaction = useMemo(() => {
return (
capabilities?.[chainId]?.atomic?.status === "ready" ||
capabilities?.[chainId]?.atomic?.status === "supported"
)
}, [capabilities, chainId])
const supportsPaymaster = useMemo(() => {
return capabilities?.[chainId]?.paymasterService?.supported === true
}, [capabilities, chainId])
return { supportsBatchingTransaction, supportsPaymaster }
}
```
#### Send a batch of sponsored transactions
```tsx
import { useSendCalls } from "wagmi/experimental"
import { useAccount, useWaitForCallsStatus } from "wagmi"
export function App() {
// Check if the external wallet supports batch transactions
const { supportsBatchingTransaction, supportsPaymaster } = useExternalWalletSupportedCapabilities()
// Get the current external connected account
const account = useAccount()
// we will use wagmi's sendCalls hook to send a batch of sponsored transactions
const { sendCalls, data: callStatus } = useSendCalls()
// we will use wagmi's waitForCallsStatus hook to wait for the batch of sponsored transactions to be mined
const { data: callReceipts } = useWaitForCallsStatus({
id: callStatus?.id
})
// Check if the batch of sponsored transactions was successful
if (callReceipts.status === "success") {
const transactionHash = callReceipts.receipts[0].transactionHash
}
// Send a batch of sponsored transactions callback
const sendTransaction = useCallback(async () => {
// Check if the external wallet is connected
if (!account.address) return
// Check if the external wallet supports batch transactions
if (!supportsBatchingTransaction) {
throw new Error("Batching transactions is not supported on this chain")
}
sendCalls({
// Array of calls to be sent
calls: [
{
to: TEST_ERC20_TOKEN_ADDRESS,
data: encodeFunctionData({
abi: erc20Abi,
functionName: "transfer",
args: [randomAddressOne, parseUnits("1", 6)]
})
},
{
to: TEST_ERC20_TOKEN_ADDRESS,
data: encodeFunctionData({
abi: erc20Abi,
functionName: "transfer",
args: [randomAddressTwo, parseUnits("1", 6)]
})
}
],
// Optional paymaster service
capabilities: supportsPaymaster
? {
paymasterService: {
url: `https://api.pimlico.io/v2/${chainId}/rpc?apikey=${pimlicoApiKey}`
}
}
: undefined
})
}, [
account.address,
sendCalls,
supportsBatchingTransaction,
supportsPaymaster
])
}
```
#### Review
Congratulations! You have successfully sent a batch of sponsored transactions from an external EOA that supports EIP-7702, if you review the transaction on the blockchain explorer, you will see that the transaction's sender address is equal to the external EOA's address.
:::
## EIP-7702 FAQs
### What is EIP-7702?
EIP-7702 is a proposal that allows EOAs to become smart accounts. It is going live in the Pectra hard fork on Ethereum Mainnet on May 7, but it is already live on some other chains like BSC Mainnet and Odyssey Testnet.
### How does EIP-7702 differ from ERC-4337?
In short, EIP-7702 allows for a way for EOAs to become smart accounts, while ERC-4337 creates a standard for smart accounts to plug into a standardized set of relayers and gas sponsorship modules (paymasters). Refer to the [ERC-4337 vs EIP-7702 guide](/guides/eip7702/erc4337-vs-eip7702) for more details.
### Is EIP-7702 compatible with ERC-4337?
Yes, EIP-7702 is compatible with ERC-4337. They cover different responsibilities. ERC-4337 is a standard for smart accounts to plug into a standardized set of relayers and gas sponsorship modules (paymasters), while EIP-7702 allows EOAs to become smart accounts. Refer to the [ERC-4337 vs EIP-7702 guide](/guides/eip7702/erc4337-vs-eip7702) for more details.
### Does Pimlico support EIP-7702?
Yes, Pimlico supports EIP-7702 by allowing an `eip7702Auth` object to be included in `eth_sendUserOperation` and `eth_estimateUserOperationGas` requests. You can read more about how to use it on the relevant reference pages for [eth\_sendUserOperation](/references/bundler/endpoints/eth_sendUserOperation) and [eth\_estimateUserOperationGas](/references/bundler/endpoints/eth_estimateUserOperationGas).
### Which chains are supported by Pimlico for EIP-7702?
Pimlico supports EIP-7702 on Ethereum Mainnet (including Sepolia), BSC Mainnet, OP-Stack chains (Base, Optimism, Zora, and others), and Odyssey Testnet.
### How does an EOA transition to smart account under EIP-7702?
By submitting a set code transaction (transaction type 4) that includes an `authorization_list`.
### How does EntrPoint v0.8 prevent frontrunning for EIP-7702 user operations?
The smart account factory is able to make the check `require(msg.sender == entryPoint.senderCreator());` to ensure only the EntryPoint's senderCreator is calling it.
In addition, the userOpHash now also depends on the delegate address so users also sign over the `7702Auth` object ([source](https://github.com/eth-infinitism/account-abstraction/blob/cc3893bcaf2272c163ce89d5eb9eadb8e6b52db7/contracts/core/EntryPoint.sol#L143))
### Is it possible to turn an EOA into a smart account under EIP-7702 for all chains?
Yes, as per the spec, by signing over a chain ID of 0, it is possible to make an authorization tuple that is valid for all chains and can be applied on all chains that support EIP-7702. With this, it is possible to for instance generate an ephemeral private key, sign over the `7702Auth` object with it, discard the private key and then use the same `7702Auth` object on all chains that support EIP-7702, even if the chain comes out many years later.
## EIP-7702
EIP-7702 is a proposal set for inclusion in the upcoming 2025 Pectra hardfork. The proposal introduces a new transaction type that allows EOA's to assign a "delegation designator" to their account. The delegation designator points to a smart contract and when a transaction is sent to the EOA, it executes the code at the designated address.
This unlocks huge benefits for EOAs, as they can now access smart account features such as batching transactions, gas sponsorships, passkeys as signers, and more.
A full demo showing how to use ERC-4337 with EIP-7702 can be found [here](/guides/eip7702/demo).
### FAQ
#### Will I be able to upgrade my wallet to any smart account implementation?
While technically possible, most wallet developers are expected to heavily restrict which smart account implementations you can upgrade to, typically limiting options to:
* Their own implementation
* A very limited set of approved smart account implementations
This restriction exists because upgrading your account with EIP-7702 effectively requires the same level of trust as handling over your private key. As a result, when implementing EIP-7702 support, you will likely need to:
1. Prompt users to upgrade their EOA wallet (e.g., Metamask) to the specific 7702 implementation that their wallet allows
2. Work within the features available through that implementation
Note: Having users provide their private key would technically allow upgrading to any implementation but is a significant security risk and is not recommended.
### How can I test it out?
You can test out EIP-7702 with Pimlico's infrastructure on the Odyssey Testnet:
For a complete guide on how to test EIP-7702, check out our [step-by-step tutorial](/guides/eip7702/demo) which walks through the process of sending a sponsored userOperation from an EOA.
## Update your existing app to sponsor gas fees for smart account users
If you are looking to sponsor gas fees for your users without embedding a wallet in your app, you can use this guide to update your existing app to sponsor gas fees for smart accounts.
### Steps
:::steps
#### Spin up an ERC-7677 proxy server
Follow the deployment guide on the [ERC-7677 Proxy Server](https://github.com/pimlicolabs/erc7677-proxy) repository readme to deploy an ERC-7677 proxy server.
#### Update your app to sponsor gas fees
Follow the [@permissionless/wagmi tutorial](/references/permissionless/wagmi/tutorial/tutorial-1) to update your app to support ERC-5792 requests and enable the paymaster service capability feature, using the public URL you created for the ERC-7677 proxy in the previous step.
:::
## Migration Guide
### 0.2.0
:::warning
This migration guide is assuming you have already migrated to ^0.1.0 and are now migrating to 0.2.0.
:::
#### Using viem 2.20.0
Viem released their own version of account abstraction primitives since version `2.18.0`.
Since permissionless.js has viem as a peer dependency and are heavily inspired by viem, we will be updating to use the native account abstraction primitives provided by viem.
For full documentation on viem's account abstraction primitives, please refer to the [viem documentation](https://viem.sh/account-abstraction).
#### Deprecated `createBundlerClient`
Viem has default [createBundlerClient](https://viem.sh/account-abstraction/clients/bundler) so we have deprecated the permissionless.js bundler client.
This means we have also deprecated the following bundler actions:
1. `chainId`
2. `estimateUserOperationGas`
3. `getUserOperationByHash`
4. `getUserOperationReceipt`
5. `sendUserOperation`
6. `supportedEntryPoints`
7. `waitForUserOperationReceipt`
#### Deprecated `getEntryPointVersion`
This is no more needed as all the permissionless versions now accepts entryPoint version explicitly.
#### Deprecated `getUserOperationHash`
This is part of `viem/account-abstraction` and now can be directly imported from `viem`.
#### Deprecated `signUserOperationHashWithECDSA`
#### Deprecated `experimental 7677`
#### Changed `signerToBiconomySmartAccount` to `toBiconomySmartAccount`
```ts
import { toBiconomySmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const biconomyAccount = await signerToBiconomySmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
...rest
}) // [!code --]
const biconomyAccount = await toBiconomySmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owners: [privateKeyToAccount(generatePrivateKey())],// [!code ++]
entryPoint: { // [!code ++]
address: entryPoint06Address, // [!code ++]
version: "0.6", // [!code ++]
} // [!code ++]
...rest
})
```
:::warning
Note, Biconomy's account doesn't work with entryPoint 0.7.
:::
#### Changed `signerToEcdsaKernelSmartAccount` to `toEcdsaKernelSmartAccount`
```ts
import { toEcdsaKernelSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const kernelAccount = await signerToEcdsaKernelSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
...rest
}) // [!code --]
const kernelAccount = await toEcdsaKernelSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owners: [privateKeyToAccount(generatePrivateKey())],// [!code ++]
entryPoint: { // optional, defaults to 0.7 // [!code ++]
address: entryPoint07Address, // [!code ++]
version: "0.7", // [!code ++]
}, // [!code ++]
...rest
})
```
#### Changed `signerToLightSmartAccount` to `toLightSmartAccount`
```ts
import { toLightSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const kernelAccount = await signerToLightSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
lightAccountVersion: "1.1.0" // [!code --]
...rest
}) // [!code --]
const kernelAccount = await toLightSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owners: [privateKeyToAccount(generatePrivateKey())],// [!code ++]
entryPoint: { // optional, defaults to 0.7 // [!code ++]
address: entryPoint06Address, // [!code ++]
version: "0.6", // [!code ++]
}, // [!code ++]
version: "1.1.0" // optional, defaults to "2.0.0" // [!code ++]
...rest
})
```
:::info
We have also added support for `2.0.0` Light Account.
:::
:::warning
The Light Account version `1.1.0` works only with EntryPoint version `0.6` while `2.0.0` works only with EntryPoint version `0.7`.
:::
#### Changed `signerToSafeSmartAccount` to `toSafeSmartAccount`
```ts
import { toSafeSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const safeAccount = await signerToSafeSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V07, // [!code --]
safeVersion: "1.4.1" // [!code --]
...rest
}) // [!code --]
const safeAccount = await toSafeSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owners: [privateKeyToAccount(generatePrivateKey())],// [!code ++]
entryPoint: { // optional, defaults to 0.7 // [!code ++]
address: entryPoint07Address, // [!code ++]
version: "0.7", // [!code ++]
}, // [!code ++]
version: "1.4.1" // [!code ++]
...rest
})
```
#### Changed `signerToSimpleSmartAccount` to `toSimpleSmartAccount`
```ts
import { toSimpleSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const simpleAccount = await signerToSimpleSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
...rest
}) // [!code --]
const simpleAccount = await toSimpleSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owner: privateKeyToAccount(generatePrivateKey()),// [!code ++]
entryPoint: { // optional, defaults to 0.7 // [!code ++]
address: entryPoint07Address, // [!code ++]
version: "0.7", // [!code ++]
}, // [!code ++]
...rest
})
```
#### Changed `signerToTrustSmartAccount` to `toTrustSmartAccount`
```ts
import { toTrustSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const trustAccount = await signerToTrustSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
...rest
}) // [!code --]
const trustAccount = await toTrustSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owner: privateKeyToAccount(generatePrivateKey()),// [!code ++]
entryPoint: { // [!code ++]
address: entryPoint06Address, // [!code ++]
version: "0.6", // [!code ++]
}, // [!code ++]
...rest
})
```
:::warning
Note, trust wallet's account doesn't work with entryPoint 0.7.
:::
#### Deprecated \`privateKeyTo\SmartAccount
All the private key to account conversion functions are now deprecated. You can use `toSmartAccount` to create a smart account instead.
Example:
```ts
import { toSafeSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount } from "viem/accounts"
const safeAccount = await toSafeSmartAccount({
client: publicClient,
owners: [privateKeyToAccount(privateKey)],
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}, // global entrypoint
version: "1.4.1",
})
```
#### Merged `createPimlicoPaymasterClient` and `createPimlicoBundlerClient` to `createPimlicoClient`
```ts
import { http } from "viem";
import { entryPoint07Address } from "viem/account-abstraction"
import { createPimlicoClient } from "permissionless/clients/pimlico"
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=`
const pimlicoPaymasterClient = createPimlicoPaymasterClient({ // [!code --]
chain: foundry, // [!code --]
transport: http(paymasterRpc), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V07 // [!code --]
}) // [!code --]
const pimlicoPaymasterClient = createPimlicoPaymasterClient({ // [!code --]
chain: foundry, // [!code --]
transport: http(paymasterRpc), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V07 // [!code --]
}) // [!code --]
const pimlicoClient = createPimlicoClient({ // [!code ++]
transport: http(pimlicoUrl), // [!code ++]
entryPoint: { // Optional, defaults to 0.7 // [!code ++]
address: entryPoint07Address, // [!code ++]
version: "0.7", // [!code ++]
} // [!code ++]
}) // [!code ++]
```
All the previous actions that were part of `createPimlicoBundlerClient` and `createPimlicoPaymasterClient` are now part of `createPimlicoClient` and can also be accessed individually.
Pimlico client also extends actions of Paymaster client by viem. It uses ERC-7677 as the default standard to communicate with Pimlico paymasters.
#### Deprecated `smartAccountClient.deployContract`
#### Changed `smartAccountClient.prepareUserOperationRequest` to `smartAccountClient.prepareUserOperation`
```ts
import { smartAccountClient } from './config'
const userOperation = await smartAccountClient.prepareUserOperationRequest({ // [!code --]
userOperation: { // [!code --]
callData: await smartAccountClient.account.encodeCallData({ // [!code --]
to: CONTRACT_ADDRESS, // [!code --]
data: "0x", // [!code --]
value: parseEther('1') // [!code --]
}), // [!code --]
} // [!code --]
}) // [!code --]
const userOperation = await smartAccountClient.prepareUserOperation({ // [!code ++]
calls: [{ // [!code ++]
to: CONTRACT_ADDRESS, // [!code ++]
data: "0x", // [!code ++]
value: parseEther('1') // [!code ++]
}] // [!code ++]
}) // [!code ++]
```
#### Changed `smartAccountClient.sendUserOperation`
```ts
import { smartAccountClient } from './config'
const userOperationHash = await smartAccountClient.sendUserOperation({
userOperation: { // [!code --]
sender: "0x6152348912fb1e78c9037D83f9d4524D4a2988Ed",
nonce: "0x8104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000",
factory: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
factoryData: "0xc5265d5d0000000000000000000000006723b44abeec4e71ebe3232bd5b455805badd22f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e412af322c018104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000014ec787ae5c34157ce61e751e54dff3612d4117663000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callData: "0xe9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000594bc666500faed35dc741f45a35c571399560d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4e688a440000000000000000000000000c67e4838d4e6682e3a5f9ec27f133e76cb3855f30000000000000000000000006152348912fb1e78c9037d83f9d4524d4a2988ed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000041aebdfb90adba067d9304980a300636506c3c9081b01f64b04f108407a890602377625ef9096946cc028743123646881c7e31a1c8d6698132c188cb4c33a3f9151b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 99985502n,
maxPriorityFeePerGas: 1221000n,
preVerificationGas: 66875n,
verificationGasLimit: 373705n,
callGasLimit: 170447n,
paymaster: "0x9d0021A869f1Ed3a661Ffe8C9B41Ec6244261d98",
paymasterData: "0x000000000000000000000000000000000000000000000000000000006638ab470000000000000000000000000000000000000000000000000000000000000000e9311d1945317dc6a1c750e8e6d0641a598beb59edc5652ed3807ca57338a7a107123e1a479386b2c91f242d2dff367c18e0ad9d1021acfe47afc890e252644e1c",
paymasterVerificationGasLimit: 20274n,
paymasterPostOpGasLimit: 1n,
signature: "0xa58d445f26b126fcd644975f0c66bd45f3e6e5b9c1acec2eeee490aa9341cfc312988231c228f84e12eac2d90ed9cd8825546d70d73b2e0fabdd6c8089ab29581b",
} // [!code --]
})
```
It also allows you to submit user operations with using `calls` object like with `sendTransaction`. You can read more about it [here](https://viem.sh/account-abstraction/actions/bundler/sendUserOperation).
#### Deprecated `smartAccountClient.sendTransactions`
Use `smartAccountClient.sendTransaction` instead. It now accepts an array of transactions. Read more about it [here](/references/permissionless/reference/smart-account-actions/sendTransaction#usage).
Example:
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{ // [!code focus]
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus]
value: parseEther('1') // [!code focus]
}, { // [!code focus]
abi: wagmiAbi, // [!code focus]
functionName: 'mint', // [!code focus]
to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus]
}] // [!code focus]
})
```
#### Error handling
We have deprecated permissionless.js error handling and now use viem's error handling.
## How to send multiple user operations in parallel
permissionless.js library lets you to send multiple transactions in parallel. This can be useful if you want to batch multiple calls in a single user operation or if you want to send user operations concurrently.
### Batching Multiple Calls
To batch multiple calls in a single user operation, you can use the `sendTransactions` method.
```ts
// [!include ~/snippets/how-to/parallel-transactions.ts:multiple-transactions]
```
### Sending Multiple User Operations in Parallel
In an Externally Owned Account (EOA), the `nonce` is a simple incrementing number. However, in smart accounts, the `nonce` consists of two components: a `key` and a `sequence`:
* 192-bit “key”
* 64-bit “sequence”
For each unique `key`, the sequence must be incremented by 1 for each transaction. This means that if you send multiple transactions in parallel, you can use different `keys` for the parallel transactions.
#### Parallel Transactions Ordering
Important thing to note is that parallel transaction's ordering is not guaranteed. So execution of random `key` on chain could be:
* \[key-C]\[sequence-0]
* \[key-A]\[sequence-0]
* \[key-B]\[sequence-0]
While execution of `sequence` for a specific `key` will always be in order. So the following is a valid order of execution:
1. \[key-C]\[sequence-0]
2. \[key-A]\[sequence-0]
3. \[key-C]\[sequence-1]
4. \[key-B]\[sequence-0]
5. \[key-A]\[sequence-1]
6. \[key-B]\[sequence-1]
:::info
Note: Pimlico bundler currently only support up to 10 parallel transactions. If you need to send more than 10 transactions in parallel feel free to contact us at [support@pimlico.io](mailto\:support@pimlico.io).
:::
In the example below, we use the current timestamp as the `key` to send parallel transactions. You can use any other value as the `key`.
```ts
// [!include ~/snippets/how-to/parallel-transactions.ts:multiple-transactions-parallel]
```
:::info
Note: The flow described in this section is only applicable to smart accounts that are already deployed, if your account is not deployed, you may run into AA10 errors as multiple userOperations will contain the factory/factoryData fields which will cause conflicts.
:::
This way, you can efficiently manage multiple transactions either sequentially or concurrently.
## Relay Transactions
Pimlico's transaction relaying service allows you to send transactions without managing private keys or dealing with gas management. With our relaying API, you can submit transactions that are guaranteed fast inclusion across over 100+ chains.
* Fast and guaranteed inclusion across 100+ chains
* You do not have to manage any private keys. Pimlico handles signing and submission
* Simplified gas management. No need to worry about gas price fluctuations
* High reliability with built-in retry mechanisms and monitoring
### How it Works
The relaying service exposes a standard wallet interface that accepts transaction calls and handles the complexity of getting them on-chain.
### Usage
Here's how to relay a transaction using Pimlico:
```ts
// [!include ~/snippets/relaying/relay-transaction.ts:main]
```
### Getting Started
To enable transaction relaying for your application, please contact us at [support@pimlico.io](mailto\:support@pimlico.io).
### Supported Chains
Transaction relaying is available on all chains supported by Pimlico. See our [supported chains](/guides/supported-chains) page for the full list.
## Tutorials
Learning-oriented lessons that take you through a series of steps to complete a project. Most useful when you want to get started with Pimlico.
[Tutorial 1](/references/permissionless/tutorial/tutorial-1) takes you through the journey of leveraging permissionless.js's high-level APIs to easily create and bundle a user operation.
[Tutorial 2](/references/permissionless/tutorial/tutorial-2) takes you through the journey of sponsoring your first user operation with USDC with an ERC-20 paymaster.
## Tutorial 1 — Send your first gasless transaction
In this tutorial, you will submit your first fully-gasless transaction from a smart account.
You will set up the necessary permissionless.js clients, create a user operation, ask Pimlico's verifying paymaster to sponsor it, and then submit it on-chain with Pimlico's bundler.
### Steps
::::steps
#### Get a Pimlico API key
[Create your API key](/guides/create-api-key)
#### Clone the Pimlico tutorial template repository
We have created a [Pimlico tutorial template repository](https://github.com/pimlicolabs/tutorial-template) that you can use to get started. It comes set up with Typescript, viem, and permissionless.js.
```bash
git clone https://github.com/pimlicolabs/tutorial-template.git pimlico-tutorial-1
cd pimlico-tutorial-1
```
Now, let's install the dependencies:
```bash
npm install
```
The main file we will be working with is `index.ts`. Let's run it to make sure everything is working:
```bash
npm start
```
If everything has been set up correctly, you should see `Hello world!` printed to the console.
#### Create the public and paymaster clients, and generate a private key
The public client will be responsible for querying the blockchain, while the paymaster client will be responsible for interacting with Pimlico's verifying paymaster endpoint and requesting sponsorship.
Make sure to replace `YOUR_PIMLICO_API_KEY` in the code below with your actual Pimlico API key.
Let's open up `index.ts`, and add the following to the bottom:
```ts
// [!include ~/snippets/tutorial-1.ts:clients]
```
#### Create the `SmartAccount` instance
For the purposes of this guide, we will be using [Safe](https://safe.global) accounts. This account is an ERC-4337 wallet controlled by a single EOA signer.
:::tip[Tip]
Want to learn more about using Safe accounts? Take a look at our [dedicated Safe guide](/references/permissionless/how-to/accounts/use-safe-account)
:::
To create the Safe account, we will use the `toSafeSmartAccount` utility function from permissionless.js. We need to specify the Safe version we are using as well as the global ERC-4337 EntryPoint address. For the signer, we will be using the previously generated private key.
Add the following to the bottom of `index.ts`:
```ts
// [!include ~/snippets/tutorial-1.ts:smartAccount]
```
Let's run this code with `npm start`. You should see the smart account address printed to the console.
```txt
Smart account address: https://sepolia.etherscan.io/address/0x374b42bCFAcf85FDCaAB84774EA15ff36D42cdA7
```
:::info
If you visit the address on Etherscan, you might notice that no contract is actually deployed to this address yet. This is because smart account are counterfactual, meaning that they are only deployed on-chain the first time you send a transaction through the account.
:::
#### Create the bundler and smart account clients
Now that we have a `SmartAccount` instance, we need to create a `SmartAccountClient` instance to be able to transact from it. `SmartAccountClient` is an almost drop-in replacement for a viem [`WalletClient`](https://viem.sh/docs/clients/wallet), but it also includes some additional functionality for interacting with smart accounts.
We then specify the optional `sponsorUserOperation` middleware function, calling the `sponsorUserOperation` function from the paymaster client. This will make sure that the user operation is sponsored by Pimlico's verifying paymaster.
Finally, we specify the `gasPrice` middleware function to fetch the gas price from the bundler that we will use to submit the user operation in the next step.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/tutorial-1.ts:smartAccountClient]
```
#### Submit a transaction from the smart account
Finally, let's submit a transaction from the smart account. We will send a transaction to the `0xd8da6bf26964af9d7eed9e03e53415d37aa96045` (vitalik.eth) address with `0x1234` as example `callData`.
Underneath the hood, the `SmartAccountClient` will build a user operation, request Pimlico paymaster sponsorship, sign it with the smart account's private key, and then submit it to the bundler. The bundler will then query for receipts until it sees the user operation included on-chain.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/tutorial-1.ts:submit]
```
Let's run this code again with `npm start`. You should see the transaction hash bundling the user operation on-chain printed to the console.
```txt
User operation included: https://sepolia.etherscan.io/tx/0x7a2b61b4b7b6e9e66c459e3c9c24c7a292fc6c740533ce35dbf58710960cc0e5
```
You can now view the transaction on the Sepolia testnet explorer. By sending this user operation, you have:
* Deployed the counterfactual smart account contract
* Had this newly-deployed smart account verify the private key's signature
* Made Pimlico's verifying paymaster sponsor the user operation's gas fees
* Executed a simple transaction to `vitalik.eth`'s address
All in a couple lines of code.
Congratulations, you are now a pioneer of Account Abstraction! 🎉
Please [get in touch](https://t.me/pimlicoHQ) if you have any questions or if you'd like to share what you're building!
::::
#### Combined code
If you want to see the complete code that combines all of the previous steps, we uploaded it to a [separate repository](https://github.com/pimlicolabs/tutorials). If you're looking to run it, remember to replace the API key with your own!
## Tutorial 2 — Submit a user operation with an ERC-20 Paymaster
:::info
You can visit our [ERC-20 Paymaster overview page](/references/paymaster/erc20-paymaster) to learn more about the design and architecture of our ERC-20 Paymaster, and the deployed smart contract addresses.
:::
In this tutorial, you will deploy an ERC-4337 smart contract wallet on Base Sepolia, and submit a user operation that pays for its gas fees with USDC using an ERC-20 Paymaster.
### Steps
::::steps
#### Get a Pimlico API key
[Create your API key](/guides/create-api-key)
#### Clone the Pimlico tutorial template repository
We have created a [Pimlico tutorial template repository](https://github.com/pimlicolabs/tutorial-template) that you can use to get started. It comes set up with Typescript, viem, and permissionless.js.
```bash
git clone https://github.com/pimlicolabs/tutorial-template.git pimlico-tutorial-2
cd pimlico-tutorial-2
```
Now, let's install the dependencies:
```bash
npm install
```
The main file we will be working with is `index.ts`. Let's run it to make sure everything is working:
```bash
npm start
```
If everything has been set up correctly, you should see `Hello world!` printed to the console.
#### Create the public and bundler clients, and generate a private key
The public client will be responsible for querying the blockchain, while the bundler client will be responsible for submitting user operations for relaying.
Make sure to replace `YOUR_PIMLICO_API_KEY` in the code below with your actual Pimlico API key.
Let's open up `index.ts`, and add the following to the bottom:
```ts
// [!include ~/snippets/tutorial-2.ts:clients]
```
#### Create the `SmartAccount` instance
For the purposes of this guide, we will be using [Safe](https://safe.global) accounts. This account is an ERC-4337 wallet controlled by a single EOA signer.
:::tip[Tip]
Want to learn more about using Safe accounts? Take a look at our [dedicated Safe guide](/references/permissionless/how-to/accounts/use-safe-account)
:::
To create the Safe account, we will use the `toSafeSmartAccount` utility function from permissionless.js.
Add the following to the bottom of `index.ts`:
```ts
// [!include ~/snippets/tutorial-2.ts:smartAccount]
```
Since we will be looking to fund our account with USDC (which is what we will use to pay gas fees with), we need to know the address where our smart wallet will be deployed.
Let's run this code with `npm start`. You should see something like this:
```txt
Smart account address: https://sepolia.basescan.org/address/0xbAd38BdCf884ED92ab370f69C0CD0B7b8a1459A1
```
#### Get Testnet USDC on Base Sepolia
Let's get some USDC on the Base Sepolia testnet to the counterfactual address of the wallet we will be deploying. This will be used to pay for the gas fees of the user operation we will be submitting.
The recommended way to do this is to use the [USDC faucet](https://faucet.circle.com/), select 'Base Sepolia' and enter the counterfactual sender address you generated in the previous step.
#### Verify you have USDC on the counterfactual sender address
To make sure you have USDC on the counterfactual sender address, let's add a check to the bottom of `index.ts`:
```ts
// [!include ~/snippets/tutorial-2.ts:checkBalance]
```
If you run this code with `npm start`, you should not see any errors, and you should see the USDC balance of the counterfactual sender address printed to the console.
```txt
Smart account USDC balance: 10 USDC
```
#### Send a transaction from the smart account, paying only with USDC for gas fees.
Finally, let's submit a transaction from the smart account. We will send a transaction to the `0xd8da6bf26964af9d7eed9e03e53415d37aa96045` (vitalik.eth) address with `0x1234` as example `callData`. We will also specify the gas price we want to use, which we fetched from the bundler in the previous step.
Underneath the hood, the `SmartAccountClient` will build a user operation with the designated ERC-20 Paymaster, sign it with the smart account's private key, and then submit it to the bundler. The bundler will then query for receipts until it sees the user operation included on-chain.
The `paymasterContext` will be passed to the paymaster endpoint, instructing it to sponsor the userOperation using ERC-20 tokens.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/tutorial-2.ts:submit]
```
Let's run this code again with `npm start`. You should see the transaction hash bundling the user operation on-chain printed to the console.
```txt
User operation included: https://sepolia.basescan.org/tx/0xf8e4fc41a134fc9530a0c019167f9dc0981874b90187717605355bdcce8b2fb7
```
You can now view the transaction on the Base Sepolia testnet explorer. By sending this user operation, you have:
* Deployed the counterfactual smart account contract
* Had your smart account approve the ERC-20 Paymaster to spend USDC
* Had this newly-deployed smart account verify the private key's signature
* Made an ERC-20 Paymaster sponsor the user operation's gas fees by taking USDC from the smart account
* Executed a simple transaction to `vitalik.eth`'s address
If you visit the address of the `sender` account on the [Base Sepolia explorer](https://sepolia.basescan.org), you should also see that some of your USDC balance has been deducted!
That's it! Congratulations!
::::
#### Combined code
If you want to see the complete code that combines all of the previous steps, we uploaded it to a [separate repository](https://github.com/pimlicolabs/tutorials). If you're looking to run it, remember to replace the API key with your own!
## Why Pimlico for Enterprises
Are you an enterprise looking to leverage smart accounts and wondering why you should use Pimlico as your account abstraction infrastructure provider?
Below are some reasons why some of the best enterprise teams in crypto have chosen to work with Pimlico.
### Expert, veteran team
When you partner with Pimlico, you partner with our veteran team that has been working in the account abstraction space for years. We are co-authors of some of the most important smart account standards (such as [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337), [ERC-7679](https://eips.ethereum.org/EIPS/eip-7679), [ERC-7677](https://eips.ethereum.org/EIPS/eip-7677)), are early supporters of many more, and are still constantly innovating at the frontier.
When you face issues, get stuck, have questions about the technicals behind smart accounts and account abstraction, or would like to brainstorm together on how to best leverage smart accounts for your enterprise, you can expect fast, helpful support from our team of developers who are always ready to help.
### Scalable and customizable
At Pimlico, we built our platform for scale. Our servers are regularly load tested, and our bundlers have been designed to be handle many hundreds of user operations in parallel. This means you can build with confidence, knowing that your smart accounts will always be able to handle the load.
Pimlico is also built to be customizable to fit your enterprise's needs. Out of the box, we support:
* [70+ chains](/guides/supported-chains)
* [All ERC-4337 smart accounts implementations, and all official EntryPoint versions](/references/permissionless/how-to/accounts/support)
* [Sponsorship policies](/guides/how-to/sponsorship-policies) with global, per user, per user operation spending limits, custom webhook validation, and more
* API key authentication and authorization options
In addition to the built in customization features, we can work with you to:
* Deploy our infrastructure and platform to custom chains
* Develop custom bundler modifications to fit your needs
* Support custom EntryPoint contracts
* Create custom paymasters to fit your needs
### Reliable and secure
When you build with Pimlico, you can trust that you are building on a dependable, battle-tested platform. We pride ourselves in our [exceptional uptime](https://status.pimlico.io) and will provide you with a comprehensive uptime SLA. Our servers are monitored 24/7, and we have a dedicated team of engineers who are always ready to respond to any issues that may arise.
When it comes to security, due to the nature of the type of infrastructure we provide, even in a worst case scenario we are never directly or indirectly in custody of user funds. Despite this, our ERC-20 paymasters [have been audited](/references/paymaster/erc20-paymaster/faqs#has-pimlicos-erc-20-paymaster-been-audited) by OpenZeppelin and QuantStamp, some of the best security firms in the space.
### Trusted by the best
Some of the best teams in crypto have already chosen to partner with Pimlico. We are proudly backed by some of the best investors in the space such as [Andreesen Horowitz](https://a16zcrypto.com/), and enterprises such as [Zora](https://zora.co/), [Safe](https://safe.global), [thirdweb](https://thirdweb.com), and [Consensys](https://consensys.io/) are among our users. By choosing Pimlico, you would be joining the ranks of some of the best teams in crypto.
### What's next?
If you're ready to get started, you can [contact us](https://cal.com/team/pimlico/20min) to start building with Pimlico today.
## Why Pimlico for Startups
Are you a startup looking to use smart accounts and wondering why you should use Pimlico as your account abstraction infrastructure provider?
Below are some reasons some of the best startup teams in crypto have chosen to build on Pimlico.
### Advanced Features
At Pimlico, we don't simply give you a bundler or paymaster URL. We are a full-fledged account abstraction infrastructure platform that provides a set of advanced tools to help you build with smart accounts.
This means you can expect features such as:
* Advanced [debugging tools](/guides/how-to/debugging/dropped-user-operations), letting you automatically decode errors and simulate failed user operations
* Comprehensive monitoring and analytics, letting you see how many requests and user operations you're sending, how much you're spending on sponsorships, and more
* [Sponsorship policies](/guides/how-to/sponsorship-policies), letting you set fine-grained conditions for which user operations you would like to sponsor, including global, per user, and per user operation spending limits, custom webhook validation, and more
* A [gas grants platform](/guides/how-to/gas-programs/gas-program), letting you apply for gas grants from our partners to help you get started with smart accounts without having to pay for gas
* Our permissionless and [audited](/references/paymaster/erc20-paymaster/faqs#has-pimlicos-erc-20-paymaster-been-audited) \[ERC-20 paymasters], letting your users pay with ERC-20 tokens like USDC for their transaction fees
* Multi-chain support, letting you build with smart accounts on [over 70+ chains](/guides/supported-chains)
* API key authentication and authorization options, letting you control how your bundlers and paymasters can be accessed
* Flexible payment methods, letting you pay for your usage with a credit card, bank transfer, crypto
* [permissionless.js](/references/permissionless/why), a smart account-focused TypeScript library that supports any bundler and paymaster providers and all major smart account implementations, built by developers for developers with over 20,000 weekly NPM downloads.
### Pay-as-you-go pricing options
With Pimlico's pay-as-you-go plan, you can build with confidence knowing that you only pay for what you use. This means you can start building with smart accounts without having to commit to a long-term contract, and can scale your usage up or down as needed.
If you prefer a more predictable billing model, we can also offer [enterprise pricing plans](/guides/pricing) that are tailored to your needs, with a discount on API credits and dedicated 1-1 support from our team.
### Lightning-fast integration times
We built Pimlico with a focus on smooth onboarding and lightning-fast integration times, making it as easy as possible for you to get started with smart accounts. The Pimlico platform is also selve-serve, so there is no need to wait for a sales call or a demo to get started.
We built our SDKs and APIs to be clean, modern, and well-typed, as well as having it be documentated comprehensively. This means you can expect to be up and running with smart accounts in a matter of hours, not weeks.
### Trusted by the best
Some of the best teams in crypto have already chosen to partner with Pimlico. Startups such as [Daimo](https://daimo.io/), [Drakula](https://drakula.app/), [ZeroDev](https://zerodev.app), and enterprises such as [Zora](https://zora.co/), [Safe](https://safe.global), [thirdweb](https://thirdweb.com), and [Consensys](https://consensys.io/) are already among our users. By choosing Pimlico, you would be joining the ranks of some of the best teams in crypto.
### What's next?
If you're ready to get started, you can sign up to the [Pimlico dashboard](https://dashboard.pimlico.io) or [contact us](https://cal.com/team/pimlico/20min) to start building with Pimlico today.
## Bundler Error Codes
The bundler implements the [EIP-7769](https://eips.ethereum.org/EIPS/eip-7769) spec which standardizes ERC-4337 bundler error codes across different bundler implementations.
### EIP-7769 Error Codes
| Error Code | Description |
| ---------- | ---------------------------------------------------------- |
| -32602 | Invalid UserOperation struct/fields |
| -32500 | Rejected by EntryPoint contract's simulateValidation |
| -32501 | Rejected by paymaster contract's validatePaymasterUserOp |
| -32502 | Violation of ERC-7562 opcode validation rules |
| -32503 | UserOperation out of time-range |
| -32504 | Paymaster throttled or banned by ERC-7562 reputation rules |
| -32505 | Paymaster stake or unstake-delay too low |
| -32507 | Wallet signature check failed |
| -32508 | Paymaster balance insufficient for pending UserOperations |
| -32521 | UserOperation execution reverted |
### Standard JSON-RPC Error Codes
| Error Code | Description |
| ---------- | ------------------------------------------------------------- |
| -32603 | Internal error - indicates an internal failure in the bundler |
## Bundler FAQs
### I successfully submitted a user operation to the bundler, but it never got included in a block. What happened?
There can be many reasons for this. The most common are:
* The user operation succeeds as it is being validated, but fails during the second round of simulations when it is being bundled by the bundler into a transaction to be submitted on-chain and is therefore dropped.
* The bundle transaction bundling the user operation fails on-chain so no logs are emitted.
* You submitted a user operation which has a nonce that is higher than the current account nonce, which means it is likely queued by the bundler until that nonce is reached.
It is recommended to use the [`pimlico_getUserOperationStatus`](/references/bundler/endpoints/pimlico_getUserOperationStatus) endpoint to check the status of your user operation.
### What are fees to deploy to app-chains?
Fees vary by network. Please reach out to [us](https://t.me/kristofgazso) for more information.
### Are Pimlico bundlers block builders?
Not by default, however we aim to get direct RPC access to sequencers whenever possible.
### Are there any API-related payments?
No, there are not. The only payments relate to the onchain payments through the user operation gas price for the bundler fees, and the offchain balance deduction for the paymaster fees.
### How is payment made for the bundler?
The bundler fees are paid for completely onchain, by setting a gas price for the user operation that is slightly higher than the gas price we use to bundle said user operation in our transaction.
### How does the onchain bundler payment take place?
When constructing your user operation, you'd use the [pimlico\_getUserOperationGasPrice](/references/bundler/endpoints/pimlico_getUserOperationGasPrice) method to select the gas prices for your user operation. When submitting to the bundler, the bundler would bundle the user operation into a transaction with a slightly lower gas price. The bundler makes as profit the small difference between the two gas prices.
### How is pimlico\_getUserOperationGasPrice different from eth\_estimateUserOperationGas?
[eth\_estimateUserOperationGas](/references/bundler/endpoints/eth_estimateUserOperationGas) estimates the gas limits for the user operation (i.e. how much total gas can be spent for the difference stages of the user operation). In contrast, [pimlico\_getUserOperationGasPrice](/references/bundler/endpoints/pimlico_getUserOperationGasPrice) responds with the gas prices (i.e. for each unit of gas, what is the amount of ETH you're willing to pay to the bundler).
### Is there be a way of tracking gas fee payments?
Our dashboards provide a way for you to view data about how much you spent on sponsorship for user operations on different chains, however there is currently no specific information on the gas price overhead paid to the bundler. However, all this information is available and can be compiled onchain through block explorers like Etherscan.
If you would like more information about gas fee payments on the Pimlico dashboard, [reach out to us!](https://t.me/pimlicoHQ)
### How do gas fees for the bundler and paymaster stack?
If you use both the bundler and the paymaster together, the fee payments would stack.
For instance, if you ended up paying a 5% overhead for the bundler and a 10% overhead for the verifying paymaster on a user operation that costs us $0.01 to include, you would end up paying a total of $0.01 \* (1.05) \* (1.1) = $0.01155 => a 15.5% overhead.
Important to note however that the bundler and paymaster are completely independent and you can use one without the other.
### What is the use case for smart accounts
Smart accounts enable user experience improvements that were not previously possible with EOA accounts.
Examples include:
* Gasless transactions
* Use of USDC and other ERC-20 tokens for gas fee payments
* Trustless Passkey and FaceID signatures and login
* Account recovery
* Signer rotation
* Multisig and social recovery schemes
* Automated transactions (e.g. for limit orders, deadman switch, auto token revocations, automated DCA investing)
* Transactions with session keys with limited account access
* Batched transactions (e.g. being able to approve and swap in the same transaction)
### When using a bundler without a verifying paymaster, who ends up paying the added fees?
When using the bundler without you sponsoring the gas fees for your users, the users themselves will be the ones covering the gas fees as well as the small bundler overhead for the user operations.
### If the bundler gets exploited, are my ERC-4337 accounts at risk?
No. If the private keys Pimlico uses were to get compromised, that would have no effect on the security of your ERC-4337 accounts. ERC-4337 was designed to make bundlers a completely permissionless entity, meaning in fact that any address could technically be a bundler by calling the `handleOps` function on the EntryPoint contract. Bundler services like Pimlico merely provide a convenient, fast, and reliable way to bundle user operations for you.
The EOA address (if there is one) that controls your ERC-4337 would be controlled by you or a separate service you trust, completely independent of the bundler and Pimlico.
## Alto Bundler

Alto ⛰️ is a performant, type-safe ERC-4337 bundler written in Typescript.
Our focus is on full type safety, transaction inclusion reliability (even in the case of sudden gas price spikes or chain reorgs), as well as transaction inclusion speed.
### Tutorials
Learning-oriented lessons that take you through a series of steps to complete a project. Most useful when you want to get started with Pimlico. They all involve the Alto Bundler.
* [Tutorial 1](/references/permissionless/tutorial/tutorial-1) leverages the Alto Bundler to create and bundle a user operation on-chain.
* [Tutorial 2](/references/permissionless/tutorial/tutorial-2) leverages the Alto Bundler again, but using the [ERC-20 paymaster](/references/paymaster/erc20-paymaster) instead of the Verifying Paymaster
### How-To Guides
Step-by-step instructions to accomplish a specific goal. They are useful when you are trying to find out how to do something with Pimlico.
### References
References provide specific technical descriptions. They are most useful when you need detailed information about Pimlico's APIs.
## Public Endpoint
Pimlico provides a public bundler endpoint for developers to test and prototype their applications without requiring an API key.
### Endpoint URL
```
https://public.pimlico.io/v2/{chain_id}/rpc
```
Replace `{chain_id}` with the chain ID of the network you want to use (e.g., `1` for Ethereum mainnet, `137` for Polygon).
### Supported Methods
The public endpoint supports all standard ERC-4337 bundler methods and Pimlico specific methods:
* `eth_sendUserOperation`
* `eth_estimateUserOperationGas`
* `eth_getUserOperationReceipt`
* `eth_getUserOperationByHash`
* `eth_supportedEntryPoints`
* `pimlico_getUserOperationGasPrice`
* `pimlico_getUserOperationStatus`
:::warning[Limited Paymaster Support]
The public endpoint supports paymaster methods only on testnets. Mainnet paymaster support requires an API key.
:::
### Rate Limits
The public endpoint has the following rate limits:
* **20 requests per minute per IP address**
:::tip[Need Higher Limits?]
For production use or higher rate limits, please [create a Pimlico API key](/guides/create-api-key) and use the authenticated endpoints.
:::
### Example Usage
```typescript
import { createSmartAccountClient } from "permissionless";
import { toSafeSmartAccount } from "permissionless/accounts";
import { createPublicClient, http, zeroAddress } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { base } from "viem/chains";
const publicClient = createPublicClient({
chain: base,
transport: http(),
});
const account = await toSafeSmartAccount({
owners: [privateKeyToAccount(privateKey)],
client: publicClient,
version: "1.4.1",
});
const pimlicoClient = createPimlicoClient({
transport: http(`https://public.pimlico.io/v2/${base.id}/rpc`), // [!code ++]
chain: base,
});
const smartAccountClient = createSmartAccountClient({
userOperation: {
estimateFeesPerGas: async () =>
(await pimlicoClient.getUserOperationGasPrice()).fast,
},
account,
chain: base,
bundlerTransport: http(`https://public.pimlico.io/v2/${base.id}/rpc`), // [!code ++]
});
const txHash = await smartAccountClient.sendTransaction({
calls: [
{
to: zeroAddress,
value: 0n,
data: "0x",
},
],
});
console.log(txHash);
```
## Self-Host Guide
This guide holds all information related to self hosting your own Alto bundler instance. Including installation, setting up, running, and troubleshooting.
### Installation (Building from source)
To install and build Alto from source, run the following commands
```bash
git clone https://github.com/pimlicolabs/alto.git
pnpm install
pnpm build
```
And then to start Alto, run
```bash
./alto help
```
### Setup And Prerequisites
Alto manages multiple executor wallets. If any wallet's balance falls below a set minimum, its balance is automatically refilled using funds from the utility wallet. The utility wallet needs to be funded before starting Alto.
By default, Alto will run in safe mode. This means that it will validate all userOps against the ERC-7562 validation rules. In safe mode, you will need a RPC that supports the debug\_traceCall endpoint. It is reccomended to run Alto with this flag disabled by setting `--safe-mode false`.
### Running
Once all the prerequisite conditions are met, Alto can be started using
```bash
./alto run
--entrypoints "0x0000000071727De22E5E9d8BAf0edAc6f37da032,0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
--executor-private-keys "0x34e9a7...,0xb08d34...,0x163cbb..."
--utility-private-key "0xe768f1..."
--rpc-url "http://localhost:8545"
--safe-mode false
```
#### Running Alto using JSON configurations
Alto can also be ran using configurations from a JSON file
Create and setup `alto-config.json`
```
{
"entrypoints": "0x0000000071727De22E5E9d8BAf0edAc6f37da032,0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"executor-private-keys": "0x34e9a7...,0xb08d34...,0x163cbb...",
"utility-private-key": "0xe768f1...",
"rpc-url": "http://localhost:8545",
"safe-mode": false
}
```
Run Alto using `alto-config.json` configurations
```bash
./alto run --config "alto-config.json"
```
:::tip[Tip]
It is recommended to run Alto using `safe-mode=false` as it will skip the ERC-7562 reputation checks and DOS prevention. To run alto with `safe-mode=true`, you will need to provide an RPC that has debug\_traceCall enabled.
:::
### compatibility
Some chains have certain compatibility restrictions that require Alto to be ran with special flags.
##### Legacy Transaction Chains
For chains that only support pre EIP-1559 transactions, alto needs to be ran with the flag
```bash
./alto run --legacy-transactions ...
```
##### OP Stack Chains
For chains that are deployed using the OP Stack, alto needs to be ran with the flag
```bash
./alto run --chain-type "op-stack" ...
```
##### Arbitrum Chains
For chains that are deployed using Arbitrum, alto needs to be ran with the flag
```bash
./alto run --chain-type "arbitrum" ...
```
## How to use Alto in CI/CD testing
To effectively integrate Alto, a performant and type-safe ERC-4337 bundler developed by Pimlico, into your CI/CD testing pipeline, we recommend utilizing Prool, a library that provides programmatic HTTP testing instances for Ethereum.rool facilitates the creation of isolated Ethereum environments, enabling efficient testing of smart contracts and related components.
**Installation Steps:**
::::steps
#### Install Anvil
You can find detailed instructions in the [Anvil documentation](https://book.getfoundry.sh/getting-started/installation).
Quickstart:
```bash
curl -L https://foundry.paradigm.xyz | bash
```
Source the bashrc file:
:::code-group
```bash [bashrc]
source $HOME/.bashrc
```
```bash [zshrc]
source $HOME/.zshrc
```
:::
Install Foundry:
```bash
foundryup
```
#### Install Prool and Alto:
Use npm to install the Prool and Alto packages:
```bash
npm install @pimlico/alto prool
```
#### Set Up Prool with Anvil:
Prool offers pre-configured instances, including Anvil, which can be used to simulate a local Ethereum execution environment. By using the `fork` option, you can avoid deploying an entry point manually. If you opt for a bare-bones Anvil server, deploying the entry point yourself will be necessary.
```ts
import { createServer } from 'prool';
import { anvil } from 'prool/instances';
const anvilPort = 8545;
const server = createServer({
instance: anvil({
port: anvilPort, // The port to run the Anvil server on
forkUrl: 'https://mainnet.base.org',
forkBlockNumber: 26008827, // This can be any block number
}),
});
await server.start();
```
#### Integrate Alto with Prool:
Prool also provides an instance for Alto, enabling seamless integration into your testing environment.
```ts
import { createServer } from 'prool';
import { anvil, alto } from 'prool/instances';
import { entryPoint06Address, entryPoint07Address } from "viem/account-abstraction"
const anvilRpc = `http://localhost:${anvilPort}`
const anvilPrivateKey =
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" // Choose any private key
const altoPort = 4337
const altoServer = createServer({
instance: (key) =>
alto({
port: altoPort, // The port to run the Alto server on
entrypoints: [entryPoint06Address, entryPoint07Address],
rpcUrl: anvilRpc,
executorPrivateKeys: [anvilPrivateKey],
}),
});
await altoServer.start();
```
::::
By following these steps, you can effectively incorporate Alto into your CI/CD testing pipeline using Prool, ensuring a robust and efficient testing environment for your Ethereum applications.
## How to use the bundler
:::tip[Tip]
We recommend using [permissionless.js](/references/permissionless/reference/smart-account-actions/sendUserOperation) as the SDK to interact with the bundler as it provides type-safe wrappers for all bundler methods.
:::
ERC-4337 bundlers are relayers that bundle user operations into transactions and submit them to the blockchain. You can interact with bundlers using standard JSON-RPC requests.
To get access to the bundler, you need to use your Pimlico API key. [Create one here](/guides/create-api-key) if you don't have one. Using the API key, you can make the following JSON-RPC requests to the bundler:
* [eth\_sendUserOperation](/references/bundler/endpoints/eth_sendUserOperation)
* [eth\_estimateUserOperationGas](/references/bundler/endpoints/eth_estimateUserOperationGas)
* [eth\_getUserOperationReceipt](/references/bundler/endpoints/eth_getUserOperationReceipt)
* [eth\_getUserOperationByHash](/references/bundler/endpoints/eth_getUserOperationByHash)
* [eth\_supportedEntryPoints](/references/bundler/endpoints/eth_supportedEntryPoints)
* [pimlico\_getUserOperationGasPrice](/references/bundler/endpoints/pimlico_getUserOperationGasPrice)
* [pimlico\_getUserOperationStatus](/references/bundler/endpoints/pimlico_getUserOperationStatus)
If you would like an end-to-end example of how to use the bundler, please refer to [tutorial 1](/references/permissionless/tutorial/tutorial-1) of the permissionless.js documentation.
## Pimlico Paymasters
Pimlico providers two different types of paymasters to allow you to abstract away the gas fees for your users.
### Verifying Paymaster
Our Verifying Paymaster allows you to load up your off-chain Pimlico balance through [our dashboard](https://dashboard.pimlico.io) and sponsor the on-chain gas fees for your users on over 100+ chains.
### ERC-20 Paymaster
Our ERC-20 Paymaster is a permissionless on-chain smart contract that lets your users pay for their own gas fees using their ERC-20 tokens.
## permissionless.js FAQs
### How to decrease inclusion time on Base using flashblocks?
Just decrease your bundler's polling interval like so:
```typescript
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: base,
pollingInterval: 200, // reduce the polling interval to fetch receipts down to 200ms // [!code ++]
bundlerTransport: http("https://api.pimlico.io/v2/8453/rpc?apikey=")
})
```
### Getting `WaitForUserOperationReceiptTimeoutError`?
This error is thrown when the bundler takes too long to bundle your user operation or the default timeout configured in permissionless.js is not enough.
This could be due to the block time of the chain you are using is more than the default timeout.
The easiest way to fix this is to increase the timeout in permissionless.js. You can do this by changing timeout value in `bundlerTransport` like so:
```typescript
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: sepolia, // or whatever chain you are using
bundlerTransport: http("https://api.pimlico.io/v2/8453/rpc?apikey=", {
timeout: 30_000 // Custom timeout
})
})
```
### Can I use social logins (Google, Facebook, etc.) with permissionless.js?
Yes! While permissionless.js doesn't directly implement social logins, you can integrate third-party providers that offer social login capabilities. Popular options include:
* [Magic](https://magic.link)
* [Privy](https://privy.io)
* [Dynamic](https://www.dynamic.xyz)
* [Web3Auth](https://web3auth.io)
These providers can be integrated with permissionless.js to enable social login experiences while leveraging smart accounts for features like gas sponsorship and transaction batching. Check out our [Signers documentation](/references/permissionless/how-to/signers) for integration guides with these providers.
### Getting `out of gas` errors?
Such errors are thrown when the provided gas limits are not enough to execute the user operation. Usually the gas limits are calculated by the bundler. If you are getting such error, you can try setting up gas limits manually.
```typescript
const userOp = await smartAccountClient.prepareUserOperation({
calls,
callGasLimit: 100_000n,
verificationGasLimit: 100_000n,
preVerificationGas: 100_000n,
})
```
### Do I need the same saltNonce to re-initialize my Safe?
When initializing a Safe account with `toSafeSmartAccount`, if you want to get the same account address when re-initializing the Safe wallet, you must use the same `saltNonce` value that was used during the initial creation. If no `saltNonce` is specified, `toSafeSmartAccount` defaults to `0n`.
Using a different `saltNonce` will result in a different account address being generated. Here's an example of how to specify the `saltNonce`:
```typescript
const safeAccount = await toSafeSmartAccount({
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: "0.7",
},
owners: [someSigner],
saltNonce: 100n, // must match the original saltNonce to get the same address
version: "1.4.1",
});
```
For more details about the `saltNonce` parameter and other configuration options, see the [reference documentation for toSafeSmartAccount](/references/permissionless/reference/accounts/toSafeSmartAccount#saltnonce).
### Getting `preVerificationGas is not enough` errors?
This error occurs when a userOperation is submitted with a preVerificationGas that is insufficient to cover the offchain overhead.
You might see an error message like:
```json
{
"message": "preVerificationGas is not enough, required: 60676, got: 48550",
"code": -32500
}
```
The `preVerificationGas` accounts for:
* Gas overhead that can't be calculated onchain
* L1 data costs when operating on L2 networks
This error typically occurs in two scenarios:
1. When calling `eth_estimateUserOperationGas` with a userOperation signature that is not the same length as the real signature.
2. When submitting a UserOperation after the bundler's preVerificationGas commitment has expired.
To resolve this issue:
1. Ensure proper dummy signature is used during estimation to ensure proper L1DataCost calculations:
* Provide a semi-valid dummy signature that matches your final signature's length
* The signature should be semi valid such that the validation doesn't revert
2. Account for gas estimation timing:
* The bundler commits to the estimated `preVerificationGas` for 30 seconds
* You should submit your UserOperation within this window to avoid insufficient preVerificationGas errors
* If you are submitting after the window, you can add a fixed overhead to your `preVerificationGas` to increase the chances it gets included
import { HomePage } from 'vocs/components'
permissionless.js
Build with ERC-4337 smart accounts, bundlers, paymasters, and user operationspermissionless.js is a TypeScript library built on viem for building with ERC-4337 smart accounts, bundlers, paymasters, and user operations. The core focuses are avoiding provider lock-in, having no dependencies, maximum viem compatibility, and a small bundle size. permissionless.js also provides high-level support for the major ERC-4337 smart accounts, including Safe, Kernel, Biconomy, SimpleAccount, TrustWallet and LightAccount.Get startedGitHub
## Overview
```ts twoslash
const pimlicoApiKey = "YOUR_API_KEY_HERE"
const bundlerUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=${pimlicoApiKey}`
const userOpHash = "0x5faea6a3af76292c2b23468bbea96ef63fb31360848be195748437f0a79106c8"
// ---cut---
// Import the required modules.
import { createSmartAccountClient } from "permissionless"
import { sepolia } from "viem/chains"
import { http } from "viem"
// Create the required clients.
const bundlerClient = createSmartAccountClient({
chain: sepolia,
bundlerTransport: http(bundlerUrl), // Use any bundler url
})
// Consume bundler, paymaster, and smart account actions!
const opReceipt = await bundlerClient.getUserOperationReceipt({
hash: userOpHash
})
// Build with strict TypeScript types
opReceipt!.actualGasUsed
// ^?
```
## Features
* **High-level smart account support**: We support a high-level API for deploying and managing smart accounts, including some of the most popular implementations ([Safe](https://safe.global), [Kernel](https://zerodev.app), [Biconomy](https://biconomy.io), [TrustWallet](https://trustwallet.com/swift), etc.)
* **Bundler support**: We support all bundler actions following [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace).
* **Gas sponsorship**: We support paymaster actions to allow you to easily sponsor gas fees.
* **User Operation utility functions**: We provide many low-level utility functions useful for dealing with User Operations.
* **Modular and extensible**: We allow you to easily create and plug in your own smart account systems, bundlers, paymasters, and signers.
* **Built on & for viem**: permissionless.js is designed to be a thin wrapper around viem, maintaining the same style and overall feel viem provides.
* and a lot more...
## Source Code
The source code for permissionless.js is available on [GitHub](https://github.com/pimlicolabs/permissionless.js)
permissionless.js is distributed under an MIT License.
We welcome contributions from the community. If you would like to contribute, please open an issue or a pull request.
Feel free to ask any questions in our [Telegram group](https://t.me/pimlicoHQ)
## Why permissionless.js \[A summary of why we built permissionless.js]
### Problems
ERC-4337 has emerged as a popular standard to tackle some of the most pressing user experience issues in the Ethereum ecosystem, but the current state of tooling in the space has been lacking. Namely, no library we have seen has been able to succeed in all of the following areas: **obsessed with developer experience, built closely on top of existing tools, flexible, and vendor-agnostic**.
At [Pimlico](https://pimlico.io) we have seen a lot of developers using our infrastructure struggle with having to string many SDKs together. Instead of getting them to learn a whole new framework or building a `@pimlico/sdk`-like library that would lock the developer to our infrastructure, we decided to build a tool that would allow developers to build on top of APIs they're already familiar with any provider they wish, mixing-and-matching all of the different parts of the stack including bundlers, paymasters, smart account providers, and signers.
### Great developer experience
Developer experience is the cornerstone of permissionless.js's design. We're committed to providing strongly-typed TypeScript APIs, comprehensive documentation on every function, and an intuitive and legible style that doesn't require you to constantly jump around files to understand the code that has been written.
### Built on top of existing tools
Developers should not have to learn a completely new framework to make their apps leverage smart accounts. [viem](https://viem.sh) and [wagmi](https://wagmi.sh) have established themselves as the most popular libraries for Ethereum development, so permissionless.js is built as a thin layer on top of them reusing as much of their standards and styles as possible. This means less work for us, and less work for developers who want to build with smart accounts.
### Flexible and extensible
Account abstraction is still an evolving space, and we want to make sure that developers can easily build and extend their own solutions for different parts of the stack as they see fit. If we don't support a bundler, a paymaster, a smart account provider, or a signer that you want to use, it should be trivial to swap it out for another one that implements the same interface or to build and connect your own without having to rewrite your entire app.
### Avoiding lock-in
Crypto developers don't like lock-in. In a world where the infrastructure provider whose proprietary SDK you've spent the last year building your app on can disappear overnight, you want to make sure that you can easily swap out different parts of the stack as you see fit, including that of the original creator of the library. If we as Pimlico want to see smart accounts succeed, we're going to have to build tooling that benefits the entire ecosystem, not just the users of our own infrastructure.
## Pimlico Platform
**Pimlico** is the world's most popular ERC-4337 account abstraction infrastructure platform. Pimlico provides a suite of tools and services to help you build, deploy, and manage smart accounts on Ethereum and other EVM-compatible chains.
## Difference between various Smart Account types in permissionless.js
permissionless.js supports 8 types of accounts. Below is an overview of each account type and their key features:
1. **Safe**
2. **Kernel**
3. **Nexus**
4. **Simple Smart Account**
5. **LightAccount**
6. **TrustWallet**
7. **Etherspot**
8. **Thirdweb**
***
### 1. **Safe**
[Safe](https://safe.global) is a robust and widely used Ethereum smart account provider. With its ERC-4337 module, Safe accounts can integrate with ERC-4337 bundlers and paymasters.
* **ERC-7579 Support**: Yes, via [Rhinestone's safe7579](https://github.com/rhinestonewtf/safe7579) module.
* **Passkeys Support**: Supported through [Safe's passkey module](https://github.com/safe-global/safe-modules/blob/466a9b8ef169003c5df856c6ecd295e6ecb9e99d/modules/passkey/README.md); not yet supported in permissionless.js.
* **Multiple Signers**: Yes, as Safe is one of the first multi-signature smart accounts.
* **Chain Support**: Available on all chains where Safe factories are deployed.
* **Usage Stats**: \~34k Safe accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Security**: Secures over $100B+ in assets, making it highly reliable.
### 2. **Kernel**
[Kernel](https://github.com/zerodevapp/kernel) by [ZeroDev](https://zerodev.app) is a highly gas-efficient smart account compatible with ERC-4337 and ERC-7579.
* **ERC-7579 Support**: Yes, authored by the ZeroDev team.
* **Passkeys Support**: Yes.
* **Multiple Signers**: Supported through flexible permission settings:
* Define who can perform actions.
* Specify conditions for actions.
* Configure what actions can be performed.
* **Usage Stats**: \~133k Kernel v3 and 771k Kernel v2 accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Audited by [ChainLight](https://github.com/zerodevapp/kernel/blob/dev/audits/chainlight_v3_0.pdf) and [Kalos](https://github.com/zerodevapp/kernel/tree/dev/audits).
### 3. **Nexus**
Biconomy [Nexus](https://github.com/bcnmy/nexus) focuses on modularity, security, and scalability. Support for the older Biconomy Smart Account is being deprecated in favor of Nexus.
* **ERC-7579 Support**: Yes.
* **Passkeys Support**: Supported via Rhinestone's Passkeys Validator (not yet in permissionless.js).
* **Multiple Signers**: Enabled using Rhinestone's session keys module.
* **Usage Stats**: \~78 Nexus accounts and 224,758 Biconomy v2 (deprecated) accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Audited by [Cyfrin](https://github.com/bcnmy/nexus/blob/dev/audits/CodeHawks-Cyfrin-Competition-170924.pdf) and [Spearbit](https://github.com/bcnmy/nexus/blob/dev/audits/report-cantinacode-biconomy-0708-final.pdf).
### 4. **Simple Smart Account**
[Simple Smart Account](https://github.com/eth-infinitism/account-abstraction/blob/v0.7.0/contracts/samples/SimpleAccount.sol) is a sample smart account by the Eth-Infinitism team.
This is supposed to be a reference implementation for ERC-4337 and not a production-ready smart account.
* **ERC-7579 Support**: No.
* **Passkeys Support**: No.
* **Multiple Signers**: No.
* **Usage Stats**: \~1.5M accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Audited by [OpenZeppelin](https://github.com/eth-infinitism/account-abstraction/blob/develop/audits/EIP_4337_%E2%80%93_Ethereum_Account_Abstraction_Incremental_Audit_Feb_2023.pdf).
### 5. **LightAccount**
[LightAccount](https://github.com/alchemyplatform/light-account) is a lightweight ERC-4337 smart account based on [SimpleAccount](https://github.com/eth-infinitism/account-abstraction/blob/v0.7.0/contracts/samples/SimpleAccount.sol) with added functionality for smart account signers.
* **ERC-7579 Support**: No.
* **Passkeys Support**: No.
* **Multiple Signers**: No.
* **Usage Stats**: \~7.3M accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Audited by [QuantStamp](https://github.com/alchemyplatform/light-account/blob/develop/audits/2024-01-09_quantstamp_aa8196b.pdf).
### 6. **TrustWallet**
[TrustWallet](https://trustwallet.com/) is a popular cryptocurrency wallet supporting a wide range of digital assets across multiple blockchains. It provides users with a secure, self-custody solution and has introduced ERC-4337-compatible features.
* **ERC-7579 Support**: No.
* **Passkeys Support**: Yes, through [Barz](https://trustwallet.com/blog/introducing-barz-smart-contract-wallet-solution), Trust Wallet's ERC-4337-compatible smart contract wallet, which supports passkey-based authentication.
* **Multiple Signers**: Yes, Trust Wallet offers multisig functionality to enhance transaction security.
* **Usage Stats**: \~36,838 accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Trust Wallet has undergone independent audits.
### 7. **Etherspot**
Details on Etherspot integration coming soon.
### 8. **Thirdweb**
Details on Thirdweb integration coming soon.
### Summary of Key Features
| Account Type | ERC-7579 | Passkeys | Multiple Signers | Accounts Created (6 months) | Audited By |
| ------------------------ | -------- | -------- | ---------------- | --------------------------- | ------------------ |
| Safe | ✅ | ✅ | ✅ | 34k | Various |
| Kernel | ✅ | ✅ | ✅ | 133k (v3), 771k (v2) | ChainLight, Kalos |
| Nexus | ✅ | ✅ | ✅ | 78 | Cyfrin, Spearbit |
| Biconomy v2 (deprecated) | ✅ | ✅ | ✅ | 224k | Cyfrin, Spearbit |
| Simple | ❌ | ❌ | ❌ | 1.5M | OpenZeppelin |
| LightAccount | ❌ | ❌ | ❌ | 7.3M | QuantStamp |
| TrustWallet | ❌ | ✅ | ✅ | 36k | Independent audits |
| Etherspot | TBD | TBD | TBD | TBD | TBD |
| Thirdweb | TBD | TBD | TBD | TBD | TBD |
### Gas Efficiency of various Smart Account types
Results (as of February 24, 2024)
**Disclaimer** the numbers are obtained from [aa-benchmark](https://github.com/zerodevapp/aa-benchmark) from ZeroDev.
Since these are gas numbers, lower is better.
| | Creation | Native transfer | ERC20 transfer | Total |
| ---------------------- | -------- | --------------- | -------------- | ------- |
| Solady ERC4337 | 212262 | 100149 | 89532 | 401943 |
| Kernel v2.1-lite | 230968 | 101002 | 90321 | 422291 |
| Kernel v2.1 | 265215 | 106460 | 96038 | 467713 |
| Biconomy (deprecated) | 270013 | 104408 | 93730 | 468151 |
| SoulWalletCore | 276529 | 101162 | 90466 | 468157 |
| LightAccount | 279820 | 100910 | 90411 | 471141 |
| Etherspot | 279219 | 103719 | 93324 | 476262 |
| ERC7579 reference | 289438 | 103811 | 93213 | 486462 |
| Kernel v2.0 | 339882 | 110018 | 99622 | 549522 |
| SimpleAccount | 383218 | 101319 | 90907 | 575444 |
| Safe 4337 | 401848 | 115469 | 105089 | 622406 |
| Alchemy ModularAccount | 827723 | 106630 | 96438 | 1030791 |
## Account Support
permissionless.js supports 6 types of accounts natively (but can easily be extended to support any compatible ERC-4337 account). The below table details which EntryPoints each account is valid for.
| Account | EntryPoint v0.8 | EntryPoint v0.7 | EntryPoint v0.6 |
| :------------ | :-------------- | :------------------------ | :------------------------ |
| Safe | ❌ | ✅ (Safe v1.4.1 and above) | ✅ (Safe v1.4.1 and above) |
| SimpleAccount | ✅ | ✅ | ✅ |
| Kernel | ❌ | ✅ | ✅ |
| Biconomy | ❌ | ❌ | ✅ |
| LightAccount | ❌ | ✅ | ✅ |
| TrustWallet | ❌ | ❌ | ✅ |
| Thirdweb | ❌ | ✅ | ✅ |
## How to create and use a Biconomy account with permissionless.js
[Biconomy Smart Account](https://github.com/bcnmy/scw-contracts) is a smart account building on the core concepts of Gnosis and Argent safes. You can use Biconomy with plugins such as session keys, and even write your own plugins.
### Steps
:::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/biconomy.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Biconomy account.
```ts
// [!include ~/snippets/accounts/biconomy.ts:clients]
```
#### Create the signer
Biconomy accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Biconomy account validates ECDSA signatures. [Any signer](/references/permissionless/how-to/signers) can be used as a signer for the Biconomy account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/biconomy.ts:signer]
```
#### Create the Biconomy account
With a signer, you can create a Biconomy account as such:
```ts
// [!include ~/snippets/accounts/biconomy.ts:smartAccount]
```
The Biconomy account address is computed deterministically from the signer, but you can optionally pass an `index` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created Biconomy account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/biconomy.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submitBatch]
```
:::
## How to create and use a Coinbase smart account with permissionless.js
:::warning[Warning]
You should still use permissionless to fetch gas prices. As the bundler might not accept the prices fetched from viem. To learn how to use permissionless to fetch gas prices, [check out this guide](/references/permissionless/reference/pimlico-actions/getUserOperationGasPrice#getuseroperationgasprice).
:::
Coinbase smart wallet accounts are supported natively by viem. We recommend using the native viem implementation for which the documentation [can be found here](https://viem.sh/account-abstraction/accounts/smart/toCoinbaseSmartAccount).
## How to use an ERC-7579 compatible smart account with permissionless.js
[ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) defines a standard for modular smart account interfaces. It also defines behavior for interoperability with minimal restrictions for accounts and modules.
Currently Safe and Kernel are the only smart accounts that implements ERC-7579.
For this guide, we will use the Safe smart accounts as an example. If you would like to find out more about the Safe smart account, you can check out the [Safe-specific guide](/references/permissionless/how-to/accounts/use-safe-account).
This guide will show you how to create and use a ERC-7579 compatible smart account with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/erc7579/erc7579.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the SafeAccount.
```ts
// [!include ~/snippets/erc7579/erc7579.ts:clients]
```
#### Create the SafeAccount
:::info
For a full list of options for creating a SafeAccount, take a look at the reference documentation page for [`toSafeSmartAccount`](/references/permissionless/reference/accounts/toSafeSmartAccount).
:::
You can also pass an `address` to use an already created SafeAccount.
```ts
// [!include ~/snippets/erc7579/erc7579.ts:smartAccount]
```
:::warning
The Safe account requires a new `safe4337ModuleAddress` & `erc7579LaunchpadAddress` for it to be 7579 compatible.
:::
:::warning
The address `0x000000333034E9f539ce08819E12c1b8Cb29084d` belongs to Rhinestone. By designating them as attesters, you authorize that only modules explicitly approved by Rhinestone can be installed on your safe.
:::
#### Create the smart account client and extend it with the ERC7579 actions
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/erc7579/erc7579.ts:smartAccountClient]
```
#### Interact with the 7579 actions
You can install a module on the Safe account using the `installModule` action.
```ts
// [!include ~/snippets/erc7579/erc7579.ts:installModule]
```
:::warning
InstallModule returns user operation hash, not transaction hash, you must use `waitForUserOperationReceipt` to wait for the user operation to be included on-chain.
:::
You can also call all other ERC7579 actions, example `supportsExecutionMode`.
```ts
// [!include ~/snippets/erc7579/erc7579.ts:supportsExecutionMode]
```
::::
## How to create and use a Kernel account with permissionless.js
:::info
ZeroDev, the author of Kernel, maintains their own in-house SDK built closely on top of permissionless.js that you can use for the account system while still plugging in all the other components from permissionless.js. Take a look at [their documentation](https://docs.zerodev.app) for more information.
:::
[Kernel](https://github.com/zerodevapp/kernel) is a **modular smart account** that supports plugins, which are smart contracts that extend the account's functionalities. You can use Kernel with popular plugins such as session keys and account recovery, and even write your own plugins. Kernel is compatible with [ERC-7579](https://erc7579.com/).
### Picking an EntryPoint
Kernel is compatible with EntryPoint versions v0.6 and v0.7.
If you are building a new application, we recommend using EntryPoint v0.7, which gives you the latest and greatest features and optimizations. If you already have an application using Kernel on EntryPoint v0.6, just stick with it -- it will be supported indefinitely.
In this guide, we will use EntryPoint v0.7.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/kernel.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Kernel account.
```ts
// [!include ~/snippets/accounts/kernel.ts:clients]
```
#### Create the signer
Kernel accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Kernel account validates ECDSA signatures. [Any signer](/references/permissionless/how-to/signers) can be used as a signer for the Kernel account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/kernel.ts:signer]
```
#### Create the Kernel account
:::info
For a full list of options for creating a Kernel account, take a look at the reference documentation page for [`toKernelSmartAccount`](/references/permissionless/reference/accounts/toKernelSmartAccount).
:::
With a signer, you can create a Kernel account as such:
```ts
// [!include ~/snippets/accounts/kernel.ts:smartAccount]
```
The Kernel address is computed deterministically from the signer, but you can optionally pass an `index` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created Kernel account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/kernel.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/kernel.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/kernel.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/kernel.ts:submitBatch]
```
::::
## How to create and use a LightAccount with permissionless.js
[LightAccount](https://github.com/alchemyplatform/light-account) is the smart account implementation made by Alchemy and inspired by SimpleAccount.
It has a few additional [features](https://github.com/alchemyplatform/light-account?tab=readme-ov-file#features), such as transferrable ownership and upgradability.
This guide will show you how to create and use a LightAccount with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/light.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the LightAccount.
```ts
// [!include ~/snippets/accounts/light.ts:clients]
```
#### Create the LightAccount
:::info
For a full list of options for creating a LightAccount, take a look at the reference documentation page for [`toLightSmartAccount`](/references/permissionless/reference/accounts/toLightSmartAccount).
:::
You can also pass an `address` to use an already created LightAccount.
```ts
// [!include ~/snippets/accounts/light.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/light.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/light.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/light.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/light.ts:submitBatch]
```
::::
## How to use MetaMask Smart Accounts with permissionless.js
:::info
MetaMask maintains their own in-house SDK built closely on top of viem that you can use for the account system while still plugging in all the other components from permissionless.js. Take a look at [their documentation](https://docs.metamask.io/delegation-toolkit/how-to/send-user-operation) for more information.
:::
The [MetaMask Delegation Toolkit](https://docs.metamask.io/delegation-toolkit/get-started/quickstart) is a collection of tools for creating MetaMask Smart Accounts. A smart account can delegate to another signer with granular permission sharing. It is built over [ERC-7710](https://eip.tools/eip/7710) and [ERC-7715](https://eip.tools/eip/7715) to support a standardized minimal interface. Requesting ERC-7715 permissions and redeeming ERC-7710 delegations are experimental features.
There are two types of accounts involved in delegation:
1. **Delegator account**: A smart account that supports programmable account behavior and advanced features such as multi-signature approvals, automated transaction batching, and custom security policies.
2. **Delegate account**: An account (smart account or EOA) that receives the delegation from the delegator account to perform actions on behalf of the delegator account.
You can use both accounts with permissionless.js.
### Installation
We will be using MetaMask's official SDK to create a smart account.
:::code-group
```bash [npm]
npm install permissionless viem @metamask/delegation-toolkit
```
```bash [yarn]
yarn add permissionless viem @metamask/delegation-toolkit
```
```bash [pnpm]
pnpm install permissionless viem @metamask/delegation-toolkit
```
```bash [bun]
bun install permissionless viem @metamask/delegation-toolkit
```
:::
### Delegator Account
::::steps
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the MetaMask smart account.
```ts
// [!include ~/snippets/accounts/metamask.ts:clients]
```
#### Create the signer
MetaMask Smart Accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/metamask.ts:signer]
```
#### Create the delegator smart account
:::info
For a full list of options for creating a MetaMask smart account, take a look at the MetaMask's documentation page for [`toMetaMaskSmartAccount`](https://docs.metamask.io/delegation-toolkit/how-to/create-smart-account).
:::
With a signer, you can create a MetaMask smart account as such:
```ts
// [!include ~/snippets/accounts/metamask.ts:smartAccount]
```
#### Create the smart account client
```ts
// [!include ~/snippets/accounts/metamask.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/metamask.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/metamask.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/metamask.ts:submitBatch]
```
::::
### Delegate Account
A delegate account is an account that receives the delegation from the delegator account to perform actions on behalf of the delegator account.
To create a delegate account, we will follow the following steps:
1. Create a delegate signer
2. Create the delegate smart account
3. Create a delegation using delegator smart account
4. Sign the delegation
5. Send transactions using delegate smart account with signed delegation
::::steps
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the MetaMask smart account.
```ts
// [!include ~/snippets/accounts/metamask.ts:clients]
```
#### Create the signer
MetaMask Smart Accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/metamask.ts:delegateSigner]
```
#### Create the delegate smart account
:::info
For a full list of options for creating a MetaMask smart account, take a look at the MetaMask's documentation page for [`toMetaMaskSmartAccount`](https://docs.metamask.io/delegation-toolkit/how-to/create-smart-account).
:::
With a delegate signer, you can create a MetaMask delegate account as such:
```ts
// [!include ~/snippets/accounts/metamask.ts:delegateSmartAccount]
```
#### Create a delegation
This example passes an empty caveats array, which means the delegate can perform any action on the delegator's behalf. We recommend restricting the delegation by adding caveat enforcers.
```ts
// [!include ~/snippets/accounts/metamask.ts:createDelegation]
```
#### Sign the delegation
```ts
// [!include ~/snippets/accounts/metamask.ts:signDelegation]
```
#### Create the smart account client
```ts
// [!include ~/snippets/accounts/metamask.ts:delegateSmartAccountClient]
```
#### Send transactions using signed delegation
```ts
// [!include ~/snippets/accounts/metamask.ts:sendTransactionWithDelegation]
```
::::
## How to create and use a Biconomy Nexus account with permissionless.js
[Biconomy Nexus Smart Account](https://github.com/bcnmy/nexus) is a smart account building on the core concepts of ERC-7579. You can use Nexus with plugins such as session keys, and even write your own plugins.
### Steps
:::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/biconomy.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Nexus account.
```ts
// [!include ~/snippets/accounts/biconomy.ts:clients]
```
#### Create the signer
Nexus accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Nexus account validates ECDSA signatures. [Any signer](/references/permissionless/how-to/signers) can be used as a signer for the Nexus account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/biconomy.ts:signer]
```
#### Create the Nexus account
With a signer, you can create a Nexus account as such:
```ts
// [!include ~/snippets/accounts/biconomy.ts:smartAccount]
```
The Nexus account address is computed deterministically from the signer, but you can optionally pass an `index` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created Nexus account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/biconomy.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submitBatch]
```
:::
## How to create and use a Safe account with multiple signers
[Safe](https://safe.global) is the most battle-tested Ethereum smart account provider. With their recent release of their ERC-4337 module, it is now possible to plug in Safe accounts to ERC-4337 bundlers and paymasters. This guide will walk you through how to create and use a Safe account with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Safe account.
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:clients]
```
#### Get the owner addresses
The Safe account will need to have a signer to sign user operations. In permissionless.js, the default Safe account validates ECDSA signatures. [Any permissionless.js-compatible signer](/references/permissionless/how-to/signers) can be used for the Safe account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:signer]
```
#### Create the Safe account
:::info
For a full list of options for creating a Safe account, take a look at the reference documentation page for [`toSafeSmartAccount`](/references/permissionless/reference/accounts/toSafeSmartAccount).
:::
With a signer, you can create a Safe account as such:
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:smartAccount]
```
:::info
You can also create a Safe account with 7579 module, read more about it [here](/references/permissionless/how-to/accounts/use-erc7579-account).
:::
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:smartAccountClient]
```
#### Prepare a user operation
Since we may not have access to all the signers at once, we should prepare a user operation and then submit it later after all the signers have signed.
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:prepare]
```
#### Collect signatures
You can use the `SafeSmartAccount.signUserOperation` method to collect signatures from the signers.
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:sign]
```
#### Submit the user operation
Once you have the final signature, you can submit the user operation.
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:submit]
```
#### Understanding the errors
If you're getting an error that starts with `GS`, it probably means that something went off with the Safe account. Checkout the Safe error codes [here](https://github.com/safe-global/safe-smart-account/blob/main/docs/error_codes.md).
::::
## How to create and use a Safe account with permissionless.js
[Safe](https://safe.global) is the most battle-tested Ethereum smart account provider. With their recent release of their ERC-4337 module, it is now possible to plug in Safe accounts to ERC-4337 bundlers and paymasters. This guide will walk you through how to create and use a Safe account with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/safe.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Safe account.
```ts
// [!include ~/snippets/accounts/safe.ts:clients]
```
#### Create the signer
The Safe account will need to have a signer to sign user operations. In permissionless.js, the default Safe account validates ECDSA signatures. [Any permissionless.js-compatible signer](/references/permissionless/how-to/signers) can be used for the Safe account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/safe.ts:signer]
```
#### Create the Safe account
:::info
For a full list of options for creating a Safe account, take a look at the reference documentation page for [`toSafeSmartAccount`](/references/permissionless/reference/accounts/toSafeSmartAccount).
:::
With a signer, you can create a Safe account as such:
```ts
// [!include ~/snippets/accounts/safe.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/safe.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/safe.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/safe.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/safe.ts:submitBatch]
```
#### Understanding the errors
If you're getting an error that starts with `GS`, it probably means that something went off with the Safe account. Checkout the Safe error codes [here](https://github.com/safe-global/safe-smart-account/blob/main/docs/error_codes.md).
::::
## How to create and use a SimpleAccount with permissionless.js
[SimpleAccount](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/accounts/SimpleAccount.sol) is the original reference sample implementation of an ERC-4337 made by the Eth-Infinitism team. Despite being a reference implementation, it is widely used in production. It allows for a single EOA signer to sign user operations for the account. This guide will show you how to create and use a SimpleAccount with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/simple.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the SimpleAccount.
```ts
// [!include ~/snippets/accounts/simple.ts:clients]
```
#### Create the SimpleAccount
:::info
For a full list of options for creating a SimpleAccount, take a look at the reference documentation page for [`toSimpleSmartAccount`](/references/permissionless/reference/accounts/toSimpleSmartAccount).
:::
You can create a SimpleAccount with the canonical module addresses by specifying the factory address the account will be deployed from. You can also pass an `address` to use an already created SimpleAccount.
```ts
// [!include ~/snippets/accounts/simple.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/simple.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/simple.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/simple.ts:submitNft]
```
::::
## How to create and use a Thirdweb account with permissionless.js
### Picking an EntryPoint
Thirdweb is compatible with EntryPoint versions v0.6 and v0.7. In this guide, we will use EntryPoint v0.7.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/thirdweb.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the account.
:::info
Get your client ID from the [Thirdweb dashboard](https://thirdweb.com/dashboard/settings/api-keys) for free RPC access.
:::
```ts
// [!include ~/snippets/accounts/thirdweb.ts:clients]
```
#### Create the signer
Thirdweb's accounts can work with any Viem signing account [available in permissionless.js](/references/permissionless/how-to/signers).
In this guide, we'll use a private key to create the signer:
```ts
// [!include ~/snippets/accounts/thirdweb.ts:signer]
```
#### Create the thirdweb account
With your new signer, you can create a thirdweb account.
```ts
// [!include ~/snippets/accounts/thirdweb.ts:smartAccount]
```
The account's address is computed deterministically from the signer, but you can optionally pass an `salt` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created thirdweb account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/thirdweb.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/thirdweb.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/thirdweb.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/thirdweb.ts:submitBatch]
```
::::
## How to create and use a Trust smart account with permissionless.js
:::info
[Trust Wallet](https://trustwallet.com/), the author of [Barz](https://github.com/trustwallet/barz), provides a smart contract security monitoring service for each and every Barz deployed on-chain including Barz created by SDK.
This is a service that TrustWallet provides to builders to build innovative products on top of a secure foundation.
Monitoring will automatically start as soon as the Barz account is deployed on-chain; for projects wanting to get security monitoring information, reach out to the smart wallet channel in [TrustWallet Discord](https://discord.gg/trustwallet).
:::
Trust Wallet is one of the most trusted wallet provider empowering more than 122 million users and is the first Web3 wallet to be [certified by ISO](https://trustwallet.com/security).
After thorough development and extensive security audits, Trust Wallet launched the [Smart Wallet](https://trustwallet.com/swift) powered by account abstraction.
With the recent opensource of their Smart Wallet system Barz, their smart account solution, together with their security infrastructure is disclosed for public to support builders build products on a secure foundation.
This guide will walk you through how to create and use a Barz account with permissionless.js
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/trustwallet.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Trust Smart Account.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:clients]
```
#### Create the TrustAccount
:::info
For a full list of options for creating a TrustAccount, take a look at the reference documentation page for [`toTrustSmartAccount`](/references/permissionless/reference/accounts/toTrustSmartAccount).
:::
You can also pass an `address` to use an already created TrustAccount.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/trustwallet.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:submitBatch]
```
::::
## How to debug dropped user operations
If a user operation is dropped in the mempool of the bundler after initially being accepted by it, the error can not be propagated by the bundler to the user. To debug what happened to these user operations, we recommend using the [User Operation Tracking](https://dashboard.pimlico.io/debugging/tracking) page on our dashboard, which will show you the exact flow of the user operation stage-by-stage through Pimlico's bundler with timestamps, including any errors it encounters.
An example screenshot of the User Operation Tracking page is shown below:

## Debugging User Operations
When debugging user operations, Pimlico provides powerful tools to help you identify and resolve issues quickly. This guide covers the essential debugging techniques using request logs and the debugging section.
### Using Request Logs
Request logs provide detailed insights into every API call made to Pimlico's infrastructure, helping you trace and troubleshoot user operations effectively.
#### Accessing Request Logs
Navigate to the **Request Logs** section in the left sidebar of your Pimlico dashboard. This displays a comprehensive list of all recent requests made using your API keys.

#### Filtering Requests
The request logs interface provides powerful filtering options to help you quickly find the specific requests you're looking for:
* **Date Range**: Narrow down requests to a specific time period using preset ranges like "Last 7 days" or define a custom date range.
* **API Key**: If you have multiple API keys, select a specific one to view only its associated requests.
* **Chain**: Filter by blockchain network to view only requests made on that chain (e.g., Base Sepolia, Ethereum Mainnet).
* **Sender**: Enter a sender address to view all requests originating from a specific account. Particularly useful when debugging issues for a specific user.
* **Method**: Filter by RPC method to see specific operation types (e.g., `eth_sendUserOperation`, `eth_estimateUserOperationGas`).
* **Response Type**: Choose between "Success" or "Error" to view only successful operations or failed ones.
* **Trace ID**: Search by a specific trace ID from an error log or previous debugging session to jump directly to that request.
#### Common Debugging Scenarios
**Failed User Operations**
To investigate failed user operations:
1. Set Response Type to "Error"
2. Filter by Method: `eth_sendUserOperation`
3. Select the relevant time period
This shows all failed user operations with their error messages, helping identify patterns or specific issues.
**Gas Estimation Issues**
For gas estimation problems:
1. Filter by Method: `eth_estimateUserOperationGas`
2. Look for error responses
3. Check request payloads to ensure correct parameters
#### Debugging Individual Requests
Once you've located the request you want to investigate, click the **debug icon** (bug symbol) next to any request entry to access detailed debugging information.

The debugging page opens with all request details automatically populated, including user operation, entrypoint, chain, timestamp and type of request, e.g `eth_sendUserOperation`, `eth_estimateUserOperationGas`:

Click on the "Debug using Tenderly" will then redirect you to Tenderly's simulation link.
### Using the Debugging Section
The debugging section can be accessed in two ways:
1. **From Request Logs**: As shown above, clicking the debug icon next to any request automatically populates all details
2. **Direct Access**: Navigate to **Debugging → Simulate** in the left sidebar to manually enter information for custom debugging
When accessing the debugging section directly, you can manually input operation details for custom debugging scenarios:
* **User Operation**: Paste the complete user operation object or hash
* **Entry Point**: Specify the entry point contract address (defaults to standard ERC-4337 entry points)
* **Chain**: Select the blockchain network where the operation was/will be executed
* **Timestamp**: Optionally provide a specific block timestamp for historical debugging
Once your operation details are loaded (either automatically or manually), you can
click on the "Debug using Tenderly" which will then redirect you to Tenderly's simulation link.

## ERC-20 Paymaster Architecture
:::info
You can view the whole ERC-20 Paymaster contract [in our repository](https://github.com/pimlicolabs).
This paymaster is an onchain contract, we can not guarantee that using the paymaster is risk-free. Please use the paymaster at your own risk.
:::
### Design
Pimlico's ERC-20 Paymaster is an ERC-4337 Paymaster that relies on an offchain API, powered by Pimlico, to supply the user with an up-to-date token price, alongside a signature from a valid signer address. The paymaster is able to pay for the gas fees of your users in exchange for ERC-20 tokens that are drawn from the user.
### Estimating the amount of tokens required for a user operation
After calling the `pimlico_getTokenQuotes` function, the Pimlico API will return two important values, `exchangeRate` and `postOpGas`. You can use these two values, alongside information about the user operation you're looking to sponsor, to get an estimate of the amount of tokens required for the user operation.
You can do this by using [this onchain function](https://github.com/pimlicolabs) on the paymaster.
## ERC-20 Paymaster Contract Addresses
Below are the contract addresses for the ERC-20 Paymaster contracts that are currently deployed.
Pimlico's ERC-20 Paymaster is deployed at the following addresses:
| EntryPoint Version | Paymaster Contract Address |
| :----------------- | :----------------------------------------- |
| v0.6 | 0x6666666666667849c56f2850848ce1c4da65c68b |
| v0.7 | 0x777777777777AeC03fd955926DbF81597e66834C |
| v0.8 | 0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402 |
All currently deployed paymasters have the same contract address on all chains, but this might not remain the case in the future.
For a list of supported tokens, please see the [supported tokens page](/references/paymaster/erc20-paymaster/supported-tokens).
## ERC-20 Paymaster FAQs
### What is a paymaster?
A Paymaster is a special smart contract under the [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) specification that user operations are able to delegate the responsibility of gas fee payments to. This means that ERC-4337 smart contract wallets no longer need to necessarily be responsible for directly paying gas fees in ETH. The paymaster contracts are able to use custom logic (with certain limitations) to decide whether or not they are willing to sponsor a user operation.
### What is an ERC-20 Paymaster?
An ERC-20 Paymaster is a specific type of paymaster that is willing to sponsor the gas fees for a user operation if and only if the smart contract wallet pays the paymaster for it in an ERC-20 token like USDC, DAI, etc. In effect, this allows smart contract wallets to pay for gas fees purely in ERC-20 tokens and means, if designed correctly, they never need to hold any native tokens like ETH.
### How do I use Pimlico's ERC-20 Paymaster?
We wrote a [tutorial](/references/permissionless/tutorial/tutorial-2) that takes you through the whole flow of deploying a Safe account and sending your first user operation sponsored with USDC.
We also have a [how-to guide](/guides/how-to/erc20-paymaster/how-to/use-paymaster) that explains how to use the ERC-20 Paymaster in your app's flow.
:::steps
### What is the contract address of the ERC-20 Paymaster?
The currently supported tokens are listed [here](/references/paymaster/erc20-paymaster/contract-addresses).
Theoretically, we can support any token on any EVM chains that have Chainlink interface compatible oracle support. If you have a token that you would like supported, please [reach out to us](https://t.me/pimlicoHQ)!
### Where can I find the ERC-20 Paymaster contract?
You can find the contract source code [here](https://github.com/pimlicolabs).
### How can I use a token that is not currently supported by Pimlico?
Please get in touch with us. For enterprise customers we will do what we can to see if we can add support for the token you are interested in.
### Does Pimlico take a fee?
Yes, the owner takes a fee that is baked into the `exchangeRate` returned by the API. This markup serves to compensate the onwer for maintaining the infrastructure and covering risks associated with price fluctuations and slippage risk.
:::
## ERC-20 Paymaster

Pimlico's ERC-20 Paymaster is a ERC-4337 paymaster that is able to pay for the gas fees of your users in exchange for ERC-20 tokens.
### Tutorials
Learning-oriented lessons that take you through a series of steps to complete a project. Most useful when you want to get started with Pimlico. They all involve the Alto Bundler.
* [Tutorial 2](/references/permissionless/tutorial/tutorial-2) leverages the ERC-20 Paymaster to sponsor the gas fees for a user operation with USDC.
### How-to Guides
How-to guides are recipes. They guide you through the steps involved in addressing key problems and use-cases. They are most useful when you need a quick solution.
* Learn how to [use the ERC-20 Paymaster, including the relevant RPC methods as part of your app's flow](/guides/how-to/erc20-paymaster/how-to/use-paymaster).
### References
References provide specific technical descriptions. They are most useful when you need detailed information about Pimlico's APIs.
* See the [official contract addresses of the ERC-20 Paymaster on each chain](/references/paymaster/erc20-paymaster/contract-addresses).
### Conceptual Guides
Conceptual guides provide high-level explanations of concepts. They are most useful when you need to understand the big picture.
* Understand more about the [architecture of our ERC20 Paymaster](/references/paymaster/erc20-paymaster/architecture).
* Check out our [FAQs](/references/paymaster/erc20-paymaster/faqs).
import SupportedTokens from '../../../../../data/supported-tokens.md'
## Supported Tokens
List of tokens that are supported by Pimlico's ERC-20 Paymaster. You can view the list of enabled tokens on your [Pimlico dashboard](https://dashboard.pimlico.io/billing/plan).
All users have access to the following tokens
USDC on the following chains:
* `Ethereum`
* `Base`
* `Polygon`
* `Arbitrum`
* `Optimism`
* `Sepolia`
* `Base Sepolia`
* `Polygon Amoy`
* `Arbitrum Sepolia`
* `Optimism Sepolia`
USDT on the following chains:
* `Ethereum`
* `Gnosis`
* `Linea`
* `Optimism`
* `Base`
* `Polygon`
* `Arbitrum`
* `BNB`
EURe on the following chains:
* `Ethereum`
* `Gnosis`
* `Polygon`
* `Arbitrum`
* `Scroll`
* `Linea`
* `Sepolia`
* `Arbitrum Sepolia`
* `Scroll Sepolia`
* `Linea Sepolia`
* `Polygon Amoy`
* `Chiado Testnet`
sETH on the following chains:
* `Ethereum`
* `Optimism`
wsETH on the following chains:
* `Ethereum`
* `Optimism`
:::info
If you have a token that you would like to see added, please [reach out to us](https://t.me/pimlicoHQ).
:::
## How to claim a gas grant
This guide explains how to claim a gas grant that has been allocated to you through an organization like Arbitrum or Safe on Pimlico.
### Prerequisites
Before claiming your gas grant, ensure you have:
* A Pimlico Dashboard account, if not create one at [dashboard.pimlico.io](https://dashboard.pimlico.io)
* A valid claim link from the grant provider
### Claim the grant
::::steps
#### Access the claim link
The grant provider e.g., a chain, protocol team, or hackathon organizer, will share a unique claim link with you. This link can only be claimed once, so handle it carefully.
#### Log in to your Pimlico account
Before clicking the claim link, ensure you are logged in to your Pimlico Dashboard with the correct account. The grant will be permanently associated with the account that claims it.

#### Choose the right account context
If you want to claim the grant for your organization rather than your personal account:
1. Check the bottom left of the sidebar to see which account is currently active
2. If you're in your personal account but want to claim for your organization, click on your account name to switch to your organization

:::info
Once claimed, the grant cannot be transferred between accounts, so make sure you're in the correct account context before proceeding.
:::
#### Click the claim link
After ensuring you're logged in with the correct account, click the claim link provided by the grant provider. You'll be redirected to a confirmation page showing:
* The grant amount allocated
* The sponsorship policy details
* Any usage restrictions or expiration dates
#### Confirm the claim
Review the grant details and click "Claim Grant" to complete the process. You'll receive a confirmation that the grant has been successfully added to your account.
::::
### Verify your claimed grant
To check that your grant has been successfully claimed and is active:
Navigate to the **Sponsorship Policies** page in your Pimlico Dashboard by clicking the link in the left sidebar or visiting [dashboard.pimlico.io/sponsorship-policies](https://dashboard.pimlico.io/sponsorship-policies)
Look for your newly claimed grant in the list of active policies.

:::tip
If you don't see your grant immediately after claiming, try refreshing the page. If it still doesn't appear, ensure you're viewing the correct account (personal vs. organization) that was used to claim the grant.
:::
### Common issues
#### "Link already claimed" error
Each claim link can only be used once. If you see this error, the link has already been claimed by another account.
#### "Invalid or expired link"
Some grants have expiration dates. Contact the grant provider if you believe you received an invalid link.
#### Grant not showing in dashboard
Make sure you claimed the grant with the correct account (personal vs. organization). Grants are tied to the specific account that claimed them.
### Next steps
* Learn how to [use sponsorship policies](/guides/how-to/sponsorship-policies) to sponsor user operations
* Monitor your grant usage through the [dashboard](https://dashboard.pimlico.io)
## How to create a gas program on Pimlico
:::info
This guide is for chain operators who want to create a gas program on Pimlico. If you are a developer looking to use a gas program made by someone else or looking to sponsor your own user operations, you should instead look in the [sponsorship policies guide](/guides/how-to/sponsorship-policies).
:::
If you are a chain operator and you would like to be able to easily give out and track gas credits to developers building on your chain, you can create and manage a gas station program using Pimlico sponsorship policies.
:::steps
#### Create a Pimlico account
If you don't already have a Pimlico account, you can create one by visiting the [Pimlico dashboard website](https://dashboard.pimlico.io).
#### Create an switch to an organization (optional)
If you are not already in an organization, you can create one by clicking on the "Create Organization" button on the dashboard.

#### Create a sponsorship policy
The gas program will be managed as a sponsorship policy. You can create a sponsorship policy by clicking on the "Create Policy" button on the [sponsorship policies page](https://dashboard.pimlico.io/sponsorship-policies).

As part of creating the policy, we recommend you consider setting a couple of fields:
* Name (to identify the policy publicly, e.g. "XYZChain Gas Program")
* Enabled Chains (to specify that the policy should only apply to your chain)
* Global Maximum (to hardcode the maximum dollars of gas that can be spent)
You can also consider setting some other fields, like custom webhooks, start and end dates, and more.
#### Give authorizations to developers to use the policy
To give authorizations to developers to use this policy, start by clicking on the newly created policy, which should take you to the policy's dashboard.
In it, you should see a "Spending Authorizations" table where you can add new authorizations.

Now click the "Create Spending Authorization" button and fill in the details of the authorization, including the name of the authorization, how many dollars of gas you're letting the developer spending, and the reset period.

After you have created the policy, you should see a page where you can copy a link that you can send to the grantee to claim the spending authorization.
To use the policy, the grantee will need to have a Pimlico account and will need to click on the link to claim the authorization, and will then be able to use that authorization to pay for gas on your chain as if it was a policy that they owned. More information about using sponsorship policies can be found in the [relevant guide](/guides/how-to/sponsorship-policies).
:::
## How to use the claimed gas grant
### Usage of Sponsorship Policies with permisionless.js
If you are using [permissionless.js](/references/permissionless), you can use the `PimlicoPaymasterClient` to use sponsorship policies.
:::steps
#### Create the clients
First we must create the public, (optionally) paymaster clients that will be used to interact with the SimpleAccount.
```ts
// [!include ~/snippets/references/platform/sponsorship-policies/index.ts:client]
```
#### Create an account
Now, create an account. This can any of the accounts supported by permissionless.js or custom accounts conforming to the interface. For this example, we'll use a Simple account.
```ts
// [!include ~/snippets/references/platform/sponsorship-policies/index.ts:account]
```
#### Create the smart account client with middleware
When creating the `smartAccountClient`, we can pass in a `middleware.sponsorUserOperation` function that will be called before a user operation is signed and sent.
This is where we can pass in the `sponsorshipPolicyId` that we want to use.
```ts
import { sepolia } from "viem/chains";
import { createSmartAccountClient } from "permissionless";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { entryPoint07Address } from "viem/account-abstraction";
export const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: sepolia,
bundlerTransport: http(
"https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY",
),
paymaster: pimlicoClient,
paymasterContext: {
sponsorshipPolicyId: "your_claimed_sponsorship_policy_id", // [!code ++]
},
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast; // if using pimlico bundlers
},
},
});
```
#### Send a sponsored transaction
Finally, let's submit a transaction from the smart account. We will send a transaction to the `0xd8da6bf26964af9d7eed9e03e53415d37aa96045` (vitalik.eth) address with `0x1234` as example `callData`.
```typescript
// [!include ~/snippets/tutorial-1.ts:submit]
```
Congratulations, you have sent a transaction using a gas grant! 🎉
:::
## Using Pimlico with Privy
This guide shows you how to enable Smart accounts on your Privy account powered by Pimlico's infrastructure.
### Why
Privy provides a seamless way to onboard users with embedded wallets and social logins. By enabling Smart accounts powered by Pimlico's infrastructure on Privy, you can:
* Sponsor gas fees to improve user experience
* Batch multiple transactions
* Use session keys for automations and uninterrupted confirmation
* Allow for additional recovery flows
### Prerequisites
Before starting, ensure you have:
* A Privy account with access to the dashboard
* Your app configured in Privy
### Configuration Steps
:::steps
#### Create an API key
[Create your API key](/guides/create-api-key) to access our infrastructure.
#### Enable Smart Wallets in the Privy Dashboard
1. Navigate to your Privy Dashboard and locate the **Smart wallets** section in the left sidebar under **Wallet Infrastructure**.
2. Toggle on **Enable smart wallets for your app**. This allows users to log in with email, socials, passkey, or wallet and create a smart wallet for them.
3. Choose your preferred smart account implementation. Pimlico supports all types of smart accounts. For this guide, we'll use **Safe** as an example.

#### Quick setup
You can either configure each chain manually or configure all chains in one go using **Quick setup**. We recommend using quick setup as it is easier and will enable Pimlico on all the chains.

#### Configure Chains
1. Select Pimlico as the **Bundler and paymaster provider**.
2. Enter your Pimlico API key. You can [create an API key here](/guides/create-api-key).
3. Select the chains you want to configure. Pimlico supports all the chains on Privy (and more).

You can check the complete list of chains we support [here](/guides/supported-chains) and can reach out to us if you need help in supporting a chain that Privy doesn't support.
#### Save Configuration
Click **Save and close** to apply your chain configuration, then click **Save changes** on the main Smart wallets page to persist all settings.

:::
:::tip[Congratulations! 🎉]
You have now successfully enabled smart accounts on your Privy account! Your users can now benefit from gas sponsorship, transaction batching, and other advanced features powered by Pimlico's infrastructure.
:::
## How to conditionally sponsor a user operation
With permissionless.js and Viem, you can conditionally sponsor a user operation. This can be useful in situations like:
* Sponsor only first 10 transactions
* Sponsor if they have a specific NFT
Let's take an example where you want to sponsor only the first 10 transactions.
:::steps
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the SimpleAccount.
```ts
// [!include ~/snippets/how-to/paymasters/conditional-sponsoring.ts:client]
```
#### Create an account
Now, create an account. This can any of the accounts supported by permissionless.js or custom accounts conforming to the interface.
```ts
// [!include ~/snippets/how-to/paymasters/conditional-sponsoring.ts:account]
```
#### Create the smart account client with the custom paymaster logic
We can sponsor a user operation conditionally by setting the paymaster field in sendTransaction only when the sponsorship conditions are met.
```ts
// [!include ~/snippets/how-to/paymasters/conditional-sponsoring.ts:smart-account-client]
```
:::
## Extending sponsorship duration
By default, verifying paymaster sponsorships are valid for 10 minutes. It’s now possible to set a custom validity period of up to 24 hours.
#### Use Cases
Extended sponsorship durations are helpful in several scenarios:
1. Multi-sigs – when there may be delays in collecting signatures
2. Scheduled transactions – For userOperations that are submitted at a later time
### Important Considerations
Your offchain Pimlico balance is held as a prefund for the duration that the paymaster signature is valid for. It’s released either when your sponsored userOp is included on-chain or when the signature expires.
* The maximum allowed duration is 24 hours (86,400 seconds).
* ERC-20 sponsorships always have a validity period of 10 minutes.
### Implementation
You can set the sponsorship duration through the `validForSeconds` parameter in the `paymasterContext`
:::code-group
```typescript [main.ts]
const userOpHash = await smartAccountClient.sendUserOperation({
calls: [
{
to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
value: 0n,
data: "0x"
},
],
// sponsorship will be valid for 1 hour
paymasterContext: { // [!code ++]
validForSeconds: 3600, // [!code ++]
}, // [!code ++]
})
```
```typescript [clients.ts]
import { sepolia } from "viem/chains"
import { createPublicClient, http } from "viem"
import { privateKeyToAccount } from "viem/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico"
import { toSafeSmartAccount } from "permissionless/accounts"
import { createSmartAccountClient } from "permissionless"
const privateKey = process.env.PRIVATE_KEY
const apiKey = process.env.PIMLICO_API_KEY
export const publicClient = createPublicClient({
chain: sepolia,
transport: http(),
})
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=${apiKey}`
const pimlicoClient = createPimlicoClient({
transport: http(pimlicoUrl),
entryPoint: {
address: entryPoint07Address,
version: "0.7",
},
})
const account = await toSafeSmartAccount({
client: publicClient,
owners: [privateKeyToAccount(privateKey)],
entryPoint: {
address: entryPoint07Address,
version: "0.7",
},
version: "1.4.1",
})
export const smartAccountClient = createSmartAccountClient({
account,
chain: sepolia,
bundlerTransport: http(pimlicoUrl),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
},
},
})
```
:::
## How to Protect Your API Keys
Keeping your API keys secure is essential. Here are three main ways to protect them:
* Restrict access to your API keys.
* Use sponsorship policies.
* Use a proxy server to handle requests to Pimlico.
### Restrict Access to Your API Keys
You can limit how your API keys are used by modifying their permissions on the [API Keys page](https://dashboard.pimlico.io/apikeys). Restrictions can include:
* **IP addresses:** Specify which IPs are allowed to make requests.
* **User agents:** Limit access to specific browsers, SDK versions, or other user agents.
* **Origins:** Define which domains are permitted to make requests.
Additionally, you can enable or disable specific API features for each key, such as:
* Bundler methods.
* Paymaster methods.
* Account APIs.
### Use Sponsorship Policies
Sponsorship Policies are hosted policies that allow you to define custom rules for sponsorships.
You can put limits to the global amount of sponsorships, the amount of sponsorships per user, and per user operation.
You can read more about Sponsorship Policies [here](/guides/how-to/sponsorship-policies).
### Use a Proxy Server
You can create a proxy server to handle requests to Pimlico. This way, you can have custom authentication, rate limiting, and other features before forwarding requests to Pimlico.
Here's an example of how you can create a proxy server for `fastify` and `express`:
:::code-group
```typescript [fastify.ts]
import Fastify from 'fastify'
import proxy from '@fastify/http-proxy'
const fastify = Fastify({ logger: true })
const PIMLICO_API_KEY = process.env.PIMLICO_API_KEY
// Middleware to check authentication
fastify.addHook('preHandler', async (request, reply) => {
const authHeader = request.headers.authorization
if (!authHeader || !isValidAuth(authHeader)) {
reply.code(401).send({ error: 'Unauthorized' })
}
})
// Setup proxy to Pimlico API
fastify.register(proxy, {
upstream: `https://api.pimlico.io/v2/137/rpc?apikey=${PIMLICO_API_KEY}`,
prefix: '/api/proxy',
rewriteRequestHeaders: (req, headers) => ({
...headers,
})
})
// Start server
fastify.listen({ port: 3000 }, (err) => {
if (err) {
fastify.log.error(err)
process.exit(1)
}
})
// Helper function to validate auth
function isValidAuth(authHeader: string): boolean {
// Implement your authentication logic here
return true
}
```
```typescript [express.ts]
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
const app = express();
const PIMLICO_API_KEY = process.env.PIMLICO_API_KEY;
const targetUrl = `https://api.pimlico.io/v2/137/rpc?apikey=${PIMLICO_API_KEY}`;
// Middleware to check authentication
app.use((req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !isValidAuth(authHeader)) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
});
// Setup proxy to Pimlico API
app.use('/api/proxy', createProxyMiddleware({
target: targetUrl,
changeOrigin: true,
pathRewrite: {
'^/api/proxy': '', // Remove '/proxy' from the path
},
onProxyReq: (proxyReq, req) => {
// Ensure JSON content type
const bodyData = JSON.stringify(req.body);
proxyReq.setHeader('Content-Type', 'application/json');
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
},
}));
// Start server
app.listen(3000, (err) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log('Server is running on port 3000');
});
// Helper function to validate auth
function isValidAuth(authHeader: string): boolean {
// Implement your authentication logic here
return true;
}
```
:::
import SmartAccounts from "./smartAccounts.mdx"
## How to use an Arcana Auth signer with permissionless.js
[Arcana Auth](https://www.arcana.network/) offers a self-custodial Web3 wallet embedded within applications, utilizing asynchronous distributed key generation algorithms for enhanced security and privacy. This wallet, accessible without the need for a browser extension, allows authenticated users to instantly access and sign blockchain transactions within the app environment.
### Setup
To use Arcana Auth with permissionless.js, first create an application that integrates with Arcana Auth.
* Refer to the [Arcana Auth documentation site](https://docs.arcana.network/) for instructions on setting up an application with the Arcana Auth.
* For a quick start, Arcana Auth provides a web app guide, available [here](https://docs.arcana.network/auth/sdk-installation/).
### Integration
Integrating permissionless.js with Arcana Auth is straightforward after setting up the project. Arcana Auth provides an Externally Owned Account (EOA) wallet to use as a signer with accounts created using permissionless.js.
#### Create the authProvider object
After following the Arcana Auth documentation, you will have access to a `authProvider` object as shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/arcana.ts:main]
```
#### Use with permissionless.js
## How to use a DFNS signer with permissionless.js
[Dfns](https://www.dfns.co/) is an MPC/TSS Wallet-as-a-Service API/SDK provider. Dfns aims to optimize the balance of security and UX by deploying key shares into a decentralized network on the backend while enabling wallet access via biometric open standards on the frontend like Webauthn. Reach out [here](https://www.dfns.co/) to set up a sandbox environment to get started.
### Setup
To use Dfns with permissionless.js, first create an application that integrates with Dfns.
* Refer to the [Dfns documentation site](https://docs.dfns.co/d/) for instructions on setting up an application with the Dfns.
### Integration
Integrating permissionless.js with Dfns is straightforward after setting up the project. Dfns provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Set up Dfns
After following the Dfns documentation, you will have access to a `dfnsWallet` object as shown below:
```typescript
// [!include ~/snippets/signers/dfns.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Dynamic signer with permissionless.js
permissionless.js allows you to plug in custom signers to control the accounts that you create. Dynamic is an embedded wallet provider that allows you to easily onboard users to your dapp. It is possible to use Dynamic as a signer with permissionless.js, allowing you to use Dynamic to create and control smart accounts and sign transactions.
:::steps
#### Install the dependencies
```bash
npm i @dynamic-labs/sdk-react-core @dynamic-labs/wagmi-connector @dynamic-labs/ethereum permissionless viem wagmi
```
#### Create the Dynamic provider
Following Dynamic's [quickstart guide](https://docs.dynamic.xyz/quickstart), set up the Dynamic provider in your app. Also integrate the DynamicWagmiConnector, which will allow you to use Dynamic as a signer with permissionless.js.
```ts
import {
DynamicContextProvider,
DynamicWidget,
} from "@dynamic-labs/sdk-react-core";
import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
export const App = () => {
return (
);
};
```
#### Create the SmartAccountClient
Create the smart account client using the Dynamic signer. Note: DynamicWagmiConnector internally sets up the WagmiConfig, so there is no need to do it separately. This is where you would configure what smart account implementation (e.g. [Safe](/references/permissionless/how-to/accounts/use-safe-account), [Kernel](/references/permissionless/how-to/accounts/use-kernel-account), Biconomy, [TrustWallet](/references/permissionless/how-to/accounts/use-trustwallet-account) [SimpleAccount](/references/permissionless/how-to/accounts/use-simple-account)) and what paymaster logic you want to use.
```ts
import { createSmartAccountClient } from "permissionless";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { useWalletClient } from "wagmi";
import { createPublicClient, http, zeroAddress } from "viem";
import { sepolia } from "viem/chains";
import { createPimlicoClient } from "permissionless/clients/pimlico"
import { entryPoint07Address } from "viem/account-abstraction"
const {
data: walletClient
} = useWalletClient()
const publicClient = createPublicClient({
chain: sepolia, // or whatever chain you are using
transport: http()
})
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=`
const pimlicoClient = createPimlicoClient({
transport: http(pimlicoUrl),
entryPoint: {
address: entryPoint07Address,
version: "0.7",
}
})
const simpleSmartAccount = await toSimpleSmartAccount({
owner: walletClient,
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
})
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: sepolia,
bundlerTransport: http(pimlicoUrl),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
},
}
})
```
#### Send a transaction
You can now send transactions as normal. The `sponsorUserOperation` function will be called before each transaction is signed and sent, applying the custom paymaster logic you have set.
```ts
const txHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
data: "0x",
value: BigInt(0)
})
```
:::
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Fireblocks signer with permissionless.js
[Fireblocks](https://www.fireblocks.com/) is a user-friendly platform designed for building blockchain-based products and managing digital asset operations. It uses a direct custody approach, combining high performance with zero counterparty risk and multi-layered security. The platform includes secure MPC-based digital asset wallets, a policy engine for governance and transaction rules, and comprehensive treasury management. Fireblocks' security framework features multiple layers, including MPC-CMP technology, secure enclaves, and a robust policy engine, ensuring protection against cyberattacks, internal threats, and human errors. It's widely used for various operations like treasury, trading, and managing NFTs, smart contracts, and user wallets.
### Setup
To use Fireblocks with permissionless.js, first create an application that integrates with Fireblocks.
* Refer to the [Fireblocks documentation site](https://developers.fireblocks.com/) for instructions on setting up an application with the Fireblocks.
* For a quick start, Fireblocks provides a guide, available [here](https://developers.fireblocks.com/docs/quickstart).
### Integration
Integrating permissionless.js with Fireblocks is straightforward after setting up the project. Fireblocks provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Fireblocks object
After following the Fireblocks documentation, you will have access to a `FireblocksWeb3Provider` object as shown that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/fireblocks.ts:main]
```
#### Use with permissionless.js
## Signers for permissionless.js
Smart accounts are able to define custom authentication and authorization schemes, but still require signatures to validate user operations coming from signers. permissionless.js which accepts `LocalAccount | EIP1193Provider | WalletClient`, that can be passed as an owner to `createSmartAccountClient`. While by default these owners have full control over the smart account, it's possible to define custom roles and permissions for owners, and mix-and-match them to create complex multi-signature schemes, as well as rotate signers and revoke their access.
In addition to pure EOA owners, permissionless.js supports a variety of third-party wallet services as owners for smart accounts. This allows you to leverage the UIs and infrastructure of these services, while still using smart accounts supported by permissionless.js.
## How to Integrate Pimlico with Lit Protocol OTP Authentication
This how-to guide will walk you through the steps to integrate Lit Protocol's OTP sign-in with email, SMS, and Whatsapp with a smart account whose user operations are relayed and sponsored by Pimlico.
:::info
Lit Protocol is an Authentication solution that lets you create and manage distributed cryptographic key-pairs for condition-based encryption and programmatic signing. A decentralized key management network, Lit can be used in place of centralized key custodians and other key management solutions. For more information on how Lit Protocol works, visit [their documentation page](https://developer.litprotocol.com/resources/how-it-works).
:::
[Stytch](https://stytch.com/) will be used to manage the OTP authentication flow.
::::steps
#### Install the required packages
```bash
npm install stytch @lit-protocol/pkp-ethers @lit-protocol/lit-auth-client @lit-protocol/auth-helpers @lit-protocol/types @lit-protocol/lit-node-client-nodejs
```
#### Make an account with Stytch and get the Project ID and Secret
You can sign up for a Stytch account [here](https://stytch.com/). Once you have an account, you can find your Project ID and Secret in the [Stytch Dashboard API Keys page](https://stytch.com/dashboard/api-keys).

#### Create a Stytch client with your Project ID and Secret
```ts
const stytchClient = new stytch.Client({
project_id: "project-test-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
secret: "secret-test-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
});
```
#### Send an OTP to the user's email, SMS, or Whatsapp
:::code-group
```ts [email]
const stytchResponse = await stytchClient.otps.email.loginOrCreate({
email: "",
})
```
```ts [SMS]
const stytchResponse = await stytchClient.otps.sms.loginOrCreate({
phone_number: "",
})
```
```ts [WhatsApp]
const stytchResponse = await stytchClient.otps.whatsapp.loginOrCreate({
phone_number: "",
})
```
:::
#### Authenticate the user with the OTP and get a session token
:::code-group
```ts [email]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.email_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
```ts [SMS]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.phone_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
```ts [WhatsApp]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.phone_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
:::
#### Get a Lit Relay Server API Key
You can get a Lit Relay Server API Key by filling out [the Lit Protocol team's form](https://forms.gle/RNZYtGYTY9BcD9MEA)
#### Mint a PKPs through Lit Protocol
```ts
const litClient = new LitAuthClient({
litRelayConfig: {
relayApiKey: '',
}
});
const session = litClient.initProvider(ProviderType.StytchOtp, {
userId: sessionStatus.session.user_id,
appId: "project-test-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
})
const authMethod = await session.authenticate({
accessToken: sessionStatus.session_jwt
})
await session.mintPKPThroughRelayer(authMethod)
const pkps = await session.fetchPKPsThroughRelayer(authMethod)
```
#### Generate the Controller Session Signatures
```ts
const litNodeClient = new LitNodeClientNodeJs({
litNetwork: 'serrano',
debug: false,
})
await litNodeClient.connect();
const resourceAbilities = [
{
resource: new LitActionResource("*"),
ability: LitAbility.PKPSigning,
},
];
const sessionKeyPair = litNodeClient.getSessionKey();
const authNeededCallback = async (params: AuthCallbackParams) => {
const response = await litNodeClient.signSessionKey({
sessionKey: sessionKeyPair,
statement: params.statement,
authMethods: [authMethod],
pkpPublicKey: pkp[pkp.length - 1].publicKey,
expiration: params.expiration,
resources: params.resources,
chainId: 1,
});
return response.authSig;
};
const sessionSigs = await litNodeClient.getSessionSigs({
chain: "ethereum",
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString(),
resourceAbilityRequests: resourceAbilities,
sessionKey: sessionKeyPair,
authNeededCallback
}).catch((err) => {
console.log("error while attempting to access session signatures: ", err)
throw err;
});
```
#### Initialize the PKP Wallet
We will now generate a wallet that can act a regular Ethers.js wallet, but will use the PKPs minted through Lit Protocol to sign transactions under the hood.
```ts
const pkpWallet = new PKPEthersWallet({
pkpPubKey: pkp[pkp.length - 1].publicKey,
rpc: "", // e.g. https://sepolia.rpc.thirdweb.com
controllerSessionSigs: sessionSigs
});
await pkpWallet.init();
```
#### Use the PKP Wallet to sign user operations and send them through Pimlico
You can now use the `pkpWallet` as a regular Ethers.js wallet to sign user operations. To submit a user operation to Pimlico, you can follow the steps to [sponsor a user operation with Pimlico's verifying paymaster](/references/paymaster/verifying-paymaster/usage) and/or [submit a user operation through Pimlico's bundler](/references/bundler/usage). If you would like to integrate Lit Protocol with the full flow of generating, signing, and submitting a user operation, you can follow the steps in [tutorial 1](/references/permissionless/tutorial/tutorial-1), replacing the signing step with the PKP wallet and using `pkpWallet.address` as the owner address of the smart account.
Modified from [tutorial 1](/references/permissionless/tutorial/tutorial-1), an example of how to use the PKP wallet to sign a user operation is shown below:
```typescript
const signature = await pkpWallet.signMessage(
ethers.utils.arrayify(await entryPoint.getUserOpHash(userOperation)),
)
userOperation.signature = signature
```
And an example of how you would generate the initCode for a SimpleAccount using the PKP wallet is shown below:
```typescript
const initCode = ethers.utils.hexConcat([
SIMPLE_ACCOUNT_FACTORY_ADDRESS,
simpleAccountFactory.interface.encodeFunctionData("createAccount", [pkpWallet.address, 0]),
])
```
::::
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Magic signer with permissionless.js
[Magic](https://magic.link/) is a popular embedded wallet provider that supports social logins. While social logins are great, your users still need to onramp in order to pay for gas, which introduces significant friction.
By combining permissionless.js with Magic, you can use Magic to enable a smooth social login experience, while using permissionless.js accounts as the smart wallets to sponsor gas for users, batch transactions, and more.
### Setup
To use Magic with permissionless.js, first create an application that integrates with Magic.
* Refer to the [Magic documentation site](https://magic.link/docs/home/welcome) for instructions on setting up an application with the Magic SDK.
* For a quick start, Magic provides a CLI to create a starter project, available [here](https://magic.link/docs/home/quickstart/cli#run-make-magic).
### Integration
Integrating permissionless.js with Magic is straightforward after setting up the Magic SDK. Magic provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Magic object
After following the Magic documentation, you will have access to a `MagicBase` object as shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/magic.ts:main]
```
#### Use with permissionless.js
## How to use a Para signer with permissionless.js
[Para](https://www.getpara.com/) offers a signing solution enabling the creation of secure, embedded MPC wallets accessible via email or social login. These wallets, compatible across different applications, offer portability, recoverability, and programmability, eliminating the need for users to establish separate signers or contract accounts for each application.
### Setup
To use Para with permissionless.js, first create an application that integrates with Para.
* Refer to the [Para documentation site](https://docs.getpara.com/welcome) for instructions on setting up an application with the Para.
* For a quick start, Para provides an example hub, available [here](https://docs.getpara.com/getting-started/examples).
### Integration
Integrating permissionless.js with Para is straightforward after setting up the project. Para provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Para signer
After following the Para documentation, you will have access to a `ParaWeb3Provider` object that you can pass as an owner to `createSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/para.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Particle Network signer with permissionless.js
[Particle Network](https://particle.network/) is an intent-centric, modular wallet-as-a-service (WaaS). By utilizing MPC-TSS for key management, Particle can streamline onboarding via familiar Web2 methods such as Google, emails, and phone numbers.
By combining permissionless.js with Particle, you can use Particle to enable a smooth social login experience, while using ZeroDev as the smart wallet to sponsor gas for users, batch transactions, and more.
### Setup
To use Particle Network with permissionless.js, first create an application that integrates with Particle Network.
* Refer to the [Particle Network documentation site](https://docs.particle.network/) for instructions on setting up an application with the Particle Network.
* For a quick start, Particle Network provides a guide, available [here](https://docs.particle.network/getting-started/get-started).
### Integration
Integrating permissionless.js with Particle Network is straightforward after setting up the project. Particle Network provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Particle Network object
After following the Particle Network documentation, you will have access to a `ParticleProvider` object as shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/particle-network.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Passkey (WebAuthn) server
This how-to guide will walk you through the steps to integrate Passkey (WebAuthn) server with a smart account whose user operations are relayed and sponsored by Pimlico.
:::info
Passkey (WebAuthn) is a modern authentication method that allows users to sign in to websites and apps using their fingerprint, face, or other biometric information. For more information on how Passkey works, visit [the documentation page](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API).
:::
### Steps
::::steps
#### Install the required packages
```bash
npm install viem ox permissionless
```
#### Create a passkeys client
You will need to create a passkeys client to interact with the passkeys server. Learn how to create your own or use passkeys server at [How to create a Passkey (WebAuthn) server](/references/permissionless/how-to/signers/passkey-server#how-to-create-a-passkey-webauthn-server).
If you want to use Pimlico's passkeys server, visit [Dashboard](https://dashboard.pimlico.io/passkey-server) and configure your passkeys server.

You will then create a passkeys client with the following code:
```ts
const passkeyServerClient = createPasskeyServerClient({
chain,
transport: http(
`https://api.pimlico.io/v2/${chain.id}/rpc?apikey=${pimlicoApiKey}`
)
})
```
#### Create credentials
```tsx
import { useState } from "react"
import {
createWebAuthnCredential,
} from "viem/account-abstraction"
export function PasskeysDemo() {
const [credential, setCredential] = useState()
const createCredential = async () => { // [!code focus]
const credential = await createWebAuthnCredential( // [!code focus]
// Start the registration process // [!code focus]
await passkeyServerClient.startRegistration({ // [!code focus]
context: { // [!code focus]
// userName that will be shown to the user when creating the passkey // [!code focus]
userName: "plusminushalf" // [!code focus]
} // [!code focus]
}) // [!code focus]
) // [!code focus]
// Verify the registration // [!code focus]
const verifiedCredential = await passkeyServerClient.verifyRegistration( // [!code focus]
{ // [!code focus]
credential, // [!code focus]
context: { // [!code focus]
// userName that will be shown to the user when creating the passkey // [!code focus]
userName: "plusminushalf" // [!code focus]
} // [!code focus]
} // [!code focus]
) // [!code focus]
setCredential(verifiedCredential) // [!code focus]
} // [!code focus]
if (!credential)
return (
)
return (
// [!code focus]
)} // [!code focus]
> // [!code focus]
) // [!code focus]
}
```
::::
## How to use a Privy signer with permissionless.js
permissionless.js allows you to plug in custom signers to control the accounts that you create. Privy is an embedded wallet provider that allows you to easily onboard users to your dapp. It is possible to use Privy as a signer with permissionless.js, allowing you to use Privy to create and control smart accounts and sign transactions.
Additionally you may want to look at Privy's guide on working with permissionless.js [here](https://docs.privy.io/guide/react/recipes/account-abstraction/pimlico).
:::steps
#### Install the dependencies
```bash
npm i @privy-io/react-auth @privy-io/wagmi-connector permissionless viem wagmi
```
#### Create the Privy provider
Following Privy's [quickstart guide](https://docs.privy.io/guide/quickstart), set up the Privy provider in your app. Also integrate the PrivyWagmiConnector, which will allow you to use Privy as a signer with permissionless.js.
```ts
import { PrivyProvider } from '@privy-io/react-auth';
import { PrivyWagmiConnector } from '@privy-io/wagmi-connector';
"}
config={{
embeddedWallets: {
createOnLogin: "all-users",
},
}}
>
{children}
;
```
#### Set Privy as the active wallet
In your app, set Privy's embedded wallet as the active wallet for wagmi
```ts
import { useWallets } from "@privy-io/react-auth";
import { usePrivyWagmi } from "@privy-io/wagmi-connector";
import { useEffect } from "react";
const { wallets } = useWallets();
const embeddedWallet = wallets.find(
(wallet) => wallet.walletClientType === "privy"
);
```
#### Create the SmartAccountClient
Create the smart account client using the Privy signer. This is where you would configure what smart account implementation (e.g. [Safe](/references/permissionless/how-to/accounts/use-safe-account), [Kernel](/references/permissionless/how-to/accounts/use-kernel-account), Biconomy, [TrustWallet](/references/permissionless/how-to/accounts/use-trustwallet-account), [SimpleAccount](/references/permissionless/how-to/accounts/use-simple-account)) and what paymaster logic you want to use.
```ts
import { createSmartAccountClient } from "permissionless";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { useWalletClient } from "wagmi";
import { createPublicClient, http, zeroAddress } from "viem";
import { sepolia } from "viem/chains";
import { createPimlicoClient } from "permissionless/clients/pimlico"
import { entryPoint07Address } from "viem/account-abstraction"
const {
data: walletClient
} = useWalletClient()
const publicClient = createPublicClient({
chain: sepolia, // or whatever chain you are using
transport: http()
})
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=`
const pimlicoClient = createPimlicoClient({
transport: http(pimlicoUrl),
entryPoint: {
address: entryPoint07Address,
version: "0.7",
}
})
const owner = embeddedWallet.getEthereumProvider()
if (!owner) {
throw new Error("No owner found")
}
const simpleSmartAccount = await toSimpleSmartAccount({
owner: owner,
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
})
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: sepolia,
bundlerTransport: http(pimlicoUrl),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
},
}
})
```
#### Send a transaction
You can now send transactions as normal. The `sponsorUserOperation` function will be called before each transaction is signed and sent, applying the custom paymaster logic you have set.
```ts
const txHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
data: "0x",
value: BigInt(0)
})
```
:::
### Using ERC20 Paymaster with Privy Smart Wallets
:::steps
#### Approve paymaster for gas fees
In your existing user operations, make sure to approve the paymaster to spend tokens for gas fees.
```ts
import { encodeFunctionData, Address } from 'viem'
const paymasterApproveData = encodeFunctionData({
abi: ERC20ABI,
functionName: 'approve',
args: [paymasterAddress as Address, BigInt(paymasterValue.toString())]
});
const calls = [
{
to: erc20Address as Address,
data: paymasterApproveData,
value: BigInt(0),
},
// ... the rest of your user operations
];
```
#### Send transaction with paymaster context
Finally, add the paymaster context to your Privy smart wallet client:
```ts
import { useSmartWallets } from '@privy-io/react-auth/smart-wallets'
const { client } = useSmartWallets();
const txHash = await client.sendTransaction({
calls,
paymasterContext: {
token: erc20Address,
},
});
```
:::
For a full example, see the [example permissionless + Privy app](https://github.com/pimlicolabs/privy-demo/blob/main/src/components/privy/privy.tsx).
:::code-group
```ts [SimpleAccount]
// [!include ~/snippets/signers/accounts/simpleSmartAccount.ts:main]
```
```ts [Safe Account]
// [!include ~/snippets/signers/accounts/safeSmartAccount.ts:main]
```
```ts [Kernel Account]
// [!include ~/snippets/signers/accounts/ecdsaKernelSmartAccount.ts:main]
```
```ts [Biconomy Account]
// [!include ~/snippets/signers/accounts/biconomySmartAccount.ts:main]
```
```ts [TrustWallet Account]
// [!include ~/snippets/signers/accounts/trustSmartAccount.ts:main]
```
:::
## How to use a Turnkey signer with permissionless.js
[Turnkey](https://turnkey.com/) is a key infrastructure provider with a great developer API and a powerful security policy engine.
By combining permissionless.js with Turnkey, you can create **custodial AA wallets** whose security is provided by Turnkey, with powerful functionalities such as sponsoring gas, batching transactions, etc.
### Setup
To use Turnkey with permissionless.js, first create an application that integrates with Turnkey.
* Refer to the [Turnkey documentation site](https://docs.turnkey.com/) for instructions on setting up an application with the Turnkey.
* For a quick start, Turnkey provides examples, available [here](https://docs.turnkey.com/getting-started/examples).
### Integration
Integrating permissionless.js with Turnkey is straightforward after setting up the project. Turnkey provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the TurnkeyClient and a Turnkey viem account
After following the Turnkey documentation, you will have access to a `TurnkeyClient`. An example is shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/turnkey.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
## How to use Web3Auth with Pimlico
[Web3Auth](https://web3auth.io) is a popular embedded wallet provider that supports a range of login methods. Web3Auth also has strong native account abstraction support allowing you to offer smooth UX for your users through features like social logins, sponsored transactions, and transaction batching.
### Setup
To enable Account Abstraction in your Web3Auth project, head to your [Web3Auth dashboard](https://dashboard.web3auth.io) and enable the Smart Accounts feature on the sidebar. Once enabled, add the Pimlico bundler and paymaster endpoints (these can be found [here](https://dashboard.pimlico.io/apikeys))

> A more details walkthrough on how to setup the dashboard can be found [here on the Web3Auth docs](https://web3auth.io/docs/sdk/web/react/advanced/smart-accounts)
### Integration
After setting up your Web3Auth project on the dashboard, make the following changes to your client side application.
#### Create the Web3Auth provider
After following the Web3Auth documentation, you will have access to a `web3auth` object as shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
import { WEB3AUTH_NETWORK, type Web3AuthOptions } from "@web3auth/modal"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { WagmiProvider } from "@web3auth/modal/react/wagmi"
import { type ReactNode } from "react"
import { Web3AuthProvider } from "@web3auth/modal/react"
if (!process.env.NEXT_PUBLIC_WEB3AUTH_CLIENT_ID) {
// get from https://dashboard.web3auth.io
throw new Error("missing NEXT_PUBLIC_WEB3AUTH_CLIENT_ID env var")
}
const web3AuthOptions: Web3AuthOptions = {
clientId: process.env.NEXT_PUBLIC_WEB3AUTH_CLIENT_ID,
web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET
}
const web3authConfig = {
web3AuthOptions
}
const queryClient = new QueryClient()
export function AppProvider({ children }: { children: ReactNode }) {
return (
{children}
)
}
```
#### Wrap your main app logic using the provider
Wrap your app's entryPoint such that it uses the provider we previously created
```typescript
"use client"
import { Home } from "@/components/home"
import { AppProvider } from "@/components/provider"
export default function Main() {
return (
)
}
```
#### Connect using Web3Auth's connect component
Use the Web3Auth hooks to handle authentication. This component will display a login button when disconnected and show the smart contract wallet address when connected:
```typescript
import { useAccount } from "wagmi"
import {
useWeb3AuthConnect,
useWeb3AuthDisconnect
} from "@web3auth/modal/react"
export function Web3AuthConnect() {
const { connect, isConnected, connectorName } = useWeb3AuthConnect()
const { disconnect } = useWeb3AuthDisconnect()
const { address } = useAccount()
if (isConnected && address) {
return (
Connected to {connectorName}
Smart Contract Wallet: {address}
)
}
return (
)
}
```
#### Send a userOperation
Once connected, you can send userOperations using the wagmi hooks. The smart account configuration from your Web3Auth dashboard will handle bundling (and gas sponsorship if a paymaster is set):
```typescript
import { useSendTransaction, useWaitForTransactionReceipt } from "wagmi"
import { zeroAddress } from "viem"
export function SendUserOperation() {
const {
data: hash,
error,
isPending,
sendTransaction
} = useSendTransaction()
const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({ hash })
const handleSendTransaction = () => {
sendTransaction({
to: zeroAddress,
data: "0x",
value: BigInt(0)
})
}
return (
{hash && (
Transaction Hash:
{hash}
)}
{isConfirming &&
Waiting for confirmation...
}
{isConfirmed &&
Transaction confirmed
}
{error &&
Error: {error.message}
}
)
}
```
For a full example, see the [example permissionless + Web3Auth app](https://github.com/pimlicolabs/web3auth-demo/blob/main/src/components/web3auth/web3auth.tsx).
## How to use Sponsorship Policies
### What are Sponsorship Policies?
Sponsorship Policies are hosted policies that allow you to define custom rules for sponsorships.
You can put limits to the global amount of sponsorships, the amount of sponsorships per user, and per user operation.
Start by going to the [sponsorship policies page](/guides/how-to/sponsorship-policies) on the Pimlico dashboard and clicking on the "Create Policy" button.

### Usage of Sponsorship Policies with permisionless.js
If you are using [permissionless.js](/references/permissionless), you can use the `PimlicoPaymasterClient` to use sponsorship policies.
:::steps
#### Create the clients
First we must create the public, (optionally) paymaster clients that will be used to interact with the SimpleAccount.
```ts
// [!include ~/snippets/references/platform/sponsorship-policies/index.ts:client]
```
#### Create an account
Now, create an account. This can any of the accounts supported by permissionless.js or custom accounts conforming to the interface. For this example, we'll use a Simple account.
```ts
// [!include ~/snippets/references/platform/sponsorship-policies/index.ts:account]
```
#### Create the smart account client with middleware
When creating the `smartAccountClient`, we can pass in a `middleware.sponsorUserOperation` function that will be called before a user operation is signed and sent.
This is where we can pass in the `sponsorshipPolicyId` that we want to use.
```ts
// [!include ~/snippets/references/platform/sponsorship-policies/index.ts:smart-account-client]
```
#### Send a sponsored transaction
Finally, let's submit a transaction from the smart account. We will send a transaction to the `0xd8da6bf26964af9d7eed9e03e53415d37aa96045` (vitalik.eth) address with `0x1234` as example `callData`.
```typescript
// [!include ~/snippets/tutorial-1.ts:submit]
```
Congratulations, you have sent a transaction using a sponsorship policy! 🎉
:::
### Usage of Sponsorship Policies API
After creating a policy, you can take its ID and use it by passing it into the `pm_sponsorUserOperation` method. If the user operation does not meet the requirements of the policy, the method will return an error.
```json
{
"jsonrpc": "2.0",
"method": "pm_sponsorUserOperation",
"params": [
{
"sender": "0x1234567890123456789012345678901234567890",
"nonce": "0x1",
"initCode": "0x",
"callData": "0x",
"callGasLimit": "0x100000",
"verificationGasLimit": "0x20000",
"preVerificationGas": "0x10000",
"maxFeePerGas": "0x3b9aca00",
"maxPriorityFeePerGas": "0x3b9aca00",
"paymasterAndData": "0x",
"signature": "0x"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
{
"sponsorshipPolicyId": "sp_amused_gladiator"
}
],
"id": 1
}
```
## How to use Sponsorship Policy webhooks
Webhooks allow you to receive real-time notifications when sponsorship-related events occur. You can use webhooks to approve or reject sponsorship requests and receive notifications about finalized sponsorships. Start by going to the [sponsorship policies page](https://dashboard.pimlico.io/sponsorship-policies) on the Pimlico dashboard, clicking on the existing policy and clicking on the "Edit button".
### Webhook Types
#### UserOperation Sponsorship
These webhooks are triggered when using the [pm\_sponsorUserOperation](/references/paymaster/erc20-paymaster/endpoints/pm_sponsorUserOperation) endpoint.
##### Request for Sponsorship
```typescript
const body = {
type: "user_operation.sponsorship.requested",
data: {
object: {
userOperation,
entryPoint,
chainId,
sponsorshipPolicyId,
apiKey
}
}
}
```
The webhook must return a response with the following structure:
```json
{
"sponsor": true // Boolean - whether to approve the sponsorship
}
```
##### Sponsorship Finalized
Sent when a UserOperation sponsorship is approved and finalized:
```typescript
const body = {
type: "user_operation.sponsorship.finalized",
data: {
object: {
userOperation,
entryPoint,
chainId,
sponsorshipPolicyId,
apiKey
}
}
}
```
{/*
### FlashFund Withdrawal
These webhooks are triggered when using the [flashfund_sponsorWithdrawal](/references/flash-fund/endpoints/flashfund_sponsorWithdrawal) endpoint.
#### Request for Withdrawal
Sent when a FlashFund withdrawal sponsorship is requested:
```typescript
const body = {
type: "flash_fund.sponsorship.requested",
data: {
object: {
recipient,
token,
amount,
signature,
chainId,
apiKey,
sponsorshipPolicyId
}
}
}
```
The webhook must return a response with the following structure:
```json
{
"sponsor": true // Boolean - whether to approve the withdrawal
}
```
#### Withdrawal Finalized
Sent when a FlashFund withdrawal is approved and finalized:
```typescript
const body = {
type: "flash_fund.sponsorship.finalized",
data: {
object: {
withdrawal,
hash,
signature,
chainId,
apiKey,
sponsorshipPolicyId
}
}
}
```
*/}
### How to verify the webhook
To verify the webhook, you can use the `@pimlico/webhook` package. You will need to provide the webhook secret, which you can find in the sponsorship policy [settings](https://dashboard.pimlico.io/sponsorship-policies).
#### Installation
```bash
pnpm install @pimlico/webhook
```
#### Usage
```typescript
import { pimlicoWebhookVerifier } from "@pimlico/webhook"
import type { VercelRequest, VercelResponse } from "@vercel/node"
const webhookSecret = process.env.PIMLICO_WEBHOOK_SECRET as string
const verifyWebhook = pimlicoWebhookVerifier(webhookSecret)
export default async function handler(req: VercelRequest, res: VercelResponse) {
const webhookEvent = verifyWebhook(
req.headers as Record,
JSON.stringify(req.body)
)
// Handle different webhook types
switch(webhookEvent.type) {
case "sponsorshipPolicy.webhook":
case "user_operation.sponsorship.requested":
case "user_operation.sponsorship.finalized":
default:
return res.status(400).json({ error: "Unknown webhook type" })
}
}
```
## Testing with BuildBear Sandboxes
This guide introduces how to setup a BuildBear Sandbox with for testing the Alto bundler and a mock paymaster. We will be using viem and permissionless to interact with the sandbox enviornment.
### Overview
BuildBear provides on-demand, isolated test environments that clone existing blockchain networks. These environments are perfect for testing account abstraction implementations as they:
* Provide a persistent, dedicated testnet environment
* Support EIP-4337 and all necessary AA infrastructure
* Allow you to fork from existing networks (Ethereum, Arbitrum, Optimism, etc.)
* Unlocked faucets for testing
* Offer an explorer interface for easy debugging
### Steps
::::steps
#### Creating and setting up a BuildBear Sandbox
1. Go to [buildbear.io](https://www.buildbear.io) and sign up for an account if you don't have one already.
2. Create a new Sandbox by clicking "Create a Sandbox" and selecting the network you want to fork (e.g., Ethereum Mainnet, Sepolia, etc.).
3. Navigate to the Plugin tab on your sandbox dashboard and install the Pimlico Plugin. Ensure the plugin appears as installed in the marketplace or Installed tab.
4. Go to your Buildbear sandbox dashboard and locate the RPC URL. Copy the RPC URL, which also serves as the Pimlico Client API endpoint. It should look something like the below.
* BuildBear Sandbox RPC URL: `https://rpc.buildbear.io/`
* Pimlico Client API: `https://rpc.buildbear.io/`
5. Once your Sandbox is created, you'll get access to:
* RPC URL
* Chain ID
* Explorer URL
* Faucet to fund your test accounts
#### Setup the clients
```ts [Setup Clients]
// [!include ~/snippets/testing/build-bear.ts:clients]
```
#### Setup the Smart Account Client
```ts [Setup Clients]
// [!include ~/snippets/testing/build-bear.ts:smartAccountClient]
```
#### Funding your smart account (Optional)
If you want to fund your smart account with native currency or ERC-20 tokens, you can use the following curl commands:
Funding native currency:
```
curl -X POST https://rpc.buildbear.io/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "buildbear_nativeFaucet",
"params": [{
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"balance": "100"
}]
}'
```
Funding ERC-20 tokens:
```
curl -X POST https://rpc.buildbear.io/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "buildbear_ERC20Faucet",
"params": [{
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"balance": "100",
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}]
}'
```
#### Sending a userOperation
:::code-group
```ts [Verifying Paymaster]
// [!include ~/snippets/testing/build-bear.ts:verifyingPaymaster]
```
```ts [ERC-20 Paymaster]
// [!include ~/snippets/testing/build-bear.ts:erc20Paymaster]
```
:::
:::info
paymaster top-ups are taken care of by the sandbox and you do need to worry about topping up the paymaster ever
:::
::::
More detailed examples and instructions can be found on [BuildBear's docs](https://app.buildbear.io/plugins/pimlico-alto-5efcae784d7b)
## Local Testing With Docker
This guide introduces a ready-to-use mock test environment, that contains:
* A local Alto bundler
* A mock paymaster
* An Anvil node
* ERC-4337 related contracts, including the EntryPoint and account factories for all major smart account implementations
The test environment is orchestrated using **docker compose**. Where the docker containers are pulled from [this repo](https://github.com/pimlicolabs/mock-aa-environment).
The mock environment is designed to mimic mainnet as closely as possible by building with the latest Alto version and by deploying all contracts (entrypoints, paymasters, smart account factories, etc.)
This has advantages over testing against a production testnet as you have more control over the testing environment and can make use of features like anvil cheatcodes.
### Steps
::::steps
#### Setup
To get started, create a **alto-config.json** file at the root of your test directory with the following contents.
```json [alto-config.json]
{
"network-name": "local",
"log-environment": "production",
"entrypoints": "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789,0x0000000071727De22E5E9d8BAf0edAc6f37da032,0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
"api-version": "v1,v2",
"rpc-url": "http://anvil:8545",
"min-balance": "0",
"utility-private-key": "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97",
"executor-private-keys": "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6,0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356,0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e,0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba,0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a",
"max-block-range": 10000,
"safe-mode": false,
"port": 4337,
"log-level": "info",
"public-client-log-level": "error",
"wallet-client-log-level": "error",
"polling-interval": 100
}
```
> Note: All private keys are test keys generated by anvil
Then, create a **docker-compose.yaml** file at the root of your test directory with the following contents.
```yaml [docker-compose.yaml]
services:
anvil:
image: ghcr.io/foundry-rs/foundry
ports: [ "8545:8545" ]
entrypoint: [ "anvil", "--host", "0.0.0.0", "--block-time", "0.1", "--silent", , "--hardfork", "prague"]
platform: linux/amd64/v8
healthcheck:
test: ["CMD-SHELL", "cast rpc web3_clientVersion | grep -c anvil > /dev/null "]
start_interval: 250ms
start_period: 10s
interval: 30s
timeout: 5s
retries: 50
contract-deployer:
image: ghcr.io/pimlicolabs/mock-contract-deployer:main
environment:
- ANVIL_RPC=http://anvil:8545
depends_on:
anvil:
condition: service_healthy
mock-paymaster:
image: ghcr.io/pimlicolabs/mock-verifying-paymaster:main
ports: [ "3000:3000" ]
environment:
- ALTO_RPC=http://alto:4337
- ANVIL_RPC=http://anvil:8545
depends_on:
anvil:
condition: service_healthy
contract-deployer:
condition: service_completed_successfully
alto:
image: ghcr.io/pimlicolabs/alto:latest
ports: [ "4337:4337" ]
entrypoint: ["node", "src/lib/cli/alto.js", "run", "--config", "/app/alto-config.json"]
depends_on:
anvil:
condition: service_healthy
contract-deployer:
condition: service_completed_successfully
volumes:
- ./alto-config.json:/app/alto-config.json
```
To start the test environment, run
```sh
docker compose up
```
Once docker has started, the following services can be accessed locally through the following endpoints:
* `Anvil` at **localhost:8545**
* `Alto Bundler` at **localhost:4337**
* `Mock Paymaster` at **localhost:3000**
You can now use permissionless like you normally would but instead of referencing the live endpoints, use the local endpoints mentioned above when creating the clients.
```ts [clients.ts] twoslash
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { http, createPublicClient } from "viem";
import { createBundlerClient, entryPoint07Address } from "viem/account-abstraction"
import { foundry } from "viem/chains";
const publicClient = createPublicClient({
chain: foundry,
transport: http("http://localhost:8545"), // [!code ++]
});
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337") // [!code ++]
});
const pimlicoClient = createPimlicoClient({
transport: http("http://localhost:3000"), // [!code ++]
entryPoint: {
address: entryPoint07Address,
version: "0.7",
}
})
```
:::info
Note: All smart account types supported by permissionless.js will work out of the box as all related factories and modules are deployed on the local anvil instance.
:::
#### Vitest Integration
You can add scripts in your **package.json** to automatically set up and tear down your mock environment when running tests.
```json [package.json]
{
"name": "aa-tests",
"scripts": {
"test": "bun run docker:up && vitest run && bun run docker:down",
"docker:up": "docker-compose up -d", // [!code ++]
"docker:down": "docker-compose down" // [!code ++]
},
"dependencies": {
"viem": "^2.9.17",
"permissionless": "^0.1.35"
},
"devDependencies": {
"vitest": "^1.5.2"
}
}
```
When writing test cases, ensure that the bundler and paymaster are fully setup before sending any request to them. To do this, make a simple health check in the `beforeAll` declaration.
:::code-group
```ts [basic.test.ts]
import { beforeAll, describe, expect, test } from "vitest";
import { ensureBundlerIsReady, ensurePaymasterIsReady } from "./healthCheck";
import { foundry } from "viem/chains";
import { http } from "viem";
import {
createBundlerClient,
entryPoint06Address,
entryPoint07Address
} from "viem/account-abstraction";
describe("Test basic bundler functions", () => {
beforeAll(async () => { // [!code ++]
await ensureBundlerIsReady(); // [!code ++]
await ensurePaymasterIsReady(); // [!code ++]
}); // [!code ++]
test("Can get chainId", async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
});
const chainId = await bundlerClient.getChainId();
expect(chainId).toEqual(foundry.id);
});
test("Can get supported entryPoints", async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
});
const supportedEntryPoints = await bundlerClient.getSupportedEntryPoints();
expect(supportedEntryPoints).toEqual([
entryPoint06Address,
entryPoint07Address,
]);
});
});
```
```ts [healthCheck.ts] twoslash
import { createBundlerClient } from "viem/account-abstraction";
import { http } from "viem";
import { foundry } from "viem/chains";
export const ensureBundlerIsReady = async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
});
while (true) {
try {
await bundlerClient.getChainId();
return;
} catch {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
};
export const ensurePaymasterIsReady = async () => {
while (true) {
try {
// mock paymaster will open up this endpoint when ready
const res = await fetch(`http://localhost:3000/ping`);
const data = await res.json();
if (data.message !== "pong") {
throw new Error("paymaster not ready yet");
}
return;
} catch {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
};
```
:::
#### Extension: Testing against forked state
If you want your tests to run against a live blockchain, you can slightly edit the **docker-compose.yaml** file to fork from the latest block by adding the anvil flag **--fork-url** and the environment variable **SKIP\_DEPLOYMENTS** to skip the local contract deployments.
```yaml [docker-compose.yaml]
services:
anvil:
image: ghcr.io/foundry-rs/foundry:nightly-c4a984fbf2c48b793c8cd53af84f56009dd1070c
ports: [ "8545:8545" ]
entrypoint: [ "anvil", "--fork-url", "https://sepolia.rpc.thirdweb.com", "--chain-id", "11155111", "--host", "0.0.0.0", "--block-time", "0.1", "--silent"] // [!code ++]
platform: linux/amd64/v8
healthcheck:
test: ["CMD-SHELL", "cast rpc web3_clientVersion | grep -c anvil > /dev/null "]
start_interval: 250ms
start_period: 10s
interval: 30s
timeout: 5s
retries: 50
contract-deployer:
image: ghcr.io/pimlicolabs/mock-contract-deployer:main
environment:
- ANVIL_RPC=http://anvil:8545
- SKIP_DEPLOYMENTS=true // [!code ++]
depends_on:
anvil:
condition: service_healthy
mock-paymaster:
image: ghcr.io/pimlicolabs/mock-verifying-paymaster:main
ports: [ "3000:3000" ]
environment:
- ALTO_RPC=http://alto:4337
- ANVIL_RPC=http://anvil:8545
depends_on:
anvil:
condition: service_healthy
contract-deployer:
condition: service_completed_successfully
alto:
image: ghcr.io/pimlicolabs/alto:latest
ports: [ "4337:4337" ]
entrypoint: ["node", "src/lib/cli/alto.js", "run", "--config", "/app/alto-config.json"]
depends_on:
anvil:
condition: service_healthy
contract-deployer:
condition: service_completed_successfully
volumes:
- ./alto-config.json:/app/alto-config.json
```
::::
## Prool
This guide introduces how to setup and run tests in a mock environment using [Prool](https://github.com/wevm/prool).
## Overview
Prool is a library by [Wevm](https://github.com/wevm/viem) that lets you programmatically interact with Ethereum server instances like Nodes, Bundlers, and Paymasters.
We will be using [Vitest](https://vitest.dev/) as our testing framework and we will be testing against a fork of Base Mainnet.
### Steps
::::steps
#### Setup
Install the following dependencies:
```bash
npm install permissionless viem prool @pimlico/alto @pimlico/mock-paymaster get-port
```
Installing the following dev dependencies:
```bash
npm install --save-dev vitest
```
In order to run tests, add the following section to your **package.json** file:
```json [package.json]
{
"scripts": {
"test": "vitest"
}
}
```
#### Create test helpers
Prool lets us run multiple test cases at once by creating separate isolated environments for each, with their own Bundler, Anvil, and Paymaster.
To make this easier, we’ll create a helper called testWithRpc that finds free ports and spins up the isolated test environment.
:::code-group
```typescript [testWithRpc.ts]
import getPort from "get-port";
import { test } from "vitest";
import { getInstances } from "./getInstances";
import { base } from "viem/chains";
let ports: number[] = [];
export const testWithRpc = test.extend<{
rpc: {
anvilRpc: string;
altoRpc: string;
paymasterRpc: string;
};
}>({
rpc: async ({}, use) => {
const altoPort = await getPort({
exclude: ports,
});
ports.push(altoPort);
const paymasterPort = await getPort({
exclude: ports,
});
ports.push(paymasterPort);
const anvilPort = await getPort({
exclude: ports,
});
ports.push(anvilPort);
const anvilRpc = `http://localhost:${anvilPort}`;
const altoRpc = `http://localhost:${altoPort}`;
const paymasterRpc = `http://localhost:${paymasterPort}`;
const forkUrl = process.env.FORK_RPC_URL || base.rpcUrls.default.http[0];
const instances = await getInstances({
forkUrl,
anvilPort,
altoPort,
paymasterPort,
});
await use({
anvilRpc,
altoRpc,
paymasterRpc,
});
await Promise.all([...instances.map((instance) => instance.stop())]);
const usedPorts = [altoPort, anvilPort, paymasterPort];
ports = ports.filter((port) => !usedPorts.includes(port));
},
});
```
```typescript [getInstances.ts]
import { paymaster } from "@pimlico/mock-paymaster";
import { anvil, alto } from "prool/instances";
import {
entryPoint06Address,
entryPoint07Address,
entryPoint08Address,
} from "viem/account-abstraction";
import { foundry } from "viem/chains";
// Private key for Anvil account 0.
const pk = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
export const getInstances = async ({
forkUrl,
anvilPort,
altoPort,
paymasterPort,
}: {
forkUrl: string;
anvilPort: number;
altoPort: number;
paymasterPort: number;
}) => {
const anvilRpc = `http://localhost:${anvilPort}`;
const altoRpc = `http://localhost:${altoPort}`;
const anvilInstance = anvil({
port: anvilPort,
chainId: foundry.id,
hardfork: "Prague",
forkUrl,
});
const altoInstance = alto({
port: altoPort,
entrypoints: [
entryPoint06Address,
entryPoint07Address,
entryPoint08Address,
],
rpcUrl: anvilRpc,
executorPrivateKeys: [pk],
utilityPrivateKey: pk,
safeMode: false,
});
const paymasterInstance = paymaster({
anvilRpc,
altoRpc,
port: paymasterPort,
});
await anvilInstance.start();
await altoInstance.start();
await paymasterInstance.start();
return [anvilInstance, altoInstance, paymasterInstance];
};
```
:::
:::note
Note Make sure you set the `FORK_RPC_URL` evn var before running your tests.
:::
#### Writing test cases
In this example, we will mock a test by sending a simple sponsored userOperation.
We use the `testWithRpc` helper to setup our test environment. The `testWithRpc` method returns the rpc endpoints that we should use when creating the Anvil, Alto, Paymaster clients.
```typescript [basic.test.ts]
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import {
createPublicClient,
createTestClient,
http,
parseEther,
zeroAddress,
} from "viem";
import { assert, describe } from "vitest";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { createSmartAccountClient } from "permissionless";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { foundry } from "viem/chains";
import { testWithRpc } from "./testWithRpc";
describe("Basic test cases", () => {
testWithRpc("Can send a sponsored userOperation", async ({ rpc }) => {
const { anvilRpc, altoRpc, paymasterRpc } = rpc;
// Setup clients.
const publicClient = createPublicClient({
chain: foundry,
transport: http(anvilRpc),
});
const pimlicoClient = createPimlicoClient({
chain: foundry,
transport: http(paymasterRpc),
});
const account = await toSimpleSmartAccount({
client: publicClient,
owner: privateKeyToAccount(generatePrivateKey()),
});
const smartAccountClient = createSmartAccountClient({
account,
bundlerTransport: http(altoRpc),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () =>
(await pimlicoClient.getUserOperationGasPrice()).fast,
},
});
// Send userOperation and wait for receipt.
const userOpHash = await smartAccountClient.sendUserOperation({
calls: [
{
to: zeroAddress,
value: 0n,
data: "0x",
},
],
});
const receipt = await smartAccountClient.waitForUserOperationReceipt({
hash: userOpHash,
});
// UserOperation should be included successfully.
expect(receipt.success).toBe(true);
});
});
```
#### Testing with ERC-20 paymaster
We can also test the ERC-20 paymaster flow. The `@pimlico/mock-paymaster` package exposes two helper functions/variables:
* `sudoMintTokens`: Helper function to mint tokens to a given address.
* `erc20Address`: Helper constant that returns the address of the supported ERC-20 token.
```typescript [erc20.test.ts]
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import {
createPublicClient,
erc20Abi,
getContract,
http,
parseEther,
zeroAddress,
} from "viem";
import { expect, describe } from "vitest";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { testWithRpc } from "../utils/testWithRpc";
import { createSmartAccountClient } from "permissionless";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { prepareUserOperationForErc20Paymaster } from "permissionless/experimental/pimlico";
import { foundry } from "viem/chains";
import { erc20Address, sudoMintTokens } from "@pimlico/mock-paymaster";
describe("Basic test cases", () => {
testWithRpc("Can send a sponsored ERC20 userOperation", async ({ rpc }) => {
const { anvilRpc, altoRpc, paymasterRpc } = rpc;
// Setup clients.
const publicClient = createPublicClient({
chain: foundry,
transport: http(anvilRpc),
});
const pimlicoClient = createPimlicoClient({
chain: foundry,
transport: http(paymasterRpc),
});
const account = await toSimpleSmartAccount({
client: publicClient,
owner: privateKeyToAccount(generatePrivateKey()),
});
// Creating smart account client based on this page
// http://docs.pimlico.io/guides/how-to/erc20-paymaster/how-to/use-paymaster
const smartAccountClient = createSmartAccountClient({
chain: foundry,
account,
bundlerTransport: http(altoRpc),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () =>
(await pimlicoClient.getUserOperationGasPrice()).fast,
prepareUserOperation:
prepareUserOperationForErc20Paymaster(pimlicoClient),
},
});
// Fund the SmartAccount with ERC-20 tokens.
await sudoMintTokens({
amount: parseEther("1"),
to: account.address,
anvilRpc,
});
// Check smartAccount balance before and after sending transaction to confirm balance decreased.
const erc20 = getContract({
address: erc20Address,
abi: erc20Abi,
client: publicClient,
});
const balanceBefore = await erc20.read.balanceOf([account.address]);
// Send a transaction and wait for receipt.
const userOpHash = await smartAccountClient.sendUserOperation({
calls: [
{
to: zeroAddress,
value: 0n,
data: "0x",
},
],
paymasterContext: {
token: erc20Address,
},
});
const receipt = await smartAccountClient.waitForUserOperationReceipt({
hash: userOpHash,
});
const balanceAfter = await erc20.read.balanceOf([account.address]);
// UserOperation should be included successfully.
expect(receipt.success).toBe(true);
expect(balanceAfter).toBeLessThan(balanceBefore);
});
});
```
:::note
Note The ERC-20 token has a 18 decimal places.
:::
::::
### Combined code
If you want to see the complete code that combines all of the previous steps, we uploaded it to a [separate repository](https://github.com/pimlicolabs/testing-with-prool). If you're looking to run it, remember to replace the API key with your own!
### Tips and tricks
* The `testWithRpc` helper creates isolated instances for a specific test case meaning vitest will run multiple tests at once.
* The setup outlined in this guide is the same one used in permissionless.js's E2E tests. for more detailed examples, refer to the test cases in permissionless.js. For example this [test case](https://github.com/pimlicolabs/permissionless.js/blob/main/packages/permissionless/actions/pimlico/sponsorUserOperation.test.ts) for the pm\_sponsorUserOperation endpoint.
* To debug the Alto/Anvil/Paymaster instance, you can print logs to stdout by calling the `.on` function. For an example of this, please refere to [this snippet](https://github.com/pimlicolabs/testing-with-prool/blob/c0b3d448e2fff1ca4a3d2829dc8585e708728f41/utils/getInstances.ts#L53-L74).
## Chain is not supported
The chain you are trying to use is not supported by Pimlico.
To see the list of supported chains, take a look at the relevant [bundler](/guides/supported-chains) and [paymaster](/guides/supported-chains) pages.
### Adding new chains
* [Reach out to us](https://t.me/pimlicoHQ) if you would like to see support for a new chain.
## Invalid 'apikey' query parameter
The API key you are trying to use is not valid. It either does not exist or has been deleted.
Learn how to [create an API key here](/guides/create-api-key).
## Validation error: Invalid discriminator value
```txt
Validation error: Invalid discriminator value. Expected 'eth_chainId' | 'eth_supportedEntryPoints' | 'eth_coinbase' | 'eth_estimateUserOperationGas' | 'eth_sendUserOperation' | 'eth_getUserOperationByHash' | 'eth_getUserOperationReceipt' | 'pm_supportedEntryPoints' | 'pm_sponsorUserOperation' | 'web3_clientVersion' | 'pimlico_getUserOperationStatus' | 'pimlico_getUserOperationGasPrice' | 'pimlico_getBalance' | 'pm_validateSponsorshipPolicies' at \"method\""
```
If you see an error similar to the one above, it means you are calling a method on the Pimlico API that is not supported. The most common reason for this is using the Pimlico API endpoint for standard public Ethereum JSON-RPC methods. As a reminder, the Pimlico API only supports bundler and paymaster methods.
### Possible solutions
Make sure you are using a standard RPC endpoint for public Ethereum methods and the Pimlico API endpoint for Pimlico-specific methods.
```ts
import { createPublicClient, http } from "viem"
const publicClient = createPublicClient({
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY") // [!code --]
transport: http("https://sepolia.rpc.thirdweb.com") // [!code ++]
})
```
## Unknown error from alto bundler
The bundler encountered an unexpected error. This is either an internal 500 error, or an error with sensitive information that can not returned.
### Possible solutions
* [Reach out to us](https://t.me/pimlicoHQ) if you see this error.
## eth\_estimateUserOperationGas
This method simulates the user operation and estimates the appropriate gas limits for it. If the operation is not successful, it will return an error.
:::tip[Tip]
This method will *not* revert if the `signature` field in the user operation fails. However, most account implementations require the signature to be well formed, which usually means it must have the correct length.
For SimpleAccount, you can use the following dummy signature to put in the `signature` field during gas estimation:
```txt
0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
```
:::
### EntryPoint 0.7 & 0.8
:::warning[Warning]
The EntryPoint 0.7 & 0.8 API expects an `UnpackedUserOperation` instead of a `PackedUserOperation`.
According to the spec it is bundler's responsibility to pack the userOp before sending it to the EntryPoint.
So make sure to send an `UnpackedUserOperation` to the bundler.
:::
:::tip[Tip]
If the UserOperation's callData fails during estimation, the bundler will return the revert reason.
:::
:::tip[Tip]
Estimations are done with a balance override on the UserOperation's sender, this allows you to estimate the gas cost even if the sender has no funds. However, if the userOperation is sent onchain when the sender has no balance, it will revert during the callphase due to out of funds.
:::
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "eth_estimateUserOperationGas",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845adb2c711129d4f3966735ed98a9f09fc4ce5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d000000000000000000000000aac5d4240af87249b3f71bc8e4a2cae074a3e419...",
"callData": "0xe9ae5c5300000000000000000000000000000000000000000000000000000000000000000000000000...",
"callGasLimit": "0x0",
"verificationGasLimit": "0x0",
"preVerificationGas": "0x0",
"maxFeePerGas": "0x7a5cf70d5",
"maxPriorityFeePerGas": "0x3b9aca00",
"paymaster": null,
"paymasterVerificationGasLimit": null,
"paymasterPostOpGasLimit": null,
"paymasterData": null,
"signature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032"
],
"id": 1
}
```
#### Returns
* **Type:** `Object`
An object containing the estimated gas values for the user operation.
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"preVerificationGas": "0xd3e3",
"verificationGasLimit": "0x60b01",
"callGasLimit": "0x13880",
"paymasterVerificationGasLimit": "0x0",
"paymasterPostOpGasLimit": "0x0"
}
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexNumber`: Unique identifier for the request from this sender.
* **factory** - `Address (optional)`: The factory contract address.
* **factoryData** - `HexData (optional)`: The factory data for account creation.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymaster** - `Address (optional)`: Address of paymaster sponsoring the transaction, or null if none.
* **paymasterVerificationGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the verification step of the paymaster, or null if no paymaster.
* **paymasterPostOpGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the post-operation step of the paymaster, or null if no paymaster.
* **paymasterData** - `HexData (optional)`: The data to pass to the paymaster during the verification step, or null if no paymaster.
* **signature** - `HexData`: The signature data. For gas estimation, this can be a dummy signature.
* **eip7702Auth** - `SignedAuthorization (optional)`: The EIP-7702 authorization data. This can be a dummy authorization for estimations.
2. **entryPoint** - `Address`: The entry point contract address.
##### SignedAuthorization Type
```typescript
{
address: Address, // addressSchema - The contract address for the authorization
chainId: Hex, // hexNumberSchema transformed to Hex - The chain ID
nonce: Hex, // hexNumberSchema transformed to Hex - The nonce
r: Hex, // hexData32Schema transformed to Hex - The r component of the signature
s: Hex, // hexData32Schema transformed to Hex - The s component of the signature
v: HexString, // hexNumberSchema - The v component of the signature
yParity: HexNumber // hexNumberSchema transformed to Hex - The y-parity value
}
```
### EntryPoint 0.6
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "eth_estimateUserOperationGas",
"params": [
{
"sender": "0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce": "0x0",
"initCode": "0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5...",
"callData": "0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000...",
"callGasLimit": "0x0",
"verificationGasLimit": "0x0",
"preVerificationGas": "0x0",
"maxPriorityFeePerGas": "0x3b9aca00",
"maxFeePerGas": "0x7a5cf70d5",
"paymasterAndData": "0x",
"signature": "0x00000000fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
],
"id": 1
}
```
#### Returns
* **Type:** `Object`
An object containing the estimated gas values for the user operation.
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"preVerificationGas": "0xdf55",
"verificationGas": "0x52503",
"verificationGasLimit": "0x52503",
"callGasLimit": "0x13880"
}
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexNumber`: Unique identifier for the request from this sender.
* **initCode** - `HexData`: The initialization code for account creation.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymasterAndData** - `HexData`: The address of the paymaster sponsoring the transaction, followed by extra data to send to the paymaster.
* **signature** - `HexData`: The signature data. For gas estimation, this can be a dummy signature.
* **eip7702Auth** - `SignedAuthorization`: (Optional) The EIP-7702 authorization data. This can be a dummy authorization for estimations.
2. **entryPoint** - `Address`: The entry point contract address.
##### SignedAuthorization Type
```typescript
{
address: Address, // addressSchema - The contract address for the authorization
chainId: Hex, // hexNumberSchema transformed to Hex - The chain ID
nonce: Hex, // hexNumberSchema transformed to Hex - The nonce
r: Hex, // hexData32Schema transformed to Hex - The r component of the signature
s: Hex, // hexData32Schema transformed to Hex - The s component of the signature
v: HexString, // hexNumberSchema - The v component of the signature
yParity: HexNumber // hexNumberSchema transformed to Hex - The y-parity value
}
```
### Using state overrides
You can use state overrides to simulate the user operation with a different state the same way you would with [`eth_call`](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-eth#eth-call) by including an optional third parameter. This is supported for both EntryPoint 0.6 and 0.7 & 0.8.
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "eth_estimateUserOperationGas",
"params": [
{
"sender": "0xa203fDb8bC335F86016F635b85389B62B189E417",
"nonce": "0x35bf2a054f92f3730b87582ef223c8d663f9eb01158154750000000000000000",
"initCode": "0x",
"callData": "0xb61d27f6000000000000000000000000530fff22987e137e7c8d2adcc4c15eb45b4fa752...",
"callGasLimit": "0x115b5c0",
"verificationGasLimit": "0x249f0",
"preVerificationGas": "0xeb11",
"maxPriorityFeePerGas": "0x12a05f200",
"maxFeePerGas": "0x5b08082fa",
"paymasterAndData": "0x",
"signature": "0xa6cc6589c8bd561cfd68d7b6b0757ef6f208e7438782939938498eee7d703260137856c840c491b3d415956265e81bf5c2184a725be2abfc365f7536b6af525e1c"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
{
"0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3": {
"balance": "0xde0b6b3a7640000"
},
"0xebe8efa441b9302a0d7eaecc277c09d20d684540": {
"stateDiff": {
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80": "0x21"
}
}
}
],
"id": 1
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object (see the structure in the EntryPoint 0.6 or 0.7 & 0.8 sections above).
2. **entryPoint** - `Address`: The entry point contract address.
3. **stateOverrides** - `Object`: (Optional) State overrides to apply for the simulation.
* Each key is an `Address`
* Each value is an object that can contain:
* **balance** - `HexNumber (optional)` The balance to set for the address
* **nonce** - `HexNumber (optional)` The nonce to set for the address
* **code** - `HexData (optional)` The code to set for the address
* **state** - `Object (optional)` Complete state to set, where each key is a 32-byte hex storage slot and each value is a 32-byte hex value
* **stateDiff** - `Object (optional)` State differences to apply, where each key is a 32-byte hex storage slot and each value is a 32-byte hex value
## eth\_getUserOperationByHash
This method fetches the user operation, given the `userOpHash`. If the user operation is not available, it will return `null`.
### Request
```json
{
"jsonrpc": "2.0",
"method": "eth_getUserOperationByHash",
"params": ["0xa5a579c6fd86c2d8a4d27f5bb22796614d3a31bbccaba8f3019ec001e001b95f"],
"id": 1
}
```
### Response
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"userOperation": {
"sender": "0x8C6bdb488F664EB98E12cB2671fE2389Cc227D33",
"nonce": "0x18554d9a95404c5e8ac591f8608a18f80000000000000000",
"initCode": "0xaee9762ce625e0a8f7b184670fb57c37bfe1d0f1296601cd000000000000000000000000417f5a41305ddc99d18b5e176521b468b2a31b86000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014c982ab3499d854fca39ff8326b27d3b8a9463c5d000000000000000000000000",
"callData": "0x519454470000000000000000000000008eb187a55b701f8990539bf219b7921d5d3bdadd00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001647c7efe6b0000000000000000000000000000000000000000000000000000000000000080bfa0715290784075e564f966fffd9898ace1d7814f833780f62e59b0791357460000000000000000000000008c6bdb488f664eb98e12cb2671fe2389cc227d3300000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000007677265676f7279000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001b1ec8da9a52488e3f60b7b7af36c2e64f1873e03a047c2c2e9061bbef4d0a9d8f1332b32afb163075273cd462140c2a24b2c9e1276adfaae0b4cb84ef78b95e8a0000000000000000000000000000000000000000000000000000000065a32e4f00000000000000000000000000000000000000000000000000000000",
"callGasLimit": "0x39b2d",
"verificationGasLimit": "0x6fb14",
"preVerificationGas": "0xc6a0",
"maxFeePerGas": "0x974038caf",
"maxPriorityFeePerGas": "0x974038c95",
"paymasterAndData": "0xe3dc822d77f8ca7ac74c30b0dffea9fcdcaaa3210000000000000000000000000000000000000000000000000000000065a3229b00000000000000000000000000000000000000000000000000000000000000009c3e8f934f2ec99974fddae7d38107a6d899c236c1b127e97d3c074cea5bb328023e295bb95254be40c47b82633676f8716c43c81aca3f2e49c2944afd1326371b",
"signature": "0x000000003ea0a3434dfd35a9eb05c4605466a7e05a5c3fc8aaba066c83bf4b43300dfd930e2203725eafa2702f860e5b6c18b4402ffb86b0d9b9c1719f1692254039810b1b"
},
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"transactionHash": "0x57465d20d634421008a167cfcfcde94847dba9d6b5d3652b071d4b84e5ce74ff",
"blockHash": "0xeaeec1eff4095bdcae44d86574cf1bf08b14b26be571b7c2290f32f9f250c103",
"blockNumber": "0x31de70e"
}
}
```
## eth\_getUserOperationReceipt
This method fetches the receipt of a user operation, given the `userOpHash`. If the receipt is not available, it will return `null`.
### Request
```json
{
"jsonrpc": "2.0",
"method": "eth_getUserOperationReceipt",
"params": ["0xa5a579c6fd86c2d8a4d27f5bb22796614d3a31bbccaba8f3019ec001e001b95f"],
"id": 1
}
```
### Response
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"userOpHash": "0xa5a579c6fd86c2d8a4d27f5bb22796614d3a31bbccaba8f3019ec001e001b95f",
"sender": "0x8C6bdb488F664EB98E12cB2671fE2389Cc227D33",
"nonce": "0x18554d9a95404c5e8ac591f8608a18f80000000000000000",
"actualGasUsed": "0x7f550",
"actualGasCost": "0x4b3b147f788710",
"success": true,
"logs": [
// ...
],
"receipt": {
"transactionHash": "0x57465d20d634421008a167cfcfcde94847dba9d6b5d3652b071d4b84e5ce74ff",
"transactionIndex": "0x20",
"blockHash": "0xeaeec1eff4095bdcae44d86574cf1bf08b14b26be571b7c2290f32f9f250c103",
"blockNumber": "0x31de70e",
"from": "0x43370996A3Aff7B66B3AC7676dD973d01Ecec039",
"to": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"cumulativeGasUsed": "0x4724c3",
"gasUsed": "0x7ff5a",
"address": null,
"logs": [
// ...
],
"logsBloom": "0x010004000800020000000040000000000000040000000000000010000004000000080000001000000212841100000000041080000000000020000240000000000800000022001000400000080000028000040000000000200001000010000000000000000a0000000000000000800800000000004110004080800110282000000000000002000000000000000000000000000200000400000000000000240040200002000000000000400000000002000140000000000000000002200000004000000002000000000021000000000000000000000000800080108020000020000000080000000000000000000000000000000000000000000108000000102000",
"status": "0x1",
"effectiveGasPrice": "0x89b098f46"
}
}
}
```
## eth\_sendUserOperation
Submits a user operation to be included on-chain and returns the `userOpHash` if successful (or queued). If the operation is not successful, it will return an error.
### EntryPoint 0.7 & 0.8
:::warning[Warning]
The EntryPoint 0.7 & 0.8 API expects an `UnpackedUserOperation` instead of a `PackedUserOperation`.
According to the spec it is bundler's responsibility to pack the userOp before sending it to the EntryPoint.
So make sure to send an `UnpackedUserOperation` to the bundler.
:::
:::warning[Warning]
For entryPoint v0.7, we will not be reverting if the execution of the `callData` field in the user operation fails.
We revert only when you are calling `eth_estimateUserOperationGas`.
:::
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "eth_sendUserOperation",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845ADB2C711129D4F3966735ED98A9F09FC4CE5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d000000000000000000000000aac5d4240af87249b3f71bc8e4a2cae074a3e419...",
"callData": "0xe9ae5c5300000000000000000000000000000000000000000000000000000000000000000000000000...",
"callGasLimit": "0x13880",
"verificationGasLimit": "0x60B01",
"preVerificationGas": "0xD3E3",
"maxPriorityFeePerGas": "0x3B9ACA00",
"maxFeePerGas": "0x7A5CF70D5",
"paymaster": "0x",
"paymasterVerificationGasLimit": "0x0",
"paymasterPostOpGasLimit": "0x0",
"paymasterData": null,
"signature": "0xa6cc6589c8bd561cfd68d7b6b0757ef6f208e7438782939938498eee7d703260137856c840c491b3d415956265e81bf5c2184a725be2abfc365f7536b6af525e1c"
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032"
],
"id": 1
}
```
#### Returns
* **Type:** `HexString32Bytes`
The hash of the user operation (`userOpHash`). This hash uniquely identifies the user operation and can be used to track its status.
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x4c31ae84205a9c862dd8d0822f427fb516448451850ee6f65351951f6a2b2154"
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender. For v0.7, this includes the key and sequence number.
* **factory** - `Address (optional)`: The factory contract address that will deploy the smart account if it doesn't exist yet.
* **factoryData** - `HexData (optional)`: The data that will be passed to the factory contract to deploy the smart account.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymaster** - `Address (optional)`: Address of paymaster sponsoring the transaction, or null if none.
* **paymasterVerificationGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the verification step of the paymaster, or null if no paymaster.
* **paymasterPostOpGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the post-operation step of the paymaster, or null if no paymaster.
* **paymasterData** - `HexData (optional)`: The data to pass to the paymaster during the verification step, or null if no paymaster.
* **signature** - `HexData`: The signature data.
* **eip7702Auth** - `SignedAuthorization (optional)`: The EIP-7702 authorization data. `SignedAuthorization Type`:
```typescript
{
address: Address, // addressSchema - The contract address for the authorization
chainId: Hex, // hexNumberSchema transformed to Hex - The chain ID
nonce: Hex, // hexNumberSchema transformed to Hex - The nonce
r: Hex, // hexData32Schema transformed to Hex - The r component of the signature
s: Hex, // hexData32Schema transformed to Hex - The s component of the signature
v: HexString, // hexNumberSchema - The v component of the signature
yParity: HexNumber // hexNumberSchema transformed to Hex - The y-parity value
}
```
2. **entryPoint** - `Address`: The entry point contract address (`0x0000000071727De22E5E9d8BAf0edAc6f37da032`).
### EntryPoint 0.6
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "eth_sendUserOperation",
"params": [
{
"sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce":"0x0",
"initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5...",
"callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000...",
"callGasLimit":"0x115b5c0",
"verificationGasLimit":"0x249f0",
"preVerificationGas":"0xeb11",
"maxPriorityFeePerGas":"0x12a05f200",
"maxFeePerGas":"0x5b08082fa",
"paymasterAndData":"0x",
"signature":"0xa6cc6589c8bd561cfd68d7b6b0757ef6f208e7438782939938498eee7d703260137856c840c491b3d415956265e81bf5c2184a725be2abfc365f7536b6af525e1c"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
],
"id": 1
}
```
#### Returns
* **Type:** `HexString32Bytes`
The hash of the user operation (`userOpHash`). This hash uniquely identifies the user operation and can be used to track its status.
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x4c31ae84205a9c862dd8d0822f427fb516448451850ee6f65351951f6a2b2154"
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **initCode** - `HexData`: The initialization code for the smart account if it doesn't exist yet.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymasterAndData** - `HexData`: The address of the paymaster contract and the data that will be passed to it.
* **signature** - `HexData`: The signature data.
* **eip7702Auth** - `SignedAuthorization (optional)`: The EIP-7702 authorization data. `SignedAuthorization Type`:
```typescript
{
address: Address, // addressSchema - The contract address for the authorization
chainId: Hex, // hexNumberSchema transformed to Hex - The chain ID
nonce: Hex, // hexNumberSchema transformed to Hex - The nonce
r: Hex, // hexData32Schema transformed to Hex - The r component of the signature
s: Hex, // hexData32Schema transformed to Hex - The s component of the signature
v: HexString, // hexNumberSchema - The v component of the signature
yParity: HexNumber // hexNumberSchema transformed to Hex - The y-parity value
}
```
2. **entryPoint** - `Address`: The entry point contract address (`0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789`).
## eth\_supportedEntryPoints
This method fetches the EntryPoint addresses supported by the bundler. The first address is the one preferred by the bundler to use.
### Request
```json
{
"jsonrpc": "2.0",
"method": "eth_supportedEntryPoints",
"params": [],
"id": 1
}
```
### Response
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": ["0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"]
}
```
## pimlico\_getUserOperationGasPrice
This method returns the gas prices that must be used for the user operation you are bundling with Pimlico bundlers.
### Request
```json
{
"jsonrpc": "2.0",
"method": "pimlico_getUserOperationGasPrice",
"params": [],
"id": 1
}
```
### Response
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"slow": {
"maxFeePerGas": "0x829b42b5",
"maxPriorityFeePerGas": "0x829b42b5"
},
"standard": {
"maxFeePerGas": "0x88d36a75",
"maxPriorityFeePerGas": "0x88d36a75"
},
"fast": {
"maxFeePerGas": "0x8f0b9234",
"maxPriorityFeePerGas": "0x8f0b9234"
}
}
}
```
## pimlico\_getUserOperationStatus
This method takes in a user operation hash and returns the status of the operation and, optionally, the transaction hash the bundler is using to bundle the user operation on-chain. The status can be one of the following:
| result | response includes transaction hash | description |
| :-------------- | :--------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- |
| `not_found` | false | the operation hash is not known to the bundler or has been rejected during validation and has never entered the mempool |
| `not_submitted` | false | the operation hash is known to the bundler but is sitting in the mempool and has not been bundled into a transaction yet |
| `submitted` | true | the operation hash is known to the bundler, has been bundled into a transaction which is currently pending in the mempool |
| `rejected` | false | the operation hash has entered the mempool but as it was being bundled into a bundle transaction the re-simulation failed and it was never submitted |
| `included` | true | the operation hash is known to the bundler and has been included on-chain |
| `failed` | true | the operation hash is known to the bundler and the transaction bundling it has been included on-chain but the bundle transaction reverted |
| `queued` | false | the operation hash is known to the bundler but is waiting in a queue before being sent to the mempool due to its nonce being too high |
:::info
Note: the transaction hash returned when the result is `submitted` can change if the bundler resubmits the user operation inside a different transaction. For this reason, when showing the pending transaction hash to the user, it is recommended to keep calling this method in case the transaction hash changes.
:::
### Request
```json
{
"jsonrpc": "2.0",
"method": "pimlico_getUserOperationStatus",
"params": ["0x9bd004b8240da8eba3a02190a72be8a70ade8ef4c581b6e59789643c5e642ac3"],
"id": 1
}
```
### Response
:::code-group
```json [not_found]
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"status": "not_found",
"transactionHash": null
}
}
```
```json [submitted]
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"status": "submitted",
"transactionHash": "0x9bd004b8240da8eba3a02190a72be8a70ade8ef4c581b6e59789643c5e642ac3"
}
}
```
```json [included]
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"status": "included",
"transactionHash": "0x9bd004b8240da8eba3a02190a72be8a70ade8ef4c581b6e59789643c5e642ac3"
}
}
```
:::
## pimlico\_simulateAssetChanges
:::warning
NOTICE: This page is still in draft and the API may change.
:::
:::note
This endpoint does not support v0.6 user operations.
:::
This method shows asset changes that a user operation would produce if executed. It simulates the user operation and shows all balance changes including native currency, ERC-20, ERC-1155, and ERC-721 tokens.
### Request
```json
{
"jsonrpc": "2.0",
"method": "pimlico_simulateAssetChanges",
"id": 4337,
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x1",
"callData": "0xe9ae5c53...",
"callGasLimit": "0x0",
"verificationGasLimit": "0x0",
"preVerificationGas": "0x0",
"maxFeePerGas": "0x7a5cf70d5",
"maxPriorityFeePerGas": "0x3b9aca00",
"paymasterVerificationGasLimit": "0x0",
"paymasterPostOpGasLimit": "0x0",
"signature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032"
]
}
```
#### Parameters
1. `userOperation`: Expects the same userOperation format as eth\_estimateUserOperationGas
* Signature must be a valid dummy signature
* Gas fields (`callGasLimit`, `verificationGasLimit`, `preVerificationGas`, `paymasterPostOpGasLimit`, `paymasterVerificationGasLimit`) are optional
* Gas price fields (`maxFeePerGas`, `maxPriorityFeePerGas`) are optional
2. `entryPoint`: EntryPoint Address
3. `blockNumber` (optional): hex encoded block number to run the simulation at (defaults to latest)
### Response
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"assetChanges": [
{
"token": {
"tokenType": "ERC-20",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"decimals": 6,
"name": "USD Coin",
"symbol": "USDC"
},
"value": {
"diff": "-1000000",
"pre": "100000000",
"post": "99000000"
}
},
{
"token": {
"tokenType": "ERC-20",
"address": "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
"decimals": 8,
"name": "Wrapped BTC",
"symbol": "WBTC"
},
"value": {
"diff": "1196",
"pre": "0",
"post": "1196"
}
},
{
"token": {
"tokenType": "NATIVE"
},
"value": {
"diff": "-1000000000000000000",
"pre": "5000000000000000000",
"post": "4000000000000000000"
}
}
]
}
}
```
### Response Format
#### Native Currency Example
```typescript
{
"token": {
"tokenType": "NATIVE"
},
"value": {
"diff": "String", // BigInt string
"pre": "String", // BigInt string
"post": "String" // BigInt string
}
}
```
#### ERC-20 Token Example
```typescript
{
"token": {
"tokenType": "ERC-20",
"address": "Address",
"decimals": "Number",
"name": "String", // (Optional)
"symbol": "String" // (Optional)
},
"value": {
"diff": "String", // BigInt string
"pre": "String", // BigInt string
"post": "String" // BigInt string
}
}
```
#### ERC-721 Token Example
```typescript
{
"token": {
"tokenType": "ERC-721",
"address": "Address",
"tokenId": "Number",
"name": "String", // (Optional)
"symbol": "String" // (Optional)
},
"value": {
"diff": "String", // BigInt string
"pre": "String", // BigInt string
"post": "String" // BigInt string
}
}
```
#### ERC-1155 Token Example
```typescript
{
"token": {
"tokenType": "ERC-1155",
"address": "Address",
"tokenId": "Number",
"name": "String", // (Optional)
"symbol": "String" // (Optional)
},
"value": {
"diff": "String", // BigInt string
"pre": "String", // BigInt string
"post": "String" // BigInt string
}
}
```
### Token Metadata Fields
Metadata fields are included when available based on token type:
* For ERC-20 tokens: `name`, `symbol`
* For ERC-721 tokens: `name`, `symbol`
* For ERC-1155 tokens: `name`, `symbol`
These fields will be included when they can be found, otherwise they'll be undefined. This is because metadata is optional in ERC-20, ERC-721, ERC-1155 standards, though most tokens do support these fields.
### Error Responses
| Error Code | Description |
| :-------------------- | :------------------------------------------------ |
| AA23 | UserOperation reverted during simulation |
| UserOperationReverted | User operation execution failed during simulation |
| SimulateValidation | Validation failed for the user operation |
## AA10 sender already constructed
The sender (i.e. the smart account) has already been created previously. You may be trying to create the account multiple times.
### Possible solutions
* Remove the `initCode` from the user operation.
```ts
const userOperationHash = await bundlerClient.sendUserOperation({
initCode: "0xaee9762ce625e0a8f7b184670fb57c37bfe5e0a8f7...", // [!code --]
initCode: "0x", // [!code ++]
..., // the rest of the user operation
})
```
## AA13 initCode failed or OOG
:::info
OOG = Out Of Gas
:::
The EntryPoint failed to create the smart account with the `initCode` provided. There are two possible reasons:
* The `initCode` ran out of gas
* The `initCode` reverted during the account deployment process
### Possible solutions
* Verify that the factory address in the `initCode` is correct (the factory address is the first 20 bytes of the `initCode`).
* Verify that the `initCode` is correct.
* Check whether the `verificationGasLimit` is sufficient for the `initCode` to complete without running out of gas.
* If the root cause is a revert, investigate why the `initCode` reverts during the account deployment process using tools like [Tenderly](https://tenderly.co/).
## AA14 initCode must return sender
The address of the smart account deployed with the `initCode` provided does not match match the `sender` address provided in the user operation.
### Possible solutions
* Verify that the `sender` address was generated deterministically from the `initCode`. (consider leveraging functions like [getSenderAddress](/references/permissionless/reference/public-actions/getSenderAddress))
* Verify that the factory address in the `initCode` is correct (the factory address is the first 20 bytes of the `initCode`).
* Verify that the `initCode` is correct.
* If all else fails, investigate why the `initCode` deploys to a different address than expected using tools like [Tenderly](https://tenderly.co/).
## AA15 initCode must create sender
The smart account deployment process with the `initCode` does not return any `sender` address. There are two possible reasons:
* The `initCode` factory is not creating an account.
* The `initCode` factory is creating an account, but is not implemented correctly as it is not returning the deployed `sender` address.
### Possible solutions
* Verify that the factory address in the `initCode` is correct (the factory address is the first 20 bytes of the `initCode`).
* Verify that the `initCode` factory is implemented correctly. The factory must deploy the smart account and return the deployed `sender` address.
* If all else fails, investigate why the `initCode` factory does not return a `sender` address using tools like [Tenderly](https://tenderly.co/).
## AA20 account not deployed
An `initCode` was not specified, but the `sender` address (i.e. the smart account) is not deployed.
### Possible solutions
* If this is the first transaction by this account, make sure the `initCode` is included in the user operation.
* If the smart account is already supposed to be deployed, verify that you have selected the correct `sender` address for the user operation.
## AA21 didn't pay prefund
You are *not* using a paymaster, but the `sender` address did not have enough native tokens to cover the gas costs associated with the user operation.
### Possible solutions
* If you are *not* using a paymaster, verify that the `sender` address has enough native tokens to cover the required prefund. Consider leveraging functions like [`getRequiredPrefund`](/references/permissionless/reference/utils/getRequiredPrefund).
```ts
const requiredPrefund = getRequiredPrefund({
userOperation
})
const senderBalance = await publicClient.getBalance({
address: userOperation.sender
})
if (senderBalance < requiredPrefund) {
throw new Error(`Sender address does not have enough native tokens`)
}
```
* If you are looking to use a paymaster to cover the gas fees, verify that the `paymasterAndData` field is set.
```ts
const userOperationHash = await bundlerClient.sendUserOperation({
paymasterAndData: "0x3b912be0270b59143985cc5c6aab452d99e2b4bb0000000000000000000000000000000000000000000000000000000064c0957400000000000000000000000000000000000000000000000000000000000000007d99385d8ef0af67affbf9944df8c121e9d1f6aef8dd82a4aeb5db310c42d3dc5b51c9e0835d94c3b22564d3d94f0e1d14e37571e46651da8de567d128a361a01b", // [!code ++]
..., // the rest of the user operation
})
```
## AA22 expired or not due
The `signature` used in the user operation is not valid, because it is outside of the time range it specified.
The smart account's `validateUserOp` function returns `validationData`, a `bytes32` field. This field is a concatenation of the `validAfter`, `validUntil`, and `aggregator` fields.
This error occurs when the `block.timestamp` falls after the `validUntil` timestamp, or before the `validAfter` timestamp.
### Possible solutions
* If you are looking to use time-based signatures, verify that the `validAfter` and `validUntil` fields are set correctly and that the user operation is sent within the specified range.
* If you are *not* looking to use time-based signatures, verify that the `validAfter` and `validUntil` fields are set to `0`.
:::tip[Tip]
If you are not looking to use neither time-based signatures nor a signature aggregator, it is recommended to simply return `uint(0)` for valid signatures and `uint(1)` for invalid signatures in the smart account's `validateUserOp` function.
:::
## AA23 reverted (or OOG)
:::info
OOG = Out Of Gas
:::
The `validateUserOp` function of the smart account either reverted or ran out of gas.
### Possible solutions
* Verify that the `verificationGasLimit` is high enough to cover the `validateUserOp` function's gas costs.
* Verify that the `validateUserOp` function is implemented with the correct logic, and that the user operation is supposed to be valid.
* If you are *not* using a paymaster, verify that the `sender` address has enough native tokens to cover the required prefund. Consider leveraging functions like [`getRequiredPrefund`](/references/permissionless/reference/utils/getRequiredPrefund).
```ts
const requiredPrefund = getRequiredPrefund({
userOperation
})
const senderBalance = await publicClient.getBalance({
address: userOperation.sender
})
if (senderBalance < requiredPrefund) {
throw new Error(`Sender address does not have enough native tokens`)
}
```
* If you *are* looking to use a paymaster to cover the gas fees, verify that the `paymasterAndData` field is set.
```ts
const userOperationHash = await bundlerClient.sendUserOperation({
paymasterAndData: "0x3b912be0270b59143985cc5c6aab452d99e2b4bb0000000000000000000000000000000000000000000000000000000064c0957400000000000000000000000000000000000000000000000000000000000000007d99385d8ef0af67affbf9944df8c121e9d1f6aef8dd82a4aeb5db310c42d3dc5b51c9e0835d94c3b22564d3d94f0e1d14e37571e46651da8de567d128a361a01b", // [!code ++]
..., // the rest of the user operation
})
```
* Make sure the signature verification logic of the smart account is implemented correctly. Namely, correct implementations should **not** revert when the signature is invalid. If you are not looking to use neither time-based signatures nor a signature aggregator, you should simply return `uint(1)` for invalid signatures in the `validateUserOp` function.
* If all else fails, investigate why the `validateUserOp` function reverted using tools like [Tenderly](https://tenderly.co/).
## AA24 signature error
The `validateUserOp` function of the smart account rejected the signature of the user operation.
### Possible solutions
* Verify that the user operation was correctly signed, and that the signature was correctly encoded in the `signature` field of the user operation.
* Most smart account implementations sign over the `userOpHash`. Make sure that the `userOpHash` is correctly computed. Consider leveraging functions like `getUserOperationHash` from `viem/account-abstraction`.
* Make sure you have selected the correct `chainId` and `entryPointAddress` when computing the `userOpHash`.
* Make sure the smart account signature verification function is correctly implemented.
* If all else fails, investigate why the `validateUserOp` function rejected the user operation signature using tools like [Tenderly](https://tenderly.co/).
## AA25 invalid account nonce
The `nonce` of the user operation is invalid.
### Possible solutions
* Verify that you are using the correct `nonce` for the user operation. The `nonce` should be the current nonce of the smart account for the selected `key`. Consider leveraging functions like [`getAccountNonce`](/references/permissionless/reference/public-actions/getAccountNonce).
* Make sure that you are not reusing a nonce that has already been used.
* Make sure that you are not using a nonce that is too far in the future (more than 10 higher than the current nonce).
* Verify that the nonce is formatted correctly.
* Use custom nonce `key` to send parallel transactions. See [How to Send Multiple Transactions](/references/permissionless/how-to/parallel-transactions##sending-multiple-transactions-in-parallel).
:::info
Instead of sequential nonce the EntryPoint implements a nonce mechanism that uses a single `uint256` nonce value in the user operation, but treats it as two values:
* 192-bit `key`
* 64-bit `sequence`
For each `key` the `sequence` is validated and incremented sequentially and monotonically by the EntryPoint for each user operation, however a new `key` can be introduced with an arbitrary value at any point.
:::
## AA30 paymaster not deployed
:::info
The first 20 bytes of the `paymasterAndData` field specify the address of the paymaster contract you are requesting to use.
:::
The paymaster contract is not deployed.
### Possible solutions
* Verify that the `paymasterAndData` field is correct, and that the first 20 bytes are the address of the paymaster contract you intend to use.
* Verify that the paymaster contract is deployed on the network you are using.
## AA31 paymaster deposit too low
:::info
The first 20 bytes of the `paymasterAndData` field specify the address of the paymaster contract you are requesting to use.
:::
The paymaster contract does not have enough funds deposited into the EntryPoint contract to cover the required prefund of the user operation.
### Possible solutions
* Verify that the `paymasterAndData` field is correct, and that the first 20 bytes are the address of the paymaster contract you intend to use.
* If you are using your own paymaster contract, deposit more funds into the EntryPoint contract through the `deposit()` function of the paymaster contract.
* If you are using a paymaster service like Pimlico, [reach out to them](https://t.me/pimlicoHQ).
## AA32 expired or not due
:::info
By default, signatures from Pimlico's verifying paymaster are only valid for 10 minutes.
:::
The paymaster's signature used in the `paymasterAndData` field of the user operation is not valid, because it is outside of the time range it specified.
The paymaster's `validatePaymasterUserOp` function returns `validationData`, a `bytes32` field. This field is a concatenation of the `validAfter`, `validUntil`, and `aggregator` fields.
This error occurs when the `block.timestamp` falls after the `validUntil` timestamp, or before the `validAfter` timestamp.
### Possible solutions
* If you are using Pimlico's paymaster, send the user operation within 10 minutes or [extend the validity period](/guides/how-to/paymasters/extending-sponsorship-duration) up to 24 hours.
* If you are using your own paymaster contract and using time-based signatures, verify that the `validAfter` and `validUntil` fields are set correctly and that the user operation is sent within the specified range.
* If you are using your own paymaster contract and *not* looking to use time-based signatures, verify that the `validAfter` and `validUntil` fields are set to `0`.
## AA33 reverted (or OOG)
:::info
OOG = Out Of Gas
:::
The `validatePaymasterUserOp` function of the paymaster contract either reverted or ran out of gas.
### Possible solutions
* Verify that the `verificationGasLimit` is high enough to cover the `validatePaymasterUserOp` function's gas costs.
* If you are using your own paymaster contract, verify that the `validatePaymasterUserOp` function is implemented with the correct logic, and that the user operation is supposed to be valid.
* If you are using your own paymaster contract, make sure the signature verification logic of the paymaster is implemented correctly. Namely, correct implementations should **not** revert when the paymaster signature is invalid. If you are not looking to use neither time-based signatures, you should simply return `uint(1)` for invalid signatures in the `validatePaymasterUserOp` function.
* If you are using a paymaster service like Pimlico, and the user operation is well formed with a high enough `verificationGasLimit`, [reach out to them](https://t.me/pimlicoHQ).
* If you are *not* looking to use a paymaster to cover the gas fees, verify that the `paymasterAndData` field is not set.
```ts
const userOperationHash = await bundlerClient.sendUserOperation({
paymasterAndData: "0x3b912be0270b59143985cc5c6aab452d99e2b4bb0000000000000000000000000000000000000000000000000000000064c0957400000000000000000000000000000000000000000000000000000000000000007d99385d8ef0af67affbf9944df8c121e9d1f6aef8dd82a4aeb5db310c42d3dc5b51c9e0835d94c3b22564d3d94f0e1d14e37571e46651da8de567d128a361a01b", // [!code --]
paymasterAndData: "0x", // [!code ++]
..., // the rest of the user operation
})
```
* If all else fails, investigate why the `validatePaymasterUserOp` function reverted using tools like [Tenderly](https://tenderly.co/).
## AA34 signature error
:::info
The first 20 bytes of the `paymasterAndData` field specify the address of the paymaster contract you are requesting to use.
:::
The `validatePaymasterUserOp` function of the paymaster contract rejected the signature of the user operation.
### Possible solutions
* If you are using your own paymaster contract, verify that the user operation was correctly signed according to your implementation, and that the paymaster signature was correctly encoded in the `paymasterAndData` field of the user operation.
* Most paymaster implementations sign over a custom hash of the user operation. Make sure that the hash is correctly computed.
* Make sure the paymaster signature verification function is correctly implemented.
* If you are using a paymaster service like Pimlico, make sure you do not modify any of the fields of the user operation after the paymaster signs over it (except the `signature` field).
* If you are using a paymaster service like Pimlico and you have not modified any of the fields except the `signature` but you are still getting this error, [reach out to them](https://t.me/pimlicoHQ).
* If all else fails, investigate why the `validatePaymasterUserOp` function rejected the paymaster signature using tools like [Tenderly](https://tenderly.co/).
## AA40 over verificationGasLimit
The amount of gas used by the smart account and paymaster verification exceeded the `verificationGasLimit` set for the user operation.
### Possible solutions
* Verify that the `verificationGasLimit` set for the user operation is high enough to cover the gas used during smart account and paymaster verification.
* If you are using the `eth_estimateUserOperationGas` or `pm_sponsorUserOperation` method from bundler provider like Pimlico to set user operation gas limits and the EntryPoint throws this error during submission, [reach out to them](https://t.me/pimlicoHQ).
* If all else fails, investigate why the smart account and/or paymaster used more gas than expected using tools like [Tenderly](https://tenderly.co/).
## AA41 too little verificationGas
The amount of gas used by the smart account verification exceeded the `verificationGasLimit` set for the user operation.
### Possible solutions
* Verify that the `verificationGasLimit` set for the user operation is high enough to cover the gas used during smart account's `validateUserOp` function.
* If you are using the `eth_estimateUserOperationGas` or `pm_sponsorUserOperation` method from bundler provider like Pimlico to set user operation gas limits and the EntryPoint throws this error during submission, [reach out to them](https://t.me/pimlicoHQ).
* If all else fails, investigate why the smart account's `validateUserOp` function used more gas than expected using tools like [Tenderly](https://tenderly.co/).
## AA50 postOp reverted
:::info
`postOp` is a function that is called by the EntryPoint on the paymaster contract after the user operation execution is completed. It is often used by paymasters to perform additional accounting logic.
:::
The paymaster contract's `postOp` function reverted.
### Possible solutions
* If you are using your own paymaster contract, verify that that you have correctly implemented the `postOp` function (if you are using one). If you do *not* intent to make use of the `postOp` function, make sure you do not set the `context` parameter in the paymaster's `validatePaymasterUserOp` function.
* If you are using a paymaster service like Pimlico and you see this error, [reach out to them](https://t.me/pimlicoHQ).
* If all else fails, investigate why the `postOp` function reverted using tools like [Tenderly](https://tenderly.co/).
## AA51 prefund below actualGasCost
The actual gas cost of the user operation ended up being higher than the prefund paid by the smart account or the paymaster.
### Possible solutions
* If you encounter this error, try increasing the `verificationGasLimit` set for the user operation.
* If you are using the `eth_estimateUserOperationGas` or `pm_sponsorUserOperation` method from bundler provider like Pimlico to set user operation gas limits and the EntryPoint throws this error during submission, [reach out to them](https://t.me/pimlicoHQ).
* If all else fails, investigate why the actual gas cost ended up being higher than the prefund paid using tools like [Tenderly](https://tenderly.co/).
## AA90 invalid beneficiary
The bundler did not set a beneficiary address when bundling the user operation.
### Possible solutions
* If you encounter this error when running self-hosted bundler, make sure you have configured the bundler correctly.
* If you are using a bundler provider like Pimlico, [reach out to them](https://t.me/pimlicoHQ).
## AA91 failed send to beneficiary
The EntryPoint's transfer to the beneficiary of the funds accrued from the user operation reverted.
### Possible solutions
* If you encounter this error when running self-hosted bundler, make sure you have configured the bundler correctly.
* If you are using a bundler provider like Pimlico, [reach out to them](https://t.me/pimlicoHQ).
## AA92 internal call only
The bundler attempted to call `innerHandleOp` function of the EntryPoint directly as opposed to through the `handleOps` function.
### Possible solutions
* If you encounter this error when running self-hosted bundler, make sure you have configured the bundler correctly.
* If you are using a bundler provider like Pimlico, [reach out to them](https://t.me/pimlicoHQ).
## AA93 invalid paymasterAndData
:::info
The first 20 bytes of the `paymasterAndData` field specify the address of the paymaster contract you are requesting to use.
:::
The `paymasterAndData` field is of an incorrect length.
### Possible solutions
* Make sure you have either not set a value for the `paymasterAndData`, or that it is at least 20 bytes long.
* If you are using a paymaster provider like Pimlico, [reach out to them](https://t.me/pimlicoHQ).
## AA94 gas values overflow
One or more of the gas values of the user operation do not fit into a `uint160`.
### Possible solutions
* Verify that all the gas limit and gas price fields in your user operation fit into `uint160`.
* If the gas limits or gas prices for your user operation causing this error are suggested by a bundler provider like Pimlico, [reach out to them](https://t.me/pimlicoHQ).
## AA95 out of gas
The bundler tried to bundle the user operation with the gas limit set too low.
### Possible solutions
* If you are using a bundler provider like Pimlico, [reach out to them](https://t.me/pimlicoHQ).
## AA96 invalid aggregator
The bundler tried to bundle aggregated user operations with an invalid aggregator.
### Possible solutions
* If you are using a bundler provider like Pimlico, [reach out to them](https://t.me/pimlicoHQ).
## EntryPoint Errors
We have listed all the errors that an ERC 4337 EntryPoint can return.
* [AA10 sender already constructed](/references/bundler/entrypoint-errors/aa10)
* [AA13 initCode failed or OOG](/references/bundler/entrypoint-errors/aa13)
* [AA14 initCode must return sender](/references/bundler/entrypoint-errors/aa14)
* [AA15 initCode must create sender](/references/bundler/entrypoint-errors/aa15)
* [AA20 account not deployed](/references/bundler/entrypoint-errors/aa20)
* [AA21 didn't pay prefund](/references/bundler/entrypoint-errors/aa21)
* [AA22 expired or not due](/references/bundler/entrypoint-errors/aa22)
* [AA23 reverted](/references/bundler/entrypoint-errors/aa23)
* [AA24 signature error](/references/bundler/entrypoint-errors/aa24)
* [AA25 invalid account nonce](/references/bundler/entrypoint-errors/aa25)
* [AA30 paymaster not deployed](/references/bundler/entrypoint-errors/aa30)
* [AA31 paymaster deposit too low](/references/bundler/entrypoint-errors/aa31)
* [AA32 paymaster expired or not due](/references/bundler/entrypoint-errors/aa32)
* [AA33 reverted](/references/bundler/entrypoint-errors/aa33)
* [AA34 signature error](/references/bundler/entrypoint-errors/aa34)
* [AA40 over verificationGasLimit](/references/bundler/entrypoint-errors/aa40)
* [AA41 too little verificationGas](/references/bundler/entrypoint-errors/aa41)
* [AA50 postOp reverted](/references/bundler/entrypoint-errors/aa50)
* [AA51 prefund below actualGasCost](/references/bundler/entrypoint-errors/aa51)
* [AA90 invalid beneficiary](/references/bundler/entrypoint-errors/aa90)
* [AA91 failed send to beneficiary](/references/bundler/entrypoint-errors/aa91)
* [AA92 internal call only](/references/bundler/entrypoint-errors/aa92)
* [AA93 invalid paymasterAndData](/references/bundler/entrypoint-errors/aa93)
* [AA94 gas values overflow](/references/bundler/entrypoint-errors/aa94)
* [AA95 out of gas](/references/bundler/entrypoint-errors/aa95)
* [AA96 invalid aggregator](/references/bundler/entrypoint-errors/aa96)
## ERC-20 Paymaster Architecture
:::info
You can view the whole ERC-20 Paymaster contract [in our repository](https://github.com/pimlicolabs).
This paymaster is an onchain contract, we can not guarantee that using the paymaster is risk-free. Please use the paymaster at your own risk.
:::
### Design
Pimlico's ERC-20 Paymaster is an ERC-4337 Paymaster that relies on an offchain API, powered by Pimlico, to supply the user with an up-to-date token price, alongside a signature from a valid signer address. The paymaster is able to pay for the gas fees of your users in exchange for ERC-20 tokens that are drawn from the user.
### Estimating the amount of tokens required for a user operation
After calling the `pimlico_getTokenQuotes` function, the Pimlico API will return two important values, `exchangeRate` and `postOpGas`. You can use these two values, alongside information about the user operation you're looking to sponsor, to get an estimate of the amount of tokens required for the user operation.
You can do this by using [this onchain function](https://github.com/pimlicolabs) on the paymaster.
## ERC-20 Paymaster Contract Addresses
Below are the contract addresses for the ERC-20 Paymaster contracts that are currently deployed.
Pimlico's ERC-20 Paymaster is deployed at the following addresses:
| EntryPoint Version | Paymaster Contract Address |
| :----------------- | :----------------------------------------- |
| v0.6 | 0x6666666666667849c56f2850848ce1c4da65c68b |
| v0.7 | 0x777777777777AeC03fd955926DbF81597e66834C |
| v0.8 | 0x888888888888Ec68A58AB8094Cc1AD20Ba3D2402 |
All currently deployed paymasters have the same contract address on all chains, but this might not remain the case in the future.
For a list of supported tokens, please see the [supported tokens page](/references/paymaster/erc20-paymaster/supported-tokens).
## ERC-20 Paymaster FAQs
### What is a paymaster?
A Paymaster is a special smart contract under the [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) specification that user operations are able to delegate the responsibility of gas fee payments to. This means that ERC-4337 smart contract wallets no longer need to necessarily be responsible for directly paying gas fees in ETH. The paymaster contracts are able to use custom logic (with certain limitations) to decide whether or not they are willing to sponsor a user operation.
### What is an ERC-20 Paymaster?
An ERC-20 Paymaster is a specific type of paymaster that is willing to sponsor the gas fees for a user operation if and only if the smart contract wallet pays the paymaster for it in an ERC-20 token like USDC, DAI, etc. In effect, this allows smart contract wallets to pay for gas fees purely in ERC-20 tokens and means, if designed correctly, they never need to hold any native tokens like ETH.
### How do I use Pimlico's ERC-20 Paymaster?
We wrote a [tutorial](/references/permissionless/tutorial/tutorial-2) that takes you through the whole flow of deploying a Safe account and sending your first user operation sponsored with USDC.
We also have a [how-to guide](/guides/how-to/erc20-paymaster/how-to/use-paymaster) that explains how to use the ERC-20 Paymaster in your app's flow.
:::steps
### What is the contract address of the ERC-20 Paymaster?
The currently supported tokens are listed [here](/references/paymaster/erc20-paymaster/contract-addresses).
Theoretically, we can support any token on any EVM chains that have Chainlink interface compatible oracle support. If you have a token that you would like supported, please [reach out to us](https://t.me/pimlicoHQ)!
### Where can I find the ERC-20 Paymaster contract?
You can find the contract source code [here](https://github.com/pimlicolabs).
### How can I use a token that is not currently supported by Pimlico?
Please get in touch with us. For enterprise customers we will do what we can to see if we can add support for the token you are interested in.
### Does Pimlico take a fee?
Yes, the owner takes a fee that is baked into the `exchangeRate` returned by the API. This markup serves to compensate the onwer for maintaining the infrastructure and covering risks associated with price fluctuations and slippage risk.
:::
## ERC-20 Paymaster

Pimlico's ERC-20 Paymaster is a ERC-4337 paymaster that is able to pay for the gas fees of your users in exchange for ERC-20 tokens.
### Tutorials
Learning-oriented lessons that take you through a series of steps to complete a project. Most useful when you want to get started with Pimlico. They all involve the Alto Bundler.
* [Tutorial 2](/references/permissionless/tutorial/tutorial-2) leverages the ERC-20 Paymaster to sponsor the gas fees for a user operation with USDC.
### How-to Guides
How-to guides are recipes. They guide you through the steps involved in addressing key problems and use-cases. They are most useful when you need a quick solution.
* Learn how to [use the ERC-20 Paymaster, including the relevant RPC methods as part of your app's flow](/guides/how-to/erc20-paymaster/how-to/use-paymaster).
### References
References provide specific technical descriptions. They are most useful when you need detailed information about Pimlico's APIs.
* See the [official contract addresses of the ERC-20 Paymaster on each chain](/references/paymaster/erc20-paymaster/contract-addresses).
### Conceptual Guides
Conceptual guides provide high-level explanations of concepts. They are most useful when you need to understand the big picture.
* Understand more about the [architecture of our ERC20 Paymaster](/references/paymaster/erc20-paymaster/architecture).
* Check out our [FAQs](/references/paymaster/erc20-paymaster/faqs).
import SupportedTokens from '../../../../../data/supported-tokens.md'
## Supported Tokens
List of tokens that are supported by Pimlico's ERC-20 Paymaster. You can view the list of enabled tokens on your [Pimlico dashboard](https://dashboard.pimlico.io/billing/plan).
All users have access to the following tokens
USDC on the following chains:
* `Ethereum`
* `Base`
* `Polygon`
* `Arbitrum`
* `Optimism`
* `Sepolia`
* `Base Sepolia`
* `Polygon Amoy`
* `Arbitrum Sepolia`
* `Optimism Sepolia`
USDT on the following chains:
* `Ethereum`
* `Gnosis`
* `Linea`
* `Optimism`
* `Base`
* `Polygon`
* `Arbitrum`
* `BNB`
EURe on the following chains:
* `Ethereum`
* `Gnosis`
* `Polygon`
* `Arbitrum`
* `Scroll`
* `Linea`
* `Sepolia`
* `Arbitrum Sepolia`
* `Scroll Sepolia`
* `Linea Sepolia`
* `Polygon Amoy`
* `Chiado Testnet`
sETH on the following chains:
* `Ethereum`
* `Optimism`
wsETH on the following chains:
* `Ethereum`
* `Optimism`
:::info
If you have a token that you would like to see added, please [reach out to us](https://t.me/pimlicoHQ).
:::
## Verifying Paymaster Common Errors
`Insufficient Pimlico balance for sponsorship, please top up`
This error means that you do not have enough Pimlico balance to sponsor the User Operation. Testnet User Operations are free to sponsor, but if you are trying to sponsor User Operations on a mainnet chain, [reach out to us](https://t.me/kristofgazso) to top up your balance.
`invalid 'apikey' query parameter` / `missing 'apikey' query parameter`
This error means that you have not provided a valid API key in the query parameters. You can [create an API key here](/guides/create-api-key).
`chain XYZ is not supported`
This error means that the chain you are trying to sponsor a User Operation on is not supported by Pimlico. You can find the list of supported chains [here](/guides/supported-chains). If there is a chain we do not yet support that you would like to see added, [reach out to us](https://t.me/kristofgazso).
## Verifying Paymaster Endpoints
All calls are in JSON-RPC format and have to be made to the following URL: `https://api.pimlico.io/v2/{chain}/rpc?apikey=[YOUR_API_KEY_HERE]`
Where `{chain}` is the chain variable (such as `sepolia` or `polygon`) as found in the [supported chains page](/guides/supported-chains)
### pm\_sponsorUserOperation (v2)
`pm_sponsorUserOperation` is the main endpoint for verifying paymasters. It takes in a User Operation, simulates it and estimates the gas limits, and then signs the User Operation with the verifying paymaster's key, sponsoring it when it is submitted on-chain. If successful, we deduct from your off-chain Pimlico balance.
You can optionally pass in a third parameter, `sponsorshipPolicyId`, which is a string that you can use to identify the sponsorship policy you want to use. To learn more about sponsorship policies, see [Sponsorship Policies](/guides/how-to/sponsorship-policies).
To use this endpoint, you must have an API key with Pimlico. [Create one here](/guides/create-api-key).
User Operations sponsored using pm\_sponsorUserOperation have a 10 minute time window during which they must be included. After this time window elapses, all unused gas will be refunded to your Pimlico balance.
This time limit is necessary in order to avoid DoS attacks, as leaving an infinite time window would allow potential attackers to accumulate User Operations and drain Pimlico's paymaster all in one go, requiring us to maintain enough balance to cover all possible User Operations we ever signed up to sponsor in the entire history of the paymaster.
If you require a longer time window for your User Operations, please get in touch!
### EntryPoint 0.7 & 0.8
:::warning[Warning]
The EntryPoint 0.7 & 0.8 API expects an `UnpackedUserOperation` instead of a `PackedUserOperation`.
According to the spec it is bundler's responsibility to pack the userOp before sending it to the EntryPoint.
So make sure to send an `UnpackedUserOperation` to the bundler.
:::
:::tip[Tip]
For entryPoint v0.7, we will revert if the execution of the `callData` in the user operation fails.
However, this is not the case for entryPoint v0.6.
:::
#### Request:
```json
{
"jsonrpc": "2.0",
"method": "pm_sponsorUserOperation",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845adb2c711129d4f3966735ed98a9f09fc4ce5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d000000000000000000000000aac5d4240af87249b3f71bc8e4a2cae074a3e4190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001243c3b752b01845ADb2C711129d4f3966735eD98a9F09fC4cE570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000014375d883Cb4afb913aC35c4B394468C4bC73d77C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData": "0xe9ae5c5300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit": "0x0",
"verificationGasLimit": "0x0",
"preVerificationGas": "0x0",
"maxFeePerGas": "0x7a5cf70d5",
"maxPriorityFeePerGas": "0x3b9aca00",
"paymaster": null,
"paymasterVerificationGasLimit": null,
"paymasterPostOpGasLimit": null,
"paymasterData": null,
"signature": "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c",
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
{ "sponsorshipPolicyId": "sp_example_policy_id" } // optional
],
"id": 1
}
```
#### Response:
```json
{
"jsonrpc": "2.0",
"result": {
"preVerificationGas": "0xd3e3",
"verificationGasLimit": "0x60b01",
"callGasLimit": "0x13880",
"paymasterPostOpGasLimit": "0x0"
"paymasterVerificationGasLimit": "0x0",
"paymaster": "0xDFF7FA1077Bce740a6a212b3995990682c0Ba66d"
"paymasterData": "0xbcd12340a2109876543210987654301098765432198765432a210987654321098765430a210987654321098765430",
},
"id": 1
}
```
### EntryPoint 0.6
#### Request:
```json
{
"jsonrpc": "2.0",
"method": "pm_sponsorUserOperation",
"params": [
{
"sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce":"0x0",
"initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit":"0x0",
"verificationGasLimit":"0x0",
"preVerificationGas":"0x0",
"maxPriorityFeePerGas":"0x3b9aca00",
"maxFeePerGas":"0x7a5cf70d5",
"paymasterAndData":"0x",
"signature":"0x00000000fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
{ "sponsorshipPolicyId": "sp_example_policy_id" } // optional
],
"id": 1
}
```
```typescript
import { JsonRpcProvider } from "@ethersproject/providers";
const chain = "sepolia"
const apiKey = "YOUR_API_KEY_HERE"
const entryPoint = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
const userOperation = ... // generate your User Operation here
const provider = new JsonRpcProvider(`https://api.pimlico.io/v2/${chain}/rpc?apikey=${apiKey}`)
const result = await provider.send("pm_sponsorUserOperation", [userOperation, {entryPoint: entryPoint}])
```
#### Response:
```json
{
"jsonrpc": "2.0",
"result": {
"paymasterAndData": "0xbcd12340a2109876543210987654301098765432198765432a210987654321098765430a210987654321098765430",
"preVerificationGas": "0xdf55",
"verificationGas": "0x52503",
"verificationGasLimit": "0x52503",
"callGasLimit": "0x13880"
},
"id": 1
}
```
### pm\_validateSponsorshipPolicies
This method validates a User Operation against an array of [sponsorship policies](https://dashboard.pimlico.io/sponsorship-policies),
and returns an array of sponsorship policies (alongside additional data for each policy) that are willing to sponsor the user operation.
This method is available for both EntryPoint 0.6 and 0.7 & 0.8.
Request:
```json
{
"jsonrpc": "2.0",
"method": "pm_validateSponsorshipPolicies",
"params": [
{
"sender": "0x1234567890123456789012345678901234567890",
"nonce": "0x1",
"initCode": "0x",
"callData": "0x",
"callGasLimit": "0x100000",
"verificationGasLimit": "0x20000",
"preVerificationGas": "0x10000",
"maxFeePerGas": "0x3b9aca00",
"maxPriorityFeePerGas": "0x3b9aca00",
"paymasterAndData": "0x",
"signature": "0x"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
["sp_crazy_kangaroo", "sp_talented_turtle"]
],
"id": 1
}
```
Response:
```json
{
"jsonrpc": "2.0",
"result": [
{
"sponsorshipPolicyId": "sp_crazy_kangaroo",
"data": {
"name": "Linea Christmas Week", // optional
"author": "Linea", // optional
"icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==", // optional
"description": "Linea is sponsoring the first 10 transactions for existing users between Christmas and New Year's Eve.", // optional
}
}
],
"id": 1
}
```
### pm\_supportedEntryPoints
This function returns the list of EntryPoint contracts that are supported on that chain.
Want to use an entryPoint that is not currently supported? Please contact us we can see if we can support it.
Request:
```json
{
"jsonrpc": "2.0",
"method": "pm_supportedEntryPoints",
"params": [],
"id": 1
}
```
Response:
```json
{
"jsonrpc": "2.0",
"result": [
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"0x0000000071727De22E5E9d8BAf0edAc6f37da032"
],
"id": 1
}
```
## Paymaster FAQs \[Frequently Asked Questions about Pimlico's Verifying Paymaster]
### What's the difference between `pm_getPaymasterData` and `pm_sponsorUserOperation`?
`pm_getPaymasterData` is part of the [ERC-7677 spec](https://www.erc7677.xyz/introduction) which standardizes the way to request sponsorship for a given userOperation. `pm_getPaymasterData` doesn't do any gas estimations and only returns back the relevant userOperation fields for sponsorship.
* For v0.6, the endpoint returns
* `paymasterAndData`
* For v0.7, the endpoint returns
* `paymaster`
* `paymasterData`.
`pm_sponsorUserOperation` is a Pimlico specific endpoint that also estimates gas as well as sponsoring the userOperation.
* For v0.6, the endpoint returns
* `paymasterAndData`
* `preVerificationGas`
* `verificationGasLimit`
* `callGasLimit`
* For v0.7, the endpoint returns
* `paymaster`
* `paymasterData`
* `paymasterVerificationGasLimit`
* `paymasterPostOpGasLimit`
* `preVerificationGas`
* `verificationGasLimit`
* `callGasLimit`
### What should I do after setting up a policy in the dashboard?
Decide whether to enable or disable the policy based on operational requirements and objectives. Enable it if you wish the policy to be active, or keep it disabled if not needed or under review.
### Can I make a transfer to load up my balance in a different currency?
Yes, you can make a transfer to load up your balance in a different currency, such as USDC. We also accept transfers in ETH. Please reach out to [us](https://t.me/kristofgazso) and provide the recipient address and specify the currency you wish to transfer.
### Are there any API-related payments?
No, there are not. The only payments relate to the onchain payments through the user operation gas price for the bundler fees, and the offchain balance deduction for the paymaster fees.
### How is payment made for the verifying paymaster?
:::info
The moment you request sponsorship, we pre-charge your balance with the maximum amount of balance your user operation could possibly spend (gas limits \* max gas price). After 15 minutes, we check onchain how much your user operation actually spent and refund the difference to your account.
:::
The verifying paymaster payments are charged offchain. We calculate how much gas in USD terms you spend and charge the percentage on top of that to your pre-loaded balance or attached card.
### How do gas fees for the bundler and paymaster stack?
If you use both the bundler and the paymaster together, the fee payments would stack.
For instance, if you ended up paying a 5% overhead for the bundler and a 10% overhead for the verifying paymaster on a user operation that costs us $0.01 to include, you would end up paying a total of $0.01 \* (1.05) \* (1.1) = $0.01155 => a 15.5% overhead.
Important to note however that the bundler and paymaster are completely independent and you can use one without the other.
### What is the use case for smart accounts
Smart accounts enable user experience improvements that were not previously possible with EOA accounts.
Examples include:
* Gasless transactions
* Use of USDC and other ERC-20 tokens for gas fee payments
* Trustless Passkey and FaceID signatures and login
* Account recovery
* Signer rotation
* Multisig and social recovery schemes
* Automated transactions (e.g. for limit orders, deadman switch, auto token revocations, automated DCA investing)
* Transactions with session keys with limited account access
* Batched transactions (e.g. being able to approve and swap in the same transaction)
### Is it possible to partially sponsor transactions with the verifying paymaster?
It is not natively possible to partially sponsor transactions with the verifying paymaster, however you can achieve a similar setup in a few different ways:
* You could sponsor all the gas fees, but for a more limited set of user operations using our [sponsorship policies](/guides/how-to/sponsorship-policies), for instance setting per user sponsorship limits, global spending limits, and more.
* You could also consider requiring your users to transfer a certain amount of ETH or ERC-20 tokens to an account you own for every transaction they do, making it so they in effect partially pay for the transaction fees.
* You could develop and maintain your own paymaster contract which allows the user to stake their tokens and make the sponsorship happen partially using the users' tokens.
### Does the verifying paymaster require upfront funding?
You can add balance on your [Pimlico dashboard](https://dashboard.pimlico.io) which will be used to sponsor gas fees, however you are also able to add a credit card, which will enable a $1000 overdraft limit which will be charged based on how much you sent at the end of every month.
:::info
If you would like to add to your balance using crypto, or you would like an overdraft limit higher than $1000, please [contact us](https://t.me/pimlicoHQ).
:::
### Would I get alerted if my balance is running out?
If you run out of balance or you reach your overdraft limit, the sponsor RPC methods will start erroring.
There is currently no alerting for your balance running low, but if this is a feature you would like please [contact us](https://t.me/pimlicoHQ).
If you would like to restrict how much you spend, consider taking a look at our [sponsorship policies](/guides/how-to/sponsorship-policies) that could allow you, for instance, to set a global spend limit of $100,000 no matter what.
## Verifying Paymaster
Pimlico's Verifying Paymaster is a combination of an API and an on-chain smart contract that allows you to sponsor gas fees for your users. By calling our API, you get back a signature that will be accepted by our smart contract and will pay for the gas fees of your users.
### Tutorials
Learning-oriented lessons that take you through a series of steps to complete a project. Most useful when you want to get started with Pimlico. They all involve the Alto Bundler.
* [Tutorial 1](/references/permissionless/tutorial/tutorial-1) leverages the Verifying Paymaster to sponsor the gas fees for a User Operation.
### References
References provide specific technical descriptions. They are most useful when you need detailed information about Pimlico's APIs.
* The [supported chains page](/guides/supported-chains) for the verifying paymaster explains the chains and networks that are supported and the API endpoints you can use to interact with it.
## How to use the Verifying Paymaster
:::tip[Tip]
We recommend using [permissionless.js](/references/permissionless/reference/smart-account-actions/sendUserOperation) as the SDK to interact with the Verifying Paymaster as it provides type-safe wrappers for paymaster methods.
:::
Paymasters are entities that user operations can delegate the responsibilities for gas fee payments for. You can interact with paymasters using JSON-RPC requests.
To get access to the paymaster, you need to [create a Pimlico API key](/guides/create-api-key) and sign up to the [Pimlico dashboard](https://dashboard.pimlico.io). Next, load up some balance to your Pimlico account in the [payment methods page](https://dashboard.pimlico.io/payment) or add your card. Then, using the API key, you can make the following JSON-RPC requests to the paymaster:
* [pm\_sponsorUserOperation](/references/paymaster/verifying-paymaster/endpoints/pm_sponsorUserOperation)
If you would like an end-to-end example of how to use the Verifying Paymaster, please refer to [tutorial 1](/references/permissionless/tutorial/tutorial-1) of the permissionless.js documentation.
## Update your existing app to sponsor gas fees for smart account users
If you are looking to sponsor gas fees for your users without embedding a wallet in your app, you can use this guide to update your existing app to sponsor gas fees for smart accounts.
### Steps
:::steps
#### Spin up an ERC-7677 proxy server
Follow the deployment guide on the [ERC-7677 Proxy Server](https://github.com/pimlicolabs/erc7677-proxy) repository readme to deploy an ERC-7677 proxy server.
#### Update your app to sponsor gas fees
Follow the [@permissionless/wagmi tutorial](/references/permissionless/wagmi/tutorial/tutorial-1) to update your app to support ERC-5792 requests and enable the paymaster service capability feature, using the public URL you created for the ERC-7677 proxy in the previous step.
:::
## Migration Guide
### 0.2.0
:::warning
This migration guide is assuming you have already migrated to ^0.1.0 and are now migrating to 0.2.0.
:::
#### Using viem 2.20.0
Viem released their own version of account abstraction primitives since version `2.18.0`.
Since permissionless.js has viem as a peer dependency and are heavily inspired by viem, we will be updating to use the native account abstraction primitives provided by viem.
For full documentation on viem's account abstraction primitives, please refer to the [viem documentation](https://viem.sh/account-abstraction).
#### Deprecated `createBundlerClient`
Viem has default [createBundlerClient](https://viem.sh/account-abstraction/clients/bundler) so we have deprecated the permissionless.js bundler client.
This means we have also deprecated the following bundler actions:
1. `chainId`
2. `estimateUserOperationGas`
3. `getUserOperationByHash`
4. `getUserOperationReceipt`
5. `sendUserOperation`
6. `supportedEntryPoints`
7. `waitForUserOperationReceipt`
#### Deprecated `getEntryPointVersion`
This is no more needed as all the permissionless versions now accepts entryPoint version explicitly.
#### Deprecated `getUserOperationHash`
This is part of `viem/account-abstraction` and now can be directly imported from `viem`.
#### Deprecated `signUserOperationHashWithECDSA`
#### Deprecated `experimental 7677`
#### Changed `signerToBiconomySmartAccount` to `toBiconomySmartAccount`
```ts
import { toBiconomySmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const biconomyAccount = await signerToBiconomySmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
...rest
}) // [!code --]
const biconomyAccount = await toBiconomySmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owners: [privateKeyToAccount(generatePrivateKey())],// [!code ++]
entryPoint: { // [!code ++]
address: entryPoint06Address, // [!code ++]
version: "0.6", // [!code ++]
} // [!code ++]
...rest
})
```
:::warning
Note, Biconomy's account doesn't work with entryPoint 0.7.
:::
#### Changed `signerToEcdsaKernelSmartAccount` to `toEcdsaKernelSmartAccount`
```ts
import { toEcdsaKernelSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const kernelAccount = await signerToEcdsaKernelSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
...rest
}) // [!code --]
const kernelAccount = await toEcdsaKernelSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owners: [privateKeyToAccount(generatePrivateKey())],// [!code ++]
entryPoint: { // optional, defaults to 0.7 // [!code ++]
address: entryPoint07Address, // [!code ++]
version: "0.7", // [!code ++]
}, // [!code ++]
...rest
})
```
#### Changed `signerToLightSmartAccount` to `toLightSmartAccount`
```ts
import { toLightSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const kernelAccount = await signerToLightSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
lightAccountVersion: "1.1.0" // [!code --]
...rest
}) // [!code --]
const kernelAccount = await toLightSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owners: [privateKeyToAccount(generatePrivateKey())],// [!code ++]
entryPoint: { // optional, defaults to 0.7 // [!code ++]
address: entryPoint06Address, // [!code ++]
version: "0.6", // [!code ++]
}, // [!code ++]
version: "1.1.0" // optional, defaults to "2.0.0" // [!code ++]
...rest
})
```
:::info
We have also added support for `2.0.0` Light Account.
:::
:::warning
The Light Account version `1.1.0` works only with EntryPoint version `0.6` while `2.0.0` works only with EntryPoint version `0.7`.
:::
#### Changed `signerToSafeSmartAccount` to `toSafeSmartAccount`
```ts
import { toSafeSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const safeAccount = await signerToSafeSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V07, // [!code --]
safeVersion: "1.4.1" // [!code --]
...rest
}) // [!code --]
const safeAccount = await toSafeSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owners: [privateKeyToAccount(generatePrivateKey())],// [!code ++]
entryPoint: { // optional, defaults to 0.7 // [!code ++]
address: entryPoint07Address, // [!code ++]
version: "0.7", // [!code ++]
}, // [!code ++]
version: "1.4.1" // [!code ++]
...rest
})
```
#### Changed `signerToSimpleSmartAccount` to `toSimpleSmartAccount`
```ts
import { toSimpleSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const simpleAccount = await signerToSimpleSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
...rest
}) // [!code --]
const simpleAccount = await toSimpleSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owner: privateKeyToAccount(generatePrivateKey()),// [!code ++]
entryPoint: { // optional, defaults to 0.7 // [!code ++]
address: entryPoint07Address, // [!code ++]
version: "0.7", // [!code ++]
}, // [!code ++]
...rest
})
```
#### Changed `signerToTrustSmartAccount` to `toTrustSmartAccount`
```ts
import { toTrustSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint06Address } from "viem/account-abstraction"
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts"
const trustAccount = await signerToTrustSmartAccount(client { // [!code --]
signer: privateKeyToAccount(generatePrivateKey()), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code --]
...rest
}) // [!code --]
const trustAccount = await toTrustSmartAccount({ // [!code ++]
client: publicClient, // [!code ++]
owner: privateKeyToAccount(generatePrivateKey()),// [!code ++]
entryPoint: { // [!code ++]
address: entryPoint06Address, // [!code ++]
version: "0.6", // [!code ++]
}, // [!code ++]
...rest
})
```
:::warning
Note, trust wallet's account doesn't work with entryPoint 0.7.
:::
#### Deprecated \`privateKeyTo\SmartAccount
All the private key to account conversion functions are now deprecated. You can use `toSmartAccount` to create a smart account instead.
Example:
```ts
import { toSafeSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { entryPoint07Address } from "viem/account-abstraction"
import { privateKeyToAccount } from "viem/accounts"
const safeAccount = await toSafeSmartAccount({
client: publicClient,
owners: [privateKeyToAccount(privateKey)],
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}, // global entrypoint
version: "1.4.1",
})
```
#### Merged `createPimlicoPaymasterClient` and `createPimlicoBundlerClient` to `createPimlicoClient`
```ts
import { http } from "viem";
import { entryPoint07Address } from "viem/account-abstraction"
import { createPimlicoClient } from "permissionless/clients/pimlico"
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=`
const pimlicoPaymasterClient = createPimlicoPaymasterClient({ // [!code --]
chain: foundry, // [!code --]
transport: http(paymasterRpc), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V07 // [!code --]
}) // [!code --]
const pimlicoPaymasterClient = createPimlicoPaymasterClient({ // [!code --]
chain: foundry, // [!code --]
transport: http(paymasterRpc), // [!code --]
entryPoint: ENTRYPOINT_ADDRESS_V07 // [!code --]
}) // [!code --]
const pimlicoClient = createPimlicoClient({ // [!code ++]
transport: http(pimlicoUrl), // [!code ++]
entryPoint: { // Optional, defaults to 0.7 // [!code ++]
address: entryPoint07Address, // [!code ++]
version: "0.7", // [!code ++]
} // [!code ++]
}) // [!code ++]
```
All the previous actions that were part of `createPimlicoBundlerClient` and `createPimlicoPaymasterClient` are now part of `createPimlicoClient` and can also be accessed individually.
Pimlico client also extends actions of Paymaster client by viem. It uses ERC-7677 as the default standard to communicate with Pimlico paymasters.
#### Deprecated `smartAccountClient.deployContract`
#### Changed `smartAccountClient.prepareUserOperationRequest` to `smartAccountClient.prepareUserOperation`
```ts
import { smartAccountClient } from './config'
const userOperation = await smartAccountClient.prepareUserOperationRequest({ // [!code --]
userOperation: { // [!code --]
callData: await smartAccountClient.account.encodeCallData({ // [!code --]
to: CONTRACT_ADDRESS, // [!code --]
data: "0x", // [!code --]
value: parseEther('1') // [!code --]
}), // [!code --]
} // [!code --]
}) // [!code --]
const userOperation = await smartAccountClient.prepareUserOperation({ // [!code ++]
calls: [{ // [!code ++]
to: CONTRACT_ADDRESS, // [!code ++]
data: "0x", // [!code ++]
value: parseEther('1') // [!code ++]
}] // [!code ++]
}) // [!code ++]
```
#### Changed `smartAccountClient.sendUserOperation`
```ts
import { smartAccountClient } from './config'
const userOperationHash = await smartAccountClient.sendUserOperation({
userOperation: { // [!code --]
sender: "0x6152348912fb1e78c9037D83f9d4524D4a2988Ed",
nonce: "0x8104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000",
factory: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
factoryData: "0xc5265d5d0000000000000000000000006723b44abeec4e71ebe3232bd5b455805badd22f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e412af322c018104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000014ec787ae5c34157ce61e751e54dff3612d4117663000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callData: "0xe9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000594bc666500faed35dc741f45a35c571399560d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4e688a440000000000000000000000000c67e4838d4e6682e3a5f9ec27f133e76cb3855f30000000000000000000000006152348912fb1e78c9037d83f9d4524d4a2988ed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000041aebdfb90adba067d9304980a300636506c3c9081b01f64b04f108407a890602377625ef9096946cc028743123646881c7e31a1c8d6698132c188cb4c33a3f9151b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 99985502n,
maxPriorityFeePerGas: 1221000n,
preVerificationGas: 66875n,
verificationGasLimit: 373705n,
callGasLimit: 170447n,
paymaster: "0x9d0021A869f1Ed3a661Ffe8C9B41Ec6244261d98",
paymasterData: "0x000000000000000000000000000000000000000000000000000000006638ab470000000000000000000000000000000000000000000000000000000000000000e9311d1945317dc6a1c750e8e6d0641a598beb59edc5652ed3807ca57338a7a107123e1a479386b2c91f242d2dff367c18e0ad9d1021acfe47afc890e252644e1c",
paymasterVerificationGasLimit: 20274n,
paymasterPostOpGasLimit: 1n,
signature: "0xa58d445f26b126fcd644975f0c66bd45f3e6e5b9c1acec2eeee490aa9341cfc312988231c228f84e12eac2d90ed9cd8825546d70d73b2e0fabdd6c8089ab29581b",
} // [!code --]
})
```
It also allows you to submit user operations with using `calls` object like with `sendTransaction`. You can read more about it [here](https://viem.sh/account-abstraction/actions/bundler/sendUserOperation).
#### Deprecated `smartAccountClient.sendTransactions`
Use `smartAccountClient.sendTransaction` instead. It now accepts an array of transactions. Read more about it [here](/references/permissionless/reference/smart-account-actions/sendTransaction#usage).
Example:
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{ // [!code focus]
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus]
value: parseEther('1') // [!code focus]
}, { // [!code focus]
abi: wagmiAbi, // [!code focus]
functionName: 'mint', // [!code focus]
to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus]
}] // [!code focus]
})
```
#### Error handling
We have deprecated permissionless.js error handling and now use viem's error handling.
## How to send multiple user operations in parallel
permissionless.js library lets you to send multiple transactions in parallel. This can be useful if you want to batch multiple calls in a single user operation or if you want to send user operations concurrently.
### Batching Multiple Calls
To batch multiple calls in a single user operation, you can use the `sendTransactions` method.
```ts
// [!include ~/snippets/how-to/parallel-transactions.ts:multiple-transactions]
```
### Sending Multiple User Operations in Parallel
In an Externally Owned Account (EOA), the `nonce` is a simple incrementing number. However, in smart accounts, the `nonce` consists of two components: a `key` and a `sequence`:
* 192-bit “key”
* 64-bit “sequence”
For each unique `key`, the sequence must be incremented by 1 for each transaction. This means that if you send multiple transactions in parallel, you can use different `keys` for the parallel transactions.
#### Parallel Transactions Ordering
Important thing to note is that parallel transaction's ordering is not guaranteed. So execution of random `key` on chain could be:
* \[key-C]\[sequence-0]
* \[key-A]\[sequence-0]
* \[key-B]\[sequence-0]
While execution of `sequence` for a specific `key` will always be in order. So the following is a valid order of execution:
1. \[key-C]\[sequence-0]
2. \[key-A]\[sequence-0]
3. \[key-C]\[sequence-1]
4. \[key-B]\[sequence-0]
5. \[key-A]\[sequence-1]
6. \[key-B]\[sequence-1]
:::info
Note: Pimlico bundler currently only support up to 10 parallel transactions. If you need to send more than 10 transactions in parallel feel free to contact us at [support@pimlico.io](mailto\:support@pimlico.io).
:::
In the example below, we use the current timestamp as the `key` to send parallel transactions. You can use any other value as the `key`.
```ts
// [!include ~/snippets/how-to/parallel-transactions.ts:multiple-transactions-parallel]
```
:::info
Note: The flow described in this section is only applicable to smart accounts that are already deployed, if your account is not deployed, you may run into AA10 errors as multiple userOperations will contain the factory/factoryData fields which will cause conflicts.
:::
This way, you can efficiently manage multiple transactions either sequentially or concurrently.
## permissionless.js
permissionless.js is a TypeScript library built on viem for interacting with ERC-4337 bundlers, paymasters, and User Operations.
### Installation
You can install permissionless.js into your project with the following command:
:::code-group
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [pnpm]
pnpm install permissionless
```
```bash [bun]
bun install permissionless
```
:::
## Tutorials
Learning-oriented lessons that take you through a series of steps to complete a project. Most useful when you want to get started with Pimlico.
[Tutorial 1](/references/permissionless/tutorial/tutorial-1) takes you through the journey of leveraging permissionless.js's high-level APIs to easily create and bundle a user operation.
[Tutorial 2](/references/permissionless/tutorial/tutorial-2) takes you through the journey of sponsoring your first user operation with USDC with an ERC-20 paymaster.
## Tutorial 1 — Send your first gasless transaction
In this tutorial, you will submit your first fully-gasless transaction from a smart account.
You will set up the necessary permissionless.js clients, create a user operation, ask Pimlico's verifying paymaster to sponsor it, and then submit it on-chain with Pimlico's bundler.
### Steps
::::steps
#### Get a Pimlico API key
[Create your API key](/guides/create-api-key)
#### Clone the Pimlico tutorial template repository
We have created a [Pimlico tutorial template repository](https://github.com/pimlicolabs/tutorial-template) that you can use to get started. It comes set up with Typescript, viem, and permissionless.js.
```bash
git clone https://github.com/pimlicolabs/tutorial-template.git pimlico-tutorial-1
cd pimlico-tutorial-1
```
Now, let's install the dependencies:
```bash
npm install
```
The main file we will be working with is `index.ts`. Let's run it to make sure everything is working:
```bash
npm start
```
If everything has been set up correctly, you should see `Hello world!` printed to the console.
#### Create the public and paymaster clients, and generate a private key
The public client will be responsible for querying the blockchain, while the paymaster client will be responsible for interacting with Pimlico's verifying paymaster endpoint and requesting sponsorship.
Make sure to replace `YOUR_PIMLICO_API_KEY` in the code below with your actual Pimlico API key.
Let's open up `index.ts`, and add the following to the bottom:
```ts
// [!include ~/snippets/tutorial-1.ts:clients]
```
#### Create the `SmartAccount` instance
For the purposes of this guide, we will be using [Safe](https://safe.global) accounts. This account is an ERC-4337 wallet controlled by a single EOA signer.
:::tip[Tip]
Want to learn more about using Safe accounts? Take a look at our [dedicated Safe guide](/references/permissionless/how-to/accounts/use-safe-account)
:::
To create the Safe account, we will use the `toSafeSmartAccount` utility function from permissionless.js. We need to specify the Safe version we are using as well as the global ERC-4337 EntryPoint address. For the signer, we will be using the previously generated private key.
Add the following to the bottom of `index.ts`:
```ts
// [!include ~/snippets/tutorial-1.ts:smartAccount]
```
Let's run this code with `npm start`. You should see the smart account address printed to the console.
```txt
Smart account address: https://sepolia.etherscan.io/address/0x374b42bCFAcf85FDCaAB84774EA15ff36D42cdA7
```
:::info
If you visit the address on Etherscan, you might notice that no contract is actually deployed to this address yet. This is because smart account are counterfactual, meaning that they are only deployed on-chain the first time you send a transaction through the account.
:::
#### Create the bundler and smart account clients
Now that we have a `SmartAccount` instance, we need to create a `SmartAccountClient` instance to be able to transact from it. `SmartAccountClient` is an almost drop-in replacement for a viem [`WalletClient`](https://viem.sh/docs/clients/wallet), but it also includes some additional functionality for interacting with smart accounts.
We then specify the optional `sponsorUserOperation` middleware function, calling the `sponsorUserOperation` function from the paymaster client. This will make sure that the user operation is sponsored by Pimlico's verifying paymaster.
Finally, we specify the `gasPrice` middleware function to fetch the gas price from the bundler that we will use to submit the user operation in the next step.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/tutorial-1.ts:smartAccountClient]
```
#### Submit a transaction from the smart account
Finally, let's submit a transaction from the smart account. We will send a transaction to the `0xd8da6bf26964af9d7eed9e03e53415d37aa96045` (vitalik.eth) address with `0x1234` as example `callData`.
Underneath the hood, the `SmartAccountClient` will build a user operation, request Pimlico paymaster sponsorship, sign it with the smart account's private key, and then submit it to the bundler. The bundler will then query for receipts until it sees the user operation included on-chain.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/tutorial-1.ts:submit]
```
Let's run this code again with `npm start`. You should see the transaction hash bundling the user operation on-chain printed to the console.
```txt
User operation included: https://sepolia.etherscan.io/tx/0x7a2b61b4b7b6e9e66c459e3c9c24c7a292fc6c740533ce35dbf58710960cc0e5
```
You can now view the transaction on the Sepolia testnet explorer. By sending this user operation, you have:
* Deployed the counterfactual smart account contract
* Had this newly-deployed smart account verify the private key's signature
* Made Pimlico's verifying paymaster sponsor the user operation's gas fees
* Executed a simple transaction to `vitalik.eth`'s address
All in a couple lines of code.
Congratulations, you are now a pioneer of Account Abstraction! 🎉
Please [get in touch](https://t.me/pimlicoHQ) if you have any questions or if you'd like to share what you're building!
::::
#### Combined code
If you want to see the complete code that combines all of the previous steps, we uploaded it to a [separate repository](https://github.com/pimlicolabs/tutorials). If you're looking to run it, remember to replace the API key with your own!
## Tutorial 2 — Submit a user operation with an ERC-20 Paymaster
:::info
You can visit our [ERC-20 Paymaster overview page](/references/paymaster/erc20-paymaster) to learn more about the design and architecture of our ERC-20 Paymaster, and the deployed smart contract addresses.
:::
In this tutorial, you will deploy an ERC-4337 smart contract wallet on Base Sepolia, and submit a user operation that pays for its gas fees with USDC using an ERC-20 Paymaster.
### Steps
::::steps
#### Get a Pimlico API key
[Create your API key](/guides/create-api-key)
#### Clone the Pimlico tutorial template repository
We have created a [Pimlico tutorial template repository](https://github.com/pimlicolabs/tutorial-template) that you can use to get started. It comes set up with Typescript, viem, and permissionless.js.
```bash
git clone https://github.com/pimlicolabs/tutorial-template.git pimlico-tutorial-2
cd pimlico-tutorial-2
```
Now, let's install the dependencies:
```bash
npm install
```
The main file we will be working with is `index.ts`. Let's run it to make sure everything is working:
```bash
npm start
```
If everything has been set up correctly, you should see `Hello world!` printed to the console.
#### Create the public and bundler clients, and generate a private key
The public client will be responsible for querying the blockchain, while the bundler client will be responsible for submitting user operations for relaying.
Make sure to replace `YOUR_PIMLICO_API_KEY` in the code below with your actual Pimlico API key.
Let's open up `index.ts`, and add the following to the bottom:
```ts
// [!include ~/snippets/tutorial-2.ts:clients]
```
#### Create the `SmartAccount` instance
For the purposes of this guide, we will be using [Safe](https://safe.global) accounts. This account is an ERC-4337 wallet controlled by a single EOA signer.
:::tip[Tip]
Want to learn more about using Safe accounts? Take a look at our [dedicated Safe guide](/references/permissionless/how-to/accounts/use-safe-account)
:::
To create the Safe account, we will use the `toSafeSmartAccount` utility function from permissionless.js.
Add the following to the bottom of `index.ts`:
```ts
// [!include ~/snippets/tutorial-2.ts:smartAccount]
```
Since we will be looking to fund our account with USDC (which is what we will use to pay gas fees with), we need to know the address where our smart wallet will be deployed.
Let's run this code with `npm start`. You should see something like this:
```txt
Smart account address: https://sepolia.basescan.org/address/0xbAd38BdCf884ED92ab370f69C0CD0B7b8a1459A1
```
#### Get Testnet USDC on Base Sepolia
Let's get some USDC on the Base Sepolia testnet to the counterfactual address of the wallet we will be deploying. This will be used to pay for the gas fees of the user operation we will be submitting.
The recommended way to do this is to use the [USDC faucet](https://faucet.circle.com/), select 'Base Sepolia' and enter the counterfactual sender address you generated in the previous step.
#### Verify you have USDC on the counterfactual sender address
To make sure you have USDC on the counterfactual sender address, let's add a check to the bottom of `index.ts`:
```ts
// [!include ~/snippets/tutorial-2.ts:checkBalance]
```
If you run this code with `npm start`, you should not see any errors, and you should see the USDC balance of the counterfactual sender address printed to the console.
```txt
Smart account USDC balance: 10 USDC
```
#### Send a transaction from the smart account, paying only with USDC for gas fees.
Finally, let's submit a transaction from the smart account. We will send a transaction to the `0xd8da6bf26964af9d7eed9e03e53415d37aa96045` (vitalik.eth) address with `0x1234` as example `callData`. We will also specify the gas price we want to use, which we fetched from the bundler in the previous step.
Underneath the hood, the `SmartAccountClient` will build a user operation with the designated ERC-20 Paymaster, sign it with the smart account's private key, and then submit it to the bundler. The bundler will then query for receipts until it sees the user operation included on-chain.
The `paymasterContext` will be passed to the paymaster endpoint, instructing it to sponsor the userOperation using ERC-20 tokens.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/tutorial-2.ts:submit]
```
Let's run this code again with `npm start`. You should see the transaction hash bundling the user operation on-chain printed to the console.
```txt
User operation included: https://sepolia.basescan.org/tx/0xf8e4fc41a134fc9530a0c019167f9dc0981874b90187717605355bdcce8b2fb7
```
You can now view the transaction on the Base Sepolia testnet explorer. By sending this user operation, you have:
* Deployed the counterfactual smart account contract
* Had your smart account approve the ERC-20 Paymaster to spend USDC
* Had this newly-deployed smart account verify the private key's signature
* Made an ERC-20 Paymaster sponsor the user operation's gas fees by taking USDC from the smart account
* Executed a simple transaction to `vitalik.eth`'s address
If you visit the address of the `sender` account on the [Base Sepolia explorer](https://sepolia.basescan.org), you should also see that some of your USDC balance has been deducted!
That's it! Congratulations!
::::
#### Combined code
If you want to see the complete code that combines all of the previous steps, we uploaded it to a [separate repository](https://github.com/pimlicolabs/tutorials). If you're looking to run it, remember to replace the API key with your own!
import VersionWarning from "../VersionWarning"
## permissionless.js FAQs
### Getting `WaitForUserOperationReceiptTimeoutError`?
This error is thrown when the bundler takes too long to bundle your user operation or the default timeout configured in permissionless.js is not enough.
This could be due to the block time of the chain you are using is more than the default timeout.
The easiest way to fix this is to increase the timeout in permissionless.js. You can do this by changing timeout value in `bundlerTransport` like so:
```typescript
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccountClient,
entryPoint: ENTRYPOINT_ADDRESS_V06,
chain: sepolia, // or whatever chain you are using
bundlerTransport: http("", {
timeout: 30_000 // Custom timeout
}),
middleware: {
gasPrice: async () => (await bundlerClient.getUserOperationGasPrice()).fast,
sponsorUserOperation: pimlicoPaymaster.sponsorUserOperation,
},
})
```
### Getting `out of gas` errors?
Such errors are thrown when the provided gas limits are not enough to execute the user operation. Usually the gas limits are calculated by the bundler. If you are getting such error, you can try setting up gas limits manually.
```typescript
const userOp = await smartAccountClient.prepareUserOperationRequest({
userOperation: {
callData,
callGasLimit: 100_000n,
verificationGasLimit: 100_000n,
preVerificationGas: 100_000n,
},
})
```
import VersionWarning from "../VersionWarning"
import { HomePage } from 'vocs/components'
permissionless.js
Build with ERC-4337 smart accounts, bundlers, paymasters, and user operationspermissionless.js is a TypeScript library built on viem for building with ERC-4337 smart accounts, bundlers, paymasters, and user operations. The core focuses are avoiding provider lock-in, having no dependencies, maximum viem compatibility, and a small bundle size. permissionless.js also provides high-level support for the major ERC-4337 smart accounts, including Safe, Kernel, Biconomy, SimpleAccount, TrustWallet and LightAccount.Get startedGitHub
## Overview
```ts
const pimlicoApiKey = "YOUR_API_KEY_HERE"
const bundlerUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=${pimlicoApiKey}`
const userOpHash = "0x5faea6a3af76292c2b23468bbea96ef63fb31360848be195748437f0a79106c8"
// ---cut---
// Import the required modules.
import { createBundlerClient } from "permissionless"
import { sepolia } from "viem/chains"
import { http } from "viem"
// Create the required clients.
const bundlerClient = createBundlerClient({
chain: sepolia,
transport: http(bundlerUrl), // Use any bundler url
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
})
// Consume bundler, paymaster, and smart account actions!
const opReceipt = await bundlerClient.getUserOperationReceipt({
hash: userOpHash
})
// Build with strict TypeScript types
opReceipt!.actualGasUsed
// ^?
```
## Features
* **High-level smart account support**: We support a high-level API for deploying and managing smart accounts, including some of the most popular implementations ([Safe](https://safe.global), [Kernel](https://zerodev.app), [Biconomy](https://biconomy.io), [TrustWallet](https://trustwallet.com/swift), etc.)
* **Bundler support**: We support all bundler actions following [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace).
* **Gas sponsorship**: We support paymaster actions to allow you to easily sponsor gas fees.
* **User Operation utility functions**: We provide many low-level utility functions useful for dealing with User Operations.
* **Modular and extensible**: We allow you to easily create and plug in your own smart account systems, bundlers, paymasters, and signers.
* **Built on & for viem**: permissionless.js is designed to be a thin wrapper around viem, maintaining the same style and overall feel viem provides.
* and a lot more...
## Source Code
The source code for permissionless.js is available on [GitHub](https://github.com/pimlicolabs/permissionless.js)
permissionless.js is distributed under an MIT License.
We welcome contributions from the community. If you would like to contribute, please open an issue or a pull request.
Feel free to ask any questions in our [Telegram group](https://t.me/pimlicoHQ)
import VersionWarning from "../VersionWarning"
## Why permissionless.js \[A summary of why we built permissionless.js]
### Problems
ERC-4337 has emerged as a popular standard to tackle some of the most pressing user experience issues in the Ethereum ecosystem, but the current state of tooling in the space has been lacking. Namely, no library we have seen has been able to succeed in all of the following areas: **obsessed with developer experience, built closely on top of existing tools, flexible, and vendor-agnostic**.
At [Pimlico](https://pimlico.io) we have seen a lot of developers using our infrastructure struggle with having to string many SDKs together. Instead of getting them to learn a whole new framework or building a `@pimlico/sdk`-like library that would lock the developer to our infrastructure, we decided to build a tool that would allow developers to build on top of APIs they're already familiar with any provider they wish, mixing-and-matching all of the different parts of the stack including bundlers, paymasters, smart account providers, and signers.
### Great developer experience
Developer experience is the cornerstone of permissionless.js's design. We're committed to providing strongly-typed TypeScript APIs, comprehensive documentation on every function, and an intuitive and legible style that doesn't require you to constantly jump around files to understand the code that has been written.
### Built on top of existing tools
Developers should not have to learn a completely new framework to make their apps leverage smart accounts. [viem](https://viem.sh) and [wagmi](https://wagmi.sh) have established themselves as the most popular libraries for Ethereum development, so permissionless.js is built as a thin layer on top of them reusing as much of their standards and styles as possible. This means less work for us, and less work for developers who want to build with smart accounts.
### Flexible and extensible
Account abstraction is still an evolving space, and we want to make sure that developers can easily build and extend their own solutions for different parts of the stack as they see fit. If we don't support a bundler, a paymaster, a smart account provider, or a signer that you want to use, it should be trivial to swap it out for another one that implements the same interface or to build and connect your own without having to rewrite your entire app.
### Avoiding lock-in
Crypto developers don't like lock-in. In a world where the infrastructure provider whose proprietary SDK you've spent the last year building your app on can disappear overnight, you want to make sure that you can easily swap out different parts of the stack as you see fit, including that of the original creator of the library. If we as Pimlico want to see smart accounts succeed, we're going to have to build tooling that benefits the entire ecosystem, not just the users of our own infrastructure.
import { HomePage } from 'vocs/components'
@permissionless/wagmi
Enable gas sponsorship and transaction batching for your app with just a couple lines of code@permissionless/wagmi is a TypeScript library built on top of permissionless.js and wagmi for quickly enabling support for EIP-5792 features on your app, including gas sponsorship and transaction batching, with just a couple lines of code. We built @permissionless/wagmi to allow app developers to support the features of new smart accounts such as Coinbase Smart Wallet without any of the complexity.Get startedGitHub
## Overview
```tsx [main.tsx]
import { PermissionlessProvider } from "@permissionless/wagmi" // [!code ++]
function Main() {
return (
// [!code ++]
{/** ... */}
// [!code ++]
)
}
```
```tsx [app.tsx]
import { useSendTransaction, useWaitForTransactionReceipt } from "wagmi" // [!code --]
import { // [!code ++]
useSendTransaction, // [!code ++]
useWaitForTransactionReceipt // [!code ++]
} from "@permissionless/wagmi" // [!code ++]
function App() {
const {
sendTransaction,
data: transactionReference,
isPending
} = useSendTransaction()
const { data: receipt, isPending: isReceiptPending } =
useWaitForTransactionReceipt({
hash: transactionReference, // [!code --]
id: transactionReference // [!code ++]
})
const sendTransactionCallback = useCallback(async () => {
console.log("Sending transaction...")
sendTransaction({
to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
data: "0x1234"
})
}, [sendTransaction])
}
```
And that's it!
## Features
* **ERC-7677 Paymaster service**: Makes it easier to interact with an external smart account that offers support.
* **Sending multiple transactions**: Makes it easier to send multiple transactions from an external smart account.
* **Built on & for wagmi**: `@permissionless/wagmi` is designed to be a thin wrapper around wagmi, maintaining the same style and overall feel wagmi provides.
* and a lot more coming soon...
## Source Code
The source code for `@permissionless/wagmi` is available on [GitHub](https://github.com/pimlicolabs/permissionless.js)
`@permissionless/wagmi` is distributed under an MIT License.
We welcome contributions from the community. If you would like to contribute, please open an issue or a pull request.
Feel free to ask any questions in our [Telegram group](https://t.me/pimlicoHQ)
## Pagination
The Pimlico platform API supports certain “list” API methods. For example, you can list sponsorship policies. These list API methods share a common structure and accept, at a minimum, the following three parameters: `limit`, `starting_after`, and `ending_before`.
Pimlico's list API methods use cursor-based pagination through the `starting_after` and `ending_before` parameters. Both parameters accept an existing object ID value (see below) and return objects in reverse chronological order. The `ending_before` parameter returns objects listed before the named object. The `starting_after` parameter returns objects listed after the named object. These parameters are mutually exclusive. You can use either the starting\_after or ending\_before parameter, but not both simultaneously.
### Query Parameters
#### limit
* **optional, default is 100**
This specifies a limit on the number of objects to return, ranging between 1 and 100.
#### starting\_after
* **optional object ID**
A cursor to use in pagination. `starting_after` is an object ID that defines your place in the list. For example, if you make a list request and receive 100 objects, ending with `obj_foo`, your subsequent call can include starting\_after=obj\_foo to fetch the next page of the list.
#### ending\_before
* **optional object ID**
A cursor to use in pagination. `ending_before` is an object ID that defines your place in the list. For example, if you make a list request and receive 100 objects, starting with `obj_bar`, your subsequent call can include ending\_before=obj\_bar to fetch the previous page of the list.
### Example Paginated Response
```json
{
"has_more": true,
"data": [
{
"id": "sp_tangible_knight",
"policy_name": "Pimlico Example Gas Fund",
"policy_status": "active",
"created_at": "2024-05-23T15:23:38",
"start_time": null,
"end_time": null,
"whitelisted_chain_ids": [
10,
8453,
34443,
690,
7777777
],
"global_limits": {
"maximum_usd": 10000000
}
},
{...},
{...},
{...},
{...}
]
}
```
## API Upgrade
We are announcing a change to our API behavior for the `eth_estimateUserOperationGas` method. Previously, our system provided a default ETH balance override when estimating gas for User Operations. This is an optional upgrade to make sure your users have enough ETH to cover the operation’s cost.
### What’s Changing?
#### No More Default ETH Balance Override
After the change goes into effect, we will not add an ETH balance override by default when you call `eth_estimateUserOperationGas`.
#### Possible AA21 Error
If a User Operation is not sponsored by a paymaster and the sender does not have enough ETH to cover the operation’s cost, you will encounter an AA21 error.
#### If Users Have Sufficient Balance
If you already expect users to hold enough ETH for non-sponsored transactions, no changes are required on your end. You should be able to upgrade without any issues. Click [here](https://dashboard.pimlico.io/apikeys/upgrade) to upgrade.
### How to upgrade?
If you signed up after 17/12/2024, your API is already on the upgraded version.
If you signed up before 17/12/2024, you can manually opt-in to the upgrade through the Pimlico dashboard by clicking [here](https://dashboard.pimlico.io/apikeys/upgrade).
### Estimating Without a User’s Balance
If you need to estimate a User Operation for a sender without sufficient balance, you can add state overrides to the `eth_estimateUserOperationGas` method.
##### Upgrade with permissionless
:::code-group
```ts [upgrade.ts]
// [!include ~/snippets/references/platform/api/upgrade/upgrade.ts]
```
```ts [createSmartAccountClient.ts]
// [!include ~/snippets/references/platform/api/upgrade/createSmartAccountClient.ts]
```
:::
##### Upgrade to the raw API
You can use state overrides to simulate the user operation with a different
state the same way you would with
[`eth_call`](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-eth#eth-call)
by including an optional third parameter. This is supported for both EntryPoint
v0.6 and v0.7.
```json
{
"jsonrpc": "2.0",
"method": "eth_estimateUserOperationGas",
"params": [
{
"sender": "0xa203fDb8bC335F86016F635b85389B62B189E417",
"nonce": "0x35bf2a054f92f3730b87582ef223c8d663f9eb01158154750000000000000000",
"initCode": "0x",
"callData": "0xb61d27f6000000000000000000000000530fff22987e137e7c8d2adcc4c15eb45b4fa752000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000184165398be00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000002fcf1000000000000000000000000000000000000000000000000000000e8d4ac25fd000000000000000000000000000000000000000000000000000001d1a94f86e3000000000000000000000000000000000000000000000000000000e8d4ac13d6000000000000000000000000000000000000000000000000000001d1a94edaef000000000000000000000000000000000000000000000000000000e8d4ac25fa00000000000000000000000000000000000000000000000000000000",
"callGasLimit": "0x115b5c0",
"verificationGasLimit": "0x249f0",
"preVerificationGas": "0xeb11",
"maxPriorityFeePerGas": "0x12a05f200",
"maxFeePerGas": "0x5b08082fa",
"paymasterAndData": "0x",
"signature": "0xa6cc6589c8bd561cfd68d7b6b0757ef6f208e7438782939938498eee7d703260137856c840c491b3d415956265e81bf5c2184a725be2abfc365f7536b6af525e1c"
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
{
"0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3": {
// Adding 100 ETH to the smart account during estimation to prevent AA21 errors while estimating
"balance": "0x56BC75E2D63100000"
},
"0xebe8efa441b9302a0d7eaecc277c09d20d684540": {
"stateDiff": {
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80": "0x21"
}
}
}
],
"id": 1
}
```
## pimlico\_getTokenQuotes
This method returns the tentative token exchange rates used by the ERC-20 Paymaster.
### Usage
```json
{
"jsonrpc": "2.0",
"method": "pimlico_getTokenQuotes",
"params": [
{
"tokens": [
"0x6b175474e89094c44da98b954eedeac495271d0f",
"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"0x514910771af9ca656af840dff83e8264ecf986ca"
]
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1"
],
"id": 1
}
```
### Parameters
The method accepts an array with the following parameters:
1. `Object`: An object containing:
* **tokens** - `Address[]`: An array of token addresses to get quotes for.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
### Returns
The method returns an object containing an array of token quotes. Each quote includes information about the token, exchange rates, and storage slots.
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"quotes": [
{
"paymaster": "0x0000000000000039cd5e8aE05257CE51C473ddd1",
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"postOpGas": "0xa7f8",
"exchangeRate": "0xe9d61943a68eaf17e8",
"exchangeRateNativeToUsd": "0xe9e52828",
"balanceSlot": "0x2",
"allowanceSlot": "0x3"
}
]
}
}
```
:::warning
If a token in the request input is not supported by the ERC-20 Paymaster, the request will still succeed but it will not be included in the `quotes` array in the response.
:::
:::info
**exchangeRateNativeToUsd** represents the exchange rate between the chain's native gas token and USD with 6 decimals of precision.
:::
### Return Fields
The response includes the following fields in each quote object:
* **paymaster** - `Address`: The address of the paymaster contract.
* **token** - `Address`: The address of the token.
* **postOpGas** - `HexNumber`: The amount of gas required for post-operation processing.
* **exchangeRate** - `HexNumber`: The exchange rate between the token and the native gas token.
* **exchangeRateNativeToUsd** - `HexNumber`: The exchange rate between the native gas token and USD with 6 decimals of precision.
* **balanceSlot** - `HexNumber`: The storage slot for the token balance.
* **allowanceSlot** - `HexNumber`: The storage slot for the token allowance.
## pm\_getPaymasterData
This method returns values to be used in paymaster-related fields of a signed user operation. The context parameter can be an object with either a `token` field (for ERC-20 mode) or a `sponsorshipPolicyId` field (for sponsorship mode), or null.
### EntryPoint v0.7
:::tip
The context parameter can be one of the following:
* An object with a `token` field containing the ERC-20 token address for token payments
* An object with a `sponsorshipPolicyId` field for sponsored transactions
* `null` for other cases
:::
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_getPaymasterData",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845ADB2C711129D4F3966735ED98A9F09FC4CE5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d0000000000000000...",
"callData": "0xe9ae5c53",
"callGasLimit": "0x13880",
"verificationGasLimit": "0x60B01",
"preVerificationGas": "0xD3E3",
"maxPriorityFeePerGas": "0x3B9ACA00",
"maxFeePerGas": "0x7A5CF70D5",
"paymaster": null,
"paymasterVerificationGasLimit": null,
"paymasterPostOpGasLimit": null,
"paymasterData": null,
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 4337
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **factory** - `Address (optional)`: The factory contract address.
* **factoryData** - `HexData (optional)`: The factory data for account creation.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymaster** - `Address (optional)`: Address of paymaster sponsoring the transaction, or null if none.
* **paymasterVerificationGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the verification step of the paymaster, or null if no paymaster.
* **paymasterPostOpGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the post-operation step of the paymaster, or null if no paymaster.
* **paymasterData** - `HexData (optional)`: The data to pass to the paymaster during the verification step, or null if no paymaster.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Returns
The method returns an object containing paymaster-related fields for EntryPoint v0.7.
```json
{
"jsonrpc": "2.0",
"id": 4337,
"result": {
"paymaster": "0x0000000000000039cd5e8aE05257CE51C473ddd1",
"paymasterData": "0x01000066d1a1a4000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000c350000000000000000000000000000000000000000000000000000000009666598f0b846603deb0a8e59b78ba3dce9c3466394ccf07795d38ecf7925dfe12c07a022c27bb199099fa54de2f5e3e87dd9c581df52e9d3d199166a31124cc1227a9921b"
}
}
```
#### Return Fields
The response includes the following fields:
* **paymaster** - `Address`: The address of the paymaster contract.
* **paymasterData** - `HexData`: The data to be used in the paymasterData field of the user operation.
### EntryPoint v0.6
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_getPaymasterData",
"params": [
{
"sender": "0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce": "0x0",
"initCode": "0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd000000...",
"callData": "0x51945447",
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
},
"callGasLimit": "0x115b5c0",
"verificationGasLimit": "0x249f0",
"preVerificationGas": "0xeb11",
"maxPriorityFeePerGas": "0x12a05f200",
"maxFeePerGas": "0x5b08082fa"
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 4337
}
```
#### Returns
The method returns an object containing paymaster-related fields for EntryPoint v0.6.
```json
{
"jsonrpc": "2.0",
"id": 4337,
"result": {
"paymasterAndData": "0x0000000000000039cd5e8aE05257CE51C473ddd101000066d1a1a4000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000c350000000000000000000000000000000000000000000000000000000009666598f0b846603deb0a8e59b78ba3dce9c3466394ccf07795d38ecf7925dfe12c07a022c27bb199099fa54de2f5e3e87dd9c581df52e9d3d199166a31124cc1227a9921b",
"preVerificationGas": "0x350f7",
"verificationGasLimit": "0x501ab",
"callGasLimit": "0x212df"
}
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **initCode** - `HexData`: The initialization code for the smart account if it doesn't exist yet.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Return Fields
The response includes the following fields:
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
* **preVerificationGas** - `HexNumber`: The updated preVerificationGas value to use in the user operation.
* **verificationGasLimit** - `HexNumber`: The updated verificationGasLimit value to use in the user operation.
* **callGasLimit** - `HexNumber`: The updated callGasLimit value to use in the user operation.
## pm\_getPaymasterStubData
This method returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. The context parameter can be an object with either a `token` field (for ERC-20 mode) or a `sponsorshipPolicyId` field (for sponsorship mode), or null.
### EntryPoint v0.7
:::tip
The context parameter can be one of the following:
* An object with a `token` field containing the ERC-20 token address for token payments
* An object with a `sponsorshipPolicyId` field for sponsored transactions
* `null` for other cases
:::
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_getPaymasterStubData",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845ADB2C711129D4F3966735ED98A9F09FC4CE5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d000000000000000000000000aac5d4240af87249b3f71bc8e4a2cae074a3e4190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001243c3b752b01845ADb2C711129d4f3966735eD98a9F09fC4cE570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000014375d883Cb4afb913aC35c4B394468C4bC73d77C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData": "0xe9ae5c53",
"callGasLimit": "0x13880",
"verificationGasLimit": "0x60B01",
"preVerificationGas": "0xD3E3",
"maxPriorityFeePerGas": "0x3B9ACA00",
"maxFeePerGas": "0x7A5CF70D5",
"paymaster": null,
"paymasterVerificationGasLimit": null,
"paymasterPostOpGasLimit": null,
"paymasterData": null,
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 4337
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **factory** - `Address (optional)`: The factory contract address.
* **factoryData** - `HexData (optional)`: The factory data for account creation.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymaster** - `Address (optional)`: Address of paymaster sponsoring the transaction, or null if none.
* **paymasterVerificationGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the verification step of the paymaster, or null if no paymaster.
* **paymasterPostOpGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the post-operation step of the paymaster, or null if no paymaster.
* **paymasterData** - `HexData (optional)`: The data to pass to the paymaster during the verification step, or null if no paymaster.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Returns
```json
{
"jsonrpc": "2.0",
"id": 4337,
"result": {
"paymaster": "0x0000000000000039cd5e8aE05257CE51C473ddd1",
"paymasterData": "0x01000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000c350000000000000000000000000000000000000000000000088ed21153e8f500000cd91f19f0f19ce862d7bec7b7d9b95457145afc6f639c28fd0360f488937bfa41e6eedcd3a46054fd95fcd0e3ef6b0bc0a615c4d975eef55c8a3517257904d5b1c",
"paymasterVerificationGasLimit": "0xc350",
"paymasterPostOpGasLimit": "0x4e20"
}
}
```
#### Return Fields
The response includes the following fields:
* **paymaster** - `Address`: The address of the paymaster contract.
* **paymasterData** - `HexData`: The data to be used in the paymasterData field of the user operation.
* **paymasterVerificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step of the paymaster.
* **paymasterPostOpGasLimit** - `HexNumber`: The amount of gas to allocate for the post-operation step of the paymaster.
### EntryPoint v0.6
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_getPaymasterStubData",
"params": [
{
"sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce":"0x0",
"initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit":"0x115b5c0",
"verificationGasLimit":"0x249f0",
"preVerificationGas":"0xeb11",
"maxPriorityFeePerGas":"0x12a05f200",
"maxFeePerGas":"0x5b08082fa",
"paymasterAndData":"0x",
"signature":"0xa6cc6589c8bd561cfd68d7b6b0757ef6f208e7438782939938498eee7d703260137856c840c491b3d415956265e81bf5c2184a725be2abfc365f7536b6af525e1c",
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 4337
}
```
#### Returns
The method returns an object containing paymaster-related fields for EntryPoint v0.6.
```json
{
"jsonrpc": "2.0",
"id": 4337,
"result": {
"paymasterAndData": "0x0000000000000039cd5e8aE05257CE51C473ddd101000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000c350000000000000000000000000000000000000000000000088ed21153e8f500000cd91f19f0f19ce862d7bec7b7d9b95457145afc6f639c28fd0360f488937bfa41e6eedcd3a46054fd95fcd0e3ef6b0bc0a615c4d975eef55c8a3517257904d5b1c"
}
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **initCode** - `HexData`: The initialization code for the smart account if it doesn't exist yet.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
* **signature** - `HexData`: The signature of the user operation.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Return Fields
The response includes the following fields:
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
## pm\_sponsorUserOperation
This method sponsors a user operation by providing all necessary paymaster-related fields and updated gas parameters. The context parameter can be an object with either a `token` field (for ERC-20 mode) or a `sponsorshipPolicyId` field (for sponsorship mode), or null.
### EntryPoint v0.7
:::tip
The context parameter can be one of the following:
* An object with a `token` field containing the ERC-20 token address for token payments
* An object with a `sponsorshipPolicyId` field for sponsored transactions
* `null` for other cases
:::
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_sponsorUserOperation",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845ADB2C711129D4F3966735ED98A9F09FC4CE5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d000000000000000000000000aac5d4240af87249b3f71bc8e4a2cae074a3e4190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001243c3b752b01845ADb2C711129d4f3966735eD98a9F09fC4cE570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000014375d883Cb4afb913aC35c4B394468C4bC73d77C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData": "0xe9ae5c53",
"callGasLimit": "0x13880",
"verificationGasLimit": "0x60B01",
"preVerificationGas": "0xD3E3",
"maxPriorityFeePerGas": "0x3B9ACA00",
"maxFeePerGas": "0x7A5CF70D5",
"paymaster": null,
"paymasterVerificationGasLimit": null,
"paymasterPostOpGasLimit": null,
"paymasterData": null,
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 1
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **factory** - `Address`: The factory contract address used to create the account if it doesn't exist yet.
* **factoryData** - `HexData`: The initialization data for the factory contract.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymaster** - `Address | null`: The paymaster contract address, or null if not using a paymaster.
* **paymasterVerificationGasLimit** - `HexNumber | null`: The amount of gas to allocate for the verification step of the paymaster.
* **paymasterPostOpGasLimit** - `HexNumber | null`: The amount of gas to allocate for the post-operation step of the paymaster.
* **paymasterData** - `HexData | null`: The data to pass to the paymaster during verification.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Returns
The method returns an object containing paymaster-related fields and updated gas parameters for EntryPoint v0.7.
```json
{
"jsonrpc": "2.0",
"result": {
"paymaster": "0x0000000000000039cd5e8aE05257CE51C473ddd1",
"paymasterData": "0x01000066d1a1a4000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000c350000000000000000000000000000000000000000000000000000000009666598f0b846603deb0a8e59b78ba3dce9c3466394ccf07795d38ecf7925dfe12c07a022c27bb199099fa54de2f5e3e87dd9c581df52e9d3d199166a31124cc1227a9921b",
"preVerificationGas": "0x350f7",
"verificationGasLimit": "0x501ab",
"callGasLimit": "0x212df",
"paymasterVerificationGasLimit": "0x6dae",
"paymasterPostOpGasLimit": "0x706e"
},
"id": 1
}
```
#### Return Fields
The response includes the following fields:
* **paymaster** - `Address`: The address of the paymaster contract.
* **paymasterData** - `HexData`: The data to be used in the paymasterData field of the user operation.
* **preVerificationGas** - `HexNumber`: The updated preVerificationGas value to use in the user operation.
* **verificationGasLimit** - `HexNumber`: The updated verificationGasLimit value to use in the user operation.
* **callGasLimit** - `HexNumber`: The updated callGasLimit value to use in the user operation.
* **paymasterVerificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step of the paymaster.
* **paymasterPostOpGasLimit** - `HexNumber`: The amount of gas to allocate for the post-operation step of the paymaster.
### EntryPoint v0.6
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_sponsorUserOperation",
"params": [
{
"sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce":"0x0",
"initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit":"0x0",
"verificationGasLimit":"0x0",
"preVerificationGas":"0x0",
"maxPriorityFeePerGas":"0x3b9aca00",
"maxFeePerGas":"0x7a5cf70d5",
"paymasterAndData":"0x",
"signature":"0x00000000fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c",
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 1
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **initCode** - `HexData`: The initialization code for the smart account if it doesn't exist yet.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
* **signature** - `HexData`: The signature of the user operation.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Returns
The method returns an object containing paymaster-related fields and updated gas parameters for EntryPoint v0.6.
```json
{
"jsonrpc": "2.0",
"result": {
"paymasterAndData": "0x0000000000000039cd5e8aE05257CE51C473ddd101000066d1a1a4000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000c350000000000000000000000000000000000000000000000000000000009666598f0b846603deb0a8e59b78ba3dce9c3466394ccf07795d38ecf7925dfe12c07a022c27bb199099fa54de2f5e3e87dd9c581df52e9d3d199166a31124cc1227a9921b",
"preVerificationGas": "0xdf55",
"verificationGas": "0x52503",
"verificationGasLimit": "0x52503",
"callGasLimit": "0x13880"
},
"id": 1
}
```
#### Return Fields
The response includes the following fields:
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
* **preVerificationGas** - `HexNumber`: The updated preVerificationGas value to use in the user operation.
* **verificationGas** - `HexNumber`: The amount of gas to allocate for the verification step.
* **verificationGasLimit** - `HexNumber`: The updated verificationGasLimit value to use in the user operation.
* **callGasLimit** - `HexNumber`: The updated callGasLimit value to use in the user operation.
## How to calculate UserOperation's costs using the ERC-20 Paymaster
This guide will show you how to estimate your user operation's cost in denomination of the ERC-20 token as well as USD.
When using Pimlico's ERC-20 Paymaster, the `pimlico_getTokenQuotes` endpoint helps you calculate the cost of your userOperation. It returns values `exchangeRate` and `exchangeRateNativeToUsd`. These are useful for determining how much to approve upfront or for displaying the cost to your users.
### Steps
:::steps
#### Define imports and create the clients
```ts
// [!include ~/snippets/erc20-paymaster/calculate-costs.ts:clients]
```
#### Prepare the user operation
Prepare the user operation for the calls you want to make.
```ts
// [!include ~/snippets/erc20-paymaster/calculate-costs.ts:prepareUserOperation]
```
#### Calculate your userOperation's cost
To find the cost of your userOperation in denomination of the ERC-20 & USD, you should use pimlico client's `estimateErc20PaymasterCost` action.
```ts
// [!include ~/snippets/erc20-paymaster/calculate-costs.ts:calculateCostInToken]
```
> Note: We divide `costInUsd` is always returned with 6 decimal places and you must format it before displaying it to the user wih `formatUnits(costInUsd, 6)`.
:::
## How to use the ERC-20 Paymaster with unlimited token approvals
When using Pimlico's ERC-20 Paymaster, the paymaster needs approval to spend funds on the payer's behalf. The amount to approve must be atleast equal to the userOperation's maxCost.
You can use the helper function below to get the required approval amount.
### Steps
:::steps
#### Define imports and create the clients
```ts
// [!include ~/snippets/erc20-paymaster/how-to.ts:clients]
```
#### Fetch your users' token holdings
For this example, we will assume that our user only holds USDC on Base Sepolia.
```ts
// [!include ~/snippets/erc20-paymaster/how-to.ts:getUserTokens]
```
#### Call `pimlico_getTokenQuotes` and calculate the approval amount needed for your userOperation
After calling `pimlico_getTokenQuotes`, use the returned values to calculate the approval amount for a specific userOperation by calling using formula from the paymaster's `getCostInToken` function.
```ts
// [!include ~/snippets/erc20-paymaster/how-to.ts:getTokenQuotes]
```
#### Reconstruct and send the userOperation
After finding the amount that we need to approve, we reconstruct the userOperation to include the token approval before sending it.
Ensure that the paymaster context includes a `token` field to inform the Pimlico endpoint to sponsor the userOperation using ERC-20 mode.
```ts
// [!include ~/snippets/erc20-paymaster/how-to.ts:sendUserOperation]
```
:::
## How to use the ERC-20 Paymaster
This guide will show you how to use the `prepareUserOperationForErc20Paymaster` function in permissionless.
This function lets you approve only the required amount of tokens for your userOperation. The approval call will be injected into the userOperation's calldata before it is sent.
### Steps
:::steps
#### Define imports and create the clients
```ts
// [!include ~/snippets/erc20-paymaster/use-paymaster-without-unlimited-approval.ts:clients]
```
#### Add the `prepareUserOperationForErc20Paymaster` override to `createSmartAccountClient`
This override will replace viem's `prepareUserOperation` action.
```ts
// [!include ~/snippets/erc20-paymaster/use-paymaster-without-unlimited-approval.ts:prepareUserOperation]
```
By default, `prepareUserOperationForErc20Paymaster` does **not** override the user's ERC-20 token balance when estimating.\
If you want to estimate the operation without relying on the user's actual token balance (for example, when the user hasn't received tokens yet), you can pass the `balanceOverride` option as `true` like:
```ts
prepareUserOperation: prepareUserOperationForErc20Paymaster(pimlicoClient, { balanceOverride: true }),
```
#### Call `sendTransaction`
Calling sendTransaction will now automatically approve the required amount of tokens for your userOperation.
If the sender has a sufficient existing approval, then the additional approval call will not be added to the calldata.
```ts
// [!include ~/snippets/erc20-paymaster/use-paymaster-without-unlimited-approval.ts:sendOp]
```
:::
## pimlico\_getSupportedTokens
This method returns the list of tokens supported by the ERC-20 Paymaster for the current user on a specific chain.
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pimlico_getSupportedTokens",
"params": [],
"id": 1
}
```
#### Parameters
This method takes no parameters.
#### Returns
The method returns an array of supported token objects. Each object contains information about a token that can be used for gas payments with the ERC-20 Paymaster.
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"name": "Dai Stablecoin",
"decimals": 18,
"symbol": "DAI",
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F"
},
{
"name": "USD Coin",
"decimals": 6,
"symbol": "USDC",
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}
]
}
```
#### Return Fields
Each token object in the response array includes the following fields:
* **name** - `string`: The full name of the token (e.g., "Dai Stablecoin").
* **decimals** - `number`: The number of decimal places the token uses.
* **symbol** - `string`: The token's symbol (e.g., "DAI", "USDC").
* **token** - `Address`: The contract address of the token.
## pimlico\_getTokenQuotes
This method returns the tentative token exchange rates used by the ERC-20 Paymaster.
### Usage
```json
{
"jsonrpc": "2.0",
"method": "pimlico_getTokenQuotes",
"params": [
{
"tokens": [
"0x6b175474e89094c44da98b954eedeac495271d0f",
"0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
"0x514910771af9ca656af840dff83e8264ecf986ca"
]
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1"
],
"id": 1
}
```
### Parameters
The method accepts an array with the following parameters:
1. `Object`: An object containing:
* **tokens** - `Address[]`: An array of token addresses to get quotes for.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
### Returns
The method returns an object containing an array of token quotes. Each quote includes information about the token, exchange rates, and storage slots.
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"quotes": [
{
"paymaster": "0x0000000000000039cd5e8aE05257CE51C473ddd1",
"token": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"postOpGas": "0xa7f8",
"exchangeRate": "0xe9d61943a68eaf17e8",
"exchangeRateNativeToUsd": "0xe9e52828",
"balanceSlot": "0x2",
"allowanceSlot": "0x3"
}
]
}
}
```
:::warning
If a token in the request input is not supported by the ERC-20 Paymaster, the request will still succeed but it will not be included in the `quotes` array in the response.
:::
:::info
**exchangeRateNativeToUsd** represents the exchange rate between the chain's native gas token and USD with 6 decimals of precision.
:::
### Return Fields
The response includes the following fields in each quote object:
* **paymaster** - `Address`: The address of the paymaster contract.
* **token** - `Address`: The address of the token.
* **postOpGas** - `HexNumber`: The amount of gas required for post-operation processing.
* **exchangeRate** - `HexNumber`: The exchange rate between the token and the native gas token.
* **exchangeRateNativeToUsd** - `HexNumber`: The exchange rate between the native gas token and USD with 6 decimals of precision.
* **balanceSlot** - `HexNumber`: The storage slot for the token balance.
* **allowanceSlot** - `HexNumber`: The storage slot for the token allowance.
## pm\_getPaymasterData
This method returns values to be used in paymaster-related fields of a signed user operation. The context parameter can be an object with either a `token` field (for ERC-20 mode) or a `sponsorshipPolicyId` field (for sponsorship mode), or null.
### EntryPoint 0.7 & 0.8
:::tip
The context parameter can be one of the following:
* An object with a `token` field containing the ERC-20 token address for token payments
* An object with a `sponsorshipPolicyId` field for sponsored transactions
* `null` for other cases
:::
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_getPaymasterData",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845ADB2C711129D4F3966735ED98A9F09FC4CE5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d0000000000000000...",
"callData": "0xe9ae5c53",
"callGasLimit": "0x13880",
"verificationGasLimit": "0x60B01",
"preVerificationGas": "0xD3E3",
"maxPriorityFeePerGas": "0x3B9ACA00",
"maxFeePerGas": "0x7A5CF70D5",
"paymaster": null,
"paymasterVerificationGasLimit": null,
"paymasterPostOpGasLimit": null,
"paymasterData": null,
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 4337
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **factory** - `Address (optional)`: The factory contract address.
* **factoryData** - `HexData (optional)`: The factory data for account creation.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymaster** - `Address (optional)`: Address of paymaster sponsoring the transaction, or null if none.
* **paymasterVerificationGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the verification step of the paymaster, or null if no paymaster.
* **paymasterPostOpGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the post-operation step of the paymaster, or null if no paymaster.
* **paymasterData** - `HexData (optional)`: The data to pass to the paymaster during the verification step, or null if no paymaster.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Returns
The method returns an object containing paymaster-related fields for EntryPoint 0.7 & 0.8.
```json
{
"jsonrpc": "2.0",
"id": 4337,
"result": {
"paymaster": "0x0000000000000039cd5e8aE05257CE51C473ddd1",
"paymasterData": "0x01000066d1a1a4000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000c350000000000000000000000000000000000000000000000000000000009666598f0b846603deb0a8e59b78ba3dce9c3466394ccf07795d38ecf7925dfe12c07a022c27bb199099fa54de2f5e3e87dd9c581df52e9d3d199166a31124cc1227a9921b"
}
}
```
#### Return Fields
The response includes the following fields:
* **paymaster** - `Address`: The address of the paymaster contract.
* **paymasterData** - `HexData`: The data to be used in the paymasterData field of the user operation.
### EntryPoint 0.6
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_getPaymasterData",
"params": [
{
"sender": "0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce": "0x0",
"initCode": "0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd000000...",
"callData": "0x51945447",
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
},
"callGasLimit": "0x115b5c0",
"verificationGasLimit": "0x249f0",
"preVerificationGas": "0xeb11",
"maxPriorityFeePerGas": "0x12a05f200",
"maxFeePerGas": "0x5b08082fa"
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 4337
}
```
#### Returns
The method returns an object containing paymaster-related fields for EntryPoint 0.6.
```json
{
"jsonrpc": "2.0",
"id": 4337,
"result": {
"paymasterAndData": "0x0000000000000039cd5e8aE05257CE51C473ddd101000066d1a1a4000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000c350000000000000000000000000000000000000000000000000000000009666598f0b846603deb0a8e59b78ba3dce9c3466394ccf07795d38ecf7925dfe12c07a022c27bb199099fa54de2f5e3e87dd9c581df52e9d3d199166a31124cc1227a9921b",
"preVerificationGas": "0x350f7",
"verificationGasLimit": "0x501ab",
"callGasLimit": "0x212df"
}
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **initCode** - `HexData`: The initialization code for the smart account if it doesn't exist yet.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Return Fields
The response includes the following fields:
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
* **preVerificationGas** - `HexNumber`: The updated preVerificationGas value to use in the user operation.
* **verificationGasLimit** - `HexNumber`: The updated verificationGasLimit value to use in the user operation.
* **callGasLimit** - `HexNumber`: The updated callGasLimit value to use in the user operation.
## pm\_getPaymasterStubData
This method returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. The context parameter can be an object with either a `token` field (for ERC-20 mode) or a `sponsorshipPolicyId` field (for sponsorship mode), or null.
### EntryPoint 0.7 & 0.8
:::tip
The context parameter can be one of the following:
* An object with a `token` field containing the ERC-20 token address for token payments
* An object with a `sponsorshipPolicyId` field for sponsored transactions
* `null` for other cases
:::
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_getPaymasterStubData",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845ADB2C711129D4F3966735ED98A9F09FC4CE5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d000000000000000000000000aac5d4240af87249b3f71bc8e4a2cae074a3e4190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001243c3b752b01845ADb2C711129d4f3966735eD98a9F09fC4cE570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000014375d883Cb4afb913aC35c4B394468C4bC73d77C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData": "0xe9ae5c53",
"callGasLimit": "0x13880",
"verificationGasLimit": "0x60B01",
"preVerificationGas": "0xD3E3",
"maxPriorityFeePerGas": "0x3B9ACA00",
"maxFeePerGas": "0x7A5CF70D5",
"paymaster": null,
"paymasterVerificationGasLimit": null,
"paymasterPostOpGasLimit": null,
"paymasterData": null,
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 4337
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **factory** - `Address (optional)`: The factory contract address.
* **factoryData** - `HexData (optional)`: The factory data for account creation.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymaster** - `Address (optional)`: Address of paymaster sponsoring the transaction, or null if none.
* **paymasterVerificationGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the verification step of the paymaster, or null if no paymaster.
* **paymasterPostOpGasLimit** - `HexNumber (optional)`: The amount of gas to allocate for the post-operation step of the paymaster, or null if no paymaster.
* **paymasterData** - `HexData (optional)`: The data to pass to the paymaster during the verification step, or null if no paymaster.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Returns
```json
{
"jsonrpc": "2.0",
"id": 4337,
"result": {
"paymaster": "0x0000000000000039cd5e8aE05257CE51C473ddd1",
"paymasterData": "0x01000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000c350000000000000000000000000000000000000000000000088ed21153e8f500000cd91f19f0f19ce862d7bec7b7d9b95457145afc6f639c28fd0360f488937bfa41e6eedcd3a46054fd95fcd0e3ef6b0bc0a615c4d975eef55c8a3517257904d5b1c",
"paymasterVerificationGasLimit": "0xc350",
"paymasterPostOpGasLimit": "0x4e20"
}
}
```
#### Return Fields
The response includes the following fields:
* **paymaster** - `Address`: The address of the paymaster contract.
* **paymasterData** - `HexData`: The data to be used in the paymasterData field of the user operation.
* **paymasterVerificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step of the paymaster.
* **paymasterPostOpGasLimit** - `HexNumber`: The amount of gas to allocate for the post-operation step of the paymaster.
### EntryPoint 0.6
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_getPaymasterStubData",
"params": [
{
"sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce":"0x0",
"initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit":"0x115b5c0",
"verificationGasLimit":"0x249f0",
"preVerificationGas":"0xeb11",
"maxPriorityFeePerGas":"0x12a05f200",
"maxFeePerGas":"0x5b08082fa",
"paymasterAndData":"0x",
"signature":"0xa6cc6589c8bd561cfd68d7b6b0757ef6f208e7438782939938498eee7d703260137856c840c491b3d415956265e81bf5c2184a725be2abfc365f7536b6af525e1c",
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"0x1",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 4337
}
```
#### Returns
The method returns an object containing paymaster-related fields for EntryPoint 0.6.
```json
{
"jsonrpc": "2.0",
"id": 4337,
"result": {
"paymasterAndData": "0x0000000000000039cd5e8aE05257CE51C473ddd101000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000c350000000000000000000000000000000000000000000000088ed21153e8f500000cd91f19f0f19ce862d7bec7b7d9b95457145afc6f639c28fd0360f488937bfa41e6eedcd3a46054fd95fcd0e3ef6b0bc0a615c4d975eef55c8a3517257904d5b1c"
}
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **initCode** - `HexData`: The initialization code for the smart account if it doesn't exist yet.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
* **signature** - `HexData`: The signature of the user operation.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Return Fields
The response includes the following fields:
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
## pm\_sponsorUserOperation
This method sponsors a user operation by providing all necessary paymaster-related fields and updated gas parameters. The context parameter can be an object with either a `token` field (for ERC-20 mode) or a `sponsorshipPolicyId` field (for sponsorship mode), or null.
### EntryPoint 0.7 & 0.8
:::tip
The context parameter can be one of the following:
* An object with a `token` field containing the ERC-20 token address for token payments
* An object with a `sponsorshipPolicyId` field for sponsored transactions
* `null` for other cases
:::
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_sponsorUserOperation",
"params": [
{
"sender": "0x5a6b47F4131bf1feAFA56A05573314BcF44C9149",
"nonce": "0x845ADB2C711129D4F3966735ED98A9F09FC4CE5700000000000000000000",
"factory": "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
"factoryData": "0xc5265d5d000000000000000000000000aac5d4240af87249b3f71bc8e4a2cae074a3e4190000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001243c3b752b01845ADb2C711129d4f3966735eD98a9F09fC4cE570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000014375d883Cb4afb913aC35c4B394468C4bC73d77C40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData": "0xe9ae5c53",
"callGasLimit": "0x13880",
"verificationGasLimit": "0x60B01",
"preVerificationGas": "0xD3E3",
"maxPriorityFeePerGas": "0x3B9ACA00",
"maxFeePerGas": "0x7A5CF70D5",
"paymaster": null,
"paymasterVerificationGasLimit": null,
"paymasterPostOpGasLimit": null,
"paymasterData": null,
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 1
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **factory** - `Address`: The factory contract address used to create the account if it doesn't exist yet.
* **factoryData** - `HexData`: The initialization data for the factory contract.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymaster** - `Address | null`: The paymaster contract address, or null if not using a paymaster.
* **paymasterVerificationGasLimit** - `HexNumber | null`: The amount of gas to allocate for the verification step of the paymaster.
* **paymasterPostOpGasLimit** - `HexNumber | null`: The amount of gas to allocate for the post-operation step of the paymaster.
* **paymasterData** - `HexData | null`: The data to pass to the paymaster during verification.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Returns
The method returns an object containing paymaster-related fields and updated gas parameters for EntryPoint 0.7 & 0.8.
```json
{
"jsonrpc": "2.0",
"result": {
"paymaster": "0x0000000000000039cd5e8aE05257CE51C473ddd1",
"paymasterData": "0x01000066d1a1a4000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000c350000000000000000000000000000000000000000000000000000000009666598f0b846603deb0a8e59b78ba3dce9c3466394ccf07795d38ecf7925dfe12c07a022c27bb199099fa54de2f5e3e87dd9c581df52e9d3d199166a31124cc1227a9921b",
"preVerificationGas": "0x350f7",
"verificationGasLimit": "0x501ab",
"callGasLimit": "0x212df",
"paymasterVerificationGasLimit": "0x6dae",
"paymasterPostOpGasLimit": "0x706e"
},
"id": 1
}
```
#### Return Fields
The response includes the following fields:
* **paymaster** - `Address`: The address of the paymaster contract.
* **paymasterData** - `HexData`: The data to be used in the paymasterData field of the user operation.
* **preVerificationGas** - `HexNumber`: The updated preVerificationGas value to use in the user operation.
* **verificationGasLimit** - `HexNumber`: The updated verificationGasLimit value to use in the user operation.
* **callGasLimit** - `HexNumber`: The updated callGasLimit value to use in the user operation.
* **paymasterVerificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step of the paymaster.
* **paymasterPostOpGasLimit** - `HexNumber`: The amount of gas to allocate for the post-operation step of the paymaster.
### EntryPoint 0.6
#### Usage
```json
{
"jsonrpc": "2.0",
"method": "pm_sponsorUserOperation",
"params": [
{
"sender":"0xb341FEAFaF71b09089d03B7D114599f8F491EE45",
"nonce":"0x0",
"initCode":"0x5de4839a76cf55d0c90e2061ef4386d962E15ae3296601cd0000000000000000000000000da6a956b9488ed4dd761e59f52fdc6c8068e6b5000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d1f57894000000000000000000000000d9ab5096a832b9ce79914329daee236f8eea039000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000014375cd3E53E18f65672E9d0Eb6AD174511b0BF98100000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callData":"0x5194544700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit":"0x0",
"verificationGasLimit":"0x0",
"preVerificationGas":"0x0",
"maxPriorityFeePerGas":"0x3b9aca00",
"maxFeePerGas":"0x7a5cf70d5",
"paymasterAndData":"0x",
"signature":"0x00000000fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c",
"eip7702Auth": {
"address": "0x1234567890123456789012345678901234567890",
"chainId": "0x1",
"nonce": "0x1",
"r": "0x1234567890123456789012345678901234567890123456789012345678901234",
"s": "0x1234567890123456789012345678901234567890123456789012345678901234",
"v": "0x1b",
"yParity": "0x1"
}
},
"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
{
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// Or alternatively: "sponsorshipPolicyId": "policy-123456"
}
],
"id": 1
}
```
#### Parameters
The method accepts an array with the following parameters:
1. **userOperation** - `Object`: The user operation object containing the following fields:
* **sender** - `Address`: The account making the operation.
* **nonce** - `HexString`: Unique identifier for the request from this sender.
* **initCode** - `HexData`: The initialization code for the smart account if it doesn't exist yet.
* **callData** - `HexData`: The data to pass to the sender during the main execution call.
* **callGasLimit** - `HexNumber`: The amount of gas to allocate the main execution call.
* **verificationGasLimit** - `HexNumber`: The amount of gas to allocate for the verification step.
* **preVerificationGas** - `HexNumber`: The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata.
* **maxFeePerGas** - `HexNumber`: Maximum fee per gas (similar to EIP-1559 max\_fee\_per\_gas).
* **maxPriorityFeePerGas** - `HexNumber`: Maximum priority fee per gas (similar to EIP-1559 max\_priority\_fee\_per\_gas).
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
* **signature** - `HexData`: The signature of the user operation.
* **eip7702Auth** - `Object (optional)`: EIP-7702 authorization data, can be null. This can be a dummy authorization. Contains the following fields:
* **address** - `Address`: The contract address that signed the authorization.
* **chainId** - `HexNumber`: The chain ID as a hex string.
* **nonce** - `HexNumber`: The nonce as a hex string.
* **r** - `HexData32`: The r component of the signature.
* **s** - `HexData32`: The s component of the signature.
* **v** - `HexNumber`: The v component of the signature.
* **yParity** - `HexNumber`: The y-parity of the signature.
2. **entryPoint** - `Address`: The entry point contract address.
3. **chainId** - `HexNumber`: The chain ID in hexadecimal format.
4. **context** - `Object | null`: Additional context information. Can be one of:
* An object with **token** - `Address`: The ERC-20 token address to use for payment.
* An object with **sponsorshipPolicyId** - `String`: The ID of the sponsorship policy to use.
* `null`: No additional context.
#### Returns
The method returns an object containing paymaster-related fields and updated gas parameters for EntryPoint 0.6.
```json
{
"jsonrpc": "2.0",
"result": {
"paymasterAndData": "0x0000000000000039cd5e8aE05257CE51C473ddd101000066d1a1a4000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000c350000000000000000000000000000000000000000000000000000000009666598f0b846603deb0a8e59b78ba3dce9c3466394ccf07795d38ecf7925dfe12c07a022c27bb199099fa54de2f5e3e87dd9c581df52e9d3d199166a31124cc1227a9921b",
"preVerificationGas": "0xdf55",
"verificationGas": "0x52503",
"verificationGasLimit": "0x52503",
"callGasLimit": "0x13880"
},
"id": 1
}
```
#### Return Fields
The response includes the following fields:
* **paymasterAndData** - `HexData`: The combined paymaster address and data to be used in the user operation.
* **preVerificationGas** - `HexNumber`: The updated preVerificationGas value to use in the user operation.
* **verificationGas** - `HexNumber`: The amount of gas to allocate for the verification step.
* **verificationGasLimit** - `HexNumber`: The updated verificationGasLimit value to use in the user operation.
* **callGasLimit** - `HexNumber`: The updated callGasLimit value to use in the user operation.
## Difference between various Smart Account types in permissionless.js
permissionless.js supports 8 types of accounts. Below is an overview of each account type and their key features:
1. **Safe**
2. **Kernel**
3. **Nexus**
4. **Simple Smart Account**
5. **LightAccount**
6. **TrustWallet**
7. **Etherspot**
8. **Thirdweb**
***
### 1. **Safe**
[Safe](https://safe.global) is a robust and widely used Ethereum smart account provider. With its ERC-4337 module, Safe accounts can integrate with ERC-4337 bundlers and paymasters.
* **ERC-7579 Support**: Yes, via [Rhinestone's safe7579](https://github.com/rhinestonewtf/safe7579) module.
* **Passkeys Support**: Supported through [Safe's passkey module](https://github.com/safe-global/safe-modules/blob/466a9b8ef169003c5df856c6ecd295e6ecb9e99d/modules/passkey/README.md); not yet supported in permissionless.js.
* **Multiple Signers**: Yes, as Safe is one of the first multi-signature smart accounts.
* **Chain Support**: Available on all chains where Safe factories are deployed.
* **Usage Stats**: \~34k Safe accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Security**: Secures over $100B+ in assets, making it highly reliable.
### 2. **Kernel**
[Kernel](https://github.com/zerodevapp/kernel) by [ZeroDev](https://zerodev.app) is a highly gas-efficient smart account compatible with ERC-4337 and ERC-7579.
* **ERC-7579 Support**: Yes, authored by the ZeroDev team.
* **Passkeys Support**: Yes.
* **Multiple Signers**: Supported through flexible permission settings:
* Define who can perform actions.
* Specify conditions for actions.
* Configure what actions can be performed.
* **Usage Stats**: \~133k Kernel v3 and 771k Kernel v2 accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Audited by [ChainLight](https://github.com/zerodevapp/kernel/blob/dev/audits/chainlight_v3_0.pdf) and [Kalos](https://github.com/zerodevapp/kernel/tree/dev/audits).
### 3. **Nexus**
Biconomy [Nexus](https://github.com/bcnmy/nexus) focuses on modularity, security, and scalability. Support for the older Biconomy Smart Account is being deprecated in favor of Nexus.
* **ERC-7579 Support**: Yes.
* **Passkeys Support**: Supported via Rhinestone's Passkeys Validator (not yet in permissionless.js).
* **Multiple Signers**: Enabled using Rhinestone's session keys module.
* **Usage Stats**: \~78 Nexus accounts and 224,758 Biconomy v2 (deprecated) accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Audited by [Cyfrin](https://github.com/bcnmy/nexus/blob/dev/audits/CodeHawks-Cyfrin-Competition-170924.pdf) and [Spearbit](https://github.com/bcnmy/nexus/blob/dev/audits/report-cantinacode-biconomy-0708-final.pdf).
### 4. **Simple Smart Account**
[Simple Smart Account](https://github.com/eth-infinitism/account-abstraction/blob/v0.7.0/contracts/samples/SimpleAccount.sol) is a sample smart account by the Eth-Infinitism team.
This is supposed to be a reference implementation for ERC-4337 and not a production-ready smart account.
* **ERC-7579 Support**: No.
* **Passkeys Support**: No.
* **Multiple Signers**: No.
* **Usage Stats**: \~1.5M accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Audited by [OpenZeppelin](https://github.com/eth-infinitism/account-abstraction/blob/develop/audits/EIP_4337_%E2%80%93_Ethereum_Account_Abstraction_Incremental_Audit_Feb_2023.pdf).
### 5. **LightAccount**
[LightAccount](https://github.com/alchemyplatform/light-account) is a lightweight ERC-4337 smart account based on [SimpleAccount](https://github.com/eth-infinitism/account-abstraction/blob/v0.7.0/contracts/samples/SimpleAccount.sol) with added functionality for smart account signers.
* **ERC-7579 Support**: No.
* **Passkeys Support**: No.
* **Multiple Signers**: No.
* **Usage Stats**: \~7.3M accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Audited by [QuantStamp](https://github.com/alchemyplatform/light-account/blob/develop/audits/2024-01-09_quantstamp_aa8196b.pdf).
### 6. **TrustWallet**
[TrustWallet](https://trustwallet.com/) is a popular cryptocurrency wallet supporting a wide range of digital assets across multiple blockchains. It provides users with a secure, self-custody solution and has introduced ERC-4337-compatible features.
* **ERC-7579 Support**: No.
* **Passkeys Support**: Yes, through [Barz](https://trustwallet.com/blog/introducing-barz-smart-contract-wallet-solution), Trust Wallet's ERC-4337-compatible smart contract wallet, which supports passkey-based authentication.
* **Multiple Signers**: Yes, Trust Wallet offers multisig functionality to enhance transaction security.
* **Usage Stats**: \~36,838 accounts created in the last 6 months ([source](https://stats.pimlico.io/accounts)).
* **Audits**: Trust Wallet has undergone independent audits.
### 7. **Etherspot**
Details on Etherspot integration coming soon.
### 8. **Thirdweb**
Details on Thirdweb integration coming soon.
### Summary of Key Features
| Account Type | ERC-7579 | Passkeys | Multiple Signers | Accounts Created (6 months) | Audited By |
| ------------------------ | -------- | -------- | ---------------- | --------------------------- | ------------------ |
| Safe | ✅ | ✅ | ✅ | 34k | Various |
| Kernel | ✅ | ✅ | ✅ | 133k (v3), 771k (v2) | ChainLight, Kalos |
| Nexus | ✅ | ✅ | ✅ | 78 | Cyfrin, Spearbit |
| Biconomy v2 (deprecated) | ✅ | ✅ | ✅ | 224k | Cyfrin, Spearbit |
| Simple | ❌ | ❌ | ❌ | 1.5M | OpenZeppelin |
| LightAccount | ❌ | ❌ | ❌ | 7.3M | QuantStamp |
| TrustWallet | ❌ | ✅ | ✅ | 36k | Independent audits |
| Etherspot | TBD | TBD | TBD | TBD | TBD |
| Thirdweb | TBD | TBD | TBD | TBD | TBD |
### Gas Efficiency of various Smart Account types
Results (as of February 24, 2024)
**Disclaimer** the numbers are obtained from [aa-benchmark](https://github.com/zerodevapp/aa-benchmark) from ZeroDev.
Since these are gas numbers, lower is better.
| | Creation | Native transfer | ERC20 transfer | Total |
| ---------------------- | -------- | --------------- | -------------- | ------- |
| Solady ERC4337 | 212262 | 100149 | 89532 | 401943 |
| Kernel v2.1-lite | 230968 | 101002 | 90321 | 422291 |
| Kernel v2.1 | 265215 | 106460 | 96038 | 467713 |
| Biconomy (deprecated) | 270013 | 104408 | 93730 | 468151 |
| SoulWalletCore | 276529 | 101162 | 90466 | 468157 |
| LightAccount | 279820 | 100910 | 90411 | 471141 |
| Etherspot | 279219 | 103719 | 93324 | 476262 |
| ERC7579 reference | 289438 | 103811 | 93213 | 486462 |
| Kernel v2.0 | 339882 | 110018 | 99622 | 549522 |
| SimpleAccount | 383218 | 101319 | 90907 | 575444 |
| Safe 4337 | 401848 | 115469 | 105089 | 622406 |
| Alchemy ModularAccount | 827723 | 106630 | 96438 | 1030791 |
## Account Support
permissionless.js supports 6 types of accounts natively (but can easily be extended to support any compatible ERC-4337 account). The below table details which EntryPoints each account is valid for.
| Account | EntryPoint v0.7 | EntryPoint v0.6 |
| :------------ | :------------------------ | :------------------------ |
| Safe | ✅ (Safe v1.4.1 and above) | ✅ (Safe v1.4.1 and above) |
| SimpleAccount | ✅ | ✅ |
| Kernel | ✅ | ✅ |
| Biconomy | ❌ | ✅ |
| LightAccount | ✅ | ✅ |
| TrustWallet | ❌ | ✅ |
| Thirdweb | ✅ | ✅ |
## How to create and use a Biconomy account with permissionless.js
[Biconomy Smart Account](https://github.com/bcnmy/scw-contracts) is a smart account building on the core concepts of Gnosis and Argent safes. You can use Biconomy with plugins such as session keys, and even write your own plugins.
### Steps
:::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/biconomy.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Biconomy account.
```ts
// [!include ~/snippets/accounts/biconomy.ts:clients]
```
#### Create the signer
Biconomy accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Biconomy account validates ECDSA signatures. [Any signer](/references/permissionless/how-to/signers) can be used as a signer for the Biconomy account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/biconomy.ts:signer]
```
#### Create the Biconomy account
With a signer, you can create a Biconomy account as such:
```ts
// [!include ~/snippets/accounts/biconomy.ts:smartAccount]
```
The Biconomy account address is computed deterministically from the signer, but you can optionally pass an `index` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created Biconomy account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/biconomy.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submitBatch]
```
:::
## How to create and use a Coinbase smart account with permissionless.js
:::warning[Warning]
You should still use permissionless to fetch gas prices. As the bundler might not accept the prices fetched from viem. To learn how to use permissionless to fetch gas prices, [check out this guide](/references/permissionless/reference/pimlico-actions/getUserOperationGasPrice#getuseroperationgasprice).
:::
Coinbase smart wallet accounts are supported natively by viem. We recommend using the native viem implementation for which the documentation [can be found here](https://viem.sh/account-abstraction/accounts/smart/toCoinbaseSmartAccount).
## How to use an ERC-7579 compatible smart account with permissionless.js
[ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) defines a standard for modular smart account interfaces. It also defines behavior for interoperability with minimal restrictions for accounts and modules.
Currently Safe and Kernel are the only smart accounts that implements ERC-7579.
For this guide, we will use the Safe smart accounts as an example. If you would like to find out more about the Safe smart account, you can check out the [Safe-specific guide](/references/permissionless/how-to/accounts/use-safe-account).
This guide will show you how to create and use a ERC-7579 compatible smart account with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/erc7579/erc7579.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the SafeAccount.
```ts
// [!include ~/snippets/erc7579/erc7579.ts:clients]
```
#### Create the SafeAccount
:::info
For a full list of options for creating a SafeAccount, take a look at the reference documentation page for [`toSafeSmartAccount`](/references/permissionless/reference/accounts/toSafeSmartAccount).
:::
You can also pass an `address` to use an already created SafeAccount.
```ts
// [!include ~/snippets/erc7579/erc7579.ts:smartAccount]
```
:::warning
The Safe account requires a new `safe4337ModuleAddress` & `erc7579LaunchpadAddress` for it to be 7579 compatible.
:::
:::warning
The address `0x000000333034E9f539ce08819E12c1b8Cb29084d` belongs to Rhinestone. By designating them as attesters, you authorize that only modules explicitly approved by Rhinestone can be installed on your safe.
:::
#### Create the smart account client and extend it with the ERC7579 actions
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/erc7579/erc7579.ts:smartAccountClient]
```
#### Interact with the 7579 actions
You can install a module on the Safe account using the `installModule` action.
```ts
// [!include ~/snippets/erc7579/erc7579.ts:installModule]
```
:::warning
InstallModule returns user operation hash, not transaction hash, you must use `waitForUserOperationReceipt` to wait for the user operation to be included on-chain.
:::
You can also call all other ERC7579 actions, example `supportsExecutionMode`.
```ts
// [!include ~/snippets/erc7579/erc7579.ts:supportsExecutionMode]
```
::::
## How to create and use a Kernel account with permissionless.js
:::info
ZeroDev, the author of Kernel, maintains their own in-house SDK built closely on top of permissionless.js that you can use for the account system while still plugging in all the other components from permissionless.js. Take a look at [their documentation](https://docs.zerodev.app) for more information.
:::
[Kernel](https://github.com/zerodevapp/kernel) is a **modular smart account** that supports plugins, which are smart contracts that extend the account's functionalities. You can use Kernel with popular plugins such as session keys and account recovery, and even write your own plugins. Kernel is compatible with [ERC-7579](https://erc7579.com/).
### Picking an EntryPoint
Kernel is compatible with EntryPoint versions v0.6 and v0.7.
If you are building a new application, we recommend using EntryPoint v0.7, which gives you the latest and greatest features and optimizations. If you already have an application using Kernel on EntryPoint v0.6, just stick with it -- it will be supported indefinitely.
In this guide, we will use EntryPoint v0.7.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/kernel.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Kernel account.
```ts
// [!include ~/snippets/accounts/kernel.ts:clients]
```
#### Create the signer
Kernel accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Kernel account validates ECDSA signatures. [Any signer](/references/permissionless/how-to/signers) can be used as a signer for the Kernel account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/kernel.ts:signer]
```
#### Create the Kernel account
:::info
For a full list of options for creating a Kernel account, take a look at the reference documentation page for [`toKernelSmartAccount`](/references/permissionless/reference/accounts/toKernelSmartAccount).
:::
With a signer, you can create a Kernel account as such:
```ts
// [!include ~/snippets/accounts/kernel.ts:smartAccount]
```
The Kernel address is computed deterministically from the signer, but you can optionally pass an `index` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created Kernel account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/kernel.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/kernel.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/kernel.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/kernel.ts:submitBatch]
```
::::
## How to create and use a LightAccount with permissionless.js
[LightAccount](https://github.com/alchemyplatform/light-account) is the smart account implementation made by Alchemy and inspired by SimpleAccount.
It has a few additional [features](https://github.com/alchemyplatform/light-account?tab=readme-ov-file#features), such as transferrable ownership and upgradability.
This guide will show you how to create and use a LightAccount with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/light.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the LightAccount.
```ts
// [!include ~/snippets/accounts/light.ts:clients]
```
#### Create the LightAccount
:::info
For a full list of options for creating a LightAccount, take a look at the reference documentation page for [`toLightSmartAccount`](/references/permissionless/reference/accounts/toLightSmartAccount).
:::
You can also pass an `address` to use an already created LightAccount.
```ts
// [!include ~/snippets/accounts/light.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/light.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/light.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/light.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/light.ts:submitBatch]
```
::::
## How to create and use a Biconomy Nexus account with permissionless.js
[Biconomy Nexus Smart Account](https://github.com/bcnmy/nexus) is a smart account building on the core concepts of ERC-7579. You can use Nexus with plugins such as session keys, and even write your own plugins.
### Steps
:::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/biconomy.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Nexus account.
```ts
// [!include ~/snippets/accounts/biconomy.ts:clients]
```
#### Create the signer
Nexus accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Nexus account validates ECDSA signatures. [Any signer](/references/permissionless/how-to/signers) can be used as a signer for the Nexus account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/biconomy.ts:signer]
```
#### Create the Nexus account
With a signer, you can create a Nexus account as such:
```ts
// [!include ~/snippets/accounts/biconomy.ts:smartAccount]
```
The Nexus account address is computed deterministically from the signer, but you can optionally pass an `index` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created Nexus account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/biconomy.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/biconomy.ts:submitBatch]
```
:::
## How to create and use a Safe account with multiple signers
[Safe](https://safe.global) is the most battle-tested Ethereum smart account provider. With their recent release of their ERC-4337 module, it is now possible to plug in Safe accounts to ERC-4337 bundlers and paymasters. This guide will walk you through how to create and use a Safe account with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Safe account.
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:clients]
```
#### Get the owner addresses
The Safe account will need to have a signer to sign user operations. In permissionless.js, the default Safe account validates ECDSA signatures. [Any permissionless.js-compatible signer](/references/permissionless/how-to/signers) can be used for the Safe account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:signer]
```
#### Create the Safe account
:::info
For a full list of options for creating a Safe account, take a look at the reference documentation page for [`toSafeSmartAccount`](/references/permissionless/reference/accounts/toSafeSmartAccount).
:::
With a signer, you can create a Safe account as such:
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:smartAccount]
```
:::info
You can also create a Safe account with 7579 module, read more about it [here](/references/permissionless/how-to/accounts/use-erc7579-account).
:::
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:smartAccountClient]
```
#### Prepare a user operation
Since we may not have access to all the signers at once, we should prepare a user operation and then submit it later after all the signers have signed.
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:prepare]
```
#### Collect signatures
You can use the `SafeSmartAccount.signUserOperation` method to collect signatures from the signers.
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:sign]
```
#### Submit the user operation
Once you have the final signature, you can submit the user operation.
```ts
// [!include ~/snippets/accounts/safe-multi-sig.ts:submit]
```
#### Understanding the errors
If you're getting an error that starts with `GS`, it probably means that something went off with the Safe account. Checkout the Safe error codes [here](https://github.com/safe-global/safe-smart-account/blob/main/docs/error_codes.md).
::::
## How to create and use a Safe account with permissionless.js
[Safe](https://safe.global) is the most battle-tested Ethereum smart account provider. With their recent release of their ERC-4337 module, it is now possible to plug in Safe accounts to ERC-4337 bundlers and paymasters. This guide will walk you through how to create and use a Safe account with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/safe.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Safe account.
```ts
// [!include ~/snippets/accounts/safe.ts:clients]
```
#### Create the signer
The Safe account will need to have a signer to sign user operations. In permissionless.js, the default Safe account validates ECDSA signatures. [Any permissionless.js-compatible signer](/references/permissionless/how-to/signers) can be used for the Safe account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/accounts/safe.ts:signer]
```
#### Create the Safe account
:::info
For a full list of options for creating a Safe account, take a look at the reference documentation page for [`toSafeSmartAccount`](/references/permissionless/reference/accounts/toSafeSmartAccount).
:::
With a signer, you can create a Safe account as such:
```ts
// [!include ~/snippets/accounts/safe.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/safe.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/safe.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/safe.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/safe.ts:submitBatch]
```
#### Understanding the errors
If you're getting an error that starts with `GS`, it probably means that something went off with the Safe account. Checkout the Safe error codes [here](https://github.com/safe-global/safe-smart-account/blob/main/docs/error_codes.md).
::::
## How to create and use a SimpleAccount with permissionless.js
[SimpleAccount](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/accounts/SimpleAccount.sol) is the original reference sample implementation of an ERC-4337 made by the Eth-Infinitism team. Despite being a reference implementation, it is widely used in production. It allows for a single EOA signer to sign user operations for the account. This guide will show you how to create and use a SimpleAccount with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/simple.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the SimpleAccount.
```ts
// [!include ~/snippets/accounts/simple.ts:clients]
```
#### Create the SimpleAccount
:::info
For a full list of options for creating a SimpleAccount, take a look at the reference documentation page for [`toSimpleSmartAccount`](/references/permissionless/reference/accounts/toSimpleSmartAccount).
:::
You can create a SimpleAccount with the canonical module addresses by specifying the factory address the account will be deployed from. You can also pass an `address` to use an already created SimpleAccount.
```ts
// [!include ~/snippets/accounts/simple.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/simple.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/simple.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/simple.ts:submitNft]
```
::::
## How to create and use a Thirdweb account with permissionless.js
### Picking an EntryPoint
Thirdweb is compatible with EntryPoint versions v0.6 and v0.7. In this guide, we will use EntryPoint v0.7.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/thirdweb.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the account.
:::info
Get your client ID from the [Thirdweb dashboard](https://thirdweb.com/dashboard/settings/api-keys) for free RPC access.
:::
```ts
// [!include ~/snippets/accounts/thirdweb.ts:clients]
```
#### Create the signer
Thirdweb's accounts can work with any Viem signing account [available in permissionless.js](/references/permissionless/how-to/signers).
In this guide, we'll use a private key to create the signer:
```ts
// [!include ~/snippets/accounts/thirdweb.ts:signer]
```
#### Create the thirdweb account
With your new signer, you can create a thirdweb account.
```ts
// [!include ~/snippets/accounts/thirdweb.ts:smartAccount]
```
The account's address is computed deterministically from the signer, but you can optionally pass an `salt` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created thirdweb account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/thirdweb.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/thirdweb.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/thirdweb.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/thirdweb.ts:submitBatch]
```
::::
## How to create and use a Trust smart account with permissionless.js
:::info
[Trust Wallet](https://trustwallet.com/), the author of [Barz](https://github.com/trustwallet/barz), provides a smart contract security monitoring service for each and every Barz deployed on-chain including Barz created by SDK.
This is a service that TrustWallet provides to builders to build innovative products on top of a secure foundation.
Monitoring will automatically start as soon as the Barz account is deployed on-chain; for projects wanting to get security monitoring information, reach out to the smart wallet channel in [TrustWallet Discord](https://discord.gg/trustwallet).
:::
Trust Wallet is one of the most trusted wallet provider empowering more than 122 million users and is the first Web3 wallet to be [certified by ISO](https://trustwallet.com/security).
After thorough development and extensive security audits, Trust Wallet launched the [Smart Wallet](https://trustwallet.com/swift) powered by account abstraction.
With the recent opensource of their Smart Wallet system Barz, their smart account solution, together with their security infrastructure is disclosed for public to support builders build products on a secure foundation.
This guide will walk you through how to create and use a Barz account with permissionless.js
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/accounts/trustwallet.ts:imports]
```
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the Trust Smart Account.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:clients]
```
#### Create the TrustAccount
:::info
For a full list of options for creating a TrustAccount, take a look at the reference documentation page for [`toTrustSmartAccount`](/references/permissionless/reference/accounts/toTrustSmartAccount).
:::
You can also pass an `address` to use an already created TrustAccount.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/accounts/trustwallet.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/accounts/trustwallet.ts:submitBatch]
```
::::
## How to conditionally sponsor a user operation
The permissionless.js library allows you to conditionally sponsor a user operation. This can be useful in situations like:
* Sponsor only first 10 transactions
* Sponsor if they have a specific NFT
The use cases can be many, but the general idea is to use the `middleware.sponsorUserOperation` parameter to conditionally sponsor a user operation.
Let's take an example where you want to sponsor only the first 10 transactions.
:::steps
#### Create the clients
First we must create the public, (optionally) pimlico paymaster clients that will be used to interact with the SimpleAccount.
```ts
// [!include ~/snippets/how-to/paymasters/conditional-sponsoring.ts:client]
```
#### Create an account
Now, create an account. This can any of the accounts supported by permissionless.js or custom accounts conforming to the interface. For this example, we'll use a Simple account.
```ts
// [!include ~/snippets/how-to/paymasters/conditional-sponsoring.ts:account]
```
#### Create the smart account client with the custom paymaster logic
When creating the `smartAccountClient`, we can pass in a `middleware.sponsorUserOperation` function that will be called before a user operation is signed and sent.
This function can conditionally choose to add a paymaster to the user operation if the desired conditions are met.
```ts
// [!include ~/snippets/how-to/paymasters/conditional-sponsoring.ts:smart-account-client]
```
:::
import SmartAccounts from "./smartAccounts.mdx"
## How to use an Arcana Auth signer with permissionless.js
[Arcana Auth](https://www.arcana.network/) offers a self-custodial Web3 wallet embedded within applications, utilizing asynchronous distributed key generation algorithms for enhanced security and privacy. This wallet, accessible without the need for a browser extension, allows authenticated users to instantly access and sign blockchain transactions within the app environment.
### Setup
To use Arcana Auth with permissionless.js, first create an application that integrates with Arcana Auth.
* Refer to the [Arcana Auth documentation site](https://docs.arcana.network/) for instructions on setting up an application with the Arcana Auth.
* For a quick start, Arcana Auth provides a web app guide, available [here](https://docs.arcana.network/auth/sdk-installation/).
### Integration
Integrating permissionless.js with Arcana Auth is straightforward after setting up the project. Arcana Auth provides an Externally Owned Account (EOA) wallet to use as a signer with accounts created using permissionless.js.
#### Create the authProvider object
After following the Arcana Auth documentation, you will have access to a `authProvider` object as shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/arcana.ts:main]
```
#### Use with permissionless.js
## How to use a DFNS signer with permissionless.js
[Dfns](https://www.dfns.co/) is an MPC/TSS Wallet-as-a-Service API/SDK provider. Dfns aims to optimize the balance of security and UX by deploying key shares into a decentralized network on the backend while enabling wallet access via biometric open standards on the frontend like Webauthn. Reach out [here](https://www.dfns.co/) to set up a sandbox environment to get started.
### Setup
To use Dfns with permissionless.js, first create an application that integrates with Dfns.
* Refer to the [Dfns documentation site](https://docs.dfns.co/d/) for instructions on setting up an application with the Dfns.
### Integration
Integrating permissionless.js with Dfns is straightforward after setting up the project. Dfns provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Set up Dfns
After following the Dfns documentation, you will have access to a `dfnsWallet` object as shown below:
```typescript
// [!include ~/snippets/signers/dfns.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Dynamic signer with permissionless.js
permissionless.js allows you to plug in custom signers to control the accounts that you create. Dynamic is an embedded wallet provider that allows you to easily onboard users to your dapp. It is possible to use Dynamic as a signer with permissionless.js, allowing you to use Dynamic to create and control smart accounts and sign transactions.
:::steps
#### Install the dependencies
```bash
npm i @dynamic-labs/sdk-react-core @dynamic-labs/wagmi-connector @dynamic-labs/ethereum permissionless viem wagmi
```
#### Create the Dynamic provider
Following Dynamic's [quickstart guide](https://docs.dynamic.xyz/quickstart), set up the Dynamic provider in your app. Also integrate the DynamicWagmiConnector, which will allow you to use Dynamic as a signer with permissionless.js.
```ts
import {
DynamicContextProvider,
DynamicWidget,
} from "@dynamic-labs/sdk-react-core";
import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
export const App = () => {
return (
);
};
```
#### Create the SmartAccountClient
Create the smart account client using the Dynamic signer. Note: DynamicWagmiConnector internally sets up the WagmiConfig, so there is no need to do it separately. This is where you would configure what smart account implementation (e.g. [Safe](/references/permissionless/how-to/accounts/use-safe-account), [Kernel](/references/permissionless/how-to/accounts/use-kernel-account), Biconomy, [TrustWallet](/references/permissionless/how-to/accounts/use-trustwallet-account) [SimpleAccount](/references/permissionless/how-to/accounts/use-simple-account)) and what paymaster logic you want to use.
```ts
import { createSmartAccountClient } from "permissionless";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { useWalletClient } from "wagmi";
import { createPublicClient, http, zeroAddress } from "viem";
import { sepolia } from "viem/chains";
import { createPimlicoClient } from "permissionless/clients/pimlico"
import { entryPoint07Address } from "viem/account-abstraction"
const {
data: walletClient
} = useWalletClient()
const publicClient = createPublicClient({
chain: sepolia, // or whatever chain you are using
transport: http()
})
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=`
const pimlicoClient = createPimlicoClient({
transport: http(pimlicoUrl),
entryPoint: {
address: entryPoint07Address,
version: "0.7",
}
})
const simpleSmartAccount = await toSimpleSmartAccount({
owner: walletClient,
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
})
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: sepolia,
bundlerTransport: http(pimlicoUrl),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
},
}
})
```
#### Send a transaction
You can now send transactions as normal. The `sponsorUserOperation` function will be called before each transaction is signed and sent, applying the custom paymaster logic you have set.
```ts
const txHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
data: "0x",
value: BigInt(0)
})
```
:::
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Fireblocks signer with permissionless.js
[Fireblocks](https://www.fireblocks.com/) is a user-friendly platform designed for building blockchain-based products and managing digital asset operations. It uses a direct custody approach, combining high performance with zero counterparty risk and multi-layered security. The platform includes secure MPC-based digital asset wallets, a policy engine for governance and transaction rules, and comprehensive treasury management. Fireblocks' security framework features multiple layers, including MPC-CMP technology, secure enclaves, and a robust policy engine, ensuring protection against cyberattacks, internal threats, and human errors. It's widely used for various operations like treasury, trading, and managing NFTs, smart contracts, and user wallets.
### Setup
To use Fireblocks with permissionless.js, first create an application that integrates with Fireblocks.
* Refer to the [Fireblocks documentation site](https://developers.fireblocks.com/) for instructions on setting up an application with the Fireblocks.
* For a quick start, Fireblocks provides a guide, available [here](https://developers.fireblocks.com/docs/quickstart).
### Integration
Integrating permissionless.js with Fireblocks is straightforward after setting up the project. Fireblocks provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Fireblocks object
After following the Fireblocks documentation, you will have access to a `FireblocksWeb3Provider` object as shown that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/fireblocks.ts:main]
```
#### Use with permissionless.js
## Signers for permissionless.js
Smart accounts are able to define custom authentication and authorization schemes, but still require signatures to validate user operations coming from signers. permissionless.js which accepts `LocalAccount | EIP1193Provider | WalletClient`, that can be passed as an owner to `createSmartAccountClient`. While by default these owners have full control over the smart account, it's possible to define custom roles and permissions for owners, and mix-and-match them to create complex multi-signature schemes, as well as rotate signers and revoke their access.
In addition to pure EOA owners, permissionless.js supports a variety of third-party wallet services as owners for smart accounts. This allows you to leverage the UIs and infrastructure of these services, while still using smart accounts supported by permissionless.js.
## How to Integrate Pimlico with Lit Protocol OTP Authentication
This how-to guide will walk you through the steps to integrate Lit Protocol's OTP sign-in with email, SMS, and Whatsapp with a smart account whose user operations are relayed and sponsored by Pimlico.
:::info
Lit Protocol is an Authentication solution that lets you create and manage distributed cryptographic key-pairs for condition-based encryption and programmatic signing. A decentralized key management network, Lit can be used in place of centralized key custodians and other key management solutions. For more information on how Lit Protocol works, visit [their documentation page](https://developer.litprotocol.com/resources/how-it-works).
:::
[Stytch](https://stytch.com/) will be used to manage the OTP authentication flow.
::::steps
#### Install the required packages
```bash
npm install stytch @lit-protocol/pkp-ethers @lit-protocol/lit-auth-client @lit-protocol/auth-helpers @lit-protocol/types @lit-protocol/lit-node-client-nodejs
```
#### Make an account with Stytch and get the Project ID and Secret
You can sign up for a Stytch account [here](https://stytch.com/). Once you have an account, you can find your Project ID and Secret in the [Stytch Dashboard API Keys page](https://stytch.com/dashboard/api-keys).

#### Create a Stytch client with your Project ID and Secret
```ts
const stytchClient = new stytch.Client({
project_id: "project-test-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
secret: "secret-test-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
});
```
#### Send an OTP to the user's email, SMS, or Whatsapp
:::code-group
```ts [email]
const stytchResponse = await stytchClient.otps.email.loginOrCreate({
email: "",
})
```
```ts [SMS]
const stytchResponse = await stytchClient.otps.sms.loginOrCreate({
phone_number: "",
})
```
```ts [WhatsApp]
const stytchResponse = await stytchClient.otps.whatsapp.loginOrCreate({
phone_number: "",
})
```
:::
#### Authenticate the user with the OTP and get a session token
:::code-group
```ts [email]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.email_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
```ts [SMS]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.phone_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
```ts [WhatsApp]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.phone_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
:::
#### Get a Lit Relay Server API Key
You can get a Lit Relay Server API Key by filling out [the Lit Protocol team's form](https://forms.gle/RNZYtGYTY9BcD9MEA)
#### Mint a PKPs through Lit Protocol
```ts
const litClient = new LitAuthClient({
litRelayConfig: {
relayApiKey: '',
}
});
const session = litClient.initProvider(ProviderType.StytchOtp, {
userId: sessionStatus.session.user_id,
appId: "project-test-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
})
const authMethod = await session.authenticate({
accessToken: sessionStatus.session_jwt
})
await session.mintPKPThroughRelayer(authMethod)
const pkps = await session.fetchPKPsThroughRelayer(authMethod)
```
#### Generate the Controller Session Signatures
```ts
const litNodeClient = new LitNodeClientNodeJs({
litNetwork: 'serrano',
debug: false,
})
await litNodeClient.connect();
const resourceAbilities = [
{
resource: new LitActionResource("*"),
ability: LitAbility.PKPSigning,
},
];
const sessionKeyPair = litNodeClient.getSessionKey();
const authNeededCallback = async (params: AuthCallbackParams) => {
const response = await litNodeClient.signSessionKey({
sessionKey: sessionKeyPair,
statement: params.statement,
authMethods: [authMethod],
pkpPublicKey: pkp[pkp.length - 1].publicKey,
expiration: params.expiration,
resources: params.resources,
chainId: 1,
});
return response.authSig;
};
const sessionSigs = await litNodeClient.getSessionSigs({
chain: "ethereum",
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString(),
resourceAbilityRequests: resourceAbilities,
sessionKey: sessionKeyPair,
authNeededCallback
}).catch((err) => {
console.log("error while attempting to access session signatures: ", err)
throw err;
});
```
#### Initialize the PKP Wallet
We will now generate a wallet that can act a regular Ethers.js wallet, but will use the PKPs minted through Lit Protocol to sign transactions under the hood.
```ts
const pkpWallet = new PKPEthersWallet({
pkpPubKey: pkp[pkp.length - 1].publicKey,
rpc: "", // e.g. https://sepolia.rpc.thirdweb.com
controllerSessionSigs: sessionSigs
});
await pkpWallet.init();
```
#### Use the PKP Wallet to sign user operations and send them through Pimlico
You can now use the `pkpWallet` as a regular Ethers.js wallet to sign user operations. To submit a user operation to Pimlico, you can follow the steps to [sponsor a user operation with Pimlico's verifying paymaster](/references/paymaster/verifying-paymaster/usage) and/or [submit a user operation through Pimlico's bundler](/references/bundler/usage). If you would like to integrate Lit Protocol with the full flow of generating, signing, and submitting a user operation, you can follow the steps in [tutorial 1](/references/permissionless/tutorial/tutorial-1), replacing the signing step with the PKP wallet and using `pkpWallet.address` as the owner address of the smart account.
Modified from [tutorial 1](/references/permissionless/tutorial/tutorial-1), an example of how to use the PKP wallet to sign a user operation is shown below:
```typescript
const signature = await pkpWallet.signMessage(
ethers.utils.arrayify(await entryPoint.getUserOpHash(userOperation)),
)
userOperation.signature = signature
```
And an example of how you would generate the initCode for a SimpleAccount using the PKP wallet is shown below:
```typescript
const initCode = ethers.utils.hexConcat([
SIMPLE_ACCOUNT_FACTORY_ADDRESS,
simpleAccountFactory.interface.encodeFunctionData("createAccount", [pkpWallet.address, 0]),
])
```
::::
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Magic signer with permissionless.js
[Magic](https://magic.link/) is a popular embedded wallet provider that supports social logins. While social logins are great, your users still need to onramp in order to pay for gas, which introduces significant friction.
By combining permissionless.js with Magic, you can use Magic to enable a smooth social login experience, while using permissionless.js accounts as the smart wallets to sponsor gas for users, batch transactions, and more.
### Setup
To use Magic with permissionless.js, first create an application that integrates with Magic.
* Refer to the [Magic documentation site](https://magic.link/docs/home/welcome) for instructions on setting up an application with the Magic SDK.
* For a quick start, Magic provides a CLI to create a starter project, available [here](https://magic.link/docs/home/quickstart/cli#run-make-magic).
### Integration
Integrating permissionless.js with Magic is straightforward after setting up the Magic SDK. Magic provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Magic object
After following the Magic documentation, you will have access to a `MagicBase` object as shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/magic.ts:main]
```
#### Use with permissionless.js
## How to use a Para signer with permissionless.js
[Para](https://www.getpara.com/) offers a signing solution enabling the creation of secure, embedded MPC wallets accessible via email or social login. These wallets, compatible across different applications, offer portability, recoverability, and programmability, eliminating the need for users to establish separate signers or contract accounts for each application.
### Setup
To use Para with permissionless.js, first create an application that integrates with Para.
* Refer to the [Para documentation site](https://docs.getpara.com/welcome) for instructions on setting up an application with the Para.
* For a quick start, Para provides an example hub, available [here](https://docs.getpara.com/getting-started/examples).
### Integration
Integrating permissionless.js with Para is straightforward after setting up the project. Para provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Para signer
After following the Para documentation, you will have access to a `ParaWeb3Provider` object that you can pass as an owner to `createSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/para.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Particle Network signer with permissionless.js
[Particle Network](https://particle.network/) is an intent-centric, modular wallet-as-a-service (WaaS). By utilizing MPC-TSS for key management, Particle can streamline onboarding via familiar Web2 methods such as Google, emails, and phone numbers.
By combining permissionless.js with Particle, you can use Particle to enable a smooth social login experience, while using ZeroDev as the smart wallet to sponsor gas for users, batch transactions, and more.
### Setup
To use Particle Network with permissionless.js, first create an application that integrates with Particle Network.
* Refer to the [Particle Network documentation site](https://docs.particle.network/) for instructions on setting up an application with the Particle Network.
* For a quick start, Particle Network provides a guide, available [here](https://docs.particle.network/getting-started/get-started).
### Integration
Integrating permissionless.js with Particle Network is straightforward after setting up the project. Particle Network provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Particle Network object
After following the Particle Network documentation, you will have access to a `ParticleProvider` object as shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/particle-network.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Passkey (WebAuthn) server
This how-to guide will walk you through the steps to integrate Passkey (WebAuthn) server with a smart account whose user operations are relayed and sponsored by Pimlico.
:::info
Passkey (WebAuthn) is a modern authentication method that allows users to sign in to websites and apps using their fingerprint, face, or other biometric information. For more information on how Passkey works, visit [the documentation page](https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API).
:::
### Steps
::::steps
#### Install the required packages
```bash
npm install viem ox permissionless
```
#### Create a passkeys client
You will need to create a passkeys client to interact with the passkeys server. Learn how to create your own or use passkeys server at [How to create a Passkey (WebAuthn) server](/references/permissionless/how-to/signers/passkey-server#how-to-create-a-passkey-webauthn-server).
If you want to use Pimlico's passkeys server, visit [Dashboard](https://dashboard.pimlico.io/passkey-server) and configure your passkeys server.

You will then create a passkeys client with the following code:
```ts
const passkeyServerClient = createPasskeyServerClient({
chain,
transport: http(
`https://api.pimlico.io/v2/${chain.id}/rpc?apikey=${pimlicoApiKey}`
)
})
```
#### Create credentials
```tsx
import { useState } from "react"
import {
createWebAuthnCredential,
} from "viem/account-abstraction"
export function PasskeysDemo() {
const [credential, setCredential] = useState()
const createCredential = async () => { // [!code focus]
const credential = await createWebAuthnCredential( // [!code focus]
// Start the registration process // [!code focus]
await passkeyServerClient.startRegistration({ // [!code focus]
context: { // [!code focus]
// userName that will be shown to the user when creating the passkey // [!code focus]
userName: "plusminushalf" // [!code focus]
} // [!code focus]
}) // [!code focus]
) // [!code focus]
// Verify the registration // [!code focus]
const verifiedCredential = await passkeyServerClient.verifyRegistration( // [!code focus]
{ // [!code focus]
credential, // [!code focus]
context: { // [!code focus]
// userName that will be shown to the user when creating the passkey // [!code focus]
userName: "plusminushalf" // [!code focus]
} // [!code focus]
} // [!code focus]
) // [!code focus]
setCredential(verifiedCredential) // [!code focus]
} // [!code focus]
if (!credential)
return (
)
return (
// [!code focus]
)} // [!code focus]
> // [!code focus]
) // [!code focus]
}
```
::::
## How to use a Privy signer with permissionless.js
permissionless.js allows you to plug in custom signers to control the accounts that you create. Privy is an embedded wallet provider that allows you to easily onboard users to your dapp. It is possible to use Privy as a signer with permissionless.js, allowing you to use Privy to create and control smart accounts and sign transactions.
Additionally you may want to look at Privy's guide on working with permissionless.js [here](https://docs.privy.io/guide/react/recipes/account-abstraction/pimlico).
:::steps
#### Install the dependencies
```bash
npm i @privy-io/react-auth @privy-io/wagmi-connector permissionless viem wagmi
```
#### Create the Privy provider
Following Privy's [quickstart guide](https://docs.privy.io/guide/quickstart), set up the Privy provider in your app. Also integrate the PrivyWagmiConnector, which will allow you to use Privy as a signer with permissionless.js.
```ts
import { PrivyProvider } from '@privy-io/react-auth';
import { PrivyWagmiConnector } from '@privy-io/wagmi-connector';
"}
config={{
embeddedWallets: {
createOnLogin: "all-users",
},
}}
>
{children}
;
```
#### Set Privy as the active wallet
In your app, set Privy's embedded wallet as the active wallet for wagmi
```ts
import { useWallets } from "@privy-io/react-auth";
import { usePrivyWagmi } from "@privy-io/wagmi-connector";
import { useEffect } from "react";
const { wallets } = useWallets();
const embeddedWallet = wallets.find(
(wallet) => wallet.walletClientType === "privy"
);
```
#### Create the SmartAccountClient
Create the smart account client using the Privy signer. This is where you would configure what smart account implementation (e.g. [Safe](/references/permissionless/how-to/accounts/use-safe-account), [Kernel](/references/permissionless/how-to/accounts/use-kernel-account), Biconomy, [TrustWallet](/references/permissionless/how-to/accounts/use-trustwallet-account), [SimpleAccount](/references/permissionless/how-to/accounts/use-simple-account)) and what paymaster logic you want to use.
```ts
import { createSmartAccountClient } from "permissionless";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { useWalletClient } from "wagmi";
import { createPublicClient, http, zeroAddress } from "viem";
import { sepolia } from "viem/chains";
import { createPimlicoClient } from "permissionless/clients/pimlico"
import { entryPoint07Address } from "viem/account-abstraction"
const {
data: walletClient
} = useWalletClient()
const publicClient = createPublicClient({
chain: sepolia, // or whatever chain you are using
transport: http()
})
const pimlicoUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=`
const pimlicoClient = createPimlicoClient({
transport: http(pimlicoUrl),
entryPoint: {
address: entryPoint07Address,
version: "0.7",
}
})
const owner = embeddedWallet.getEthereumProvider()
if (!owner) {
throw new Error("No owner found")
}
const simpleSmartAccount = await toSimpleSmartAccount({
owner: owner,
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
})
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: sepolia,
bundlerTransport: http(pimlicoUrl),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
},
}
})
```
#### Send a transaction
You can now send transactions as normal. The `sponsorUserOperation` function will be called before each transaction is signed and sent, applying the custom paymaster logic you have set.
```ts
const txHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
data: "0x",
value: BigInt(0)
})
```
:::
For a full example, see the [example permissionless + Privy app](https://github.com/pimlicolabs/privy-demo/blob/main/src/components/privy/privy.tsx).
:::code-group
```ts [SimpleAccount]
// [!include ~/snippets/signers/accounts/simpleSmartAccount.ts:main]
```
```ts [Safe Account]
// [!include ~/snippets/signers/accounts/safeSmartAccount.ts:main]
```
```ts [Kernel Account]
// [!include ~/snippets/signers/accounts/ecdsaKernelSmartAccount.ts:main]
```
```ts [Biconomy Account]
// [!include ~/snippets/signers/accounts/biconomySmartAccount.ts:main]
```
```ts [TrustWallet Account]
// [!include ~/snippets/signers/accounts/trustSmartAccount.ts:main]
```
:::
## How to use a Turnkey signer with permissionless.js
[Turnkey](https://turnkey.com/) is a key infrastructure provider with a great developer API and a powerful security policy engine.
By combining permissionless.js with Turnkey, you can create **custodial AA wallets** whose security is provided by Turnkey, with powerful functionalities such as sponsoring gas, batching transactions, etc.
### Setup
To use Turnkey with permissionless.js, first create an application that integrates with Turnkey.
* Refer to the [Turnkey documentation site](https://docs.turnkey.com/) for instructions on setting up an application with the Turnkey.
* For a quick start, Turnkey provides examples, available [here](https://docs.turnkey.com/getting-started/examples).
### Integration
Integrating permissionless.js with Turnkey is straightforward after setting up the project. Turnkey provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the TurnkeyClient and a Turnkey viem account
After following the Turnkey documentation, you will have access to a `TurnkeyClient`. An example is shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/turnkey.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Web3Auth signer with permissionless.js
[Web3Auth](https://web3auth.io/) is a popular embedded wallet provider that supports social logins. While social logins are great, your users still need to onramp in order to pay for gas, which introduces significant friction.
By combining permissionless.js with Web3Auth, you can use Web3Auth to enable a smooth social login experience, while using permissionless.js accounts as the smart wallets to sponsor gas for users, batch transactions, and more.
### Setup
To use Web3Auth with permissionless.js, first create an application that integrates with Web3Auth.
* Refer to the [Web3Auth documentation site](https://web3auth.io/docs/index.html) for instructions on setting up an application with the Web3Auth.
* For a quick start, Web3Auth provides example starter projects, available [here](https://web3auth.io/docs/examples?product=Plug+and+Play\&sdk=Plug+and+Play+Web+Modal+SDK).
### Integration
Integrating permissionless.js with Web3Auth is straightforward after setting up the project. Web3Auth provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Web3Auth object
After following the Web3Auth documentation, you will have access to a `web3auth` object as shown below that you can pass as an owner to `createeSmartAccountClient`:
```typescript
// [!include ~/snippets/signers/web3auth.ts:main]
```
#### Use with permissionless.js
import SmartAccounts from "./smartAccounts.mdx"
For a full example, see the [example permissionless + Web3Auth app](https://github.com/pimlicolabs/web3auth-demo/blob/main/src/components/web3auth/web3auth.tsx).
## Testing with BuildBear Sandboxes
This guide introduces how to setup a BuildBear Sandbox with for testing the Alto bundler and a mock paymaster. We will be using viem and permissionless to interact with the sandbox enviornment.
### Overview
BuildBear provides on-demand, isolated test environments that clone existing blockchain networks. These environments are perfect for testing account abstraction implementations as they:
* Provide a persistent, dedicated testnet environment
* Support EIP-4337 and all necessary AA infrastructure
* Allow you to fork from existing networks (Ethereum, Arbitrum, Optimism, etc.)
* Unlocked faucets for testing
* Offer an explorer interface for easy debugging
### Steps
::::steps
#### Creating and setting up a BuildBear Sandbox
1. Go to [buildbear.io](https://www.buildbear.io) and sign up for an account if you don't have one already.
2. Create a new Sandbox by clicking "Create a Sandbox" and selecting the network you want to fork (e.g., Ethereum Mainnet, Sepolia, etc.).
3. Navigate to the Plugin tab on your sandbox dashboard and install the Pimlico Plugin. Ensure the plugin appears as installed in the marketplace or Installed tab.
4. Go to your Buildbear sandbox dashboard and locate the RPC URL. Copy the RPC URL, which also serves as the Pimlico Client API endpoint. It should look something like the below.
* BuildBear Sandbox RPC URL: `https://rpc.buildbear.io/`
* Pimlico Client API: `https://rpc.buildbear.io/`
5. Once your Sandbox is created, you'll get access to:
* RPC URL
* Chain ID
* Explorer URL
* Faucet to fund your test accounts
#### Setup the clients
```ts [Setup Clients]
// [!include ~/snippets/testing/build-bear.ts:clients]
```
#### Setup the Smart Account Client
```ts [Setup Clients]
// [!include ~/snippets/testing/build-bear.ts:smartAccountClient]
```
#### Funding your smart account (Optional)
If you want to fund your smart account with native currency or ERC-20 tokens, you can use the following curl commands:
Funding native currency:
```
curl -X POST https://rpc.buildbear.io/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "buildbear_nativeFaucet",
"params": [{
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"balance": "100"
}]
}'
```
Funding ERC-20 tokens:
```
curl -X POST https://rpc.buildbear.io/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "buildbear_ERC20Faucet",
"params": [{
"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"balance": "100",
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}]
}'
```
#### Sending a userOperation
:::code-group
```ts [Verifying Paymaster]
// [!include ~/snippets/testing/build-bear.ts:verifyingPaymaster]
```
```ts [ERC-20 Paymaster]
// [!include ~/snippets/testing/build-bear.ts:erc20Paymaster]
```
:::
:::info
paymaster top-ups are taken care of by the sandbox and you do need to worry about topping up the paymaster ever
:::
::::
More detailed examples and instructions can be found on [BuildBear's docs](https://app.buildbear.io/plugins/pimlico-alto-5efcae784d7b)
## Local Testing With Docker
This guide introduces a ready-to-use mock test environment, that contains:
* A local Alto bundler
* A mock verifying paymaster
* An Anvil node
* ERC-4337 related contracts, including the EntryPoint and account factories for all major smart account implementations.
The test environment is orchestrated using **docker compose**. Where the docker containers are pulled from [this repo](https://github.com/pimlicolabs/mock-aa-environment).
The mock environment is designed to mimic mainnet as closely as possible by building with the latest Alto version and by deploying all contracts (entrypoints, paymasters, smart account factories, etc.)
This has advantages over testing against a production testnet as you have more control over the testing environment and can make use of features like anvil cheatcodes.
### Steps
::::steps
#### Setup
To get started, create a **alto-config.json** file at the root of your test directory with the following contents.
```json [alto-config.json]
{
"network-name": "local",
"log-environment": "production",
"entrypoints": "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789,0x0000000071727De22E5E9d8BAf0edAc6f37da032",
"balance-override-enabled": "true",
"api-version": "v1,v2",
"rpc-url": "http://anvil:8545",
"min-balance": "0",
"utility-private-key": "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97",
"executor-private-keys": "0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6,0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356,0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e,0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba,0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a",
"max-block-range": 10000,
"safe-mode": false,
"port": 4337,
"log-level": "info",
"public-client-log-level": "error",
"wallet-client-log-level": "error",
"polling-interval": 100
}
```
> Note: All private keys are test keys generated by anvil
Then, create a **docker-compose.yaml** file at the root of your test directory with the following contents.
```yaml [docker-compose.yaml]
services:
anvil:
image: ghcr.io/foundry-rs/foundry:nightly-c4a984fbf2c48b793c8cd53af84f56009dd1070c
ports: [ "8545:8545" ]
entrypoint: [ "anvil", "--host", "0.0.0.0", "--block-time", "0.1", "--silent"]
platform: linux/amd64/v8
healthcheck:
test: ["CMD-SHELL", "cast rpc web3_clientVersion | grep -c anvil > /dev/null "]
start_interval: 250ms
start_period: 10s
interval: 30s
timeout: 5s
retries: 50
contract-deployer:
image: ghcr.io/pimlicolabs/mock-contract-deployer:main
environment:
- ANVIL_RPC=http://anvil:8545
depends_on:
anvil:
condition: service_healthy
mock-paymaster:
image: ghcr.io/pimlicolabs/mock-verifying-paymaster:main
ports: [ "3000:3000" ]
environment:
- ALTO_RPC=http://alto:4337
- ANVIL_RPC=http://anvil:8545
depends_on:
anvil:
condition: service_healthy
contract-deployer:
condition: service_completed_successfully
alto:
image: ghcr.io/pimlicolabs/alto:latest
ports: [ "4337:4337" ]
entrypoint: ["node", "src/lib/cli/alto.js", "run", "--config", "/app/alto-config.json"]
depends_on:
anvil:
condition: service_healthy
contract-deployer:
condition: service_completed_successfully
volumes:
- ./alto-config.json:/app/alto-config.json
```
To start the test environment, run
```sh
docker compose up
```
Once docker has started, the following services can be accessed locally through the following endpoints:
* `Anvil` at **localhost:8545**
* `Alto Bundler` at **localhost:4337**
* `Mock Paymaster` at **localhost:3000**
You can now use permissionless like you normally would but instead of referencing the live endpoints, use the local endpoints mentioned above when creating the clients.
```ts [clients.ts] twoslash
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { http, createPublicClient } from "viem";
import { createBundlerClient, entryPoint07Address } from "viem/account-abstraction"
import { foundry } from "viem/chains";
const publicClient = createPublicClient({
chain: foundry,
transport: http("http://localhost:8545"), // [!code ++]
});
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337") // [!code ++]
});
const pimlicoClient = createPimlicoClient({
transport: http("http://localhost:3000"), // [!code ++]
entryPoint: {
address: entryPoint07Address,
version: "0.7",
}
})
```
:::info
Note: All smart account types supported by permissionless.js will work out of the box as all related factories and modules are deployed on the local anvil instance.
:::
#### Vitest Integration
You can add scripts in your **package.json** to automatically set up and tear down your mock environment when running tests.
```json [package.json]
{
"name": "aa-tests",
"scripts": {
"test": "bun run docker:up && vitest run && bun run docker:down",
"docker:up": "docker-compose up -d", // [!code ++]
"docker:down": "docker-compose down" // [!code ++]
},
"dependencies": {
"viem": "^2.9.17",
"permissionless": "^0.1.35"
},
"devDependencies": {
"vitest": "^1.5.2"
}
}
```
When writing test cases, ensure that the bundler and paymaster are fully setup before sending any request to them. To do this, make a simple health check in the `beforeAll` declaration.
:::code-group
```ts [basic.test.ts]
import { beforeAll, describe, expect, test } from "vitest";
import { ensureBundlerIsReady, ensurePaymasterIsReady } from "./healthCheck";
import { foundry } from "viem/chains";
import { http } from "viem";
import {
createBundlerClient,
entryPoint06Address,
entryPoint07Address
} from "viem/account-abstraction";
describe("Test basic bundler functions", () => {
beforeAll(async () => { // [!code ++]
await ensureBundlerIsReady(); // [!code ++]
await ensurePaymasterIsReady(); // [!code ++]
}); // [!code ++]
test("Can get chainId", async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
});
const chainId = await bundlerClient.getChainId();
expect(chainId).toEqual(foundry.id);
});
test("Can get supported entryPoints", async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
});
const supportedEntryPoints = await bundlerClient.getSupportedEntryPoints();
expect(supportedEntryPoints).toEqual([
entryPoint06Address,
entryPoint07Address,
]);
});
});
```
```ts [healthCheck.ts] twoslash
import { createBundlerClient } from "viem/account-abstraction";
import { http } from "viem";
import { foundry } from "viem/chains";
export const ensureBundlerIsReady = async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
});
while (true) {
try {
await bundlerClient.getChainId();
return;
} catch {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
};
export const ensurePaymasterIsReady = async () => {
while (true) {
try {
// mock paymaster will open up this endpoint when ready
const res = await fetch(`http://localhost:3000/ping`);
const data = await res.json();
if (data.message !== "pong") {
throw new Error("paymaster not ready yet");
}
return;
} catch {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
};
```
:::
#### Extension: Testing against forked state
If you want your tests to run against a live blockchain, you can slightly edit the **docker-compose.yaml** file to fork from the latest block by adding the anvil flag **--fork-url** and the environment variable **SKIP\_DEPLOYMENTS** to skip the local contract deployments.
```yaml [docker-compose.yaml]
services:
anvil:
image: ghcr.io/foundry-rs/foundry:nightly-c4a984fbf2c48b793c8cd53af84f56009dd1070c
ports: [ "8545:8545" ]
entrypoint: [ "anvil", "--fork-url", "https://sepolia.rpc.thirdweb.com", "--chain-id", "11155111", "--host", "0.0.0.0", "--block-time", "0.1", "--silent"] // [!code ++]
platform: linux/amd64/v8
healthcheck:
test: ["CMD-SHELL", "cast rpc web3_clientVersion | grep -c anvil > /dev/null "]
start_interval: 250ms
start_period: 10s
interval: 30s
timeout: 5s
retries: 50
contract-deployer:
image: ghcr.io/pimlicolabs/mock-contract-deployer:main
environment:
- ANVIL_RPC=http://anvil:8545
- SKIP_DEPLOYMENTS=true // [!code ++]
depends_on:
anvil:
condition: service_healthy
mock-paymaster:
image: ghcr.io/pimlicolabs/mock-verifying-paymaster:main
ports: [ "3000:3000" ]
environment:
- ALTO_RPC=http://alto:4337
- ANVIL_RPC=http://anvil:8545
depends_on:
anvil:
condition: service_healthy
contract-deployer:
condition: service_completed_successfully
alto:
image: ghcr.io/pimlicolabs/alto:latest
ports: [ "4337:4337" ]
entrypoint: ["node", "src/lib/cli/alto.js", "run", "--config", "/app/alto-config.json"]
depends_on:
anvil:
condition: service_healthy
contract-deployer:
condition: service_completed_successfully
volumes:
- ./alto-config.json:/app/alto-config.json
```
::::
## to7702KernelSmartAccount
Creates a KernelAccount instance by delegating `owner` using EIP-7702. Check out [this guide](/guides/eip7702/demo) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
import { to7702KernelSmartAccount } from "permissionless/accounts"
import { entryPoint07Address } from "viem/account-abstraction"
import { publicClient } from "./publicClient"
import { owner } from "./owner"
const kernelAccount = await to7702KernelSmartAccount({
client: publicClient,
version: "0.3.1"
entryPoint: {
address: entryPoint07Address,
version: "0.7"
},
owner
});
```
```ts [publicClient.ts]
// [!include ~/snippets/publicClient.ts:client]
```
```ts [owner.ts]
// [!include ~/snippets/owner.ts]
```
:::
### Returns
* **Type:** `SmartAccount>`
The Kernel smart account instance.
### Parameters
#### client
* **Type:** `Client`
A public client as smart account needs access to the Network to query for information about its state (e.g. nonce, address, etc).
#### owner
* **Type:** `[LocalAccount | EIP1193Provider | WalletClient]`
The owner which will be delegated to kernel smart account.
#### entryPoint (optional)
* **Type:** `{ address: Address, version: "0.6" | "0.7" }`
The address and the version of the EntryPoint contract. If not provided, entryPoint 0.7 will be used.
#### accountLogicAddress (optional)
* **Type:** `Address`
The address of the Kernel account logic that will be used while delegating the owner.
## to7702SimpleSmartAccount
Creates a SimpleAccount instance by delegating `owner` using EIP-7702.
### Usage
:::code-group
```ts [example.ts]
import { to7702SimpleSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { owner } from "./owner"
const simpleSmartAccount = await to7702SimpleSmartAccount({
client: publicClient,
owner
})
```
```ts [publicClient.ts]
// [!include ~/snippets/publicClient.ts:client]
```
```ts [owner.ts]
// [!include ~/snippets/owner.ts]
```
:::
### Returns
* **Type:** `SimpleSmartAccountImplementation`
This represents the smart account instance.
### Parameters
#### client
* **Type:** `Client`
A public client is required as the smart account needs access to the network to query information about its state (e.g., nonce, address, etc.).
#### owner
* **Type:** `LocalAccount | EIP1193Provider | WalletClient`
The owner which will be delegated to simple smart account.
#### accountLogicAddress (optional)
* **Type:** `Address`
Ownership will be delegated to this account logic address.
:::info
The accountLogicAddress for v0.8 of the EntryPoint is `0xe6Cae83BdE06E4c305530e199D7217f42808555B`.
:::
#### entryPoint (optional)
* **Type:** `{ address: Address, version: "0.8" }`
This specifies the address and version of the EntryPoint contract. If not provided, entryPoint 0.8 will be used.
## toKernelSmartAccount
Creates a KernelAccount instance controlled by a `owners`. Check out [this guide](/guides/how-to/accounts/use-kernel-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
import { toKernelSmartAccount } from "permissionless/accounts"
import { entryPoint07Address } from "viem/account-abstraction"
import { publicClient } from "./publicClient"
import { owner } from "./owner"
const kernelAccount = await toKernelSmartAccount({
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: "0.7"
},
owners: [owner],
version: "0.3.1"
});
```
```ts [publicClient.ts]
// [!include ~/snippets/publicClient.ts:client]
```
```ts [owner.ts]
// [!include ~/snippets/owner.ts]
```
:::
### Returns
* **Type:** `SmartAccount`
The Kernel smart account instance.
### Parameters
#### client
* **Type:** `Client`
A public client as smart account needs access to the Network to query for information about its state (e.g. nonce, address, etc).
#### owners
* **Type:** `[LocalAccount | EIP1193Provider | WalletClient]`
The owners that will be used to sign messages and user operations.
#### entryPoint (optional)
* **Type:** `{ address: Address, version: "0.6" | "0.7" }`
The address and the version of the EntryPoint contract. If not provided, entryPoint 0.7 will be used.
#### index (optional)
* **Type:** `bigint`
The index (which is basically a salt) that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the deterministic smart account address will be used.
#### version (optional)
* **Type:** `"0.2.1" | "0.2.2" | "0.2.3" | "0.2.4" | "0.3.0-beta" | "0.3.1"`
The version of the Kernel contract that will be used. `0.2.x` can only be used with entryPoint 0.6. `0.3.x` can only be used with entryPoint 0.7.
#### factoryAddress (optional)
* **Type:** `Address`
The address of the Kernel factory that will be used to deploy the smart account. This is only used if the version is `0.2.x`.
#### metaFactoryAddress (optional)
* **Type:** `Address`
The address of the Kernel meta factory that will be used to deploy the smart account. This is only used if the version is `0.3.x`.
#### accountLogicAddress (optional)
* **Type:** `Address`
The address of the Kernel account logic that will be used to deploy the smart account.
#### validatorAddress (optional)
* **Type:** `Address`
The address of the ECDSA validator that will be used to deploy the smart account.
## toLightSmartAccount
Creates a LightAccount instance controlled by a `owner`.
### Usage
:::code-group
```ts [example.ts]
import { toLightSmartAccount } from "permissionless/accounts"
import { entryPoint07Address } from "viem/account-abstraction"
import { publicClient } from "./publicClient"
import { owner } from "./owner"
const lightAccount = await toLightSmartAccount({
entryPoint: {
address: entryPoint07Address,
version: "0.7"
},
client: publicClient,
version: "2.0.0", // or "1.1.0"
owner
})
```
```ts [publicClient.ts]
// [!include ~/snippets/publicClient.ts:client]
```
```ts [owner.ts]
// [!include ~/snippets/owner.ts]
```
:::
### Returns
* **Type:** `SmartAccount>`
The smart account instance.
### Parameters
#### client
* **Type:** `Client`
A public client as smart account needs access to the Network to query for information about its state (e.g. nonce, address, etc).
#### owner
* **Type:** `LocalAccount | EIP1193Provider | WalletClient`
The owner that will be used to sign messages and user operations.
#### entryPoint (optional)
* **Type:** `{ address: Address, version: "0.6" | "0.7" }`
The address and the version of the EntryPoint contract. If not provided, entryPoint 0.7 will be used.
#### version
* **Type:** `LightAccountVersion`
The light account version. Currently `v1.1.0` and `v2.0.0` is supported. `v1.1.0` can only be used with entryPoint 0.6. `v2.0.0` can only be used with entryPoint 0.7.
#### factoryAddress (optional)
* **Type:** `Address`
The address of the light account factory that will be used to deploy the smart account.
:::info
The canonical light account factory for v1.1.0 is `0x00004EC70002a32400f8ae005A26081065620D20`
:::
#### index (optional)
* **Type:** `bigint`
It represents salt nonce that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
## toNexusSmartAccount
Creates a Biconomy's [Nexus SmartAccount](https://github.com/bcnmy/nexus) instance controlled by a `owner`.
### Usage
:::code-group
```ts [example.ts]
import { toSimpleSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { owner } from "./owner"
const simpleSmartAccount = await toNexusSmartAccount({
owner,
client: publicClient,
version: "1.0.0"
})
```
```ts [publicClient.ts]
// [!include ~/snippets/publicClient.ts:client]
```
```ts [owner.ts]
// [!include ~/snippets/owner.ts]
```
:::
### Returns
* **Type:** `SmartAccount`
The smart account instance.
### Parameters
#### client
* **Type:** `Client`
A public client as smart account needs access to the Network to query for information about its state (e.g. nonce, address, etc).
#### owners
* **Type:** `[LocalAccount | EIP1193Provider | WalletClient]`
The owners that will be used to sign messages and user operations.
#### version
* **Type:** `1.0.0`
The version of the Nexus smart account that will be used. Currently only `1.0.0` is supported.
#### factoryAddress (optional)
* **Type:** `Address`
The address of the nexus account factory that will be used to deploy the smart account.
:::info
The default factory address is `0x00000bb19a3579F4D779215dEf97AFbd0e30DB55`
:::
#### validatorAddress (optional)
* **Type:** `Address`
The address of the nexus validator that will be used to deploy the smart account.
:::info
The default validator address is `0x00000004171351c442B202678c48D8AB5B321E8f`
:::
#### attesters (optional)
* **Type:** `[Address]`
The addresses of the attesters that will be used when installing modules.
#### threshold (optional)
* **Type:** `number`
The minimum number of attesters that must validate a module for it to be valid.
#### entryPoint (optional)
* **Type:** `{ address: Address, version: "0.7" }`
The address and the version of the EntryPoint contract. Only 0.7 is supported.
#### index (optional)
* **Type:** `bigint`
It represents salt nonce that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
## toSafeSmartAccount
Creates a Safe smart account instance controlled by a `owners`.
### Usage
:::code-group
```ts [example.ts]
import { toSafeSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { owner } from "./owner"
import { entryPoint07Address } from "viem/account-abstraction"
const safeAccount = await toSafeSmartAccount({
client: publicClient,
owner,
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}, // global entrypoint
version: "1.4.1",
})
```
```ts [publicClient.ts]
// [!include ~/snippets/publicClient.ts:client]
```
```ts [owner.ts]
// [!include ~/snippets/owner.ts]
```
:::
### Returns
* **Type:** `SmartAccount`
The smart account instance.
### Parameters
#### client
* **Type:** `Client`
A public client as smart account needs access to the Network to query for information about its state (e.g. nonce, address, etc).
#### owners
* **Type:** `[LocalAccount | EIP1193Provider | WalletClient]`
The owners that will be used to sign messages and user operations.
#### safeVersion
* **Type:** `1.4.1`
The version of the Safe contract that will be used.
#### entryPoint (optional)
* **Type:** `{ address: Address, version: "0.6" | "0.7" }`
The address and the version of the EntryPoint contract. If not provided, entryPoint 0.7 will be used.
#### saltNonce (optional)
* **Type:** `bigint`
The salt nonce that will be used to deploy the smart account. If not provided, `0` will be used.
#### addModuleLibAddress (optional)
* **Type:** `Address`
The address of the `AddModuleLib` contract. If not provided, the default address for the Safe version will be used.
#### safe4337ModuleAddress (optional)
* **Type:** `Address`
The address of the `Safe4337Module` contract. If not provided, the default address for the Safe version will be used.
#### safeProxyFactoryAddress (optional)
* **Type:** `Address`
The address of the `SafeProxyFactory` contract. If not provided, the default address for the Safe version will be used.
#### safeSingletonAddress (optional)
* **Type:** `Address`
The address of the `SafeSingleton` contract. If not provided, the default address for the Safe version will be used.
#### multiSendAddress (optional)
* **Type:** `Address`
The address of the `MultiSend` contract. If not provided, the default address for the Safe version will be used.
#### multiSendCallOnlyAddress (optional)
* **Type:** `Address`
The address of the `MultiSendCallOnly` contract. If not provided, the default address for the Safe version will be used.
#### safeModules (optional)
* **Type:** `Address[]`
:::info
The ERC-4337 module will always be added and does not need to be provided.
:::
The addresses of the Safe modules that will be added during deployment.
#### setupTransactions (optional)
* **Type:** `Call[]`
:::info
This can be useful, for example, to approve tokens to an ERC-20 paymaster contract so it can pay for the gas of the first user operation.
:::
An array of calls that will be executed during the deployment of the smart account (when the `initCode` is executed).
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
#### validUnit (optional)
* **Type:** `number`
The timestamp until which the signature is valid by default.
#### paymentToken (optional)
* **Type:** `Address`
The address of the token that will be passed in the setup function of the Safe contract.
#### payment (optional)
* **Type:** `bigint`
The amount of the payment token that will be passed in the setup function of the Safe contract.
#### paymentReceiver (optional)
* **Type:** `Address`
The address that will be passed in the setup function of the Safe contract.
#### validators (optional)
* **Type:** `{ address: Address; context: Address }[]`
The default validators for the safe contract, these must be 7579 compliant and the safe MUST be initialised with the 7479 module.
#### executors (optional)
* **Type:** `{ address: Address; context: Address }[]`
The default executors for the safe contract, these must be 7579 compliant and the safe MUST be initialised with the 7479 module.
#### fallbacks (optional)
* **Type:** `{ address: Address; context: Address }[]`
The default fallback handlers for the safe contract, these must be 7579 compliant and the safe MUST be initialised with the 7479 module.
#### hooks (optional)
* **Type:** `{ address: Address; context: Address }[]`
The default hooks for the safe contract, these must be 7579 compliant and the safe MUST be initialised with the 7479 module.
#### attesters (optional)
* **Type:** `Address[]`
The attesters for the safe contract, these must be 7579 compliant and the safe MUST be initialised with the 7479 module.
#### attestersThreshold (optional)
* **Type:** `number`
The attesters threshold for the safe contract, these must be 7579 compliant and the safe MUST be initialised with the 7479 module.
## toSimpleSmartAccount
Creates a SimpleAccount instance controlled by a `owner`.
### Usage
:::code-group
```ts [example.ts]
import { toSimpleSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { owner } from "./owner"
const simpleSmartAccount = await toSimpleSmartAccount({
owner,
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
})
```
```ts [publicClient.ts]
// [!include ~/snippets/publicClient.ts:client]
```
```ts [owner.ts]
// [!include ~/snippets/owner.ts]
```
:::
### Returns
* **Type:** `SmartAccount`
The smart account instance.
### Parameters
#### client
* **Type:** `Client`
A public client as smart account needs access to the Network to query for information about its state (e.g. nonce, address, etc).
#### owner
* **Type:** `LocalAccount | EIP1193Provider | WalletClient`
The owner that will be used to sign messages and user operations.
#### factoryAddress (optional)
* **Type:** `Address`
The address of the simple account factory that will be used to deploy the smart account.
:::info
The canonical simple account factory for v0.6 of the EntryPoint is `0x9406Cc6185a346906296840746125a0E44976454`
:::
#### entryPoint (optional)
* **Type:** `{ address: Address, version: "0.6" | "0.7" }`
The address and the version of the EntryPoint contract. If not provided, entryPoint 0.7 will be used.
#### index (optional)
* **Type:** `bigint`
It represents salt nonce that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
## toTrustSmartAccount
Creates a Trust Wallet Smart Account instance controlled by a `owner`. Check out [this guide](/references/permissionless/how-to/accounts/use-trustwallet-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
import { toTrustSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { owner } from "./owner"
const account = await toTrustSmartAccount(client, {
client: publicClient,
owner
})
```
```ts [publicClient.ts]
// [!include ~/snippets/publicClient.ts:client]
```
```ts [owner.ts]
// [!include ~/snippets/owner.ts]
```
:::
### Returns
* **Type:** `SmartAccount`
The Trust smart account instance.
### Parameters
#### client
* **Type:** `Client`
A public client as smart account needs access to the Network to query for information about its state (e.g. nonce, address, etc).
#### owner
* **Type:** `LocalAccount | EIP1193Provider | WalletClient`
The owner that will be used to sign messages and user operations.
#### entryPoint (optional)
* **Type:** `{ address: Address, version: "0.6" | "0.7" }`
The address and the version of the EntryPoint contract. If not provided, entryPoint 0.7 will be used.
#### index (optional)
* **Type:** `bigint`
The index (which is basically a salt) that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
#### secp256k1VerificationFacetAddress (optional)
* **Type:** `Address`
The address of the `Secp256k1VerificationFacet` contract. If not provided, the default address for the Trust version will be used.
## Pimlico Client
A Pimlico Bundler Client is an interface to official ERC-4337 & ERC-7677 JSON-RPC API methods that include sending user operations, estimation user operation gas limits, getting user operation receipts, supporting paymaster rpc methods and more as well as the Pimlico-specific bundler methods [pimlico\_getUserOperationStatus](/references/bundler/endpoints/pimlico_getUserOperationStatus) and [pimlico\_getUserOperationGasPrice](/references/bundler/endpoints/pimlico_getUserOperationGasPrice).
To create a Pimlico Client, you can use the `createPimlicoClient` method.
### Import
```ts twoslash
import { createPimlicoClient } from 'permissionless/clients/pimlico'
```
### Usage
Initialize th client with your desired Chain (e.g. mainnet) and Transport (e.g. http) from viem. Pimlico client will by default have bundler actions, paymaster actions and custom pimlico actions.
```ts twoslash
import { http } from 'viem'
import { sepolia } from 'viem/chains'
import { createPimlicoClient } from 'permissionless/clients/pimlico'
import { entryPoint07Address } from "viem/account-abstraction"
const pimlicoClient = createPimlicoClient({
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE"),
entryPoint: {
address: entryPoint07Address,
version: "0.7",
}
})
```
Then you can consume Pimlico Bundler Actions:
```ts
const userOperationGasPrice = await pimlicoClient.getUserOperationGasPrice()
```
Alternatively, you can create a barebon client and extend pimlico specific actions:
```ts twoslash
import { http, createClient } from 'viem'
import { sepolia } from 'viem/chains'
import { pimlicoActions } from "permissionless/actions/pimlico";
import { bundlerActions, paymasterActions, entryPoint07Address } from "viem/account-abstraction";
const client = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE"),
})
.extend(bundlerActions)
.extend(paymasterActions)
.extend(
pimlicoActions({
entryPoint: {
address: entryPoint07Address,
version: "0.7",
}
})
)
```
## Smart Account Client
A Smart Account Client is an almost drop-in replacement for a standard viem [walletClient](https://viem.sh/docs/clients/wallet) but for managing smart accounts instead of EOA accounts. In addition, a Smart Account Client also contains, depending on the underlying smart account implementation used, a few extra actions that are specific to smart accounts.
To create a Smart Account Client, use the `createSmartAccountClient` function.
### Import
```ts twoslash
import { createSmartAccountClient } from 'permissionless'
```
### Usage
Initialize a Smart Account Client with your desired account (which can be created using a function like `toSimpleSmartAccount`), chain, bundler transport, and optionally a sponsorUserOperation middleware by using the `createSmartAccountClient` method:
```ts
import { http } from "viem"
import { sepolia } from "viem/chains"
import { createSmartAccountClient } from "permissionless"
import { simpleSmartAccount } from "./simpleSmartAccount"; // created elsewhere
import { pimlicoClient } from "./pimlicoClient"; // created elsewhere
const pimlicoBundlerUrl = `https://api.pimlico.io/v2/sepolia/rpc?apikey=`
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: sepolia,
bundlerTransport: http(pimlicoBundlerUrl),
paymaster: pimlicoClient, // optional
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast // only when using pimlico bundler
},
}
})
```
Then you can consume Smart Account Actions and access properties:
```ts
const smartAccountAddress = await smartAccountClient.account.address
```
## accountId
Gets the accountId of the smart account as defined in [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579). Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
Create a smart account client with one of the following accounts that support ERC-7579:
:::code-group
```ts [Kernel Account]
// [!include ~/snippets/erc7579/ecdsaKernelSmartAccount.ts]
```
```ts [Safe Account]
// [!include ~/snippets/erc7579/safeSmartAccount.ts]
```
```ts [Nexus Account]
// [!include ~/snippets/erc7579/biconomySmartAccount.ts]
```
:::
Use the `accountId` method to get the accountId of the smart account.
```ts
const accountId = await smartAccountClient.accountId()
console.log(accountId)
```
### Returns
* **Type:** `string`
The account id of the smart account.
### Parameters
#### account (optional)
* **Type:** `SmartAccount`
If your `SmartAccountClient` doesn't have an account, you should provide one here.
## installModule
Installs a [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) module to the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
Create a smart account client with one of the following accounts that support ERC-7579:
:::code-group
```ts [Kernel Account]
// [!include ~/snippets/erc7579/ecdsaKernelSmartAccount.ts]
```
```ts [Safe Account]
// [!include ~/snippets/erc7579/safeSmartAccount.ts]
```
```ts [Nexus Account]
// [!include ~/snippets/erc7579/biconomySmartAccount.ts]
```
:::
Use the `installModule` method to install a module to the smart account.
```ts
const ownableExecutorModule = "0x4Fd8d57b94966982B62e9588C27B4171B55E8354"
const moduleData = encodePacked(["address"], ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"])
const userOpHash = await smartAccountClient.installModule({
type: "executor",
address: ownableExecutorModule,
context: moduleData,
})
const receipt = await smartAccountClient.waitForUserOperationReceipt({ hash: userOpHash })
```
### Returns
* **Type:** `hash`
The user operation hash.
:::warning
This is a user operation hash, not a transaction hash, you must use `waitForUserOperationReceipt` to wait for the user operation to be included onchain.
:::
### Parameters
#### address
* **Type:** `Address`
Address of the module to install.
#### type
* **Type:** `ModuleType`
Type of the module to install. Accepted values are `"validator" | "executor" | "fallback" | "hook"`.
#### context
* **Type:** `Hex`
Context bytes that will be passed to the module as part of `initData`.
#### maxFeePerGas (optional)
* **Type:** `bigint`
The maximum fee per gas that the user is willing to pay for this user operation. If not provided, the bundler will use its own recommendation.
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
The maximum priority fee per gas that the user is willing to pay for this user operation. If not provided, the bundler will use its own recommendation.
#### nonce (optional)
* **Type:** `bigint`
The nonce of the smart account that will be used to send this user operatino. If not provided, current nonce will be used.
## isModuleInstalled
Checks if an [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) module is installed on the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
Create a smart account client with one of the following accounts that support ERC-7579:
:::code-group
```ts [Kernel Account]
// [!include ~/snippets/erc7579/ecdsaKernelSmartAccount.ts]
```
```ts [Safe Account]
// [!include ~/snippets/erc7579/safeSmartAccount.ts]
```
```ts [Nexus Account]
// [!include ~/snippets/erc7579/biconomySmartAccount.ts]
```
:::
Use the `isModuleInstalled` method to check if a module is installed on the smart account.
```ts
const ownableExecutorModule = "0x4Fd8d57b94966982B62e9588C27B4171B55E8354"
const moduleData = encodePacked(["address"], ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"])
const isInstalled = await smartAccountClient.isModuleInstalled({
type: "executor",
address: ownableExecutorModule,
context: moduleData,
})
```
### Returns
* **Type:** `boolean`
True if the module is installed, false otherwise.
### Parameters
#### address
* **Type:** `Address`
Address of the module to install.
#### type
* **Type:** `ModuleType`
Type of the module to install. Accepted values are `"validator" | "executor" | "fallback" | "hook"`.
#### context
* **Type:** `Hex`
Context bytes that will be passed to the module as part of `additionalContext`.
## supportsExecutionMode
Checks if a [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) execution mode is supported on the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
:::info
According to the specification, not all execution modes must be supported. So it is recommended to use this function to check if the execution mode is supported before using it.
:::
### Usage
Create a smart account client with one of the following accounts that support ERC-7579:
:::code-group
```ts [Kernel Account]
// [!include ~/snippets/erc7579/ecdsaKernelSmartAccount.ts]
```
```ts [Safe Account]
// [!include ~/snippets/erc7579/safeSmartAccount.ts]
```
```ts [Nexus Account]
// [!include ~/snippets/erc7579/biconomySmartAccount.ts]
```
:::
Use the `supportsExecutionMode` method to check if an execution mode is supported on the smart account.
```ts
const isExecutionModeSupported = await smartAccountClient.supportsExecutionMode({
type: "delegatecall",
revertOnError: true,
selector: "0x",
context: "0x",
})
```
### Returns
* **Type:** `boolean`
True if the execution mode is supported, false otherwise.
### Parameters
#### type
* **Type:** `CallType`
Type of the call. Accepted values are `"call" | "delegatecall" | "staticcall"`.
#### revertOnError (optional)
* **Type:** `boolean`
If true, the execution will revert if the call fails. Defaults to false.
#### selector (optional)
* **Type:** `Hex`
The selector of the function to call. If not provided, `0x` will be used.
#### context (optional)
* **Type:** `Hex`
Context bytes that will be passed to the module as part of `modeContext`.
## supportsModule
Checks if a [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) module type is supported by the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
Create a smart account client with one of the following accounts that support ERC-7579:
:::code-group
```ts [Kernel Account]
// [!include ~/snippets/erc7579/ecdsaKernelSmartAccount.ts]
```
```ts [Safe Account]
// [!include ~/snippets/erc7579/safeSmartAccount.ts]
```
```ts [Nexus Account]
// [!include ~/snippets/erc7579/biconomySmartAccount.ts]
```
:::
Use the `supportsModule` method to check if a module type is supported on the smart account.
```ts
const isModuleSupported = await smartAccountClient.supportsModule({
type: "fallback",
})
```
### Returns
* **Type:** `boolean`
True if the module type is supported, false otherwise.
### Parameters
#### type
* **Type:** `ModuleType`
Type of the module to check. Accepted values are `"validator" | "executor" | "fallback" | "hook"`.
## uninstallModule
Uninstalls a [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) module from the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
Create a smart account client with one of the following accounts that support ERC-7579:
:::code-group
```ts [Kernel Account]
// [!include ~/snippets/erc7579/ecdsaKernelSmartAccount.ts]
```
```ts [Safe Account]
// [!include ~/snippets/erc7579/safeSmartAccount.ts]
```
```ts [Nexus Account]
// [!include ~/snippets/erc7579/biconomySmartAccount.ts]
```
:::
Use the `uninstallModule` method to uninstall a module from the smart account.
```ts
const ownableExecutorModule = "0x4Fd8d57b94966982B62e9588C27B4171B55E8354"
const moduleData = encodePacked(["address"], ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"])
const userOpHash = await smartAccountClient.uninstallModule({
type: "executor",
address: ownableExecutorModule,
context: moduleData,
})
const receipt = await smartAccountClient.waitForUserOperationReceipt({ hash: userOpHash })
```
### Returns
* **Type:** `hash`
The user operation hash.
:::warning
This is a user operation hash, not a transaction hash, you must use `waitForUserOperationReceipt` to wait for the user operation to be included onchain.
:::
### Parameters
#### address
* **Type:** `Address`
Address of the module to uninstall.
#### type
* **Type:** `ModuleType`
Type of the module to uninstall. Accepted values are `"validator" | "executor" | "fallback" | "hook"`.
#### context
* **Type:** `Hex`
Context bytes that will be passed to the module as part of `deInitData`.
#### maxFeePerGas (optional)
* **Type:** `bigint`
The maximum fee per gas that the user is willing to pay for this user operation. If not provided, the bundler will use its own recommendation.
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
The maximum priority fee per gas that the user is willing to pay for this user operation. If not provided, the bundler will use its own recommendation.
#### nonce (optional)
* **Type:** `bigint`
The nonce of the smart account that will be used to send this user operation. If not provided, current nonce will be used.
## getPaymasterData
Retrieves paymaster-related User Operation properties to be used for sending the User Operation from Pimlico.
Internally uses [ERC-7677's `pm_getPaymasterData` method](https://github.com/ethereum/ERCs/blob/master/ERCS/erc-7677.md#pm_getpaymasterdata).
### Usage
:::code-group
```ts twoslash [example.ts]
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
```ts twoslash [config.ts] filename="config.ts"
import { http } from 'viem'
import { createPaymasterClient } from 'viem/account-abstraction'
export const paymasterClient = createPaymasterClient({
transport: http('https://public.pimlico.io/v2/11155111/rpc'),
})
```
:::
### Returns
```ts
{
paymaster: Address
paymasterData: Hex
paymasterVerificationGasLimit: bigint
paymasterPostOpGasLimit: bigint
}
```
Paymasted-related User Operation properties.
### Parameters
#### callData
* **Type:** `Hex`
The data to pass to the `sender` during the main execution call.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000', // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### callGasLimit (optional)
* **Type:** `bigint`
The amount of gas to allocate the main execution call.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n, // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### chainId
* **Type:** `number`
Chain ID to target.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
chainId: 1, // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### context (optional)
* **Type:** `unknown`
Paymaster specific fields.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
context: { // [!code focus]
sponsorshipPolicyId: 'abc123', // [!code focus]
}, // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### entryPointAddress
* **Type:** `Address`
EntryPoint address to target.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
chainId: 1,
entryPointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### factory (optional)
* **Type:** `Address`
Account Factory address.
:::warning
This property should only be populated when the Smart Account has not been deployed yet.
:::
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e', // [!code focus]
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### factoryData (optional)
* **Type:** `Hex`
Call data to execute on the Account Factory to deploy a Smart Account.
:::warning
This property should only be populated when the Smart Account has not been deployed yet.
:::
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000', // [!code focus]
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### maxFeePerGas (optional)
* **Type:** `bigint`
Maximum fee per gas for User Operation execution.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n, // [!code focus]
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Maximum priority fee per gas for User Operation execution.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n, // [!code focus]
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### nonce
* **Type:** `bigint`
Nonce for the User Operation.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n, // [!code focus]
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### preVerificationGas (optional)
* **Type:** `bigint`
Extra gas to pay the Bunder.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
preVerificationGas: 69420n, // [!code focus]
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### sender
* **Type:** `Address`
Sender for the User Operation.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
preVerificationGas: 69420n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f', // [!code focus]
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### signature
* **Type:** `Hex`
Signature for the User Operation.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
preVerificationGas: 69420n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c' // [!code focus]
})
```
#### verificationGasLimit (optional)
* **Type:** `bigint`
The amount of gas to allocate for the verification step.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c',
verificationGasLimit: 69420n, // [!code focus]
})
```
## getPaymasterStubData
Retrieves paymaster-related User Operation properties to be used for User Operation gas estimation from Pimlico.
Internally uses [ERC-7677's `pm_getPaymasterStubData` method](https://github.com/ethereum/ERCs/blob/master/ERCS/erc-7677.md#pm_getpaymasterstubdata).
### Usage
:::code-group
```ts twoslash [example.ts]
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
```ts twoslash [config.ts] filename="config.ts"
import { http } from 'viem'
import { createPaymasterClient } from 'viem/account-abstraction'
export const paymasterClient = createPaymasterClient({
transport: http('https://public.pimlico.io/v2/11155111/rpc'),
})
```
:::
### Returns
```ts
{
isFinal: boolean
paymaster: Address
paymasterData: Hex
paymasterVerificationGasLimit: bigint
paymasterPostOpGasLimit: bigint
sponsor: { name: string; icon: string }
}
```
Paymasted-related User Operation properties.
### Parameters
#### callData
* **Type:** `Hex`
The data to pass to the `sender` during the main execution call.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000', // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### callGasLimit (optional)
* **Type:** `bigint`
The amount of gas to allocate the main execution call.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n, // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### chainId
* **Type:** `number`
Chain ID to target.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
chainId: 1, // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### context (optional)
* **Type:** `unknown`
Paymaster specific fields.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
context: { // [!code focus]
sponsorshipPolicyId: 'abc123', // [!code focus]
}, // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### entryPointAddress
* **Type:** `Address`
EntryPoint address to target.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
chainId: 1,
entryPointAddress: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', // [!code focus]
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### factory (optional)
* **Type:** `Address`
Account Factory address.
:::warning
This property should only be populated when the Smart Account has not been deployed yet.
:::
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e', // [!code focus]
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### factoryData (optional)
* **Type:** `Hex`
Call data to execute on the Account Factory to deploy a Smart Account.
:::warning
This property should only be populated when the Smart Account has not been deployed yet.
:::
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000', // [!code focus]
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### maxFeePerGas (optional)
* **Type:** `bigint`
Maximum fee per gas for User Operation execution.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n, // [!code focus]
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Maximum priority fee per gas for User Operation execution.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n, // [!code focus]
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### nonce
* **Type:** `bigint`
Nonce for the User Operation.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n, // [!code focus]
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### preVerificationGas (optional)
* **Type:** `bigint`
Extra gas to pay the Bunder.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
preVerificationGas: 69420n, // [!code focus]
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### sender
* **Type:** `Address`
Sender for the User Operation.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
preVerificationGas: 69420n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f', // [!code focus]
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c'
})
```
#### signature
* **Type:** `Hex`
Signature for the User Operation.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
preVerificationGas: 69420n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c' // [!code focus]
})
```
#### verificationGasLimit (optional)
* **Type:** `bigint`
The amount of gas to allocate for the verification step.
```ts twoslash
import { paymasterClient } from './config'
const paymasterArgs = await paymasterClient.getPaymasterStubData({
callData: '0xb61d27f600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000',
callGasLimit: 69420n,
factory: '0xfb6dab6200b8958c2655c3747708f82243d3f32e',
factoryData: '0xf14ddffc000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000',
maxFeePerGas: 14510554812n,
maxPriorityFeePerGas: 2000000000n,
nonce: 0n,
sender: '0xE911628bF8428C23f179a07b081325cAe376DE1f',
signature: '0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c',
verificationGasLimit: 69420n, // [!code focus]
})
```
## getUserOperationGasPrice
Return the gas price that Pimlico's bundler will accept for the User Operation.
### Usage
:::code-group
```ts [example.ts]
import { pimlicoClient } from "./client"
const smartAccountClient = createSmartAccountClient({
account,
chain: sepolia,
bundlerTransport: http(pimlicoUrl),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
},
}
})
/* other gas alternatives */
const gasPriceResult = await pimlicoClient.getUserOperationGasPrice()
/**
* {
"slow": {
"maxFeePerGas": 3267781404n,
"maxPriorityFeePerGas": 3267781404n
},
"standard": {
"maxFeePerGas": 3439769899n,
"maxPriorityFeePerGas": 3439769899n
},
"fast": {
"maxFeePerGas": 3620810421n,
"maxPriorityFeePerGas": 3620810421n
}
}
*/
```
```ts [client.ts]
// [!include ~/snippets/pimlicoClient.ts:client]
```
:::
### Returns
* **Type:** `GetUserOperationGasPriceReturnType`
The `maxFeePerGas` and `maxPriorityFeePerGas` values for the different gas price tiers. Choosing a faster gas price will mean faster inclusion by the Pimlico Bundler.
### JSON-RPC Method
[`pimlico_getUserOperationGasPrice`](/references/bundler/endpoints/pimlico_getUserOperationGasPrice)
## getUserOperationStatus
Return a status of the User Operation as well as potentially the hash of the transaction being used to bundle the User Operation.
### Usage
:::code-group
```ts [example.ts]
import { pimlicoClient } from "./client"
const getStatusResult = await pimlicoClient.getUserOperationStatus({
hash: "0x3c037f957fde5d87e35d5b8582f6c274343bcf3bc0e010d72fc2de0e27f4a6aa"
})
/**
* {
* "status": "included",
* "transactionHash": "0x975a6bac5f562a2cb8218945b4e4304f6d10afa4aa6f02830e6d6dcafc450d66"
* }
*/
```
```ts [client.ts]
// [!include ~/snippets/pimlicoClient.ts:client]
```
:::
### Returns
* **Type:** `GetUserOperationStatusReturnType`
The status of the User Operation ("not\_found" | "not\_submitted" | "submitted" | "rejected" | "reverted" | "included" | "failed" | "queued") as well as the hash of the transaction being used to bundle the User Operation. If the hash is not known (for instance because the User Operation has not been submitted yet), the hash will be `null`.
### Parameters
#### hash
* **Type:** `Hash`
The hash of the User Operation.
### JSON-RPC Method
[`pimlico_getUserOperationStatus`](/references/bundler/endpoints/pimlico_getUserOperationStatus)
## validateSponsorshipPolicies
Validates a User Operation against an array of [sponsorship policies](https://dashboard.pimlico.io/sponsorship-policies), and returns an array of sponsorship policies (alongside additional data for each policy) that are willing to sponsor the user operation.
### Usage
:::code-group
```ts [example.ts]
import { pimlicoPaymasterClient } from "./client"
const sponsorResult = await pimlicoPaymasterClient.validateSponsorshipPolicies({
userOperation: {
sender: "0x0C123D90Da0a640fFE54a2359D159629065775C5",
nonce: 3n,
initCode: "0x",
callData: "0x18dfb3c7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c09000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c090000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000043a4eacb7839f202d9cab465dbdd77d4fabe0a1800000000000000000000000000000000000000000000000003782dace9d90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000982e148216e3aa6b38f9d901ef578b5c06dd750200000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 113000000n,
maxPriorityFeePerGas: 113000100n,
signature: "0xf1513a8537a079a4d728bb87099b2c901e2c9034e60c95a4d41ac1ed75d6ee90270d52b48af30aa036e9a205ea008e1c62b317e7b3f88b3f302d45fb1ba76a191b"
},
sponsorshipPolicyIds: ["sp_crazy_kangaroo", "sp_malevolent_badger"]
})
/**
* [
* {
* "sponsorshipPolicyId": "sp_crazy_kangaroo",
* "data": {
* "name": "Linea Christmas Week",
* "author": "Linea",
* "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
* "description": "Linea is sponsoring the first 10 transactions for existing users between Christmas and New Year's Eve.",
* }
* }
* ]
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { pimlicoActions } from 'permissionless/actions/pimlico'
import { entryPoint07Address } from "viem/account-abstraction"
export const pimlicoPaymasterClient = createClient({
chain: mainnet,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(pimlicoActions({
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
}))
```
:::
### Returns
* **Type:** `ValidateSponsorshipPoliciesReturnType`
An array of sponsorship policy IDs and (optionally) extra data to be used by front-ends for sponsorship policies that are willing to sponsor the user operation.
```json
{
sponsorshipPolicyId: string
data: {
name: string | null
author: string | null
icon: string | null
description: string | null
}
}[]
```
#### name (optional)
* **Type:** `string | null`
The name of the sponsorship policy.
#### author (optional)
* **Type:** `string | null`
The author of the sponsorship policy.
#### icon (optional)
* **Type:** `string | null`
The icon of the sponsorship policy. The icon must be a data URI as defined in RFC-2397.
#### description (optional)
* **Type:** `string | null`
The description of the sponsorship policy.
### Parameters
#### userOperation
* **Type:** `UserOperation`
The User Operation object.
#### sponsorshipPolicyIds
* **Type:** `string[]`
An array of sponsorship policy IDs to validate against.
### JSON-RPC Method
[`pm_validateSponsorshipPolicies`](/references/paymaster/verifying-paymaster/endpoints)
## getAccountNonce
Returns the current nonce of the smart account for a specified key.
### Usage
:::code-group
```ts [example.ts]
import { publicClient } from "./client.ts"
import { getAccountNonce } from "permissionless"
import { entryPoint07Address } from "viem/account-abstraction"
const nonce = await getAccountNonce(publicClient, {
address: "0x277F6C1D8d4faFA3d8DcC837489cd69d86c682BA",
entryPointAddress: entryPoint07Address,
key: 0n // optional
})
// 23n
```
```ts [client.ts]
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
export const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://mainnet.infura.io/v3/...")
})
```
:::
### Returns
* **Type:** `bigint`
The current nonce of the smart account for the specified key (both the key and the sequence concatenated together into one uint256).
### Parameters
#### address
* **Type:** `Address`
The address of the smart account.
#### entryPoint
* **Type:** `Address`
The entry point address.
#### key (optional)
* **Type:** `bigint`
The key of the nonce. Defaults to 0n.
Instead of sequential nonce, ERC-4337 implements a nonce mechanism that uses a single uint256 nonce value in the UserOperation, but treats it as two values, a 192-bit “key”, and a 64-bit “sequence”. These values are represented on-chain in the EntryPoint contract. For each key the sequence is validated and incremented sequentially and monotonically by the EntryPoint for each UserOperation, however a new key can be introduced with an arbitrary value at any point.
## getSenderAddress
Return a sender address corresponding to the initCode of the User Operation where the smart account will be deployed.
### Usage
:::code-group
```ts [example.ts]
import { publicClient } from "./client.ts"
import { getSenderAddress } from "permissionless"
const senderAddress = await getSenderAddress(publicClient, {
factory: "0x91E60e0613810449d098b0b5Ec8b51A0FE8c8985",
factoryData: "0x5fbfb9cf000000000000000000000000cafb211a4ea1290370d43732fed4da817a2e11ed0000000000000000000000000000000000000000000000000000000000000000"
entryPointAddress: entryPoint07Address
})
// "0x0C123D90Da0a640fFE54a2359D159629065775C5"
```
```ts [client.ts]
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
export const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://mainnet.infura.io/v3/...")
})
```
:::
### Returns
* **Type:** `Address`
The sender address corresponding to the initCode of the User Operation where the smart account will be deployed.
### Parameters
#### initCode
* **Type:** `Hex`
The initCode of the User Operation that deploys the smart account.
#### entryPoint
* **Type:** `Address`
The entry point address.
## estimateUserOperationGas
`SmartAccountClient` extends viem's `BundlerClient` and so extends the `estimateUserOperationGas` method.
You can view the full list of options for `estimateUserOperationGas` [in viem's documentation](https://viem.sh/account-abstraction/actions/bundler/estimateUserOperationGas).
## getChainId
`SmartAccountClient` extends viem's `BundlerClient` and so extends the `getChainId` method.
You can view the full list of options for `getChainId` [in viem's documentation](https://viem.sh/account-abstraction/actions/bundler/getChainId).
## getSupportedEntryPoints
`SmartAccountClient` extends viem's `BundlerClient` and so extends the `getSupportedEntryPoints` method.
You can view the full list of options for `getSupportedEntryPoints` [in viem's documentation](https://viem.sh/account-abstraction/actions/bundler/getSupportedEntryPoints).
## getUserOperation
`SmartAccountClient` extends viem's `BundlerClient` and so extends the `getUserOperation` method.
You can view the full list of options for `getUserOperation` [in viem's documentation](https://viem.sh/account-abstraction/actions/bundler/getUserOperation).
## getUserOperationReceipt
`SmartAccountClient` extends viem's `BundlerClient` and so extends the `getUserOperationReceipt` method.
You can view the full list of options for `getUserOperationReceipt` [in viem's documentation](https://viem.sh/account-abstraction/actions/bundler/getUserOperationReceipt).
## prepareUserOperation
`SmartAccountClient` extends viem's `BundlerClient` and so extends the `prepareUserOperation` method.
You can view the full list of options for `prepareUserOperation` [in viem's documentation](https://viem.sh/account-abstraction/actions/bundler/prepareUserOperation).
## sendTransaction
Send a transaction from a smart account using the same `sendTransaction` interface as viem.
Internally, sendTransaction calls the smartAccount's `prepareUserOperation`, `sendUserOperation`, and `waitForUserOperationReceipt` actions.
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient, simpleSmartAccount } from "./smartAccountClient"
import { parseAbiItem, encodeFunctionData } from "viem"
const hash = await smartAccountClient.sendTransaction({
to: "0x0488bEE1Ec682db0F0E74AB52faFdDdEf10Af123",
data: encodeFunctionData({
abi: [parseAbiItem('function mint()')]
}),
value: 0n
})
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/smartAccountClient.ts:client]
```
:::
You could also use the `sendTransaction` method to send multiple transactions in a single batch like so:
```ts
const hash = await smartAccountClient.sendTransaction({
calls: [
{
to: "0x0488bEE1Ec682db0F0E74AB52faFdDdEf10Af123",
data: encodeFunctionData({
abi: [parseAbiItem('function mint()')]
}),
value: 0n
},
{
to: "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc",
abi: [{"inputs":[],"name":"getLastGreeter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"greet","outputs":[],"stateMutability":"nonpayable","type":"function"}],
functionName: "greet",
args: [],
}
]
})
```
### Returns
* **Type:** `Hash`
The transaction hash of the mined userOperation
### Parameters
#### account
* **Type:** `SmartAccount`
The Account to use for User Operation execution.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account, // [!code focus]
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}]
})
```
#### calls
* **Type:** `({ data?: Hex | undefined, to: Address, value?: bigint | undefined } | { abi: Abi, functionName: string, args: unknown[], to: Address, value?: bigint | undefined })[]`
The calls to execute in the User Operation.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{ // [!code focus]
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', // [!code focus]
value: parseEther('1') // [!code focus]
}, { // [!code focus]
abi: wagmiAbi, // [!code focus]
functionName: 'mint', // [!code focus]
to: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', // [!code focus]
}] // [!code focus]
})
```
:::tip
You can also pass raw call data via the `callData` property:
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
callData: '0xdeadbeef', // [!code focus]
})
```
:::
#### callGasLimit (optional)
* **Type:** `bigint`
The amount of gas to allocate the main execution call.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
callGasLimit: 69420n, // [!code focus]
})
```
#### factory (optional)
* **Type:** `Address`
Account Factory address.
:::warning
This property should only be populated when the Smart Account has not been deployed yet.
:::
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
factory: '0x1234567890123456789012345678901234567890', // [!code focus]
factoryData: '0xdeadbeef',
})
```
#### factoryData (optional)
* **Type:** `Hex`
Call data to execute on the Account Factory to deploy a Smart Account.
:::warning
This property should only be populated when the Smart Account has not been deployed yet.
:::
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
factory: '0x1234567890123456789012345678901234567890',
factoryData: '0xdeadbeef', // [!code focus]
})
```
#### maxFeePerGas (optional)
* **Type:** `bigint`
Maximum fee per gas for User Operation execution.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
maxFeePerGas: 420n, // [!code focus]
})
```
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Maximum priority fee per gas for User Operation execution.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
maxPriorityFeePerGas: 420n,
maxFeePerGas: 10n, // [!code focus]
})
```
#### nonce (optional)
* **Type:** `bigint`
Nonce for the User Operation.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
nonce: 10n, // [!code focus]
})
```
#### paymaster (optional)
* **Type:** `Address | true | PaymasterClient | PaymasterActions`
Sets Paymaster configuration for the User Operation.
* If `paymaster: Address`, it will use the provided Paymaster contract address for sponsorship.
* If `paymaster: PaymasterClient`, it will use the provided Paymaster Client eg [Pimlico Client](/references/permissionless/reference/clients/pimlicoClient) for sponsorship.
* If `paymaster: true`, it will be assumed that the Bundler Client also supports Paymaster RPC methods (e.g. `pm_getPaymasterData`), and use them for sponsorship.
##### Using a Paymaster Contract Address
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB', // [!code focus]
paymasterData: '0xdeadbeef',
})
```
##### Using a Paymaster Client
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const paymasterClient = createPaymasterClient({ // [!code focus]
transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}') // [!code focus]
}) // [!code focus]
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: paymasterClient, // [!code focus]
})
```
##### Using the Bundler Client as Paymaster
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: true, // [!code focus]
})
```
#### paymasterContext (optional)
* **Type:** `unknown`
Paymaster specific fields.
:::warning
This property is only available if **`paymaster` is a Paymaster Client**.
:::
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const paymasterClient = createPaymasterClient({
transport: http('https://api.pimlico.io/v2/1/rpc?apikey={API_KEY}')
})
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: paymasterClient,
paymasterContext: { // [!code focus]
policyId: 'abc123' // [!code focus]
}, // [!code focus]
})
```
#### paymasterData (optional)
* **Type:** `Address`
Call data to execute on the Paymaster contract.
:::warning
This property is only available if **`paymaster` is an address**.
:::
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef', // [!code focus]
})
```
#### paymasterPostOpGasLimit (optional)
* **Type:** `bigint`
The amount of gas to allocate for the Paymaster post-operation code.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef',
paymasterPostOpGasLimit: 69420n, // [!code focus]
})
```
#### paymasterVerificationGasLimit (optional)
* **Type:** `bigint`
The amount of gas to allocate for the Paymaster validation code.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
paymaster: '0x942fD5017c0F60575930D8574Eaca13BEcD6e1bB',
paymasterData: '0xdeadbeef',
paymasterVerificationGasLimit: 69420n, // [!code focus]
})
```
#### preVerificationGas (optional)
* **Type:** `bigint`
Extra gas to pay the Bunder.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
preVerificationGas: 69420n, // [!code focus]
})
```
#### signature (optional)
* **Type:** `Hex`
Signature for the User Operation.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
signature: '0x...', // [!code focus]
})
```
#### verificationGasLimit (optional)
* **Type:** `bigint`
The amount of gas to allocate for the verification step.
```ts
import { account, smartAccountClient } from './config'
import { parseEther } from 'viem'
const hash = await smartAccountClient.sendTransaction({
account,
calls: [{
to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8',
value: parseEther('1')
}],
verificationGasLimit: 69420n, // [!code focus]
})
```
#### to (optional)
* **Type:** `0x${string}`
The transaction recipient or contract address.
#### data (optional)
* **Type:** `0x${string}`
A contract hashed method call with encoded args.
#### value (optional)
* **Type:** `bigint`
Value in wei sent with this transaction.
#### maxFeePerGas (optional)
* **Type:** `bigint`
Total fee per gas (in wei), inclusive of `maxPriorityFeePerGas`. Only applies to EIP-1559 Transactions.
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Max priority fee per gas (in wei). Only applies to EIP-1559 Transactions.
#### nonce (optional)
* **Type:** `number`
Unique number identifying this transaction.
#### account (optional)
* **Type:** `SmartAccount`
The Account to send the transaction from.
## sendUserOperation
`SmartAccountClient` extends viem's `BundlerClient` and so extends the `sendUserOperation` method.
You can view the full list of options for `sendUserOperation` [in viem's documentation](https://viem.sh/account-abstraction/actions/bundler/sendUserOperation).
## waitForUserOperationReceipt
`SmartAccountClient` extends viem's `BundlerClient` and so extends the `waitForUserOperationReceipt` method.
You can view the full list of options for `waitForUserOperationReceipt` [in viem's documentation](https://viem.sh/account-abstraction/actions/bundler/waitForUserOperationReceipt).
## writeContract
Uses a smart account to executes a write function on a contract.
A "write" function on a Solidity contract modifies the state of the blockchain.
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient } from "./smartAccountClient"
import { simpleAbi } from './abi'
const hash = await smartAccountClient.writeContract({
address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
abi: simpleAbi,
functionName: 'mint',
})
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/smartAccountClient.ts:client]
```
```ts [abi.ts]
export const simpleAbi = [
...
{
inputs: [],
name: "mint",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
...
] as const;
```
:::
### Return Value
* **Type:** `Hash`
The transaction hash of the associated write operation
### Parameters
#### address
* **Type:** `Address`
The contract address.
#### abi
* **Type:** `Abi`
The contract's ABI.
#### functionName
* **Type:** `string`
A function to extract from the ABI.
#### args (optional)
* **Type:** Inferred from ABI.
Arguments to pass to function call.
#### gas (optional)
* **Type:** `bigint`
The gas limit for the transaction. Note that passing a gas limit also skips the gas estimation step.
#### nonce (optional)
* **Type:** `number`
Unique number identifying this transaction.
#### value (optional)
* **Type:** `number`
Value in wei sent with this transaction.
#### gasPrice (optional)
* **Type:** `bigint`
The price (in wei) to pay per gas. Only applies to Legacy Transactions
#### maxFeePerGas (optional)
* **Type:** `bigint`
Total fee per gas (in wei), inclusive of `maxPriorityFeePerGas`. Only applies to EIP-1559 Transactions
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Max priority fee per gas (in wei). Only applies to EIP-1559 Transactions
#### dataSuffix (optional)
* **Type:** `Hex`
Data to append to the end of the calldata.
#### account (optional)
* **Type:** `SmartAccount`
The Account to write to the contract from.
## getRequiredPrefund
Returns the minimum required funds in wei in the senders's smart account to execute the user operation.
### Import
```ts
import { getRequiredPrefund } from "permissionless"
```
### Usage
```ts
import { getRequiredPrefund } from "permissionless"
const requiredPrefund = getRequiredPrefund({
userOperation
})
// 354000000000000n
```
### Returns
`BigInt`
The requied prefund in wei.
### Parameters
#### userOperation
* **Type:** `UserOperation`
The User Operation to get the prefund for.
import VersionWarning from "../../VersionWarning"
## Update your existing app to sponsor gas fees for smart account users
If you are looking to sponsor gas fees for your users without embedding a wallet in your app, you can use this guide to update your existing app to sponsor gas fees for smart accounts.
### Steps
:::steps
#### Spin up an ERC-7677 proxy server
Follow the deployment guide on the [ERC-7677 Proxy Server](https://github.com/pimlicolabs/erc7677-proxy) repository readme to deploy an ERC-7677 proxy server.
#### Update your app to sponsor gas fees
Follow Step 3 on the [erc7677.xyz guide](https://www.erc7677.xyz/guides/sponsoring-transactions#3-send-eip-5792-requests-with-a-paymaster-service-capability) to update your app to support ERC-5792 requests and enable the paymaster service capability feature, using the public URL you created for the ERC-7677 proxy in the previous step.
:::
import VersionWarning from "../../VersionWarning"
## Error Handling
Similarly to how [error handling in viem works](https://viem.sh/docs/error-handling), every module in permissionless.js exports an accompanying error type which you can use to strongly type your `catch` statements.
These types come in the form of `ErrorType`. For example, the `estimateUserOperationGas` action exports a `EstimateUserOperationGasErrorType` type.
Unfortunately, [TypeScript doesn't have an abstraction for typed exceptions](https://github.com/microsoft/TypeScript/issues/13219), so the most pragmatic & vanilla approach would be to explicitly cast error types in the `catch` statement.
:::code-group
```ts [example.ts] twoslash
// @noErrors
// @filename: client.ts
import { createBundlerClient } from 'permissionless'
import { http } from "viem"
import { mainnet } from 'viem/chains'
export const bundlerClient = createBundlerClient({
chain: mainnet,
transport: http(),
entryPoint: ENTRYPOINT_ADDRESS_V06,
})
// @filename: index.ts
// ---cut---
import {
type EstimateUserOperationGasErrorType,
type SenderAlreadyDeployedError,
ENTRYPOINT_ADDRESS_V07
} from 'permissionless'
import { bundlerClient } from './client'
try {
const estimateResult = await bundlerClient.estimateUserOperationGas({
userOperation,
})
} catch (e) {
const estimationError = e as EstimateUserOperationGasErrorType<"v0.7">
estimationError.name
// ^?
const error = estimationError.walk(
(e) => e instanceof SenderAlreadyDeployedError,
)
if (error instanceof SenderAlreadyDeployedError) {
error.name
// ^?
}
}
```
```ts [client.ts]
import { createBundlerClient } from 'permissionless'
import { http } from "viem"
import { mainnet } from 'viem/chains'
export const bundlerClient = createBundlerClient({
chain: mainnet,
transport: http(),
entryPoint: ENTRYPOINT_ADDRESS_V06,
})
```
:::
import VersionWarning from "../../VersionWarning"
## Local Testing
This guide introduces a ready-to-use mock test environment, that contains:
* A local Alto bundler
* A mock verifying paymaster
* An Anvil node
* ERC-4337 related contracts, including the EntryPoint and account factories for all major ERC-4337 account implementations.
The test environment is orchestrated using **docker compose**. Where the docker containers are pulled from [this repo](https://github.com/pimlicolabs/mock-aa-environment).
The mock environment is designed to mimic mainnet as closely as possible by building with the latest Alto version and by deploying all contracts (entrypoints, paymasters, smart account factories, etc.)
This has advantages over testing against a production testnet as you have more control over the testing environment and can make use of features like anvil cheatcodes.
### Steps
::::steps
#### Setup
To get started, create a **docker-compose.yaml** file at the root of your test directory with the following contents.
```yaml [docker-compose.yaml]
services:
anvil:
image: ghcr.io/foundry-rs/foundry:nightly-f6208d8db68f9acbe4ff8cd76958309efb61ea0b
ports: ["8545:8545"]
entrypoint:
["anvil", "--host", "0.0.0.0", "--block-time", "1", "--silent"]
platform: linux/amd64/v8
mock-paymaster:
image: ghcr.io/pimlicolabs/mock-verifying-paymaster:main
ports: ["3000:3000"]
environment:
- ALTO_RPC=http://alto:4337
- ANVIL_RPC=http://anvil:8545
alto:
image: ghcr.io/pimlicolabs/mock-alto-bundler:main
ports: ["4337:4337"]
environment:
- ANVIL_RPC=http://anvil:8545
```
To start the test environment, run
```sh
docker compose up
```
Once docker has started, the following services can be accessed locally through the following endpoints:
* `Anvil` at **localhost:8545**
* `Alto Bundler` at **localhost:4337**
* `Mock Paymaster` at **localhost:3000**
You can now use permissionless like you normally would but instead of referencing the live endpoints, use the local endpoints mentioned above when creating the clients.
```ts [clients.ts]
import { createBundlerClient, ENTRYPOINT_ADDRESS_V07 } from "permissionless";
import { createPimlicoPaymasterClient } from "permissionless/clients/pimlico";
import { http, createPublicClient } from "viem";
import { foundry } from "viem/chains";
const publicClient = createPublicClient({
transport: http("http://localhost:8545"), // [!code ++]
});
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"), // [!code ++]
entryPoint: ENTRYPOINT_ADDRESS_V07,
});
const paymasterClient = createPimlicoPaymasterClient({
chain: foundry,
transport: http("http://localhost:3000"), // [!code ++]
entryPoint: ENTRYPOINT_ADDRESS_V07,
});
```
:::info
Note: All smart account types supported by permissionless.js will work out of the box as all related factories and modules are deployed on the local anvil instance.
:::
#### Vitest Integration
You can add scripts in your **package.json** to automatically set up and tear down your mock environment when running tests.
```json [package.json]
{
"name": "aa-tests",
"scripts": {
"test": "bun run docker:up && vitest run && bun run docker:down",
"docker:up": "docker-compose up -d", // [!code ++]
"docker:down": "docker-compose down" // [!code ++]
},
"dependencies": {
"viem": "^2.9.17",
"permissionless": "^0.1.35"
},
"devDependencies": {
"vitest": "^1.5.2"
}
}
```
When writing test cases, ensure that the bundler and paymaster are fully setup before sending any request to them. To do this, make a simple health check in the `beforeAll` declaration.
:::code-group
```ts [basic.test.ts]
import { beforeAll, describe, expect, test } from "vitest";
import { ensureBundlerIsReady, ensurePaymasterIsReady } from "./healthCheck";
import { foundry } from "viem/chains";
import { http } from "viem";
import {
createBundlerClient,
ENTRYPOINT_ADDRESS_V06,
ENTRYPOINT_ADDRESS_V07,
} from "permissionless";
describe("Test basic bundler functions", () => {
beforeAll(async () => {
// [!code ++]
await ensureBundlerIsReady(); // [!code ++]
await ensurePaymasterIsReady(); // [!code ++]
}); // [!code ++]
test("Can get chainId", async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
entryPoint: ENTRYPOINT_ADDRESS_V07,
});
const chainId = await bundlerClient.chainId();
expect(chainId).toEqual(foundry.id);
});
test("Can get supported entryPoints", async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
entryPoint: ENTRYPOINT_ADDRESS_V07,
});
const supportedEntryPoints = await bundlerClient.supportedEntryPoints();
expect(supportedEntryPoints).toEqual([
ENTRYPOINT_ADDRESS_V06,
ENTRYPOINT_ADDRESS_V07,
]);
});
});
```
```ts [healthCheck.ts]
import { createBundlerClient, ENTRYPOINT_ADDRESS_V07 } from "permissionless";
import { http } from "viem";
import { foundry } from "viem/chains";
export const ensureBundlerIsReady = async () => {
const bundlerClient = createBundlerClient({
chain: foundry,
transport: http("http://localhost:4337"),
entryPoint: ENTRYPOINT_ADDRESS_V07,
});
while (true) {
try {
await bundlerClient.chainId();
return;
} catch {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
};
export const ensurePaymasterIsReady = async () => {
while (true) {
try {
// mock paymaster will open up this endpoint when ready
const res = await fetch(`http://localhost:3000/ping`);
const data = await res.json();
if (data.message !== "pong") {
throw new Error("paymaster not ready yet");
}
return;
} catch {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
};
```
:::
For a more detailed example, take a look at the [permissionless.js E2E integration tests](https://github.com/pimlicolabs/permissionless.js/tree/main/packages/permissionless-test) which uses the same mock environment as this guide.
#### Extension: Testing against forked state
If you want your tests to run against a live blockchain, you can slightly edit the **docker-compose.yaml** file to fork from the latest block by adding the anvil flag **--fork-url** and the environment variable **SKIP\_DEPLOYMENTS** to skip the local contract deployments.
```yaml [docker-compose.yaml]
services:
anvil:
image: ghcr.io/foundry-rs/foundry:nightly-f6208d8db68f9acbe4ff8cd76958309efb61ea0b
ports: [ "8545:8545" ]
entrypoint: [ "anvil", "--fork-url", "https://sepolia.rpc.thirdweb.com", "--chain-id", "11155111", "--host", "0.0.0.0", "--block-time", "0.1", "--silent"] // [!code ++]
platform: linux/amd64/v8
mock-paymaster:
image: ghcr.io/pimlicolabs/mock-verifying-paymaster:main
ports: [ "3000:3000" ]
environment:
- ALTO_RPC=http://alto:4337
- ANVIL_RPC=http://anvil:8545
alto:
image: ghcr.io/pimlicolabs/mock-alto-bundler:main
ports: [ "4337:4337" ]
environment:
- ANVIL_RPC=http://anvil:8545
- SKIP_DEPLOYMENTS=true // [!code ++]
```
::::
import VersionWarning from "../../VersionWarning"
## Migration Guide
### 0.1.0
:::warning
This migration guide is assuming you migrating to ^0.1.0, but you are still looking to use EntryPoint version v0.6.
:::
#### Replaced transport with bundlerTransport inside `createSmartAccountClient`
```ts
const smartAccountClient = createSmartAccountClient({
transport: http(bundlerUrl), // [!code --]
bundlerTransport: http(bundlerUrl), // [!code ++]
// ...
})
```
#### Replaced sponsorUserOperation middleware API
Instead of accepting just a `sponsorUserOperation` middleware, `createSmartAccountClient` now accepts a `middleware` function that can specify a `sponsorUserOperation` function internally, as well as a `gasPrice` function.
```ts
const smartAccountClient = createSmartAccountClient({
sponsorUserOperation: paymasterClient.sponsorUserOperation, // [!code --]
middleware: { // [!code ++]
gasPrice: async () => { // [!code ++]
return (await bundlerClient.getUserOperationGasPrice()).fast // [!code ++]
}, // [!code ++]
sponsorUserOperation: paymasterClient.sponsorUserOperation, // [!code ++]
}, // [!code ++]
// ...
})
```
In addition, a function can also be passed to `middleware` directly.
```ts
const smartAccountClient = createSmartAccountClient({
middleware: async ({ userOperation, entryPoint }) => {
// ...
return modifiedUserOperation
}
// ...
})
```
#### `sponsorUserOperation` in `pimlicoPaymasterActions` and `stackupPaymasterActions` now returns just the `paymasterAndData` and estimated gas limits instead of the whole user operation
```ts
const sponsorResult = await paymasterClient.sponsorUserOperation({
userOperation: userOperation,
entryPoint: ENTRYPOINT_ADDRESS_V06
})
/**
* {
"paymasterAndData": "0xe3dc822D77f8cA7ac74c30B0dfFEA9FcDCAAA321000000000000000000000000000000000000000000000000000000006514ac7d000000000000000000000000000000000000000000000000000000000000000071eee8c38559ef6872351c16a312cefbc545344a3f7cc1b910d059a0d5c613012763e6b1ce31080a975ddcba12817305a62a322e3ec8f106bd2181b0fd1391cf1c",
"preVerificationGas": 61230n,
"verificationGasLimit": 93823n,
"callGasLimit": 134849n
}
*/
```
#### Modified `providerToSmartAccountSigner` API to use keyword arguments
```ts
import { providerToSmartAccountSigner } from "permissionless"
const provider = // ...
const signerAddress = // ...
const smartAccountSigner = await providerToSmartAccountSigner(provider, signerAddress) // [!code --]
const smartAccountSigner = await providerToSmartAccountSigner(provider, { signerAddress: signerAddress }) // [!code ++]
```
#### All clients must now take in an `entryPoint` address
```ts
const bundlerClient = createBundlerClient({
transport: http(bundlerUrl),
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code ++]
})
const smartAccountClient = createSmartAccountClient({
account,
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code ++]
chain: sepolia,
bundlerTransport: http(bundlerUrl),
})
const bundlerClient = createPimlicoBundlerClient({
transport: http(bundlerUrl),
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code ++]
})
export const paymasterClient = createPimlicoPaymasterClient({
transport: http(paymasterUrl),
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code ++]
})
export const paymasterClient = createStackupPaymasterClient({
transport: http(paymasterUrl),
entryPoint: ENTRYPOINT_ADDRESS_V06, // [!code ++]
})
const bundlerClient = createClient({
transport: http(bundlerUrl),
chain: sepolia,
})
.extend(bundlerActions) // [!code --]
.extend(bundlerActions(ENTRYPOINT_ADDRESS_V06)) // [!code ++]
```
#### `entryPoint` is no longer passed in to bundler, paymaster, and smart account actions
```ts
const userOperationHash = await bundlerClient.sendUserOperation({
userOperation: sponsoredUserOperation,
entryPoint: ENTRYPOINT_ADDRESS_V07, // [!code --]
})
```
import VersionWarning from "../../VersionWarning"
## How to send multiple user operations in parallel
permissionless.js library lets you to send multiple transactions in parallel. This can be useful if you want to batch multiple calls in a single user operation or if you want to send user operations concurrently.
### Batching Multiple Calls
To batch multiple calls in a single user operation, you can use the `sendTransactions` method.
```ts
// [!include ~/snippets/v0_1/how-to/parallel-transactions.ts:multiple-transactions]
```
### Sending Multiple User Operations in Parallel
In an Externally Owned Account (EOA), the `nonce` is a simple incrementing number. However, in smart accounts, the `nonce` consists of two components: a `key` and a `sequence`:
* 192-bit “key”
* 64-bit “sequence”
For each unique `key`, the sequence must be incremented by 1 for each transaction. This means that if you send multiple transactions in parallel, you can use different `keys` for the parallel transactions.
#### Parallel Transactions Ordering
Important thing to note is that parallel transaction's ordering is not guaranteed. So execution of random `key` on chain could be:
* \[key-C]\[sequence-0]
* \[key-A]\[sequence-0]
* \[key-B]\[sequence-0]
While execution of `sequence` for a specific `key` will always be in order. So the following is a valid order of execution:
1. \[key-C]\[sequence-0]
2. \[key-A]\[sequence-0]
3. \[key-C]\[sequence-1]
4. \[key-B]\[sequence-0]
5. \[key-A]\[sequence-1]
6. \[key-B]\[sequence-1]
:::info
Note: Pimlico bundler currently only support up to 10 parallel transactions. If you need to send more than 10 transactions in parallel feel free to contact us at [support@pimlico.io](mailto\:support@pimlico.io).
:::
In the example below, we use the current timestamp as the `key` to send parallel transactions. You can use any other value as the `key`.
```ts
// [!include ~/snippets/v0_1/how-to/parallel-transactions.ts:multiple-transactions-parallel]
```
This way, you can efficiently manage multiple transactions either sequentially or concurrently.
import VersionWarning from "../../VersionWarning"
## permissionless.js
permissionless.js is a TypeScript library built on viem for interacting with ERC-4337 bundlers, paymasters, and User Operations.
### Installation
You can install permissionless.js into your project with the following command:
:::code-group
```bash [npm]
npm install permissionless
```
```bash [yarn]
yarn add permissionless
```
```bash [pnpm]
pnpm install permissionless
```
```bash [bun]
bun install permissionless
```
:::
import VersionWarning from "../../VersionWarning"
## Tutorials
Learning-oriented lessons that take you through a series of steps to complete a project. Most useful when you want to get started with Pimlico.
[Tutorial 1](/references/permissionless/v0_1/tutorial/tutorial-1) takes you through the journey of leveraging permissionless.js's high-level APIs to easily create and bundle a user operation.
[Tutorial 2](/references/permissionless/v0_1/tutorial/tutorial-2) takes you through the journey of creating a user operation, getting it sponsored by our verifying paymaster, and then sending it to be included on-chain with our bundler.
import VersionWarning from "../../VersionWarning"
## Tutorial 1 — Send your first gasless transaction
In this tutorial, you will submit your first fully-gasless transaction from a smart account.
You will set up the necessary permissionless.js clients, create a user operation, ask Pimlico's verifying paymaster to sponsor it, and then submit it on-chain with Pimlico's bundler.
### Steps
::::steps
#### Get a Pimlico API key
[Create your API key](/guides/create-api-key)
#### Clone the Pimlico tutorial template repository
We have created a [Pimlico tutorial template repository](https://github.com/pimlicolabs/tutorial-template) that you can use to get started. It comes set up with Typescript, viem, and permissionless.js.
```bash
git clone https://github.com/pimlicolabs/tutorial-template.git pimlico-tutorial-1
cd pimlico-tutorial-1
```
Now, let's install the dependencies:
```bash
npm install
```
The main file we will be working with is `index.ts`. Let's run it to make sure everything is working:
```bash
npm start
```
If everything has been set up correctly, you should see `Hello world!` printed to the console.
#### Create the public and paymaster clients, and generate a private key
The public client will be responsible for querying the blockchain, while the paymaster client will be responsible for interacting with Pimlico's verifying paymaster endpoint and requesting sponsorship.
Make sure to replace `YOUR_PIMLICO_API_KEY` in the code below with your actual Pimlico API key.
Let's open up `index.ts`, and add the following to the bottom:
```ts
// [!include ~/snippets/v0_1/tutorial-1.ts:clients]
```
#### Create the `SmartAccount` instance
For the purposes of this guide, we will be using [Safe](https://safe.global) accounts. This account is an ERC-4337 wallet controlled by a single EOA signer.
:::tip[Tip]
Want to learn more about using Safe accounts? Take a look at our [dedicated Safe guide](/references/permissionless/how-to/accounts/use-safe-account)
:::
To create the Safe account, we will use the `signerToSafeSmartAccount` utility function from permissionless.js. We need to specify the Safe version we are using as well as the global ERC-4337 EntryPoint address. For the signer, we will be using the previously generated private key.
Add the following to the bottom of `index.ts`:
```ts
// [!include ~/snippets/v0_1/tutorial-1.ts:smartAccount]
```
Let's run this code with `npm start`. You should see the smart account address printed to the console.
```txt
Smart account address: https://sepolia.etherscan.io/address/0x374b42bCFAcf85FDCaAB84774EA15ff36D42cdA7
```
:::info
If you visit the address on Etherscan, you might notice that no contract is actually deployed to this address yet. This is because smart account are counterfactual, meaning that they are only deployed on-chain the first time you send a transaction through the account.
:::
#### Create the bundler and smart account clients
Now that we have a `SmartAccount` instance, we need to create a `SmartAccountClient` instance to be able to transact from it. `SmartAccountClient` is an almost drop-in replacement for a viem [`WalletClient`](https://viem.sh/docs/clients/wallet), but it also includes some additional functionality for interacting with smart accounts.
We then specify the optional `sponsorUserOperation` middleware function, calling the `sponsorUserOperation` function from the paymaster client. This will make sure that the user operation is sponsored by Pimlico's verifying paymaster.
Finally, we specify the `gasPrice` middleware function to fetch the gas price from the bundler that we will use to submit the user operation in the next step.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/v0_1/tutorial-1.ts:smartAccountClient]
```
#### Submit a transaction from the smart account
Finally, let's submit a transaction from the smart account. We will send a transaction to the `0xd8da6bf26964af9d7eed9e03e53415d37aa96045` (vitalik.eth) address with `0x1234` as example `callData`.
Underneath the hood, the `SmartAccountClient` will build a user operation, request Pimlico paymaster sponsorship, sign it with the smart account's private key, and then submit it to the bundler. The bundler will then query for receipts until it sees the user operation included on-chain.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/v0_1/tutorial-1.ts:submit]
```
Let's run this code again with `npm start`. You should see the transaction hash bundling the user operation on-chain printed to the console.
```txt
User operation included: https://sepolia.etherscan.io/tx/0x7a2b61b4b7b6e9e66c459e3c9c24c7a292fc6c740533ce35dbf58710960cc0e5
```
You can now view the transaction on the Sepolia testnet explorer. By sending this user operation, you have:
* Deployed the counterfactual smart account contract
* Had this newly-deployed smart account verify the private key's signature
* Made Pimlico's verifying paymaster sponsor the user operation's gas fees
* Executed a simple transaction to `vitalik.eth`'s address
All in a couple lines of code.
Congratulations, you are now a pioneer of Account Abstraction! 🎉
Please [get in touch](https://t.me/pimlicoHQ) if you have any questions or if you'd like to share what you're building!
::::
#### Combined code
If you want to see the complete code that combines all of the previous steps, we uploaded it to a [separate repository](https://github.com/pimlicolabs/tutorials). If you're looking to run it, remember to replace the API key with your own!
import VersionWarning from "../../VersionWarning"
## Tutorial 2 — Submit a user operation with a Verifying Paymaster
:::note
This is a low-level tutorial that walks you through the steps of constructing a user operation from scratch. If you would like to leverage permissionless.js's high-level functions, take a look at [tutorial 1](/references/permissionless/tutorial/tutorial-1).
:::
In this tutorial, you will generate a user operation, ask Pimlico's verifying paymaster to sponsor it, and then submit the sponsored user operation on-chain with Pimlico's Alto bundler.
### Steps
:::steps
#### Get a Pimlico API key
[Create your API key](/guides/create-api-key)
#### Clone the Pimlico tutorial template repository
We have created a [Pimlico tutorial template repository](https://github.com/pimlicolabs/tutorial-template) that you can use to get started. It comes set up with Typescript, viem, and permissionless.js.
```bash
git clone https://github.com/pimlicolabs/tutorial-template.git pimlico-tutorial-2
cd pimlico-tutorial-2
```
Now, let's install the dependencies:
```bash
npm install
```
The main file we will be working with is `index.ts`. Let's run it to make sure everything is working:
```bash
npm start
```
If everything has been set up correctly, you should see `Hello world!` printed to the console.
#### Create the viem clients
We will be using three different clients for this example.
1. Standard publicClient for normal Ethereum RPC calls — [https://sepolia.rpc.thirdweb.com](https://sepolia.rpc.thirdweb.com)
2. Pimlico v1 api for the Bundler methods — [https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR\_PIMLICO\_API\_KEY](https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY)
3. Pimlico v2 api for the Paymaster methods — [https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR\_PIMLICO\_API\_KEY](https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY)
Make sure to replace `YOUR_PIMLICO_API_KEY` in the code below with your actual Pimlico API key.
Let's open up `index.ts`, and add the following to the bottom:
```ts
// [!include ~/snippets/v0_1/tutorial-2.ts:clients]
```
#### Generate the factory and factoryData
For the purposes of this guide, we will be using the [SimpleAccount.sol](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/accounts/SimpleAccount.sol) wallet found in the eth-infinitism repository. This Wallet is a simple ERC-4337 wallet controlled by a single EOA signer.
At `0x91E60e0613810449d098b0b5Ec8b51A0FE8c8985`, most chain already have deployed a [SimpleAccountFactory.sol](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/accounts/SimpleAccountFactory.sol) contract, that is able to easily deploy new SimpleAccount instances via the `createAccount` function. We will be leveraging this contract to help us generate the `factory` and `factoryData`.
Requesting a smart contract deployment is done through the `factory` and `factoryData` field, where the `factory` field specifies the address the EntryPoint will call, and the `factoryData` corresponds to the data that will be called on that factory.
Add the following to the bottom of `index.ts`:
```ts
// [!include ~/snippets/v0_1/tutorial-2.ts:initCode]
```
Let's run this code with `npm start`. You should see the generated initCode printed to the console.
```txt
Generated factoryData: 0x9406cc6185a346906296840746125a0e449764545fbfb9cf000000000000000000000000508a7005f997a21cd662d6fb41ad69f945c129c10000000000000000000000000000000000000000000000000000000000000000
```
#### Calculate the sender address
Now that we have the `factory` and `factoryData`, we have to calculate the corresponding `sender` address, which is the address the `SimpleAccount` will be deployed to, and thereby the address which will handle the verification and execution steps of the UserOperation.
We do this by calling the `getSenderAddress` utility function on the EntryPoint. Upon success, it will revert with a special error type that contains the counterfactual address of the smart contract wallet that will be deployed
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/v0_1/tutorial-2.ts:sender]
```
Let's run this code with `npm start`. You should see the address printed to the console.
```txt
Calculated sender address: 0xbAd38BdCf884ED92ab370f69C0CD0B7b8a1459A1
```
#### Generate the callData
Now, let's decide on the `callData` that we want the wallet to actually execute once the UserOperation passes verification.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/v0_1/tutorial-2.ts:callData]
```
Above, we are leveraging the `execute` function of the SimpleWallet, which simply calls an arbitrary address, with arbitrary value and arbitrary callData.
Let's run this code with `npm start`. You should see the callData printed to the console.
```txt
Generated callData: 0xb61d27f6000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa9604500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000
```
#### Fill out remaining UserOperation values
We're almost there, now let's fill out the rest of the UserOperation values.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/v0_1/tutorial-2.ts:remaining]
```
#### Request Pimlico verifying paymaster sponsorship
To make the operation gasless, we will leverage Pimlico's verifying paymaster. Using paymasters allows you to delegate the gas fee payment to a third-party contract that can decide whether it is willing to pay the gas fees for the user operation. In this case, Pimlico's verifying paymaster checks whether its off-chain signer has signed off on the user operation. To request Pimlico's signer to sign your user operation, call the `pm_sponsorUserOperation` endpoint or use the `sponsorUserOperation` method from the permissionless.js Pimlico paymaster actions.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/v0_1/tutorial-2.ts:paymaster]
```
Let's run this code with `npm start`. You should see something like this:
```txt
Received paymaster sponsor result: {
paymaster: '0xcF60744ef322396a6d0a5B7d396F5814176855F1',
paymasterVerificationGasLimit: 526114n,
paymasterPostOpGasLimit: 75900n,
paymasterData: '000000000000000000000000000000000000000000000000000000006518a0c100000000000000000000000000000000000000000000000000000000000000003251910f0e14691ca19a8e2cca216ce77a1ce8d002e975e113cfbc36d8e489a550dbb24ff485ee61c1b91cf8e20261d307ef79f22cf9359fba3271f721dade4d1b',
preVerificationGas: 247487n,
verificationGasLimit: 526114n,
callGasLimit: 75900n
}
```
Great! Now we have received the gas limit estimates and added the paymaster-related fields containing Pimlico's signature to our UserOperation.
#### Sign the UserOperation
The last field to fill out is the `signature`. This is a simple ECDSA signature consistent with typical Ethereum private key signing procedures.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/v0_1/tutorial-2.ts:sign]
```
Let's run this code with `npm start`. You should see something like this:
```txt
Generated signature: 0xcaae357fcd3882f1ea4b48f7dcce9d7f2482794ab72d1075ce5d7fcef4c5ec03265fe03e5fd7f8af65a4cd05b7e01300f3938f7e245dc8038748ddef93d5f4061c
```
#### Submit the UserOperation to be bundled
Finally, we're ready to submit the UserOperation to Pimlico's bundler, which will include it on-chain. The `eth_sendUserOperation` RPC call or the `sendUserOperation` method from permissionless.js will submit the UserOperation to the bundler.
You can also query for receipts to keep checking the status of the UserOperation until it is included.
Add the following to the bottom of `index.ts`:
```typescript
// [!include ~/snippets/v0_1/tutorial-2.ts:submit]
```
If we run this code with `npm start`, we will go through the whole flow of executing the User Operation. You should see something like this:
```txt
UserOperation included: https://sepolia.etherscan.io/tx/0xe0a47e0e6637d0d4062cda3683afe2f5809bf545b917888277905de38f08e188
```
Once the UserOperation is included, you can view the transaction on the Sepolia testnet explorer.
That's it! You've successfully generated a UserOperation and submitted it using Pimlico's Alto bundler.
By leveraging Pimlico's paymaster, you were able to make the User Operation completely gasless, and by using Pimlico's Alto bundler, you were able to submit the User Operation to the chain without having to worry about maintaining your own relaying infrastructure.
Congratulations, you are now a pioneer of Account Abstraction! 🎉
Please get in touch if you have any questions or if you'd like to share what you're building!
:::
#### Combined code
If you want to see the complete code that combines all of the previous steps, we uploaded it to a [separate repository](https://github.com/pimlicolabs/tutorials). If you're looking to run it, remember to replace the API key with your own!
## Check if an external account supports paymasters
If you have added support to sponsor transactions for external accounts using `@permissionless/wagmi`, you can use this guide to check if the external account supports ERC-7677 paymasters and display custom UI for your users.
### Steps
:::steps
#### Import useCapabilities
```tsx
import { useCapabilities } from 'wagmi/experimental'
```
#### Fetch the available capabilities
```tsx
import { useCapabilities } from 'wagmi/experimental'
function App() {
const capabilities = useCapabilities()
}
```
#### Parse the capabilities for the currect chain
```tsx
import { useCapabilities } from 'wagmi/experimental'
function App() {
const capabilities = useCapabilities()
const account = useAccount() // [!code focus]
const capabilitiesForChain = capabilities[account.chainId] // [!code focus]
}
```
#### Check if the paymaster service is supported
```tsx
import { useCapabilities } from 'wagmi/experimental'
function App() {
const capabilities = useCapabilities()
const account = useAccount()
const capabilitiesForChain = capabilities[account.chainId]
const paymasterServiceSupported = capabilitiesForChain?.paymasterService?.supported // [!code focus]
}
```
:::
## Tutorials
Learning-oriented lessons that take you through a series of steps to complete a project. Most useful when you want to get started with Pimlico.
[Tutorial 1](/references/permissionless/wagmi/tutorial/tutorial-1) takes you through the journey of leveraging @permissionless/wagmi's high-level APIs to easily sponsor your first gasless transaction for an external smart account using 7677.
## Tutorial 1 — Send your first gasless transaction
In this tutorial, you will submit your first fully-gasless transaction from an external smart account.
You will set up the necessary @permissionless/wagmi' PermissionlessProvider, paymasterService, ask Pimlico's verifying paymaster to sponsor it, and then ask external smartAccount to submit it on-chain.
### Steps
::::steps
#### Get a Pimlico API key
[Create your API key](/guides/create-api-key)
#### Create a wagmi app
For new projects, it is recommended to set up your Wagmi app using the create-wagmi command line interface (CLI). This will create a new Wagmi project using TypeScript and install the required dependencies.
:::code-group
```bash [pnpm]
pnpm create wagmi
```
```bash [npm]
npm create wagmi@latest
```
```bash [yarn]
yarn create wagmi
```
```bash [bun]
bun create wagmi
```
:::
Once the command runs, you'll see some prompts to complete. Make sure you select `React` as `@permissionless/wagmi` only supports React as of now.
After the prompts, create-wagmi will create a directory with your project name and install the required dependencies. Check out the README.md for further instructions (if required).
#### Install @permissionless/wagmi
:::code-group
```bash [pnpm]
pnpm install @permissionless/wagmi
```
```bash [npm]
npm install @permissionless/wagmi
```
```bash [yarn]
yarn install @permissionless/wagmi
```
```bash [bun]
bun install @permissionless/wagmi
```
:::
#### Create capabilities & config
Create capabilities for the chains that you would like to sponsor the transactions for the users.
```tsx
export const config = createConfig({
chains: [baseSepolia],
connectors: [
// coinbase wallet is one of the smart accounts that supports ERC-7677
coinbaseWallet({ appName: "Pimlico", preference: "smartWalletOnly" })
],
transports: {
[baseSepolia.id]: http("https://sepolia.base.org")
}
})
const capabilities = {
paymasterService: {
[baseSepolia.id]: {
url: `https://api.pimlico.io/v2/${baseSepolia.id}/rpc?apikey=${pimlicoApiKey}`
}
}
}
```
In this example, you can integrate any service provider that complies with the ERC-7677 standard. For demonstration purposes, we are using Pimlico as the paymaster service provider.
Ensure you configure the service for all the blockchain networks where you plan to sponsor transactions. We are showcasing sponsorship for baseSepolia.
#### Wrap App in Context Provider
Wrap your app in the `PermissionlessProvider` React Context Provider and pass the `capabilities` you created earlier.
:::code-group
```tsx [App.tsx]
import { WagmiProvider } from "wagmi"
import { baseSepolia } from "wagmi/chains"
import { PermissionlessProvider } from "@permissionless/wagmi" // [!code focus]
import { config, capabilities } from "./wagmi.ts" // [!code focus]
ReactDOM.createRoot(root).render(
// [!code focus]
{/** ... */}
// [!code focus]
)
```
```tsx [wagmi.ts]
export const config = createConfig({
chains: [baseSepolia],
connectors: [
// coinbase wallet is one of the smart accounts that supports ERC-7677
coinbaseWallet({ appName: "Pimlico", preference: "smartWalletOnly" })
],
transports: {
[baseSepolia.id]: http("https://sepolia.base.org")
}
})
export const capabilities = {
paymasterService: {
[baseSepolia.id]: {
url: `https://api.pimlico.io/v2/${baseSepolia.id}/rpc?apikey=${pimlicoApiKey}`
}
}
}
```
:::
Check out the [PermissionlessProvider]() docs to learn more about React Context in @permissionless/wagmi.
#### Use @permissionless/wagmi
Now that everything is set up, every component inside the Permissionless Provider can use Permissionless React Hooks.
```tsx [App.tsx]
import { useSendTransaction, useWaitForTransactionReceipt } from "wagmi" // [!code --] // [!code focus]
import { // [!code ++] // [!code focus]
useSendTransaction, // [!code ++] // [!code focus]
useWaitForTransactionReceipt // [!code ++] // [!code focus]
} from "@permissionless/wagmi" // [!code ++] // [!code focus]
function App() {
const {
sendTransaction,
data: transactionReference,
isPending
} = useSendTransaction()
const { data: receipt, isPending: isReceiptPending } =
useWaitForTransactionReceipt({
id: transactionReference
})
const sendTransactionCallback = useCallback(async () => {
console.log("Sending transaction...")
sendTransaction({
to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
data: "0x1234"
})
}, [sendTransaction])
return (
Send test transaction
{isPending &&
Sending transaction...
}
{transactionReference && (
Awaiting confirmation: {transactionReference}
)}
{receipt &&
{receipt.status}
}
)
}
```
As you notice, `@permissionless/wagmi` exposes the same api as `wagmi`. `@permissionless/wagmi` is a drop-in replacement for `wagmi` that provides the same functionality, but with the added benefit of being able to use sponsor transactions.
:::info
The above code will sponsor transactions only when the external account is a smart account and supports ERC-7677. If the external account doesn't have the capability, the transaction will be sent as a normal transaction.
:::
::::
To test the above code connect using Coinbase SmartAccount and try sending your first transaction. By sending this transaction, you will have:
* Made Pimlico's verifying paymaster sponsor the user operation's gas fees
* Executed a simple sponsored transaction to `vitalik.eth`'s address from the Coinbase SmartAccount
All in a couple lines of code.
Congratulations, you are now a pioneer of Account Abstraction! 🎉
Please [get in touch](https://t.me/pimlicoHQ) if you have any questions or if you'd like to share what you're building!
## Create a sponsorship policy
Creates a sponsorship policy.
### HTTP Request
`POST /v2/account/sponsorship_policies`
### Query Parameters
* **apikey**
Your Pimlico API key.
### Request Body
The sponsorship policy object, without the `id` and `created_at` and `policy_status` fields.
### Returns
Returns the created sponsorship policy object.
### Example Request
```shell
curl -X POST "https://api.pimlico.io/v2/account/sponsorship_policies?apikey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"policy_name": "Newly Created Policy",
"limits": {
"global": {
"user_operation_spending": {
"amount": 20000000,
"currency": "USD"
}
}
}
}'
```
### Example Response
```json
{
"id": "sp_mighty_chameleon",
"policy_name": "Newly Created Policy",
"policy_status": "active",
"created_at": "2024-07-16T17:36:23.296Z",
"start_time": null,
"end_time": null,
"chain_ids": {
"allowlist": null
},
"limits": {
"global": {
"user_operation_spending": {
"amount": 20000000,
"currency": "USD"
}
}
}
}
```
## Sponsorship Policies API
Sponsorship policies represent the rules that govern which user operations you are willing to sponsor. Use it to customize the conditions under which you are willing to subsidize the gas fees for your users.
Related guide: [How to use Sponsorship Policies](/guides/how-to/sponsorship-policies)
:::note[Endpoints]
List all sponsorship policies:
[`GET /v2/account/sponsorship_policies`](/references/platform/api/sponsorship-policies/list)
Retrieve a specific sponsorship policy:
[`GET /v2/account/sponsorship_policies/:id`](/references/platform/api/sponsorship-policies/retrieve)
Create a new sponsorship policy:
[`POST /v2/account/sponsorship_policies`](/references/platform/api/sponsorship-policies/create)
Update an existing sponsorship policy:
[`POST /v2/account/sponsorship_policies/:id`](/references/platform/api/sponsorship-policies/update)
:::
## List all sponsorship policies
Returns a list of your sponsorship policies. The sponsorship policies are returned sorted by creation date, with the most recent sponsorship policies appearing first.
### HTTP Request
`GET /v2/account/sponsorship_policies`
### Query Parameters
* **apikey**
Your Pimlico API key.
* **limit:** optional, default is 100
This specifies a limit on the number of objects to return, ranging between 1 and 100.
* **starting\_after:** optional object ID
A cursor to use in pagination. `starting_after` is an object ID that defines your place in the list. For example, if you make a list request and receive 100 objects, ending with `obj_foo`, your subsequent call can include starting\_after=obj\_foo to fetch the next page of the list.
* **ending\_before:** optional object ID
A cursor to use in pagination. `ending_before` is an object ID that defines your place in the list. For example, if you make a list request and receive 100 objects, starting with `obj_bar`, your subsequent call can include ending\_before=obj\_bar to fetch the previous page of the list.
### Returns
Returns the sponsorship policy object.
### Example Request
```shell
curl -X GET "https://api.pimlico.io/v2/account/sponsorship_policies/sp_tangible_knight?apikey=YOUR_API_KEY"
```
### Example Response
```json
{
"has_more": true,
"data": [
{
"id": "sp_tangible_knight",
"policy_name": "Pimlico Example Gas Fund",
"policy_status": "active",
"created_at": "2024-05-23T15:23:38Z",
"start_time": null,
"end_time": null,
"chain_ids": {
"allowlist": [
10,
8453,
34443,
690,
7777777
]
},
"limits": {
"global": {
"user_operation_spending": {
"amount": 10000000,
"currency": "USD"
}
}
}
},
{...},
{...},
{...},
{...}
]
}
```
### See more
[Pagination](/references/platform/api/pagination)
## The Sponsorship Policy object
### Attributes
:::note
* **id:** `string`
The unique identifier for the sponsorship policy. Maximum 255 characters.
:::
:::note
* **policy\_name:** `string`
The name of the sponsorship policy. Maximum 255 characters.
:::
:::note
* **policy\_status:** `enum`
The status of the sponsorship policy. Possible values are `active`, `inactive`, and `deleted`.
:::
:::note
* **created\_at:** `string`
The date and time the sponsorship policy was created formatted as an ISO 8601 string (e.g. 2025-05-23T15:23:38Z).
:::
:::note
* **start\_time:** `string` or `null`
The date and time the sponsorship policy will become active formatted as an ISO 8601 string (e.g. 2025-05-23T15:23:38Z). If `null`, the policy is active immediately.
:::
:::note
* **end\_time:** `string` or `null`
The date and time the sponsorship policy will become inactive formatted as an ISO 8601 string (e.g. 2025-05-23T15:23:38Z). If `null`, there is no end time after which the policy is inactive. Must be in the future.
:::
:::note
* **webhook\_endpoint:** `string` or `null`
The URL of the webhook endpoint to call when a user operation is sponsored under this policy.
:::
:::note
* **webhook\_enabled:** `boolean` or `null`
Whether the webhook is enabled for this policy.
:::
::::note
* **chain\_ids:** `object` or `null`
An object containing the chain IDs that are allowed to use the sponsorship policy.
:::note
* **allowlist:** `array` or `null`
An array of chain IDs that are allowed to use the sponsorship policy. Must contain between 1 and 1000 unique chain IDs, and all must be supported by Pimlico. If `null`, the policy is valid for all chains.
:::
::::
::::::note
* **limits:** `object` or `null`
An object containing the spending limits for the sponsorship policy. The object has the following attributes:
:::::note
* **global:** `object` or `null`
An object containing the global spending limits across all users for the sponsorship policy.
::::note
* **user\_operation\_spending:** `object` or `null`
An object containing the spending limit for user operations.
:::note
* **amount:** `number`
The spending limit amount. Must be greater than 0. The API expects amounts to be provided in a currency's smallest unit. For example, to set the limit to 10 USD, provide an amount value of 1000 (that is, 1000 cents).
* **currency:** `enum`
The currency of the spending limit amount. Currently only `USD` is supported.
:::
::::
:::note
* **maximum\_user\_operation\_count:** `number` or `null`
The maximum number of user operations allowed. Must be greater than or equal to 0.
:::
:::note
* **reset\_interval:** `enum` or `null`
The interval at which the limits reset. Possible values are `never`, `daily`, `weekly`, or `monthly`. Default is `never`.
:::
:::::
:::::note
* **user:** `object` or `null`
An object containing the per-user spending limits for the sponsorship policy.
::::note
* **user\_operation\_spending:** `object` or `null`
An object containing the spending limit for user operations per user.
:::note
* **amount:** `number`
The spending limit amount. Must be greater than 0.
* **currency:** `enum`
The currency of the spending limit amount. Currently only `USD` is supported.
:::
::::
:::note
* **maximum\_user\_operation\_count:** `number` or `null`
The maximum number of user operations allowed per user. Must be greater than or equal to 0.
:::
:::note
* **reset\_interval:** `enum` or `null`
The interval at which the user limits reset. Possible values are `never`, `daily`, `weekly`, or `monthly`. Default is `never`.
:::
:::::
:::::note
* **user\_operation:** `object` or `null`
An object containing the per-user-operation spending limits for the sponsorship policy.
::::note
* **user\_operation\_spending:** `object` or `null`
An object containing the spending limit for each individual user operation.
:::note
* **amount:** `number`
The maximum amount allowed for a single user operation. Must be greater than 0.
* **currency:** `enum`
The currency of the spending limit amount. Currently only `USD` is supported.
:::
::::
:::::
::::::
### Example
```json
{
"id": "sp_tangible_knight",
"policy_name": "Pimlico Example Gas Fund",
"policy_status": "active",
"created_at": "2025-05-23T15:23:38Z",
"start_time": null,
"end_time": null,
"webhook_endpoint": "https://example.com/webhook",
"webhook_enabled": true,
"chain_ids": {
"allowlist": [
10,
8453,
34443,
690,
7777777
]
},
"limits": {
"global": {
"user_operation_spending": {
"amount": 10000000,
"currency": "USD"
},
"maximum_user_operation_count": 1000,
"reset_interval": "monthly"
},
"user": {
"user_operation_spending": {
"amount": 100000,
"currency": "USD"
},
"maximum_user_operation_count": 10,
"reset_interval": "daily"
},
"user_operation": {
"user_operation_spending": {
"amount": 10000,
"currency": "USD"
}
}
}
}
```
## Retrieve a sponsorship policy
Retrieves a sponsorship policy by its ID.
### HTTP Request
`GET /v2/account/sponsorship_policies/:id`
### Query Parameters
* **apikey**
Your Pimlico API key.
### Returns
Returns the sponsorship policy object.
### Example Request
```shell
curl -X GET "https://api.pimlico.io/v2/account/sponsorship_policies/sp_tangible_knight?apikey=YOUR_API_KEY"
```
### Example Response
```json
{
"id": "sp_tangible_knight",
"policy_name": "Pimlico Example Gas Fund",
"policy_status": "active",
"created_at": "2024-05-23T15:23:38Z",
"start_time": null,
"end_time": null,
"chain_ids": {
"allowlist": [
10,
8453,
34443,
690,
7777777
]
},
"limits": {
"global": {
"user_operation_spending": {
"amount": 10000000,
"currency": "USD"
}
}
}
}
```
## Update a sponsorship policy
Updates a sponsorship policy. Any parameters not provided will be left unchanged.
### HTTP Request
`POST /v2/account/sponsorship_policies/:id`
### Query Parameters
* **apikey**
Your Pimlico API key.
### Request Body
The sponsorship policy object, without the `id` and `created_at` fields.
### Returns
Returns the updated sponsorship policy object.
### Example Request
```shell
curl -X POST "https://api.pimlico.io/v2/account/sponsorship_policies/sp_tangible_knight?apikey=YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"policy_name": "New Policy Name",
"limits": {
"global": {
"user_operation_spending": {
"amount": 20000000,
"currency": "USD"
}
}
}
}'
```
### Example Response
```json
{
"id": "sp_tangible_knight",
"policy_name": "New Policy Name",
"policy_status": "active",
"created_at": "2024-05-23T15:23:38Z",
"start_time": null,
"end_time": null,
"chain_ids": {
"allowlist": [
10,
8453,
34443,
690,
7777777
]
},
"limits": {
"global": {
"user_operation_spending": {
"amount": 20000000,
"currency": "USD"
}
}
}
}
```
import VersionWarning from "../../../VersionWarning"
## getPaymasterData
Return values to be used in paymaster-related fields of a signed user operation. This method calls `pm_getPaymasterData` and is part of the experimental [ERC-7677 specification](https://github.com/ethereum/ERCs/pull/360/files).
### Usage
:::code-group
```ts [example.ts]
import { paymasterEip7677Client } from "./client"
import { sepolia } from 'viem/chains'
const paymasterReturnData = paymasterEip7677Client.getPaymasterStubData({
userOperation: {
sender: "0x6152348912fb1e78c9037D83f9d4524D4a2988Ed",
nonce: 20n,
factory: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
factoryData:
"0xc5265d5d0000000000000000000000006723b44abeec4e71ebe3232bd5b455805badd22f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e412af322c018104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000014ec787ae5c34157ce61e751e54dff3612d4117663000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callData:
"0xe9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000594bc666500faed35dc741f45a35c571399560d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4e688a440000000000000000000000000c67e4838d4e6682e3a5f9ec27f133e76cb3855f30000000000000000000000006152348912fb1e78c9037d83f9d4524d4a2988ed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000041aebdfb90adba067d9304980a300636506c3c9081b01f64b04f108407a890602377625ef9096946cc028743123646881c7e31a1c8d6698132c188cb4c33a3f9151b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 99985502n,
maxPriorityFeePerGas: 1221000n,
preVerificationGas: 66875n,
verificationGasLimit: 373705n,
callGasLimit: 170447n,
paymasterVerificationGasLimit: 20274n,
paymasterPostOpGasLimit: 1n,
},
})
/**
* {
* paymaster: "0x9d0021A869f1Ed3a661Ffe8C9B41Ec6244261d98",
* paymasterData: "0x000000000000000000000000000000000000000000000000000000006638ab470000000000000000000000000000000000000000000000000000000000000000e9311d1945317dc6a1c750e8e6d0641a598beb59edc5652ed3807ca57338a7a107123e1a479386b2c91f242d2dff367c18e0ad9d1021acfe47afc890e252644e1c"
* }
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
import { paymasterActionsEip7677 } from 'permissionless/experimental'
export const paymasterEip7677Client = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(paymasterActionsEip7677(ENTRYPOINT_ADDRESS_V07))
```
:::
### Returns
* **Type:** `GetPaymasterDataReturnType`
The paymaster values to be used in the paymaster-related fields of a signed user operation.
For EntryPoint v0.7, the return type is:
```ts
{
paymaster: Address,
paymasterData: Hex
}
```
For EntryPoint v0.6, the return type is:
```ts
{
paymasterAndData: Hex
}
```
### JSON-RPC Method
[`pm_getPaymasterData`](https://github.com/ethereum/ERCs/pull/360/files)
import VersionWarning from "../../../VersionWarning"
## getPaymasterStubData
Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation. This method calls `pm_getPaymasterStubData` and is part of the experimental [ERC-7677 specification](https://github.com/ethereum/ERCs/pull/360/files).
### Usage
:::code-group
```ts [example.ts]
import { paymasterEip7677Client } from "./client"
import { sepolia } from 'viem/chains'
const paymasterStubReturnData = paymasterEip7677Client.getPaymasterStubData({
userOperation: {
sender: "0x6152348912fb1e78c9037D83f9d4524D4a2988Ed",
nonce: 20n,
factory: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
factoryData:
"0xc5265d5d0000000000000000000000006723b44abeec4e71ebe3232bd5b455805badd22f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e412af322c018104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000014ec787ae5c34157ce61e751e54dff3612d4117663000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callData:
"0xe9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000594bc666500faed35dc741f45a35c571399560d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4e688a440000000000000000000000000c67e4838d4e6682e3a5f9ec27f133e76cb3855f30000000000000000000000006152348912fb1e78c9037d83f9d4524d4a2988ed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000041aebdfb90adba067d9304980a300636506c3c9081b01f64b04f108407a890602377625ef9096946cc028743123646881c7e31a1c8d6698132c188cb4c33a3f9151b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 99985502n,
maxPriorityFeePerGas: 1221000n,
preVerificationGas: 66875n,
verificationGasLimit: 373705n,
callGasLimit: 170447n,
},
})
/**
* {
* paymaster: "0x9d0021A869f1Ed3a661Ffe8C9B41Ec6244261d98",
* paymasterData: "0x000000000000000000000000000000000000000000000000000000006638ab470000000000000000000000000000000000000000000000000000000000000000e9311d1945317dc6a1c750e8e6d0641a598beb59edc5652ed3807ca57338a7a107123e1a479386b2c91f242d2dff367c18e0ad9d1021acfe47afc890e252644e1c",
* paymasterVerificationGasLimit: 20274n,
* paymasterPostOpGasLimit: 1n
* }
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
import { paymasterActionsEip7677 } from 'permissionless/experimental'
export const paymasterEip7677Client = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(paymasterActionsEip7677(ENTRYPOINT_ADDRESS_V07))
```
:::
### Returns
* **Type:** `GetPaymasterStubDataReturnType`
The paymaster values to be used in the paymaster-related fields of a signed user operation.
For EntryPoint v0.7, the return type is:
```ts
{
paymaster: Address,
paymasterData: Hex,
paymasterVerificationGasLimit: bigint | undefined,
paymasterPostOpGasLimit: bigint | undefined
}
```
For EntryPoint v0.6, the return type is:
```ts
{
paymasterAndData: Hex
}
```
### JSON-RPC Method
[`pm_getPaymasterStubData`](https://github.com/ethereum/ERCs/pull/360/files)
import VersionWarning from "../../../VersionWarning"
## Account Support
permissionless.js supports 6 types of accounts natively (but can easily be extended to support any compatible ERC-4337 account). The below table details which EntryPoints each account is valid for.
| Account | EntryPoint v0.7 | EntryPoint v0.6 |
| :------------ | :------------------------ | :------------------------ |
| Safe | ✅ (Safe v1.4.1 and above) | ✅ (Safe v1.4.1 and above) |
| SimpleAccount | ✅ | ✅ |
| Kernel | ✅ | ✅ |
| Biconomy | ❌ | ✅ |
| LightAccount | ❌ | ✅ |
| TrustWallet | ❌ | ✅ |
import VersionWarning from "../../../VersionWarning"
## How to create and use a Biconomy account with permissionless.js
[Biconomy Smart Account](https://github.com/bcnmy/scw-contracts) is a smart account building on the core concepts of Gnosis and Argent safes. You can use Biconomy with plugins such as session keys, and even write your own plugins.
### Steps
:::steps
#### Import the required packages
```ts
// [!include ~/snippets/v0_1/accounts/biconomy.ts:imports]
```
#### Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the Biconomy account.
```ts
// [!include ~/snippets/v0_1/accounts/biconomy.ts:clients]
```
#### Create the signer
Biconomy accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Biconomy account validates ECDSA signatures. [Any signer](/references/permissionless/how-to/signers) can be used as a signer for the Biconomy account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/v0_1/accounts/biconomy.ts:signer]
```
#### Create the Biconomy account
With a signer, you can create a Biconomy account as such:
```ts
// [!include ~/snippets/v0_1/accounts/biconomy.ts:smartAccount]
```
The Biconomy account address is computed deterministically from the signer, but you can optionally pass an `index` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created Biconomy account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/v0_1/accounts/biconomy.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/v0_1/accounts/biconomy.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/v0_1/accounts/biconomy.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/v0_1/accounts/biconomy.ts:submitBatch]
```
:::
import VersionWarning from "../../../VersionWarning"
## How to use an ERC-7579 compatible smart account with permissionless.js
[ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) defines a standard for modular smart account interfaces. It also defines behavior for interoperability with minimal restrictions for accounts and modules.
Currently Safe and Kernel are the only smart accounts that implements ERC-7579.
For this guide, we will use the Safe smart accounts as an example. If you would like to find out more about the Safe smart account, you can check out the [Safe-specific guide](/references/permissionless/how-to/accounts/use-safe-account).
This guide will show you how to create and use a ERC-7579 compatible smart account with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/v0_1/erc7579/erc7579.ts:imports]
```
#### Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the SafeAccount.
```ts
// [!include ~/snippets/v0_1/erc7579/erc7579.ts:clients]
```
#### Create the SafeAccount
:::info
For a full list of options for creating a SafeAccount, take a look at the reference documentation page for [`signerToSafeSmartAccount`](/references/permissionless/v0_1/reference/accounts/signerToSafeSmartAccount).
:::
You can also pass an `address` to use an already created SafeAccount.
```ts
// [!include ~/snippets/v0_1/erc7579/erc7579.ts:smartAccount]
```
:::warning
The Safe account requires a new `safe4337ModuleAddress` & `erc7579LaunchpadAddress` for it to be 7579 compatible.
:::
#### Create the smart account client and extend it with the ERC7579 actions
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/v0_1/erc7579/erc7579.ts:smartAccountClient]
```
#### Interact with the 7579 actions
You can install a module on the Safe account using the `installModule` action.
```ts
// [!include ~/snippets/v0_1/erc7579/erc7579.ts:installModule]
```
:::warning
InstallModule returns user operation hash, not transaction hash, you must use `waitForUserOperationReceipt` to wait for the user operation to be included on-chain.
:::
You can also call all other ERC7579 actions, example `supportsExecutionMode`.
```ts
// [!include ~/snippets/v0_1/erc7579/erc7579.ts:supportsExecutionMode]
```
::::
import VersionWarning from "../../../VersionWarning"
## How to create and use a Kernel account with permissionless.js
:::info
ZeroDev, the author of Kernel, maintains their own in-house SDK built closely on top of permissionless.js that you can use for the account system while still plugging in all the other components from permissionless.js. Take a look at [their documentation](https://docs.zerodev.app) for more information.
:::
[Kernel](https://github.com/zerodevapp/kernel) is a **modular smart account** that supports plugins, which are smart contracts that extend the account's functionalities. You can use Kernel with popular plugins such as session keys and account recovery, and even write your own plugins. Kernel is compatible with [ERC-7579](https://erc7579.com/).
### Picking an EntryPoint
Kernel is compatible with EntryPoint versions v0.6 and v0.7.
If you are building a new application, we recommend using EntryPoint v0.7, which gives you the latest and greatest features and optimizations. If you already have an application using Kernel on EntryPoint v0.6, just stick with it -- it will be supported indefinitely.
In this guide, we will use EntryPoint v0.7.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/v0_1/accounts/kernel.ts:imports]
```
#### Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the Kernel account.
```ts
// [!include ~/snippets/v0_1/accounts/kernel.ts:clients]
```
#### Create the signer
Kernel accounts can work with a variety of signing algorithms such as ECDSA, passkeys, and multisig. In permissionless.js, the default Kernel account validates ECDSA signatures. [Any signer](/references/permissionless/how-to/signers) can be used as a signer for the Kernel account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/v0_1/accounts/kernel.ts:signer]
```
#### Create the Kernel account
:::info
For a full list of options for creating a Kernel account, take a look at the reference documentation page for [`signerToKernelSmartAccount`](/references/permissionless/v0_1/reference/accounts/signerToKernelSmartAccount).
:::
With a signer, you can create a Kernel account as such:
```ts
// [!include ~/snippets/v0_1/accounts/kernel.ts:smartAccount]
```
The Kernel address is computed deterministically from the signer, but you can optionally pass an `index` to create any number of different accounts using the same signer. You can also pass an `address` to use an already created Kernel account.
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/v0_1/accounts/kernel.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/v0_1/accounts/kernel.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/v0_1/accounts/kernel.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/v0_1/accounts/kernel.ts:submitBatch]
```
::::
import VersionWarning from "../../../VersionWarning"
## How to create and use a LightAccount with permissionless.js
[LightAccount](https://github.com/alchemyplatform/light-account) is the smart account implementation made by Alchemy and inspired by SimpleAccount.
It has a few additional [features](https://github.com/alchemyplatform/light-account?tab=readme-ov-file#features), such as transferrable ownership and upgradability.
This guide will show you how to create and use a LightAccount with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/v0_1/accounts/light.ts:imports]
```
#### Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the LightAccount.
```ts
// [!include ~/snippets/v0_1/accounts/light.ts:clients]
```
#### Create the LightAccount
:::info
For a full list of options for creating a LightAccount, take a look at the reference documentation page for [`signerToLightSmartAccount`](/references/permissionless/v0_1/reference/accounts/signerToLightSmartAccount).
:::
You can also pass an `address` to use an already created LightAccount.
```ts
// [!include ~/snippets/v0_1/accounts/light.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/v0_1/accounts/light.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/v0_1/accounts/light.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/v0_1/accounts/light.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/v0_1/accounts/light.ts:submitBatch]
```
::::
import VersionWarning from "../../../VersionWarning"
## How to create and use a Safe account with permissionless.js
[Safe](https://safe.global) is the most battle-tested Ethereum smart account provider. With their recent release of their ERC-4337 module, it is now possible to plug in Safe accounts to ERC-4337 bundlers and paymasters. This guide will walk you through how to create and use a Safe account with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/v0_1/accounts/safe.ts:imports]
```
#### Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the Safe account.
```ts
// [!include ~/snippets/v0_1/accounts/safe.ts:clients]
```
#### Create the signer
The Safe account will need to have a signer to sign user operations. In permissionless.js, the default Safe account validates ECDSA signatures. [Any permissionless.js-compatible signer](/references/permissionless/how-to/signers) can be used for the Safe account.
For example, to create a signer based on a private key:
```ts
// [!include ~/snippets/v0_1/accounts/safe.ts:signer]
```
#### Create the Safe account
:::info
For a full list of options for creating a Safe account, take a look at the reference documentation page for [`signerToSafeSmartAccount`](/references/permissionless/v0_1/reference/accounts/signerToSafeSmartAccount).
:::
With a signer, you can create a Safe account as such:
```ts
// [!include ~/snippets/v0_1/accounts/safe.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/v0_1/accounts/safe.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/v0_1/accounts/safe.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/v0_1/accounts/safe.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/v0_1/accounts/safe.ts:submitBatch]
```
#### Understanding the errors
If you're getting an error that starts with `GS`, it probably means that something went off with the Safe account. Checkout the Safe error codes [here](https://github.com/safe-global/safe-smart-account/blob/main/docs/error_codes.md).
::::
import VersionWarning from "../../../VersionWarning"
## How to create and use a SimpleAccount with permissionless.js
[SimpleAccount](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/accounts/SimpleAccount.sol) is the original reference sample implementation of an ERC-4337 made by the Eth-Infinitism team. Despite being a reference implementation, it is widely used in production. It allows for a single EOA signer to sign user operations for the account. This guide will show you how to create and use a SimpleAccount with permissionless.js.
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/v0_1/accounts/simple.ts:imports]
```
#### Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the SimpleAccount.
```ts
// [!include ~/snippets/v0_1/accounts/simple.ts:clients]
```
#### Create the SimpleAccount
:::info
For a full list of options for creating a SimpleAccount, take a look at the reference documentation page for [`signerToSimpleSmartAccount`](/references/permissionless/v0_1/reference/accounts/signerToSimpleSmartAccount).
:::
You can create a SimpleAccount with the canonical module addresses by specifying the factory address the account will be deployed from. You can also pass an `address` to use an already created SimpleAccount.
```ts
// [!include ~/snippets/v0_1/accounts/simple.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/v0_1/accounts/simple.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/v0_1/accounts/simple.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/v0_1/accounts/simple.ts:submitNft]
```
::::
import VersionWarning from "../../../VersionWarning"
## How to create and use a Trust smart account with permissionless.js
:::info
[Trust Wallet](https://trustwallet.com/), the author of [Barz](https://github.com/trustwallet/barz), provides a smart contract security monitoring service for each and every Barz deployed on-chain including Barz created by SDK.
This is a service that TrustWallet provides to builders to build innovative products on top of a secure foundation.
Monitoring will automatically start as soon as the Barz account is deployed on-chain; for projects wanting to get security monitoring information, reach out to the smart wallet channel in [TrustWallet Discord](https://discord.gg/trustwallet).
:::
Trust Wallet is one of the most trusted wallet provider empowering more than 122 million users and is the first Web3 wallet to be [certified by ISO](https://trustwallet.com/security).
After thorough development and extensive security audits, Trust Wallet launched the [Smart Wallet](https://trustwallet.com/swift) powered by account abstraction.
With the recent opensource of their Smart Wallet system Barz, their smart account solution, together with their security infrastructure is disclosed for public to support builders build products on a secure foundation.
This guide will walk you through how to create and use a Barz account with permissionless.js
### Steps
::::steps
#### Import the required packages
```ts
// [!include ~/snippets/v0_1/accounts/trustwallet.ts:imports]
```
#### Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the Trust Smart Account.
```ts
// [!include ~/snippets/v0_1/accounts/trustwallet.ts:clients]
```
#### Create the TrustAccount
:::info
For a full list of options for creating a TrustAccount, take a look at the reference documentation page for [`signerToTrustSmartAccount`](/references/permissionless/v0_1/reference/accounts/signerToTrustSmartAccount).
:::
You can also pass an `address` to use an already created TrustAccount.
```ts
// [!include ~/snippets/v0_1/accounts/trustwallet.ts:smartAccount]
```
#### Create the smart account client
The smart account client is a permissionless.js client that is meant to serve as an almost drop-in replacement for viem's [walletClient](https://viem.sh/docs/clients/wallet).
```ts
// [!include ~/snippets/v0_1/accounts/trustwallet.ts:smartAccountClient]
```
#### Send a transaction
Transactions using permissionless.js simply wrap around user operations. This means you can switch to permissionless.js from your existing viem EOA codebase with minimal-to-no changes.
```ts
// [!include ~/snippets/v0_1/accounts/trustwallet.ts:submit]
```
This also means you can also use viem Contract instances to transact without any modifications.
```ts
// [!include ~/snippets/v0_1/accounts/trustwallet.ts:submitNft]
```
You can also send an array of transactions in a single batch.
```ts
// [!include ~/snippets/v0_1/accounts/trustwallet.ts:submitBatch]
```
::::
import VersionWarning from "../../../VersionWarning"
## How to conditionally sponsor a user operation
The permissionless.js library allows you to conditionally sponsor a user operation. This can be useful in situations like:
* Sponsor only first 10 transactions
* Sponsor if they have a specific NFT
The use cases can be many, but the general idea is to use the `middleware.sponsorUserOperation` parameter to conditionally sponsor a user operation.
Let's take an example where you want to sponsor only the first 10 transactions.
:::steps
#### Create the clients
First we must create the public, bundler, and (optionally) paymaster clients that will be used to interact with the SimpleAccount.
```ts
// [!include ~/snippets/v0_1/how-to/paymasters/conditional-sponsoring.ts:client]
```
#### Create an account
Now, create an account. This can any of the accounts supported by permissionless.js or custom accounts conforming to the interface. For this example, we'll use a Simple account.
```ts
// [!include ~/snippets/v0_1/how-to/paymasters/conditional-sponsoring.ts:account]
```
#### Create the smart account client with the custom paymaster logic
When creating the `smartAccountClient`, we can pass in a `middleware.sponsorUserOperation` function that will be called before a user operation is signed and sent.
This function can conditionally choose to add a paymaster to the user operation if the desired conditions are met.
```ts
// [!include ~/snippets/v0_1/how-to/paymasters/conditional-sponsoring.ts:smart-account-client]
```
:::
import VersionWarning from "../../../VersionWarning"
import SmartAccounts from "./smartAccounts.mdx"
## How to use an Arcana Auth signer with permissionless.js
[Arcana Auth](https://www.arcana.network/) offers a self-custodial Web3 wallet embedded within applications, utilizing asynchronous distributed key generation algorithms for enhanced security and privacy. This wallet, accessible without the need for a browser extension, allows authenticated users to instantly access and sign blockchain transactions within the app environment.
### Setup
To use Arcana Auth with permissionless.js, first create an application that integrates with Arcana Auth.
* Refer to the [Arcana Auth documentation site](https://docs.arcana.network/) for instructions on setting up an application with the Arcana Auth.
* For a quick start, Arcana Auth provides a web app guide, available [here](https://docs.arcana.network/auth/sdk-installation/).
### Integration
Integrating permissionless.js with Arcana Auth is straightforward after setting up the project. Arcana Auth provides an Externally Owned Account (EOA) wallet to use as a signer with accounts created using permissionless.js.
#### Create the authProvider object
After following the Arcana Auth documentation, you will have access to a `authProvider` object as shown below which you can use to create a `SmartAccountSigner` object:
```typescript
// [!include ~/snippets/v0_1/signers/arcana.ts:main]
```
#### Use with permissionless.js
import VersionWarning from "../../../VersionWarning"
import SmartAccounts from "./smartAccounts.mdx"
## How to use a DFNS signer with permissionless.js
[Dfns](https://www.dfns.co/) is an MPC/TSS Wallet-as-a-Service API/SDK provider. Dfns aims to optimize the balance of security and UX by deploying key shares into a decentralized network on the backend while enabling wallet access via biometric open standards on the frontend like Webauthn. Reach out [here](https://www.dfns.co/) to set up a sandbox environment to get started.
### Setup
To use Dfns with permissionless.js, first create an application that integrates with Dfns.
* Refer to the [Dfns documentation site](https://docs.dfns.co/d/) for instructions on setting up an application with the Dfns.
### Integration
Integrating permissionless.js with Dfns is straightforward after setting up the project. Dfns provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Set up Dfns
After following the Dfns documentation, you will have access to a `dfnsWallet` object as shown below:
```typescript
// [!include ~/snippets/v0_1/signers/dfns.ts:main]
```
#### Use with permissionless.js
import VersionWarning from "../../../VersionWarning"
## How to use a Dynamic signer with permissionless.js
permissionless.js allows you to plug in custom signers to control the accounts that you create. Dynamic is an embedded wallet provider that allows you to easily onboard users to your dapp. It is possible to use Dynamic as a signer with permissionless.js, allowing you to use Dynamic to create and control smart accounts and sign transactions.
:::steps
#### Install the dependencies
```bash
npm i @dynamic-labs/sdk-react-core @dynamic-labs/wagmi-connector @dynamic-labs/ethereum permissionless viem wagmi
```
#### Create the Dynamic provider
Following Dynamic's [quickstart guide](https://docs.dynamic.xyz/quickstart), set up the Dynamic provider in your app. Also integrate the DynamicWagmiConnector, which will allow you to use Dynamic as a signer with permissionless.js.
```ts
import {
DynamicContextProvider,
DynamicWidget,
} from "@dynamic-labs/sdk-react-core";
import { DynamicWagmiConnector } from "@dynamic-labs/wagmi-connector";
import { EthereumWalletConnectors } from "@dynamic-labs/ethereum";
export const App = () => {
return (
);
};
```
#### Create the SmartAccountClient
Create the smart account client using the Dynamic signer. Note: DynamicWagmiConnector internally sets up the WagmiConfig, so there is no need to do it separately. This is where you would configure what smart account implementation (e.g. [Safe](/references/permissionless/how-to/accounts/use-safe-account), [Kernel](/references/permissionless/how-to/accounts/use-kernel-account), Biconomy, [TrustWallet](/references/permissionless/how-to/accounts/use-trustwallet-account) [SimpleAccount](/references/permissionless/how-to/accounts/use-simple-account)) and what paymaster logic you want to use.
```ts
import { createSmartAccountClient, walletClientToSmartAccountSigner, ENTRYPOINT_ADDRESS_V06 } from "permissionless";
import { signerToSimpleSmartAccount } from "permissionless/accounts";
import { useWalletClient } from "wagmi";
import { createPublicClient, http, zeroAddress } from "viem";
import { sepolia } from "viem/chains";
const {
data: walletClient
} = useWalletClient()
const publicClient = createPublicClient({
chain: sepolia, // or whatever chain you are using
transport: http()
})
const signer = walletClientToSmartAccountSigner(walletClient)
const simpleSmartAccountClient = await signerToSimpleSmartAccount(publicClient, {
entryPoint: ENTRYPOINT_ADDRESS_V06,
signer,
})
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccountClient,
entryPoint: ENTRYPOINT_ADDRESS_V06,
chain: sepolia, // or whatever chain you are using
bundlerTransport: http("", {
timeout: 30_000 // optional
}),
middleware: {
gasPrice: async () => (await bundlerClient.getUserOperationGasPrice()).fast,
sponsorUserOperation: pimlicoPaymaster.sponsorUserOperation,
},
})
```
#### Send a transaction
You can now send transactions as normal. The `sponsorUserOperation` function will be called before each transaction is signed and sent, applying the custom paymaster logic you have set.
```ts
const txHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
data: "0x",
value: BigInt(0)
})
```
:::
import VersionWarning from "../../../VersionWarning"
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Fireblocks signer with permissionless.js
[Fireblocks](https://www.fireblocks.com/) is a user-friendly platform designed for building blockchain-based products and managing digital asset operations. It uses a direct custody approach, combining high performance with zero counterparty risk and multi-layered security. The platform includes secure MPC-based digital asset wallets, a policy engine for governance and transaction rules, and comprehensive treasury management. Fireblocks' security framework features multiple layers, including MPC-CMP technology, secure enclaves, and a robust policy engine, ensuring protection against cyberattacks, internal threats, and human errors. It's widely used for various operations like treasury, trading, and managing NFTs, smart contracts, and user wallets.
### Setup
To use Fireblocks with permissionless.js, first create an application that integrates with Fireblocks.
* Refer to the [Fireblocks documentation site](https://developers.fireblocks.com/) for instructions on setting up an application with the Fireblocks.
* For a quick start, Fireblocks provides a guide, available [here](https://developers.fireblocks.com/docs/quickstart).
### Integration
Integrating permissionless.js with Fireblocks is straightforward after setting up the project. Fireblocks provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Fireblocks object
After following the Fireblocks documentation, you will have access to a `FireblocksWeb3Provider` object as shown below which you can use to create a `SmartAccountSigner` object:
```typescript
// [!include ~/snippets/v0_1/signers/fireblocks.ts:main]
```
#### Use with permissionless.js
import VersionWarning from "../../../VersionWarning"
## Signers for permissionless.js
Smart accounts are able to define custom authentication and authorization schemes, but still require signatures to validate user operations coming from signers. permissionless.js defines the `SmartAccountSigner` interface, which is a generic interface that signers for smart accounts can implement. While by default these signers have full control over the smart account, it's possible to define custom roles and permissions for signers, and mix-and-match them to create complex multi-signature schemes, as well as rotate signers and revoke their access.
In addition to pure EOA signers, permissionless.js supports a variety of third-party wallet services as signers for smart accounts. This allows you to leverage the UIs and infrastructure of these services, while still using smart accounts supported by permissionless.js.
import VersionWarning from "../../../VersionWarning"
## How to Integrate Pimlico with Lit Protocol OTP Authentication
This how-to guide will walk you through the steps to integrate Lit Protocol's OTP sign-in with email, SMS, and Whatsapp with a smart account whose user operations are relayed and sponsored by Pimlico.
:::info
Lit Protocol is an Authentication solution that lets you create and manage distributed cryptographic key-pairs for condition-based encryption and programmatic signing. A decentralized key management network, Lit can be used in place of centralized key custodians and other key management solutions. For more information on how Lit Protocol works, visit [their documentation page](https://developer.litprotocol.com/resources/how-it-works).
:::
[Stytch](https://stytch.com/) will be used to manage the OTP authentication flow.
::::steps
#### Install the required packages
```bash
npm install stytch @lit-protocol/pkp-ethers @lit-protocol/lit-auth-client @lit-protocol/auth-helpers @lit-protocol/types @lit-protocol/lit-node-client-nodejs
```
#### Make an account with Stytch and get the Project ID and Secret
You can sign up for a Stytch account [here](https://stytch.com/). Once you have an account, you can find your Project ID and Secret in the [Stytch Dashboard API Keys page](https://stytch.com/dashboard/api-keys).

#### Create a Stytch client with your Project ID and Secret
```ts
const stytchClient = new stytch.Client({
project_id: "project-test-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
secret: "secret-test-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
});
```
#### Send an OTP to the user's email, SMS, or Whatsapp
:::code-group
```ts [email]
const stytchResponse = await stytchClient.otps.email.loginOrCreate({
email: "",
})
```
```ts [SMS]
const stytchResponse = await stytchClient.otps.sms.loginOrCreate({
phone_number: "",
})
```
```ts [WhatsApp]
const stytchResponse = await stytchClient.otps.whatsapp.loginOrCreate({
phone_number: "",
})
```
:::
#### Authenticate the user with the OTP and get a session token
:::code-group
```ts [email]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.email_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
```ts [SMS]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.phone_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
```ts [WhatsApp]
const authResponse = await stytchClient.otps.authenticate({
method_id: stytchResponse.phone_id,
code: otpResponse.code,
session_duration_minutes: 60 * 24 * 7,
})
const sessionStatus = await stytchClient.sessions.authenticate({
session_token: authResponse.session_token,
})
```
:::
#### Get a Lit Relay Server API Key
You can get a Lit Relay Server API Key by filling out [the Lit Protocol team's form](https://forms.gle/RNZYtGYTY9BcD9MEA)
#### Mint a PKPs through Lit Protocol
```ts
const litClient = new LitAuthClient({
litRelayConfig: {
relayApiKey: '',
}
});
const session = litClient.initProvider(ProviderType.StytchOtp, {
userId: sessionStatus.session.user_id,
appId: "project-test-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
})
const authMethod = await session.authenticate({
accessToken: sessionStatus.session_jwt
})
await session.mintPKPThroughRelayer(authMethod)
const pkps = await session.fetchPKPsThroughRelayer(authMethod)
```
#### Generate the Controller Session Signatures
```ts
const litNodeClient = new LitNodeClientNodeJs({
litNetwork: 'serrano',
debug: false,
})
await litNodeClient.connect();
const resourceAbilities = [
{
resource: new LitActionResource("*"),
ability: LitAbility.PKPSigning,
},
];
const sessionKeyPair = litNodeClient.getSessionKey();
const authNeededCallback = async (params: AuthCallbackParams) => {
const response = await litNodeClient.signSessionKey({
sessionKey: sessionKeyPair,
statement: params.statement,
authMethods: [authMethod],
pkpPublicKey: pkp[pkp.length - 1].publicKey,
expiration: params.expiration,
resources: params.resources,
chainId: 1,
});
return response.authSig;
};
const sessionSigs = await litNodeClient.getSessionSigs({
chain: "ethereum",
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString(),
resourceAbilityRequests: resourceAbilities,
sessionKey: sessionKeyPair,
authNeededCallback
}).catch((err) => {
console.log("error while attempting to access session signatures: ", err)
throw err;
});
```
#### Initialize the PKP Wallet
We will now generate a wallet that can act a regular Ethers.js wallet, but will use the PKPs minted through Lit Protocol to sign transactions under the hood.
```ts
const pkpWallet = new PKPEthersWallet({
pkpPubKey: pkp[pkp.length - 1].publicKey,
rpc: "", // e.g. https://sepolia.rpc.thirdweb.com
controllerSessionSigs: sessionSigs
});
await pkpWallet.init();
```
#### Use the PKP Wallet to sign user operations and send them through Pimlico
You can now use the `pkpWallet` as a regular Ethers.js wallet to sign user operations. To submit a user operation to Pimlico, you can follow the steps to [sponsor a user operation with Pimlico's verifying paymaster](/references/paymaster/verifying-paymaster/usage) and/or [submit a user operation through Pimlico's bundler](/references/bundler/usage). If you would like to integrate Lit Protocol with the full flow of generating, signing, and submitting a user operation, you can follow the steps in [tutorial 1](/references/permissionless/tutorial/tutorial-1), replacing the signing step with the PKP wallet and using `pkpWallet.address` as the owner address of the smart account.
Modified from [tutorial 1](/references/permissionless/tutorial/tutorial-1), an example of how to use the PKP wallet to sign a user operation is shown below:
```typescript
const signature = await pkpWallet.signMessage(
ethers.utils.arrayify(await entryPoint.getUserOpHash(userOperation)),
)
userOperation.signature = signature
```
And an example of how you would generate the initCode for a SimpleAccount using the PKP wallet is shown below:
```typescript
const initCode = ethers.utils.hexConcat([
SIMPLE_ACCOUNT_FACTORY_ADDRESS,
simpleAccountFactory.interface.encodeFunctionData("createAccount", [pkpWallet.address, 0]),
])
```
::::
import VersionWarning from "../../../VersionWarning"
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Magic signer with permissionless.js
[Magic](https://magic.link/) is a popular embedded wallet provider that supports social logins. While social logins are great, your users still need to onramp in order to pay for gas, which introduces significant friction.
By combining permissionless.js with Magic, you can use Magic to enable a smooth social login experience, while using permissionless.js accounts as the smart wallets to sponsor gas for users, batch transactions, and more.
### Setup
To use Magic with permissionless.js, first create an application that integrates with Magic.
* Refer to the [Magic documentation site](https://magic.link/docs/home/welcome) for instructions on setting up an application with the Magic SDK.
* For a quick start, Magic provides a CLI to create a starter project, available [here](https://magic.link/docs/home/quickstart/cli#run-make-magic).
### Integration
Integrating permissionless.js with Magic is straightforward after setting up the Magic SDK. Magic provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Magic object
After following the Magic documentation, you will have access to a `MagicBase` object as shown below that you can use to create the `SmartAccountSigner` object:
```typescript
// [!include ~/snippets/v0_1/signers/magic.ts:main]
```
#### Use with permissionless.js
import VersionWarning from "../../../VersionWarning"
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Para signer with permissionless.js
[Para](https://www.getpara.com/) offers a signing solution enabling the creation of secure, embedded MPC wallets accessible via email or social login. These wallets, compatible across different applications, offer portability, recoverability, and programmability, eliminating the need for users to establish separate signers or contract accounts for each application.
### Setup
To use Para with permissionless.js, first create an application that integrates with Para.
* Refer to the [Para documentation site](https://docs.getpara.com/) for instructions on setting up an application with the Para.
* For a quick start, Para provides an example hub, available [here](https://docs.getpara.com/getting-started/examples).
### Integration
Integrating permissionless.js with Para is straightforward after setting up the project. Para provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Para signer
After following the Para documentation, you will have access to a `ParaWeb3Provider` object that you can use to create a `SmartAccountSigner` object:
```typescript
// [!include ~/snippets/v0_1/signers/para.ts:main]
```
#### Use with permissionless.js
import VersionWarning from "../../../VersionWarning"
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Particle Network signer with permissionless.js
[Particle Network](https://particle.network/) is an intent-centric, modular wallet-as-a-service (WaaS). By utilizing MPC-TSS for key management, Particle can streamline onboarding via familiar Web2 methods such as Google, emails, and phone numbers.
By combining permissionless.js with Particle, you can use Particle to enable a smooth social login experience, while using ZeroDev as the smart wallet to sponsor gas for users, batch transactions, and more.
### Setup
To use Particle Network with permissionless.js, first create an application that integrates with Particle Network.
* Refer to the [Particle Network documentation site](https://docs.particle.network/) for instructions on setting up an application with the Particle Network.
* For a quick start, Particle Network provides a guide, available [here](https://docs.particle.network/getting-started/get-started).
### Integration
Integrating permissionless.js with Particle Network is straightforward after setting up the project. Particle Network provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Particle Network object
After following the Particle Network documentation, you will have access to a `ParticleProvider` object as shown below that you can use to create a `SmartAccountSigner` object:
```typescript
// [!include ~/snippets/v0_1/signers/particle-network.ts:main]
```
#### Use with permissionless.js
import VersionWarning from "../../../VersionWarning"
## How to use a Privy signer with permissionless.js
permissionless.js allows you to plug in custom signers to control the accounts that you create. Privy is an embedded wallet provider that allows you to easily onboard users to your dapp. It is possible to use Privy as a signer with permissionless.js, allowing you to use Privy to create and control smart accounts and sign transactions.
Additionally you may want to look at Privy's guide on working with permissionless.js [here](https://docs.privy.io/guide/react/recipes/account-abstraction/pimlico).
:::steps
#### Install the dependencies
```bash
npm i @privy-io/react-auth @privy-io/wagmi-connector permissionless viem wagmi
```
#### Create the Privy provider
Following Privy's [quickstart guide](https://docs.privy.io/guide/quickstart), set up the Privy provider in your app. Also integrate the PrivyWagmiConnector, which will allow you to use Privy as a signer with permissionless.js.
```ts
import { PrivyProvider } from '@privy-io/react-auth';
import { PrivyWagmiConnector } from '@privy-io/wagmi-connector';
"}
config={{
embeddedWallets: {
createOnLogin: "all-users",
},
}}
>
{children}
;
```
#### Set Privy as the active wallet
In your app, set Privy's embedded wallet as the active wallet for wagmi
```ts
import { useWallets } from "@privy-io/react-auth";
import { usePrivyWagmi } from "@privy-io/wagmi-connector";
import { useEffect } from "react";
const { wallets } = useWallets();
const embeddedWallet = wallets.find(
(wallet) => wallet.walletClientType === "privy"
);
const { setActiveWallet } = usePrivyWagmi()
useEffect(() => setActiveWallet(embeddedWallet), [embeddedWallet])
```
#### Create the SmartAccountClient
Create the smart account client using the Privy signer. This is where you would configure what smart account implementation (e.g. [Safe](/references/permissionless/how-to/accounts/use-safe-account), [Kernel](/references/permissionless/how-to/accounts/use-kernel-account), Biconomy, [TrustWallet](/references/permissionless/how-to/accounts/use-trustwallet-account), [SimpleAccount](/references/permissionless/how-to/accounts/use-simple-account)) and what paymaster logic you want to use.
```ts
import { createSmartAccountClient, walletClientToSmartAccountSigner, ENTRYPOINT_ADDRESS_V06 } from "permissionless";
import { signerToSimpleSmartAccount } from "permissionless/accounts";
import { useWalletClient } from "wagmi";
import { createPublicClient, http, zeroAddress } from "viem";
import { sepolia } from "viem/chains";
const {
data: walletClient
} = useWalletClient()
const publicClient = createPublicClient({
chain: sepolia, // or whatever chain you are using
transport: http()
})
const signer = walletClientToSmartAccountSigner(walletClient)
const simpleSmartAccountClient = await signerToSimpleSmartAccount(publicClient, {
entryPoint: ENTRYPOINT_ADDRESS_V06,
signer,
})
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccountClient,
entryPoint: ENTRYPOINT_ADDRESS_V06,
chain: sepolia, // or whatever chain you are using
bundlerTransport: http("", {
timeout: 30_000 // optional
}),
middleware: {
gasPrice: async () => (await bundlerClient.getUserOperationGasPrice()).fast,
sponsorUserOperation: pimlicoPaymaster.sponsorUserOperation,
},
})
```
#### Send a transaction
You can now send transactions as normal. The `sponsorUserOperation` function will be called before each transaction is signed and sent, applying the custom paymaster logic you have set.
```ts
const txHash = await smartAccountClient.sendTransaction({
to: zeroAddress,
data: "0x",
value: BigInt(0)
})
```
:::
For a full example, see the [example permissionless + Privy app](https://github.com/pimlicolabs/privy-demo/blob/main/src/components/privy/privy.tsx).
import VersionWarning from "../../../VersionWarning"
:::code-group
```ts [SimpleAccount]
// [!include ~/snippets/v0_1/signers/accounts/simpleSmartAccount.ts:main]
```
```ts [Safe Account]
// [!include ~/snippets/v0_1/signers/accounts/safeSmartAccount.ts:main]
```
```ts [Kernel Account]
// [!include ~/snippets/v0_1/signers/accounts/ecdsaKernelSmartAccount.ts:main]
```
```ts [Biconomy Account]
// [!include ~/snippets/v0_1/signers/accounts/biconomySmartAccount.ts:main]
```
```ts [TrustWallet Account]
// [!include ~/snippets/v0_1/signers/accounts/trustSmartAccount.ts:main]
```
:::
import VersionWarning from "../../../VersionWarning"
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Turnkey signer with permissionless.js
[Turnkey](https://turnkey.com/) is a key infrastructure provider with a great developer API and a powerful security policy engine.
By combining permissionless.js with Turnkey, you can create **custodial AA wallets** whose security is provided by Turnkey, with powerful functionalities such as sponsoring gas, batching transactions, etc.
### Setup
To use Turnkey with permissionless.js, first create an application that integrates with Turnkey.
* Refer to the [Turnkey documentation site](https://docs.turnkey.com/) for instructions on setting up an application with the Turnkey.
* For a quick start, Turnkey provides examples, available [here](https://docs.turnkey.com/getting-started/examples).
### Integration
Integrating permissionless.js with Turnkey is straightforward after setting up the project. Turnkey provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the TurnkeyClient and a Turnkey viem account
After following the Turnkey documentation, you will have access to a `TurnkeyClient`. An example is shown below that you can use to create a `SmartAccountSigner` object:
```typescript
// [!include ~/snippets/v0_1/signers/turnkey.ts:main]
```
#### Use with permissionless.js
import VersionWarning from "../../../VersionWarning"
import SmartAccounts from "./smartAccounts.mdx"
## How to use a Web3Auth signer with permissionless.js
[Web3Auth](https://web3auth.io/) is a popular embedded wallet provider that supports social logins. While social logins are great, your users still need to onramp in order to pay for gas, which introduces significant friction.
By combining permissionless.js with Web3Auth, you can use Web3Auth to enable a smooth social login experience, while using permissionless.js accounts as the smart wallets to sponsor gas for users, batch transactions, and more.
### Setup
To use Web3Auth with permissionless.js, first create an application that integrates with Web3Auth.
* Refer to the [Web3Auth documentation site](https://web3auth.io/docs/index.html) for instructions on setting up an application with the Web3Auth.
* For a quick start, Web3Auth provides example starter projects, available [here](https://web3auth.io/docs/examples?product=Plug+and+Play\&sdk=Plug+and+Play+Web+Modal+SDK).
### Integration
Integrating permissionless.js with Web3Auth is straightforward after setting up the project. Web3Auth provides an Externally Owned Account (EOA) wallet to use as a signer with permissionless.js accounts.
#### Create the Web3Auth object
After following the Web3Auth documentation, you will have access to a `web3auth` object as shown below that you can use to create a `SmartAccountSigner` object:
```typescript
// [!include ~/snippets/v0_1/signers/web3auth.ts:main]
```
#### Use with permissionless.js
For a full example, see the [example permissionless + Web3Auth app](https://github.com/pimlicolabs/web3auth-demo/blob/main/src/components/web3auth/web3auth.tsx).
import VersionWarning from "../../../VersionWarning"
## signerToKernelSmartAccount
Creates a KernelAccount instance controlled by a `SmartAccountSigner`. Check out [this guide](/references/permissionless/how-to/accounts/use-kernel-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
import { signerToKernelSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { signer } from "./signer"
const kernelAccount = await signerToEcdsaKernelSmartAccount(publicClient, {
signer,
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", // v0.6 entrypoint
index: 0n, // optional
address: "0x..." // optional, only if you are using an already created account
});
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `KernelSmartAccount`
The Kernel smart account instance.
### Parameters
#### signer
* **Type:** `SmartAccountSigner`
The signer that will be used to sign messages and user operations.
#### entryPoint
* **Type:** `Address`
The address of the EntryPoint contract.
#### index (optional)
* **Type:** `bigint`
The index (which is basically a salt) that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
import VersionWarning from "../../../VersionWarning"
## signerToLightSmartAccount
Creates a LightAccount instance controlled by a `SmartAccountSigner`.
### Usage
:::code-group
```ts [example.ts]
import { signerToLightSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { signer } from "./signer"
const lightAccount = await signerToLightSmartAccount(publicClient, {
signer,
lightAccountVersion: 'v1.1.0',
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
})
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `LightSmartAccount`
The smart account instance.
### Parameters
#### signer
* **Type:** `SmartAccountSigner`
The signer that will be used to sign messages and user operations.
#### lightAccountVersion
* **Type:** `LightAccountVersion`
The light account version. Currently only `v1.1.0` is supported.
#### factoryAddress
* **Type:** `Address`
The address of the light account factory that will be used to deploy the smart account.
:::info
The canonical light account factory for v1.1.0 is `0x00004EC70002a32400f8ae005A26081065620D20`
:::
#### entryPoint
* **Type:** `Address`
The address of the EntryPoint contract.
#### index (optional)
* **Type:** `bigint`
It represents salt nonce that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
import VersionWarning from "../../../VersionWarning"
## signerToSafeSmartAccount
Creates a Safe smart account instance controlled by a `SmartAccountSigner`.
### Usage
:::code-group
```ts [example.ts]
import { ENTRYPOINT_ADDRESS_V07 } from "permissionless"
import { signerToSafeSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { signer } from "./signer"
const safeAccount = await signerToSafeSmartAccount(publicClient, {
signer,
safeVersion: "1.4.1",
entryPoint: ENTRYPOINT_ADDRESS_V07,
})
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `SafeSmartAccount`
The smart account instance.
### Parameters
#### signer
* **Type:** `SmartAccountSigner`
The signer that will be used to sign messages and user operations.
#### safeVersion
* **Type:** `1.4.1`
The version of the Safe contract that will be used.
#### entryPoint
* **Type:** `Address`
The address of the EntryPoint contract.
#### saltNonce (optional)
* **Type:** `bigint`
The salt nonce that will be used to deploy the smart account. If not provided, `0` will be used.
#### addModuleLibAddress (optional)
* **Type:** `Address`
The address of the `AddModuleLib` contract. If not provided, the default address for the Safe version will be used.
#### safe4337ModuleAddress (optional)
* **Type:** `Address`
The address of the `Safe4337Module` contract. If not provided, the default address for the Safe version will be used.
#### safeProxyFactoryAddress (optional)
* **Type:** `Address`
The address of the `SafeProxyFactory` contract. If not provided, the default address for the Safe version will be used.
#### safeSingletonAddress (optional)
* **Type:** `Address`
The address of the `SafeSingleton` contract. If not provided, the default address for the Safe version will be used.
#### multiSendAddress (optional)
* **Type:** `Address`
The address of the `MultiSend` contract. If not provided, the default address for the Safe version will be used.
#### multiSendCallOnlyAddress (optional)
* **Type:** `Address`
The address of the `MultiSendCallOnly` contract. If not provided, the default address for the Safe version will be used.
#### safeModules (optional)
* **Type:** `Address[]`
:::info
The ERC-4337 module will always be added and does not need to be provided.
:::
The addresses of the Safe modules that will be added during deployment.
#### setupTransactions (optional)
* **Type:** `Call[]`
:::info
This can be useful, for example, to approve tokens to an ERC-20 paymaster contract so it can pay for the gas of the first user operation.
:::
An array of calls that will be executed during the deployment of the smart account (when the `initCode` is executed).
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
import VersionWarning from "../../../VersionWarning"
## signerToSimpleSmartAccount
Creates a SimpleAccount instance controlled by a `SmartAccountSigner`.
### Usage
:::code-group
```ts [example.ts]
import { signerToSimpleSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { signer } from "./signer"
const simpleAccount = await signerToSimpleSmartAccount(publicClient, {
signer,
factoryAddress: "0x9406Cc6185a346906296840746125a0E44976454",
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
})
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `SimpleSmartAccount`
The smart account instance.
### Parameters
#### signer
* **Type:** `SmartAccountSigner`
The signer that will be used to sign messages and user operations.
#### factoryAddress
* **Type:** `Address`
The address of the simple account factory that will be used to deploy the smart account.
:::info
The canonical simple account factory for v0.6 of the EntryPoint is `0x9406Cc6185a346906296840746125a0E44976454`
:::
#### entryPoint
* **Type:** `Address`
The address of the EntryPoint contract.
#### index (optional)
* **Type:** `bigint`
It represents salt nonce that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
import VersionWarning from "../../../VersionWarning"
## signerToTrustSmartAccount
Creates a Trust Wallet Smart Account instance controlled by a `SmartAccountSigner`. Check out [this guide](/references/permissionless/how-to/accounts/use-trustwallet-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
import { signerToTrustSmartAccount } from "permissionless/accounts"
import { publicClient } from "./publicClient"
import { signer } from "./signer"
const account = await signerToTrustSmartAccount(client, {
signer,
entryPoint: ENTRYPOINT_ADDRESS_V06,
address: "0x..." // optional, only if you are using an already created account
})
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `TrustSmartAccount`
The Trust smart account instance.
### Parameters
#### signer
* **Type:** `SmartAccountSigner`
The signer that will be used to sign messages and user operations.
#### entryPoint
* **Type:** `Address`
The address of the EntryPoint contract.
#### index (optional)
* **Type:** `bigint`
The index (which is basically a salt) that will be used to deploy the smart account. If not provided, `0` will be used.
#### address (optional)
* **Type:** `Address`
:::warning
If you provide an address, the smart account can not be deployed. This should be used if you want to interact with an existing smart account.
:::
The address of the smart account. If not provided, the determinstic smart account address will be used.
import VersionWarning from "../../../VersionWarning"
## estimateUserOperationGas
Estimate the gas limits for a User Operation.
### Usage
:::code-group
```ts [example.ts]
import { bundlerClient } from "./client"
const gasEstimate = await bundlerClient.estimateUserOperationGas({
userOperation: {
sender: "0x0C123D90Da0a640fFE54a2359D159629065775C5",
nonce: 3n,
initCode: "0x",
callData: "0x18dfb3c7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c09000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c090000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000043a4eacb7839f202d9cab465dbdd77d4fabe0a1800000000000000000000000000000000000000000000000003782dace9d90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000982e148216e3aa6b38f9d901ef578b5c06dd750200000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000",
callGasLimit: 50305n,
maxPriorityFeePerGas: 113000100n,
paymasterAndData: "0xe93eca6595fe94091dc1af46aac2a8b5d79907700000000000000000000000000000000000000000000000000000000065133b6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005d3d07ae8973ba1b8a26d0d72d8882dfa97622942a63c4b655f4928385ce587f6aa2fa1ab347e615d5f39e1214d18f426375da8a01514fb126eb0bb29f0c319d1b",
signature: "0xf1513a8537a079a4d728bb87099b2c901e2c9034e60c95a4d41ac1ed75d6ee90270d52b48af30aa036e9a205ea008e1c62b317e7b3f88b3f302d45fb1ba76a191b"
},
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
})
/**
* {
* preVerificationGas: 56135n,
* verificationGasLimit: 132202n,
* callGasLimit: 74900n
* }
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions } from 'permissionless'
export const bundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(bundlerActions)
```
:::
### Returns
* **Type:** `EstimateUserOperationGasReturnType`
* **preVerificationGas:** `bigint`
* **verificationGasLimit:** `bigint`
* **callGasLimit:** `bigint`
The estimated User Operation gas limits.
### Parameters
#### userOperation
* **Type:** `UserOperation`
The User Operation object, with `callGasLimit`, `preVerificationGas` and `verificationGasLimit` being optional.
#### entryPoint
* **Type:** `Address`
The address of the entry point contract.
### JSON-RPC Method
[`eth_estimateUserOperationGas`](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace)
import VersionWarning from "../../../VersionWarning"
## getUserOperationByHash
Return a User Operation based on a User Operation hash.
### Usage
:::code-group
```ts [example.ts]
import { bundlerClient } from "./client"
const userOperationByHash = await bundlerClient.getUserOperationByHash({
hash: "0x3c037f957fde5d87e35d5b8582f6c274343bcf3bc0e010d72fc2de0e27f4a6aa"
})
/**
* {
"userOperation": {
"sender": "0x277F6C1D8d4faFA3d8DcC837489cd69d86c682BA",
"nonce": 3n,
"initCode": "0x67df23f0c2a43bcc7727e5aa76112f286619ea0e5fbfb9cf00000000000000000000000035764204352772776d5f4c9aae84e06e03a143c00000000000000000000000000000000000000000000000000000000000000000",
"callData": "0xb61d27f6000000000000000000000000c7cae1986efb06d5e8041724b4a53ad165ffa022000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
"callGasLimit": 1500000n,
"verificationGasLimit": 500000n,
"preVerificationGas": 54728n,
"maxFeePerGas": 3620810421n,
"maxPriorityFeePerGas": 3620810391n,
"paymasterAndData": "0x1dbab8914a76e9c294bb192d6cec9dbec193922100000000000000000000000000000000000000000000000000000000651357d50000000000000000000000000000000000000000000000000000000000000000f0aa703445cb7ecf59fc0b5411af08b93afc357356af37eba6d49b2d91bdbc83621fdc45be2e833bdd76038c2e284f1c44a0f150360c3966bb8510d618238aaa1b",
"signature": "0x073fecd688deed195a6210fec2851a33c7556b531753b698bebfd5c5491ff64520776269a5ce5f80caa4456258c725feeccea4265f007e269c7912a8d3f1c8531b"
},
"entryPoint": "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
"transactionHash": "0x975a6bac5f562a2cb8218945b4e4304f6d10afa4aa6f02830e6d6dcafc450d66",
"blockHash": "0x195f37194aedaf129e756f35613178aa197949f0cbdcd684f8573b1afc34924c",
"blockNumber": 40552848n
}
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions } from 'permissionless'
export const bundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(bundlerActions)
```
:::
### Returns
* **Type:** `UserOperationByHashResult`
The User Operation information with Entry Point, TransactionHash, BlockHash, and BlockNumber fields.
### Parameters
#### hash
* **Type:** `Hash`
The User Operation hash.
### JSON-RPC Method
[`eth_estimateUserOperationByHash`](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace)
import VersionWarning from "../../../VersionWarning"
## getUserOperationReceipt
Return a User Operation receipt based on a User Operation hash.
### Usage
:::code-group
```ts [example.ts]
import { bundlerClient } from "./client"
const receipt = await bundlerClient.getUserOperationReceipt({
hash: "0x3c037f957fde5d87e35d5b8582f6c274343bcf3bc0e010d72fc2de0e27f4a6aa"
})
/**
* {
* "userOpHash": "0x3c037f957fde5d87e35d5b8582f6c274343bcf3bc0e010d72fc2de0e27f4a6aa",
* "sender": "0x277F6C1D8d4faFA3d8DcC837489cd69d86c682BA",
* "nonce": 3n,
* "actualGasUsed": 437678n,
* "actualGasCost": 1584749056877268n,
* "success": true,
* "logs": [
* ...
* ],
* "receipt": {
* ...
* },
* "logsBloom": "0x00000400000200000000000100000000400000000000000000000000020000000008000022000000000200110000000004108000000000000000020000000000000001000000000002000000000002800000000000000000000100000001000000000008180000000000000000000000000000000200804080800000000000000000000000000000000000000020001000001200000480000000000000000000200000000000000008400000000400000000800400000408000002000000004000000020000000000001001000040000000000000000800000108000000000000000000000020080000000000800000000000000002000004000000820120000",
* "status": "0x1",
* "effectiveGasPrice": 24163152145n
* }
* }
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions } from 'permissionless'
export const bundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(bundlerActions)
```
:::
### Returns
* **Type:** `UserOperationReceipt`
The receipt of the User Operation.
### Parameters
#### hash
* **Type:** `Hash`
The User Operation hash.
### JSON-RPC Method
[`eth_estimateUserOperationGas`](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace)
import VersionWarning from "../../../VersionWarning"
## sendUserOperation
Submits a user operation to the bundler.
### Usage
:::code-group
```ts [example.ts]
import { bundlerClient } from "./client"
const userOpHash = await bundlerClient.sendUserOperation({
userOperation: {
sender: "0x6152348912fb1e78c9037D83f9d4524D4a2988Ed",
nonce: "0x8104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000",
factory: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
factoryData:
"0xc5265d5d0000000000000000000000006723b44abeec4e71ebe3232bd5b455805badd22f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e412af322c018104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000014ec787ae5c34157ce61e751e54dff3612d4117663000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callData:
"0xe9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000594bc666500faed35dc741f45a35c571399560d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4e688a440000000000000000000000000c67e4838d4e6682e3a5f9ec27f133e76cb3855f30000000000000000000000006152348912fb1e78c9037d83f9d4524d4a2988ed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000041aebdfb90adba067d9304980a300636506c3c9081b01f64b04f108407a890602377625ef9096946cc028743123646881c7e31a1c8d6698132c188cb4c33a3f9151b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 99985502n,
maxPriorityFeePerGas: 1221000n,
preVerificationGas: 66875n,
verificationGasLimit: 373705n,
callGasLimit: 170447n,
paymaster: "0x9d0021A869f1Ed3a661Ffe8C9B41Ec6244261d98",
paymasterData:
"0x000000000000000000000000000000000000000000000000000000006638ab470000000000000000000000000000000000000000000000000000000000000000e9311d1945317dc6a1c750e8e6d0641a598beb59edc5652ed3807ca57338a7a107123e1a479386b2c91f242d2dff367c18e0ad9d1021acfe47afc890e252644e1c",
paymasterVerificationGasLimit: 20274n
paymasterPostOpGasLimit: 1n,
signature:
"0xa58d445f26b126fcd644975f0c66bd45f3e6e5b9c1acec2eeee490aa9341cfc312988231c228f84e12eac2d90ed9cd8825546d70d73b2e0fabdd6c8089ab29581b",
},
entryPoint: "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
})
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions } from 'permissionless'
export const bundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(bundlerActions)
```
:::
### Returns
* **Type:** `Hash`
The user operation hash.
### Parameters
#### userOperation
* **Type:** `UserOperation`
The full user operation object.
#### entryPoint
* **Type:** `Address`
The address of the entry point contract.
### JSON-RPC Method
[`eth_sendUserOperation`](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace)
import VersionWarning from "../../../VersionWarning"
## supportedEntryPoints
Return the Entry Points supported by the bundler.
### Usage
:::code-group
```ts [example.ts]
import { bundlerClient } from "./client"
const supportedEntryPoints = await bundlerClient.supportedEntryPoints()
/**
* ["0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"]
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions } from 'permissionless'
export const bundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(bundlerActions)
```
:::
### Returns
* **Type:** `Address[]`
The Entry Points supported by the bundler.
### JSON-RPC Method
[`eth_supportedEntryPoints`](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace)
import VersionWarning from "../../../VersionWarning"
## waitForUserOperationReceipt
Waits for the User Operation to be included on a block (one confirmation), and then returns the User Operation Receipt.
:::warning
If you are getting `WaitForUserOperationReceiptTimeoutError`. Use custom timeout in transport, please see the [FAQs](/references/permissionless/faqs#getting-waitforuseroperationreceipttimeouterror) for more information.
:::
### Usage
:::code-group
```ts [example.ts]
import { bundlerClient } from "./client"
const receipt = await bundlerClient.waitForUserOperationReceipt({
hash: "0x3c037f957fde5d87e35d5b8582f6c274343bcf3bc0e010d72fc2de0e27f4a6aa"
})
/**
* {
* "userOpHash": "0x3c037f957fde5d87e35d5b8582f6c274343bcf3bc0e010d72fc2de0e27f4a6aa",
* "sender": "0x277F6C1D8d4faFA3d8DcC837489cd69d86c682BA",
* "nonce": 3n,
* "actualGasUsed": 437678n,
* "actualGasCost": 1584749056877268n,
* "success": true,
* "logs": [
* ...
* ],
* "receipt": {
* ...
* },
* "logsBloom": "0x00000400000200000000000100000000400000000000000000000000020000000008000022000000000200110000000004108000000000000000020000000000000001000000000002000000000002800000000000000000000100000001000000000008180000000000000000000000000000000200804080800000000000000000000000000000000000000020001000001200000480000000000000000000200000000000000008400000000400000000800400000408000002000000004000000020000000000001001000040000000000000000800000108000000000000000000000020080000000000800000000000000002000004000000820120000",
* "status": "0x1",
* "effectiveGasPrice": 24163152145n
* }
* }
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions } from 'permissionless'
export const bundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(bundlerActions)
```
:::
### Returns
* **Type:** `UserOperationReceipt`
The receipt of the User Operation.
### Parameters
#### hash
* **Type:** `Hash`
The User Operation hash.
#### pollingInterval (optional)
* **Type:** `number`
Polling frequency (in ms). Defaults to the client's `pollingInterval` config
#### timeout (optional)
* **Type:** `number`
Optional timeout (in milliseconds) to wait before polling is stopped.
### JSON-RPC Method
[`eth_estimateUserOperationGas`](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace)
import VersionWarning from "../../../VersionWarning"
## Bundler Client
A Bundler Client is an interface to official ERC-4337 JSON-RPC API methods that include sending user operations, estimation user operation gas limits, getting user operation receipts and more.
To create a Bundler Client, create a basic client and extend it with the Bundler Actions.
### Import
```ts
import { bundlerActions } from 'permissionless'
```
### Usage
Initialize a Bundler Client with your desired Chain (e.g. mainnet) and Transport (e.g. http) from viem by creating a simple client and extending it with the standard bundler methods.
```ts
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions, ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
const bundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(bundlerActions(ENTRYPOINT_ADDRESS_V07))
```
Then you can consume Bundler Actions:
```ts
const supportedEntryPoints = await bundlerClient.supportedEntryPoints()
```
Alternatively, you can initialize a Bundler Client with the `createBundlerClient` method:
```ts
import { http } from 'viem'
import { sepolia } from 'viem/chains'
import { createBundlerClient, ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
const bundlerClient = createBundlerClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE"),
entryPoint: ENTRYPOINT_ADDRESS_V07
})
```
import VersionWarning from "../../../VersionWarning"
## Pimlico Bundler Client
A Pimlico Bundler Client is an interface to official ERC-4337 JSON-RPC API methods that include sending user operations, estimation user operation gas limits, getting user operation receipts and more as well as the Pimlico-specific bundler methods [pimlico\_getUserOperationStatus](/references/bundler/endpoints/pimlico_getUserOperationStatus) and [pimlico\_getUserOperationGasPrice](/references/bundler/endpoints/pimlico_getUserOperationGasPrice).
To create a Pimlico Bundler Client, create a basic client and extend it with the Bundler Actions and Pimlico Bundler Actions.
### Import
```ts
import { pimlicoBundlerActions } from 'permissionless/actions/pimlico'
```
### Usage
Initialize a Bundler Client with your desired Chain (e.g. mainnet) and Transport (e.g. http) from viem by creating a simple client and extending it with the Pimlico bundler methods and (optionally) the standard bundler methods.
```ts
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions, ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
import { pimlicoBundlerActions } from 'permissionless/actions/pimlico'
const pimlicoBundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE"),
})
.extend(bundlerActions(ENTRYPOINT_ADDRESS_V07))
.extend(pimlicoBundlerActions(ENTRYPOINT_ADDRESS_V07))
```
Then you can consume Pimlico Bundler Actions:
```ts
const userOperationGasPrice = await pimlicoBundlerClient.getUserOperationGasPrice()
```
Alternatively, you can initialize a Bundler Client with the `createPimlicoBundlerClient` method:
```ts
import { http } from 'viem'
import { sepolia } from 'viem/chains'
import { ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
import { createPimlicoBundlerClient } from "permissionless/clients/pimlico";
const pimlicoBundlerClient = createPimlicoBundlerClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE"),
entryPoint: ENTRYPOINT_ADDRESS_V07
})
```
import VersionWarning from "../../../VersionWarning"
## Pimlico Paymaster Client
:::warning
The Pimlico paymaster actions in permissionless.js only work with the v2 Pimlico API. The v1 API for paymasters is deprecated and will be removed in the future.
This version, instead of simply signing over the passed-in User Operation, estimates the gas values for the User Operation and then signs over the estimated gas values, returning not only the `paymasterAndData` but also the `preVerificationGas`, `verificationGasLimit`, and `callGasLimit`.
:::
A Pimlico Paymaster Client is an interface to the [Pimlico-specific paymaster methods](/references/paymaster/verifying-paymaster/endpoints).
To create a Pimlico Paymaster Client, create a basic client and extend it with the Pimlico Paymaster Actions.
### Import
```ts
import { pimlicoPaymasterActions } from 'permissionless/actions/pimlico'
```
### Usage
Initialize a Bundler Client with your desired Chain (e.g. mainnet) and Transport (e.g. http) from viem by creating a simple client and extending it with the Pimlico paymaster methods.
```ts
import { createClient, http } from 'viem'
import { sepolia } from 'viem/chains'
import { bundlerActions, ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
import { pimlicoPaymasterActions } from 'permissionless/actions/pimlico'
const bundlerClient = createClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE"),
})
.extend(pimlicoPaymasterActions(ENTRYPOINT_ADDRESS_V07))
```
Then you can consume Pimlico Paymaster Actions:
```ts
const sponsorResult = await bundlerClient.sponsorUserOperation({
userOperation: {
sender: "0x0C123D90Da0a640fFE54a2359D159629065775C5",
nonce: 3n,
callData: "0x18dfb3c7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c09000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c090000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000043a4eacb7839f202d9cab465dbdd77d4fabe0a1800000000000000000000000000000000000000000000000003782dace9d90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000982e148216e3aa6b38f9d901ef578b5c06dd750200000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 113001000n,
maxPriorityFeePerGas: 113000000n,
signature: "0xf1513a8537a079a4d728bb87099b2c901e2c9034e60c95a4d41ac1ed75d6ee90270d52b48af30aa036e9a205ea008e1c62b317e7b3f88b3f302d45fb1ba76a191b"
}
})
```
Alternatively, you can initialize a Paymaster Client with the `createPimlicoPaymasterClient` method:
```ts
import { http } from 'viem'
import { sepolia } from 'viem/chains'
import { ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
import { createPimlicoPaymasterClient } from "permissionless/clients/pimlico";
const pimlicoPaymasterClient = createPimlicoPaymasterClient({
chain: sepolia,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE"),
entryPoint: ENTRYPOINT_ADDRESS_V07
})
```
import VersionWarning from "../../../VersionWarning"
## Smart Account Client
A Smart Account Client is an almost drop-in replacement for a standard viem [walletClient](https://viem.sh/docs/clients/wallet) but for managing smart accounts instead of EOA accounts. In addition, a Smart Account Client also contains, depending on the underlying smart account implementation used, a few extra actions that are specific to smart accounts.
To create a Smart Account Client, use the `createSmartAccountClient` function.
### Import
```ts
import { createSmartAccountClient } from 'permissionless'
```
### Usage
Initialize a Smart Account Client with your desired account (which can be created using a function like `signerToSimpleSmartAccount`), chain, bundler transport, and optionally a sponsorUserOperation middleware by using the `createSmartAccountClient` method:
```ts
import { http } from 'viem'
import { sepolia } from 'viem/chains'
import { createSmartAccountClient, ENTRYPOINT_ADDRESS_V07 } from "permissionless";
import { simpleSmartAccount } from "./simpleSmartAccount"; // created elsewhere
import { pimlicoPaymaster } from "./paymaster"; // created elsewhere
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
entryPoint: ENTRYPOINT_ADDRESS_V07
chain: sepolia, // or whatever chain you are using
bundlerTransport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_PIMLICO_API_KEY"),
middleware: {
sponsorUserOperation: pimlicoPaymaster.sponsorUserOperation // if using a paymaster
}
})
```
Then you can consume Smart Account Actions and access properties:
```ts
const smartAccountAddress = await smartAccountClient.address
```
import VersionWarning from "../../../VersionWarning"
## accountId
Gets the accountId of the smart account as defined in [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579). Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
// [!include ~/snippets/v0_1/erc7579/accountId.ts:safe-smart-account-client]
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `string`
The account id of the smart account.
### Parameters
#### account (optional)
* **Type:** `SmartAccount`
If your `SmartAccountClient` doesn't have an account, you should provide one here.
import VersionWarning from "../../../VersionWarning"
## installModule
Installs a [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) module to the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
// [!include ~/snippets/v0_1/erc7579/installModule.ts:safe-smart-account-client]
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `hash`
The user operation hash.
:::warning
This is a user operation hash, not a transaction hash, you must use `waitForUserOperationReceipt` to wait for the user operation to be included onchain.
:::
### Parameters
#### address
* **Type:** `Address`
Address of the module to install.
#### type
* **Type:** `ModuleType`
Type of the module to install. Accepted values are `"validator" | "executor" | "fallback" | "hook"`.
#### context
* **Type:** `Hex`
Context bytes that will be passed to the module as part of `initData`.
#### maxFeePerGas (optional)
* **Type:** `bigint`
The maximum fee per gas that the user is willing to pay for this user operation. If not provided, the bundler will use its own recommendation.
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
The maximum priority fee per gas that the user is willing to pay for this user operation. If not provided, the bundler will use its own recommendation.
#### nonce (optional)
* **Type:** `bigint`
The nonce of the smart account that will be used to send this user operatino. If not provided, current nonce will be used.
import VersionWarning from "../../../VersionWarning"
## isModuleInstalled
Checks if an [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) module is installed on the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
// [!include ~/snippets/v0_1/erc7579/isModuleInstalled.ts:safe-smart-account-client]
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `boolean`
True if the module is installed, false otherwise.
### Parameters
#### address
* **Type:** `Address`
Address of the module to install.
#### type
* **Type:** `ModuleType`
Type of the module to install. Accepted values are `"validator" | "executor" | "fallback" | "hook"`.
#### context
* **Type:** `Hex`
Context bytes that will be passed to the module as part of `additionalContext`.
import VersionWarning from "../../../VersionWarning"
## supportsExecutionMode
Checks if a [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) execution mode is supported on the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
:::info
According to the specification, not all execution modes must be supported. So it is recommended to use this function to check if the execution mode is supported before using it.
:::
### Usage
:::code-group
```ts [example.ts]
// [!include ~/snippets/v0_1/erc7579/supportsExecutionMode.ts:safe-smart-account-client]
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `boolean`
True if the execution mode is supported, false otherwise.
### Parameters
#### type
* **Type:** `CallType`
Type of the call. Accepted values are `"call" | "delegatecall" | "staticcall"`.
#### revertOnError (optional)
* **Type:** `boolean`
If true, the execution will revert if the call fails. Defaults to false.
#### selector (optional)
* **Type:** `Hex`
The selector of the function to call. If not provided, `0x` will be used.
#### context (optional)
* **Type:** `Hex`
Context bytes that will be passed to the module as part of `modeContext`.
import VersionWarning from "../../../VersionWarning"
## supportsModule
Checks if a [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) module type is supported by the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
// [!include ~/snippets/v0_1/erc7579/supportsModule.ts:safe-smart-account-client]
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `boolean`
True if the module type is supported, false otherwise.
### Parameters
#### type
* **Type:** `ModuleType`
Type of the module to check. Accepted values are `"validator" | "executor" | "fallback" | "hook"`.
import VersionWarning from "../../../VersionWarning"
## uninstallModule
Uninstalls a [ERC-7579](https://eips.ethereum.org/EIPS/eip-7579) module from the smart account. Check out [this guide](/references/permissionless/how-to/accounts/use-erc7579-account) for a complete tutorial.
### Usage
:::code-group
```ts [example.ts]
// [!include ~/snippets/v0_1/erc7579/uninstallModule.ts:safe-smart-account-client]
```
```ts [publicClient.ts]
// [!include ~/snippets/v0_1/publicClient.ts:client]
```
```ts [signer.ts]
// [!include ~/snippets/v0_1/signer.ts]
```
:::
### Returns
* **Type:** `hash`
The user operation hash.
:::warning
This is a user operation hash, not a transaction hash, you must use `waitForUserOperationReceipt` to wait for the user operation to be included onchain.
:::
### Parameters
#### address
* **Type:** `Address`
Address of the module to uninstall.
#### type
* **Type:** `ModuleType`
Type of the module to uninstall. Accepted values are `"validator" | "executor" | "fallback" | "hook"`.
#### context
* **Type:** `Hex`
Context bytes that will be passed to the module as part of `deInitData`.
#### maxFeePerGas (optional)
* **Type:** `bigint`
The maximum fee per gas that the user is willing to pay for this user operation. If not provided, the bundler will use its own recommendation.
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
The maximum priority fee per gas that the user is willing to pay for this user operation. If not provided, the bundler will use its own recommendation.
#### nonce (optional)
* **Type:** `bigint`
The nonce of the smart account that will be used to send this user operation. If not provided, current nonce will be used.
import VersionWarning from "../../../VersionWarning"
## Errors \[Glossary of Errors in permissionless.js.]
All errors in permissionless.js extend the standard viem [`BaseError`](https://github.com/wevm/viem/blob/main/src/errors/base.ts).
### EntryPoint
#### `SenderAlreadyDeployedError`
#### `InitCodeRevertedError`
#### `SenderAddressMismatchError`
#### `InitCodeDidNotDeploySenderError`
#### `SenderNotDeployedError`
#### `SmartAccountInsufficientFundsError`
#### `SmartAccountSignatureValidityPeriodError`
#### `SmartAccountValidationRevertedError`
#### `InvalidSmartAccountSignatureError`
#### `InvalidSmartAccountNonceError`
#### `InvalidBeneficiaryAddressError`
#### `InvalidAggregatorError`
#### `VerificationGasLimitTooLowError`
#### `ActualGasCostTooHighError`
#### `GasValuesOverflowError`
#### `BundlerOutOfGasError`
#### `PaymasterNotDeployedError`
#### `PaymasterDepositTooLowError`
#### `PaymasterExpiredOrNotDueError`
#### `PaymasterValidationRevertedError`
#### `PaymasterDataRejectedError`
#### `PaymasterPostOpRevertedError`
#### `InvalidPaymasterAndDataError`
import VersionWarning from "../../../VersionWarning"
## getUserOperationGasPrice
Return a User Operation based on a User Operation hash.
### Usage
:::code-group
```ts [example.ts]
import { pimlicoBundlerClient } from "./client"
const smartAccountClient = createSmartAccountClient({
account: safeAccount,
entryPoint: ENTRYPOINT_ADDRESS_V06,
chain: sepolia,
bundlerTransport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=API_KEY"),
middleware: {
gasPrice: async () => (await pimlicoBundlerClient.getUserOperationGasPrice()).fast, // use pimlico bundler to get gas prices, if using pimlico
sponsorUserOperation: paymasterClient.sponsorUserOperation, // optional
},
})
/* other gas alternatives */
const gasPriceResult = await pimlicoBundlerClient.getUserOperationGasPrice()
/**
* {
"slow": {
"maxFeePerGas": 3267781404n,
"maxPriorityFeePerGas": 3267781404n
},
"standard": {
"maxFeePerGas": 3439769899n,
"maxPriorityFeePerGas": 3439769899n
},
"fast": {
"maxFeePerGas": 3620810421n,
"maxPriorityFeePerGas": 3620810421n
}
}
*/
```
```ts [client.ts]
// [!include ~/snippets/v0_1/pimlicoBundlerClient.ts:client]
```
:::
### Returns
* **Type:** `GetUserOperationGasPriceReturnType`
The `maxFeePerGas` and `maxPriorityFeePerGas` values for the different gas price tiers. Choosing a faster gas price will mean faster inclusion by the Pimlico Bundler.
### JSON-RPC Method
[`pimlico_getUserOperationGasPrice`](/references/bundler/endpoints/pimlico_getUserOperationGasPrice)
import VersionWarning from "../../../VersionWarning"
## getUserOperationStatus
Return a status of the User Operation as well as potentially the hash of the transaction being used to bundle the User Operation.
### Usage
:::code-group
```ts [example.ts]
import { pimlicoBundlerClient } from "./client"
const getStatusResult = await pimlicoBundlerClient.getUserOperationStatus({
hash: "0x3c037f957fde5d87e35d5b8582f6c274343bcf3bc0e010d72fc2de0e27f4a6aa"
})
/**
* {
* "status": "included",
* "transactionHash": "0x975a6bac5f562a2cb8218945b4e4304f6d10afa4aa6f02830e6d6dcafc450d66"
* }
*/
```
```ts [client.ts]
// [!include ~/snippets/v0_1/pimlicoBundlerClient.ts:client]
```
:::
### Returns
* **Type:** `GetUserOperationStatusReturnType`
The status of the User Operation ("not\_found" | "not\_submitted" | "submitted" | "rejected" | "reverted" | "included" | "failed" | "queued") as well as the hash of the transaction being used to bundle the User Operation. If the hash is not known (for instance because the User Operation has not been submitted yet), the hash will be `null`.
### Parameters
#### hash
* **Type:** `Hash`
The hash of the User Operation.
### JSON-RPC Method
[`pimlico_getUserOperationStatus`](/references/bundler/endpoints/pimlico_getUserOperationStatus)
import VersionWarning from "../../../VersionWarning"
## sponsorUserOperation
:::warning
The Pimlico paymaster actions in permissionless.js only work with the v2 Pimlico API. The v1 API for paymasters is deprecated and will be removed in the future.
This version, instead of simply signing over the passed-in User Operation, estimates the gas values for the User Operation and then signs over the estimated gas values, returning not only the `paymasterAndData` but also the `preVerificationGas`, `verificationGasLimit`, and `callGasLimit`.
:::
Estimates the gas values for the User Operation and sponsors the operation with the new gas values.
### Usage
:::code-group
```ts [example.ts]
import { pimlicoPaymasterClient } from "./client"
const sponsorResult = await pimlicoPaymasterClient.sponsorUserOperation({
userOperation: {
sender: "0x0C123D90Da0a640fFE54a2359D159629065775C5",
nonce: 3n,
callData: "0x18dfb3c7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c09000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c090000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000043a4eacb7839f202d9cab465dbdd77d4fabe0a1800000000000000000000000000000000000000000000000003782dace9d90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000982e148216e3aa6b38f9d901ef578b5c06dd750200000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 113000000n,
maxPriorityFeePerGas: 113000100n,
signature: "0xf1513a8537a079a4d728bb87099b2c901e2c9034e60c95a4d41ac1ed75d6ee90270d52b48af30aa036e9a205ea008e1c62b317e7b3f88b3f302d45fb1ba76a191b"
},
sponsorshipPolicyId: "sp_crazy_kangaroo" // optional, only if using Pimlico sponsorship policies
})
/**
* {
"paymaster": "0xe3dc822D77f8cA7ac74c30B0dfFEA9FcDCAAA321",
"paymasterVerificationGasLimit": 93823n,
"paymasterPostOpGasLimit": 134849n,
"paymasterData": "000000000000000000000000000000000000000000000000000000006514ac7d000000000000000000000000000000000000000000000000000000000000000071eee8c38559ef6872351c16a312cefbc545344a3f7cc1b910d059a0d5c613012763e6b1ce31080a975ddcba12817305a62a322e3ec8f106bd2181b0fd1391cf1c",
"preVerificationGas": 61230n,
"verificationGasLimit": 93823n,
"callGasLimit": 134849n
}
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
import { pimlicoPaymasterActions } from 'permissionless/actions/pimlico'
export const pimlicoPaymasterClient = createClient({
chain: mainnet,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(pimlicoPaymasterActions(ENTRYPOINT_ADDRESS_V07))
```
:::
### Returns
* **Type:** `SponsorUserOperationReturnType`
The estimated gas values for the User Operation and the paymaster related data that includes the Pimlico paymaster address and Pimlico's paymaster signature.
### Parameters
#### userOperation
* **Type:** `UserOperation`
The User Operation object.
#### sponsorshipPolicyId (optional)
* **Type:** `string`
The ID of the [sponsorship policy](https://dashboard.pimlico.io/sponsorship-policies) to use. If used, this call will error if the policy is not willing to sponsor this user operation. It is recommended to use pm\_validateSponsorshipPolicies before calling this method.
### JSON-RPC Method
[`pm_sponsorUserOperation`](/references/paymaster/verifying-paymaster/endpoints#pm_sponsoruseroperation)
import VersionWarning from "../../../VersionWarning"
## validateSponsorshipPolicies
Validates a User Operation against an array of [sponsorship policies](https://dashboard.pimlico.io/sponsorship-policies), and returns an array of sponsorship policies (alongside additional data for each policy) that are willing to sponsor the user operation.
### Usage
:::code-group
```ts [example.ts]
import { pimlicoPaymasterClient } from "./client"
const sponsorResult = await pimlicoPaymasterClient.validateSponsorshipPolicies({
userOperation: {
sender: "0x0C123D90Da0a640fFE54a2359D159629065775C5",
nonce: 3n,
initCode: "0x",
callData: "0x18dfb3c7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c09000000000000000000000000d2f598c826429eee7c071c02735549acd88f2c090000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000044a9059cbb00000000000000000000000043a4eacb7839f202d9cab465dbdd77d4fabe0a1800000000000000000000000000000000000000000000000003782dace9d90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000982e148216e3aa6b38f9d901ef578b5c06dd750200000000000000000000000000000000000000000000000005d423c655aa000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 113000000n,
maxPriorityFeePerGas: 113000100n,
signature: "0xf1513a8537a079a4d728bb87099b2c901e2c9034e60c95a4d41ac1ed75d6ee90270d52b48af30aa036e9a205ea008e1c62b317e7b3f88b3f302d45fb1ba76a191b"
},
sponsorshipPolicyIds: ["sp_crazy_kangaroo", "sp_malevolent_badger"]
})
/**
* [
* {
* "sponsorshipPolicyId": "sp_crazy_kangaroo",
* "data": {
* "name": "Linea Christmas Week",
* "author": "Linea",
* "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
* "description": "Linea is sponsoring the first 10 transactions for existing users between Christmas and New Year's Eve.",
* }
* }
* ]
*/
```
```ts [client.ts]
import { createClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { ENTRYPOINT_ADDRESS_V07 } from 'permissionless'
import { pimlicoPaymasterActions } from 'permissionless/actions/pimlico'
export const pimlicoPaymasterClient = createClient({
chain: mainnet,
transport: http("https://api.pimlico.io/v2/sepolia/rpc?apikey=YOUR_API_KEY_HERE")
}).extend(pimlicoPaymasterActions(ENTRYPOINT_ADDRESS_V07))
```
:::
### Returns
* **Type:** `ValidateSponsorshipPoliciesReturnType`
An array of sponsorship policy IDs and (optionally) extra data to be used by front-ends for sponsorship policies that are willing to sponsor the user operation.
```json
{
sponsorshipPolicyId: string
data: {
name: string | null
author: string | null
icon: string | null
description: string | null
}
}[]
```
#### name (optional)
* **Type:** `string | null`
The name of the sponsorship policy.
#### author (optional)
* **Type:** `string | null`
The author of the sponsorship policy.
#### icon (optional)
* **Type:** `string | null`
The icon of the sponsorship policy. The icon must be a data URI as defined in RFC-2397.
#### description (optional)
* **Type:** `string | null`
The description of the sponsorship policy.
### Parameters
#### userOperation
* **Type:** `UserOperation`
The User Operation object.
#### sponsorshipPolicyIds
* **Type:** `string[]`
An array of sponsorship policy IDs to validate against.
### JSON-RPC Method
[`pm_validateSponsorshipPolicies`](/references/paymaster/verifying-paymaster/endpoints)
import VersionWarning from "../../../VersionWarning"
## getAccountNonce
Returns the current nonce of the smart account for a specified key.
### Usage
:::code-group
```ts [example.ts]
import { publicClient } from "./client.ts"
import { getAccountNonce, ENTRYPOINT_ADDRESS_V07 } from "permissionless"
const nonce = await getAccountNonce(publicClient, {
address: "0x277F6C1D8d4faFA3d8DcC837489cd69d86c682BA",
entryPoint: ENTRYPOINT_ADDRESS_V07,
key: 0n // optional
})
// 23n
```
```ts [client.ts]
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
export const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://mainnet.infura.io/v3/...")
})
```
:::
### Returns
* **Type:** `bigint`
The current nonce of the smart account for the specified key (both the key and the sequence concatenated together into one uint256).
### Parameters
#### address
* **Type:** `Address`
The address of the smart account.
#### entryPoint
* **Type:** `Address`
The entry point address.
#### key (optional)
* **Type:** `bigint`
The key of the nonce. Defaults to 0n.
Instead of sequential nonce, ERC-4337 implements a nonce mechanism that uses a single uint256 nonce value in the UserOperation, but treats it as two values, a 192-bit “key”, and a 64-bit “sequence”. These values are represented on-chain in the EntryPoint contract. For each key the sequence is validated and incremented sequentially and monotonically by the EntryPoint for each UserOperation, however a new key can be introduced with an arbitrary value at any point.
import VersionWarning from "../../../VersionWarning"
## getSenderAddress
Return a sender address corresponding to the initCode of the User Operation where the smart account will be deployed.
### Usage
:::code-group
```ts [example.ts]
import { publicClient } from "./client.ts"
import { getSenderAddress } from "permissionless"
const senderAddress = await getSenderAddress(publicClient, {
factory: "0x91E60e0613810449d098b0b5Ec8b51A0FE8c8985",
factoryData: "0x5fbfb9cf000000000000000000000000cafb211a4ea1290370d43732fed4da817a2e11ed0000000000000000000000000000000000000000000000000000000000000000"
entryPoint: ENTRYPOINT_ADDRESS_V07
})
// "0x0C123D90Da0a640fFE54a2359D159629065775C5"
```
```ts [client.ts]
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
export const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://mainnet.infura.io/v3/...")
})
```
:::
### Returns
* **Type:** `Address`
The sender address corresponding to the initCode of the User Operation where the smart account will be deployed.
### Parameters
#### initCode
* **Type:** `Hex`
The initCode of the User Operation that deploys the smart account.
#### entryPoint
* **Type:** `Address`
The entry point address.
import VersionWarning from "../../../VersionWarning"
## deployContract
deploys a contract to the network from a smart account, given bytecode & constructor arguments.
:::info
This action is only supported by some smart account types.
:::
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient, simpleSmartAccount } from "./smartAccountClient"
import { parseAbiItem, encodeFunctionData } from "viem"
import { pimlicoBundlerClient } from "./bundlerClient"
import { abi } from "./abi"
const hash = await smartAccountClient.deployContract({
abi,
bytecode: "0x606060405260008060146101..."
})
```
```ts [abi.ts]
export const abi = [
...
{
inputs: [],
stateMutability: "nonpayable",
type: "constructor",
},
...
] as const;
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/v0_1/smartAccountClient.ts:client]
```
```ts [bundlerClient.ts]
// [!include ~/snippets/v0_1/pimlicoBundlerClient.ts:client]
```
:::
### Returns
* **Type:** `Hash`
The transaction hash of the mined userOperation
### Parameters
#### abi
* **Type:** `Abi`
The contract's ABI.
#### bytecode
* **Type:** `Hex`
The contract's bytecode.
#### args (if required)
* **Type:** Inferred from ABI.
Constructor arguments to call upon deployment.
#### account (optional)
* **Type:** `SmartAccount`
The Account to deploy the contract from.
import VersionWarning from "../../../VersionWarning"
## prepareUserOperationRequest
Prepares a user operation request for signing by populating a `sender`, `nonce`, `initCode`, gas price values, gas limit values, and optionally requesting sponsorship from a paymaster.
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient, simpleSmartAccount } from "./smartAccountClient"
import { pimlicoBundlerClient } from "./bundlerClient"
import { parseAbiItem, encodeFunctionData } from "viem"
const callData = await simpleSmartAccount.encodeCallData({
to: "0x0488bEE1Ec682db0F0E74AB52faFdDdEf10Af123",
data: encodeFunctionData({
abi: [parseAbiItem('function mint()')]
}),
value: 0n
})
// only if using pimlico
const gasPrices = await pimlicoBundlerClient.getUserOperationGasPrice()
const userOperation = await smartAccountClient.prepareUserOperationRequest({
userOperation: {
callData, // callData is the only required field in the partial user operation
maxFeePerGas: gasPrices.fast.maxFeePerGas,
maxPriorityFeePerGas: gasPrices.fast.maxPriorityFeePerGas
}
})
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/v0_1/smartAccountClient.ts:client]
```
```ts [bundlerClient.ts]
// [!include ~/snippets/v0_1/pimlicoBundlerClient.ts:client]
```
:::
### Returns
* **Type:** `UserOperation`
The `UserOperation` with the remaining fields (except the `signature`) filled.
### Parameters
#### userOperation
* **Type:** `Partial`
The partial user operation. Only the `callData` field is required.
#### sponsorUserOperation (optional)
* **Type:** `(args: { userOperation: UserOperation, entryPoint: Address }) => Promise`
The paymaster middleware function (if one is used).
#### account (optional)
* **Type:** `SmartAccount`
The smart account that will be used to fetch the `nonce`, `initCode`, `sender`, and the gas price values if they are not already filled.
import VersionWarning from "../../../VersionWarning"
## sendTransaction
Send a transaction from a smart account using the same `sendTransaction` interface as viem.
Internally, sendTransaction calls the smartAccount's `prepareUserOperationRequest`, `sendUserOperation`, and `waitForUserOperationReceipt` actions.
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient, simpleSmartAccount } from "./smartAccountClient"
import { parseAbiItem, encodeFunctionData } from "viem"
const hash = await smartAccountClient.sendTransaction({
to: "0x0488bEE1Ec682db0F0E74AB52faFdDdEf10Af123",
data: encodeFunctionData({
abi: [parseAbiItem('function mint()')]
}),
value: 0n
})
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/v0_1/smartAccountClient.ts:client]
```
:::
### Returns
* **Type:** `Hash`
The transaction hash of the mined userOperation
### Parameters
#### to
* **Type:** `0x${string}`
The transaction recipient or contract address.
#### data (optional)
* **Type:** `0x${string}`
A contract hashed method call with encoded args.
#### value (optional)
* **Type:** `bigint`
Value in wei sent with this transaction.
#### maxFeePerGas (optional)
* **Type:** `bigint`
Total fee per gas (in wei), inclusive of `maxPriorityFeePerGas`. Only applies to EIP-1559 Transactions.
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Max priority fee per gas (in wei). Only applies to EIP-1559 Transactions.
#### nonce (optional)
* **Type:** `number`
Unique number identifying this transaction.
#### account (optional)
* **Type:** `SmartAccount`
The Account to send the transaction from.
import VersionWarning from "../../../VersionWarning"
## sendTransactions
Send a batch of transaction from a smart account.
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient, simpleSmartAccount } from "./smartAccountClient"
import { pimlicoBundlerClient } from "./bundlerClient"
import { parseAbiItem, encodeFunctionData } from "viem"
const hash = await smartAccountClient.sendTransactions([
{
to: "0x0488bEE1Ec682db0F0E74AB52faFdDdEf10Af123",
data: encodeFunctionData({
abi: [parseAbiItem("function mint()")],
}),
value: 0n,
},
{
to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
data: "0x68656c6c6f",
value: 0n,
},
]);
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/v0_1/smartAccountClient.ts:client]
```
```ts [bundlerClient.ts]
// [!include ~/snippets/v0_1/pimlicoBundlerClient.ts:client]
```
:::
### Returns
* **Type:** `Hash`
The transaction hash of the mined userOperation
### Parameters
Takes in an array of transaction objects with the following fields
#### to
* **Type:** `0x${string}`
The transaction recipient or contract address.
#### data (optional)
* **Type:** `0x${string}`
A contract hashed method call with encoded args.
#### value (optional)
* **Type:** `bigint`
Value in wei sent with this transaction.
#### maxFeePerGas (optional)
* **Type:** `bigint`
Total fee per gas (in wei), inclusive of `maxPriorityFeePerGas`. Only applies to EIP-1559 Transactions.
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Max priority fee per gas (in wei). Only applies to EIP-1559 Transactions.
#### nonce (optional)
* **Type:** `number`
Unique number identifying this transaction.
#### account (optional)
* **Type:** `SmartAccount`
The Account to send the transaction from.
import VersionWarning from "../../../VersionWarning"
## sendUserOperation
Submits a user operation to the smart account client's underlying bundler.
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient } from "./smartAccountClient"
const userOpHash = await smartAccountClient.sendUserOperation({
userOperation: {
sender: "0x6152348912fb1e78c9037D83f9d4524D4a2988Ed",
nonce: "0x8104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000",
factory: "0xd703aaE79538628d27099B8c4f621bE4CCd142d5",
factoryData: "0xc5265d5d0000000000000000000000006723b44abeec4e71ebe3232bd5b455805badd22f0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e412af322c018104e3ad430ea6d354d013a6789fdfc71e671c4300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000014ec787ae5c34157ce61e751e54dff3612d4117663000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
callData: "0xe9ae5c530100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000594bc666500faed35dc741f45a35c571399560d80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e4e688a440000000000000000000000000c67e4838d4e6682e3a5f9ec27f133e76cb3855f30000000000000000000000006152348912fb1e78c9037d83f9d4524d4a2988ed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000041aebdfb90adba067d9304980a300636506c3c9081b01f64b04f108407a890602377625ef9096946cc028743123646881c7e31a1c8d6698132c188cb4c33a3f9151b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
maxFeePerGas: 99985502n,
maxPriorityFeePerGas: 1221000n,
preVerificationGas: 66875n,
verificationGasLimit: 373705n,
callGasLimit: 170447n,
paymaster: "0x9d0021A869f1Ed3a661Ffe8C9B41Ec6244261d98",
paymasterData: "0x000000000000000000000000000000000000000000000000000000006638ab470000000000000000000000000000000000000000000000000000000000000000e9311d1945317dc6a1c750e8e6d0641a598beb59edc5652ed3807ca57338a7a107123e1a479386b2c91f242d2dff367c18e0ad9d1021acfe47afc890e252644e1c",
paymasterVerificationGasLimit: 20274n
paymasterPostOpGasLimit: 1n,
signature: "0xa58d445f26b126fcd644975f0c66bd45f3e6e5b9c1acec2eeee490aa9341cfc312988231c228f84e12eac2d90ed9cd8825546d70d73b2e0fabdd6c8089ab29581b",
}
})
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/v0_1/smartAccountClient.ts:client]
```
:::
### Returns
* **Type:** `Hash`
The user operation hash.
### Parameters
#### userOperation
* **Type:** `UserOperation`
The full user operation object.
import VersionWarning from "../../../VersionWarning"
## signMessage
Uses a smart account to sign and calculate an Ethereum-specific signature in [EIP-191 format](https://eips.ethereum.org/EIPS/eip-191): `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`.
With the calculated signature, you can:
* use `verifyMessage` to verify the signature.
* use `recoverMessageAddress` to recover the signing address from a signature.
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient } from "./smartAccountClient"
const hash = await smartAccountClient.signMessage({
message: "build with smart accounts!"
})
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/v0_1/smartAccountClient.ts:client]
```
:::
### Returns
* **Type:**\* `Hex`
The signed message.
### Parameters
#### message
* **Type:** `string | { raw: Hex | ByteArray }`
Message to sign.
import VersionWarning from "../../../VersionWarning"
## signTypedData
Signs typed data from a smart account and calculates an Ethereum-specific signature in [https://eips.ethereum.org/EIPS/eip-712](https://eips.ethereum.org/EIPS/eip-712): `sign(keccak256("\x19\x01" ‖ domainSeparator ‖ hashStruct(message)))`
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient } from "./smartAccountClient"
const signedData = await smartAccountClient.signTypedData({
domain: {
name: "Ether Mail",
version: "1",
chainId: 1,
verifyingContract: getAddress("0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"),
},
types: {
Person: [
{ name: "name", type: "string" },
{ name: "wallet", type: "address" },
],
Mail: [
{ name: "from", type: "Person" },
{ name: "to", type: "Person" },
{ name: "contents", type: "string" },
],
},
primaryType: "Mail" as const,
message: {
from: {
name: "Bob",
wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
},
to: {
name: "Alice",
wallet: "0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa",
},
contents: "Hello Alice!",
},
})
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/v0_1/smartAccountClient.ts:client]
```
:::
### Returns
* **Type:** `Hex`
The signed data.
### Parameters
#### domain
* **Type:** `TypedDataDomain`
The typed data domain.
#### types
* **Type:** `TypedData`
The type definitions for the typed data.
#### primaryType
* **Type:** Inferred `string`.
The primary type to extract from `types` and use in `value`.
#### message
* **Type:** Inferred from `types` & `primaryType`.
The message to sign
import VersionWarning from "../../../VersionWarning"
## writeContract
Uses a smart account to executes a write function on a contract.
A "write" function on a Solidity contract modifies the state of the blockchain.
### Usage
:::code-group
```ts [example.ts]
import { smartAccountClient } from "./smartAccountClient"
import { simpleAbi } from './abi'
const hash = await smartAccountClient.writeContract({
address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
abi: simpleAbi,
functionName: 'mint',
})
```
```ts [smartAccountClient.ts]
// [!include ~/snippets/v0_1/smartAccountClient.ts:client]
```
```ts [abi.ts]
export const simpleAbi = [
...
{
inputs: [],
name: "mint",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
...
] as const;
```
:::
### Return Value
* **Type:** `Hash`
The transaction hash of the associated write operation
### Parameters
#### address
* **Type:** `Address`
The contract address.
#### abi
* **Type:** `Abi`
The contract's ABI.
#### functionName
* **Type:** `string`
A function to extract from the ABI.
#### args (optional)
* **Type:** Inferred from ABI.
Arguments to pass to function call.
#### gas (optional)
* **Type:** `bigint`
The gas limit for the transaction. Note that passing a gas limit also skips the gas estimation step.
#### nonce (optional)
* **Type:** `number`
Unique number identifying this transaction.
#### value (optional)
* **Type:** `number`
Value in wei sent with this transaction.
#### gasPrice (optional)
* **Type:** `bigint`
The price (in wei) to pay per gas. Only applies to Legacy Transactions
#### maxFeePerGas (optional)
* **Type:** `bigint`
Total fee per gas (in wei), inclusive of `maxPriorityFeePerGas`. Only applies to EIP-1559 Transactions
#### maxPriorityFeePerGas (optional)
* **Type:** `bigint`
Max priority fee per gas (in wei). Only applies to EIP-1559 Transactions
#### dataSuffix (optional)
* **Type:** `Hex`
Data to append to the end of the calldata.
#### account (optional)
* **Type:** `SmartAccount`
The Account to write to the contract from.
import VersionWarning from "../../../VersionWarning"
## getRequiredPrefund
Returns the minimum required funds in wei in the senders's smart account to execute the user operation.
### Import
```ts
import { getRequiredPrefund } from "permissionless"
```
### Usage
```ts
import { getRequiredPrefund } from "permissionless"
const requiredPrefund = getRequiredPrefund({
userOperation
})
// 354000000000000n
```
### Returns
`BigInt`
The requied prefund in wei.
### Parameters
#### userOperation
* **Type:** `UserOperation`
The User Operation to get the prefund for.
import VersionWarning from "../../../VersionWarning"
## getUserOperationHash
Generates the hash of the User Operation [as calculated by the EntryPoint](https://github.com/eth-infinitism/account-abstraction/blob/465f22310fcc639964e6ab5f8aa648770c6d8981/contracts/core/EntryPoint.sol#L296-L302).
### Import
```ts
import { getUserOperationHash } from "permissionless"
```
### Usage
```ts
import { getUserOperationHash, ENTRYPOINT_ADDRESS_V07 } from "permissionless"
const userOperationHash = getUserOperationHash({
userOperation: ...,
chainId: 5,
entryPoint: ENTRYPOINT_ADDRESS_V07
})
// "0xf0d56baa398828898360589d5ef75b24b12be0829ef1e1037013d1b6d9dda1f2"
```
### Returns
`Hash`
The hash of the User Operation
### Parameters
#### userOperation
* **Type:** `UserOperation`
The User Operation to hash.
#### chainId
* **Type:** `number`
The chain ID of the chain on which the User Operation is being executed.
#### entryPoint
* **Type:** `Address`
The entry point address.
import VersionWarning from "../../../VersionWarning"
## providerToSmartAccountSigner
Takes an EIP-1193 provider and returns a signer that can be used as a signer with permissionless accounts.
### Import
```ts
import { providerToSmartAccountSigner } from "permissionless"
```
### Usage
```ts
import { type EIP1193Provider } from "viem"
declare global {
interface Window {
ethereum: EIP1193Provider;
}
}
// ---cut---
import { providerToSmartAccountSigner } from "permissionless"
const smartAccountSigner = await providerToSmartAccountSigner(window.ethereum)
```
### Returns
`SmartAccountSigner`
The smart account signer that can be used with permissionless accounts.
### Parameters
#### provider
* **Type:** `EIP1193Provider`
The EIP-1193 provider.
#### signerAddress (optional)
* **Type:** `Address`
The address of the signer. If not provided, it will automatically be fetched from the provider using `eth_requestAccounts`.
import VersionWarning from "../../../VersionWarning"
## signUserOperationHashWithECDSA
Signs the hash of the User Operation with the standard ECDSA signature scheme (used by all EOAs and most smart wallets).
Can take in either the hash of the User Operation or a combination of the User Operation, chain ID and entry point address (calculating the hash internally).
Requires either a walletClient or a localAccount to be passed in that will be used to sign the hash.
### Import
```ts
import { signUserOperationHashWithECDSA } from "permissionless"
```
### Usage
The function can take in either a combination of `userOperation` and `chainId` or a `userOperationHash`, or alternatively a `hash` can be passed in directly.
```ts
import { signUserOperationHashWithECDSA, ENTRYPOINT_ADDRESS_V07 } from "permissionless"
const signature = await signUserOperationHashWithECDSA({
account: localAccount, // or client: walletClient
userOperation: ...,
chainId: 5,
entryPoint: ENTRYPOINT_ADDRESS_V07
})
// "0x2d9420e06e41603c60ce395cfab6821f09c3c2461734dd785f24084a8ee138774f5d1910fb6d2d560934b21946a9667ada6077b5ac143c0d6a5d93e903c172101b"
// or
const signature = await signUserOperationHashWithECDSA({
account: localAccount, // or client: walletClient
hash: "0xf0d56baa398828898360589d5ef75b24b12be0829ef1e1037013d1b6d9dda1f2"
})
// "0x2d9420e06e41603c60ce395cfab6821f09c3c2461734dd785f24084a8ee138774f5d1910fb6d2d560934b21946a9667ada6077b5ac143c0d6a5d93e903c172101b"
```
### Returns
`Hex`
The 65-byte ECDSA signature of the hash of the User Operation.
### Parameters
#### account (mutually exclusive with client)
* **Type:** `LocalAccount`
The account to sign the hash with.
#### client (mutually exclusive with account)
* **Type:** `WalletClient`
The client to sign the hash with.
#### hash (mutually exclusive with userOperation, entryPoint and chainId)
* **Type:** `Hex`
The hash of the User Operation to sign.
#### userOperation (mutually exclusive with hash)
* **Type:** `UserOperation`
The User Operation to hash.
#### chainId (mutually exclusive with hash)
* **Type:** `number`
The chain ID of the chain on which the User Operation is being executed.
#### entryPoint (mutually exclusive with hash)
* **Type:** `Address`
The entry point address.
import VersionWarning from "../../../VersionWarning"
## walletClientToSmartAccountSigner
Takes a walletClient and returns a custom signer that can be used as a signer with permissionless accounts. This is especially useful when using `useWallet` from wagmi, allowing for an easy way to plug in custom frontend wagmi signers to permissionless accounts.
### Import
```ts
import { walletClientToSmartAccountSigner } from "permissionless"
```
### Usage
```ts
import { walletClientToSmartAccountSigner } from "permissionless"
const smartAccountSigner = walletClientToSmartAccountSigner(walletClient)
```
### Returns
`SmartAccountSigner`
The smart account signer that can be used with permissionless accounts.
### Parameters
* **Type:** `WalletClient`
The viem/wagmi wallet client.
## PermissionlessProvider
React Context Provider for Permissionless.
### Import
```ts twoslash
import { PermissionlessProvider } from "@permissionless/wagmi"
```
### Usage
:::code-group
```ts [main.ts]
import { PermissionlessProvider } from "@permissionless/wagmi" // [!code focus]
// enables ERC-7677 if external account is a smart account and supports 7677 // [!code focus]
// e.g. Coinbase Smart Account // [!code focus]
const capabilities = { // [!code focus]
paymasterService: { // [!code focus]
[baseSepolia.id]: { // [!code focus]
url: `https://api.pimlico.io/v2/${baseSepolia.id}/rpc?apikey=${pimlicoApiKey}` // [!code focus]
} // [!code focus]
} // [!code focus]
} // [!code focus]
function App() {
return (
// [!code focus]
{/** ... */}
// [!code focus]
)
}
```
```ts twoslash [wagmi.ts]
import { http, createConfig } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
:::
### Parameters
```ts
import { type PermissionlessProviderProps } from "@permissionless/wagmi"
```
#### capabilities
`WalletCapabilities | undefined`
Capability to use for the calls (e.g. specifying a paymaster).
## useSendTransaction
Hook for creating, signing, and sending transactions to networks.
### Import
```ts
import { useSendTransaction } from '@permissionless/wagmi'
```
### Usage
```tsx [index.tsx]
import { useSendTransaction } from '@permissionless/wagmi'
import { parseEther } from 'viem'
function App() {
const { sendTransaction } = useSendTransaction()
return (
)
}
```
:::warning
`sendTransaction` from @permissionless/wagmi returns a reference to fetch receipt and not transaction hash. Hence it should not be used to fetch transaction details or shown to the user.
:::
### Parameters
```ts
import { type UseSendTransactionParameters } from 'wagmi'
```
#### config
`Config | undefined`
[`Config`](https://wagmi.sh/react/api/createConfig#config) to use instead of retrieving from the from nearest [`WagmiProvider`](https://wagmi.sh/react/api/WagmiProvider).
:::code-group
```tsx [index.tsx]
import { useSendTransaction } from 'wagmi'
import { config } from './config' // [!code focus]
function App() {
const result = useSendTransaction({
config, // [!code focus]
})
}
```
```tsx [config.ts]
import { http, createConfig } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
:::
#### mutation
Same mutation object that you can pass to `useSendTransaction` [hook from wagmi](https://wagmi.sh/react/api/hooks/useSendTransaction#mutation).
### Return Type
```ts
import { type UseSendTransactionReturnType } from 'wagmi'
```
#### sendTransaction
`(variables: SendTransactionVariables, { onSuccess, onSettled, onError }) => void`
The mutation function you can call with variables to trigger the mutation and optionally hooks on additional callback options.
* ##### variables
`SendTransactionVariables`
The variables object to pass to the `sendTransaction` action.
* ##### onSuccess
`(data: string, variables: SendTransactionVariables, context: TContext) => void`
This function will fire when the mutation is successful and will be passed the mutation's result.
* ##### onError
`(error: SendTransactionErrorType, variables: SendTransactionVariables, context: TContext | undefined) => void`
This function will fire if the mutation encounters an error and will be passed the error.
* ##### onSettled
`(data: string | undefined, error: SendTransactionErrorType | null, variables: SendTransactionVariables, context: TContext | undefined) => void`
* This function will fire when the mutation is either successfully fetched or encounters an error and be passed either the data or error
* If you make multiple requests, `onSuccess` will fire only after the latest call you've made.
#### sendTransactionAsync
`(variables: SendTransactionVariables, { onSuccess, onSettled, onError }) => Promise`
Similar to [`sendTransaction`](#sendTransaction) but returns a promise which can be awaited.
#### data
`string | undefined`
* A transaction reference
* Defaults to `undefined`
* The last successfully resolved data for the mutation.
#### error
`SendTransactionErrorType | null`
The error object for the mutation, if an error was encountered.
#### failureCount
`number`
* The failure count for the mutation.
* Incremented every time the mutation fails.
* Reset to `0` when the mutation succeeds.
#### failureReason
`SendTransactionErrorType | null`
* The failure reason for the mutation retry.
* Reset to `null` when the mutation succeeds.
#### isError / isIdle / isPending / isSuccess
`boolean`
Boolean variables derived from [`status`](#status).
#### isPaused
`boolean`
* will be `true` if the mutation has been `paused`.
* see [Network Mode](https://tanstack.com/query/v5/docs/react/guides/network-mode) for more information.
#### reset
`() => void`
A function to clean the mutation internal state (e.g. it resets the mutation to its initial state).
#### status
`'idle' | 'pending' | 'error' | 'success'`
* `'idle'` initial status prior to the mutation function executing.
* `'pending'` if the mutation is currently executing.
* `'error'` if the last mutation attempt resulted in an error.
* `'success'` if the last mutation attempt was successful.
#### submittedAt
`number`
* The timestamp for when the mutation was submitted.
* Defaults to `0`.
#### variables
`SendTransactionVariables | undefined`
* The variables object passed to [`sendTransaction`](#sendTransaction).
* Defaults to `undefined`.
## useWaitForTransactionReceipt
Hook that waits for the transaction to be included on a block, and then returns the transaction receipt. If the transaction reverts, then the action will throw an error. Replacement detection (e.g. sped up transactions) is also supported.
### Import
```ts
import { useWaitForTransactionReceipt } from '@permissionless/wagmi'
```
### Usage
```tsx [index.tsx]
import {
useSendTransaction,
useWaitForTransactionReceipt // [!code focus]
} from "@permissionless/wagmi"
function App() {
const { // [!code focus]
sendTransaction, // [!code focus]
data: transactionReference, // [!code focus]
isPending
} = useSendTransaction()
const { data: receipt, } = // [!code focus]
useWaitForTransactionReceipt({ // [!code focus]
id: transactionReference // [!code focus]
}) // [!code focus]
return (
Send test transaction
{isPending &&
Sending transaction...
}
{transactionReference && isReceiptPending && (
Awaiting confirmation of transaction.
)}
{receipt &&
{receipt.status}
} // [!code focus]
// [!code focus]
)
}
```
### Parameters
```ts
import { type UseWaitForTransactionReceiptParameters } from '@permissionless/wagmi'
```
#### chainId
`config['chains'][number]['id'] | undefined`
ID of chain to use when fetching data.
```ts [index.ts]
import { useWaitForTransactionReceipt } from 'wagmi'
import { mainnet } from 'wagmi/chains'
function App() {
const result = useWaitForTransactionReceipt({
chainId: mainnet.id, // [!code focus]
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
})
}
```
#### config
`Config | undefined`
[`Config`](https://wagmi.sh/react/api/createConfig#config) to use instead of retrieving from the from nearest [`WagmiProvider`](https://wagmi.sh/react/api/WagmiProvider).
\::: code-group
```tsx [index.tsx]
import { useWaitForTransactionReceipt } from 'wagmi'
import { config } from './config' // [!code focus]
function App() {
const result = useWaitForTransactionReceipt({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
config, // [!code focus]
})
}
```
```tsx [config.ts]
import { http, createConfig } from 'wagmi'
import { mainnet, sepolia } from 'wagmi/chains'
export const config = createConfig({
chains: [mainnet, sepolia],
transports: {
[mainnet.id]: http(),
[sepolia.id]: http(),
},
})
```
\:::
#### confirmations
`number | undefined`
The number of confirmations (blocks that have passed) to wait before resolving.
```ts [index.ts]
import { useWaitForTransactionReceipt } from 'wagmi'
function App() {
const result = useWaitForTransactionReceipt({
confirmations: 2, // [!code focus]
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
})
}
```
#### onReplaced
`(({ reason: 'replaced' | 'repriced' | 'cancelled'; replacedTransaction: Transaction; transaction: Transaction; transactionReceipt: TransactionReceipt }) => void) | undefined`
Optional callback to emit if the transaction has been replaced.
```ts [index.ts]
import { useWaitForTransactionReceipt } from 'wagmi'
function App() {
const result = useWaitForTransactionReceipt({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
onReplaced: replacement => console.log(replacement), // [!code focus]
})
}
```
#### pollingInterval
`number | undefined`
* Polling frequency (in milliseconds).
* Defaults to the [Config's `pollingInterval` config](https://wagmi.sh/core/api/createConfig#pollingintervall).
```ts [index.ts]
import { useWaitForTransactionReceipt } from 'wagmi'
function App() {
const result = useWaitForTransactionReceipt({
hash: '0x4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d',
pollingInterval: 1_000, // [!code focus]
})
}
```
#### id
`string | undefined`
The transaction reference to wait for. [`enabled`](#enabled) set to `false` if `hash` is `undefined`.
```ts [index.ts]
import { useWaitForTransactionReceipt } from 'wagmi'
function App() {
const result = useWaitForTransactionReceipt({
id: '4ca7ee652d57678f26e887c149ab0735f41de37bcad58c9f6d3ed5824f15b74d', // [!code focus]
})
}
```
#### query
Same query object that you can pass to `useWaitForTransactionReceipt` [hook from wagmi](https://wagmi.sh/react/api/hooks/useWaitForTransactionReceipt#query).
{/* */}
### Return Type
```ts
import { type UseWaitForTransactionReceiptReturnType } from 'wagmi'
```
Same return type as `useWaitForTransactionReceipt` from [wagmi](https://wagmi.sh/react/api/hooks/useWaitForTransactionReceipt#return-type).