Paying Gas with ERC20s
A smart account can pay gas with ERC20 tokens. As a result, your users don't have to own any native gas tokens (e.g. ETH) in order to use Web3. Instead, they can just use stablecoins or even your project's own tokens. When your users pay gas with ERC20 tokens, we add 5% to the exchange rate to make a profit.
ZeroDev supports two USDC paymasters: the Circle paymaster and the Legacy paymaster. Generally, the Circle paymaster gives you better exchange rates, while the Legacy paymaster is available on more networks.
Circle Paymaster
Start by signing a USDC permit for the paymaster. The permit specifies the maximum amount of USDC that the signing account wants to spent on gas.
import {
constructUSDCPermitForCirclePaymaster
} from "@zerodev/sdk"
const { signature: permit } = await account.signTypedData(
await constructUSDCPermitForCirclePaymaster({
ownerAddress: account.address,
// Allow $10 USDC maximum to be spent on gas
value: 10000000
})
)
Next, you can create a Circle paymaster client:
import {
createCircleUSDCPaymasterClient
} from "@zerodev/sdk"
const paymasterClient = createCircleUSDCPaymasterClient({
chain,
transport: http('ZERODEV_PAYMASTER_RPC'),
permit: permit,
})
const kernelClient = createKernelAccountClient({
// other options...
paymaster: paymasterClient,
})
Now you can use the Kernel client as usual, and you will be paying gas with USDC!
Legacy Paymaster
On a high level, you need to do two things to enable a smart account to pay gas in a particular ERC20 token:
- Set up the Kernel client with the ERC20 paymaster.
- Ensure that enough ERC20 tokens have been approved for the ERC20 paymaster.
- This step is necessary because the ERC20 paymaster needs to withdraw ERC20 tokens from the smart account.
Let's go through these two steps next.
Set up Kernel Client
When you set up an account, do this:
import {
createZeroDevPaymasterClient,
createKernelAccountClient,
gasTokenAddresses,
} from "@zerodev/sdk"
import {
getEntryPoint
} from "@zerodev/sdk/constants"
import { mainnet } from "viem/chains"
// use whatever chain you want
const chain = mainnet.id
const entryPoint = getEntryPoint("0.7")
const paymasterClient = createZeroDevPaymasterClient({
chain,
transport: http('ZERODEV_PAYMASTER_RPC'), // get the RPC on ZeroDev dashboard
})
const kernelClient = createKernelAccountClient({
// other options...
paymaster: paymasterClient,
paymasterContext: { token: gasTokenAddresses[chain.id]['USDC'] }
})
Approve ERC20 tokens for paymaster
Use the getERC20PaymasterApproveCall
function to construct a call that approves the paymaster with the ERC20 tokens:
import { getERC20PaymasterApproveCall } from "@zerodev/sdk"
const userOpHash = await kernelClient.sendUserOperation({
callData: await kernelClient.account.encodeCalls([
await getERC20PaymasterApproveCall(paymasterClient, {
gasToken: gasTokenAddresses[chain.id]['USDC'],
approveAmount: parseEther('0.1'),
entryPoint,
}),
]),
})
Thanks to batching, it's possible to batch the approval with the UserOp you want to send:
const userOpHash = await kernelClient.sendUserOperation({
callData: await account.encodeCalls([
// The approval
await getERC20PaymasterApproveCall(paymasterClient, {
gasToken: gasTokenAddresses[chain.id]['USDC'],
approveAmount: parseEther('0.1'),
entryPoint,
}),
// The actual call
{
to: '0x...'
data: "0x...",
value: BigInt(0),
},
]),
})
Note that you only have to approve once, as long as the approval amount is sufficient for many UserOps. The paymaster contract by Pimlico has been audited, it's widely used and generally considered safe.
Estimate Gas in ERC20s
If you need to estimate gas in terms of a ERC20 token, do this:
const userOperation = await kernelClient.prepareUserOperation({
// replace this with your actual calldata
callData: await account.encodeCalls([{
to: zeroAddress,
value: BigInt(0),
data: "0x"
}])
})
const erc20Amount = await paymasterClient.estimateGasInERC20({
userOperation,
// replace this with the token you want
gasTokenAddress: gasTokenAddresses[chain.id]["USDC"]
})
You can also see a code example for estimating gas here.
Supported Tokens
Currently, our ERC20 Paymaster supports USDC on the following networks:
- Ethereum Mainnet
- Polygon
- Base
- Optimism
- Arbitrum
If you want to use a different token, you can either reach out to us or deploy your own Self-Funded ERC20 Paymaster.
Deploy Your Own ERC20 Paymaster
If you want to support a custom ERC20 token, you can deploy and manage your own ERC20 paymaster contract. To do this:
- Visit the ZeroDev dashboard to deploy a self-funded ERC20 paymaster contract
- Fund the contract with the native currency (e.g., ETH)
- Set a conversion rate for the ERC20 token you would like to enable for the paymaster
Once deployed, you can use your custom paymaster by copying the paymaster URL from the dashboard and using it as the transport
parameter when creating the paymaster client:
const paymasterClient = createZeroDevPaymasterClient({
chain,
transport: http('YOUR_CUSTOM_PAYMASTER_URL'), // from the self-funded paymaster page on the dashboard
})
The rest of the integration remains the same as described above.