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
Define imports and create the clients
import { createSmartAccountClient, getRequiredPrefund } from "permissionless"
import { toSafeSmartAccount } from "permissionless/accounts"
import { createPimlicoClient } from "permissionless/clients/pimlico"
import { type Address, createPublicClient, getAddress, http, maxUint256, parseAbi } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { entryPoint07Address } from "viem/account-abstraction"
import { base } from "viem/chains"
const chain = base
export const publicClient = createPublicClient({
chain,
transport: http("https://mainnet.base.org"),
})
const pimlicoUrl = `https://api.pimlico.io/v2/${chain.id}/rpc?apikey=${process.env.PIMLICO_API_KEY}`
const pimlicoClient = createPimlicoClient({
chain,
transport: http(pimlicoUrl),
entryPoint: {
address: entryPoint07Address,
version: "0.7",
},
})
const account = await toSafeSmartAccount({
client: publicClient,
owners: [
privateKeyToAccount("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
],
entryPoint: {
address: entryPoint07Address,
version: "0.7",
},
version: "1.4.1",
})
const smartAccountClient = createSmartAccountClient({
account,
chain,
bundlerTransport: http(pimlicoUrl),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
},
},
})
Fetch quote info for a certain token
For this example, we will be fetching quote info for USDC on base.
const token = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
const quotes = await pimlicoClient.getTokenQuotes({
tokens: [getAddress(token)],
})
const postOpGas: bigint = quotes[0].postOpGas
const exchangeRate: bigint = quotes[0].exchangeRate
const exchangeRateNativeToUsd: bigint = quotes[0].exchangeRateNativeToUsd
const paymaster: Address = quotes[0].paymaster
Calculate your userOperation's prefund
The prefund represents your userOperation's max cost.
const op = await smartAccountClient.prepareUserOperation({
calls: [
{
to: token,
abi: parseAbi(["function approve(address, uint)"]),
args: [paymaster, maxUint256],
},
{
to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
value: 0n,
data: "0x1234",
},
],
paymasterContext: {
token,
},
})
const userOperationPrefund = getRequiredPrefund({
userOperation: op,
entryPointVersion: "0.7",
})
Calculate your userOperation's maxCost in denomination of the ERC-20 token
To find the cost of your userOperation in denomition of the ERC-20, you should use this formula. This is the same formula that the paymaster executes onchain, the source code can be found on the singleton-paymaster repo.
// represents the userOperation's max cost in demoniation of wei
const maxCostInWei = userOperationPrefund + postOpGas * op.maxFeePerGas
// represents the userOperation's max cost in token demoniation (wei)
const maxCostInToken = (maxCostInWei * exchangeRate) / BigInt(1e18)
Calculate your userOperation's maxCost in denomination of USD
The exchangeRateNativeToUsd represents the exchange rate of the chain's native gas token to USD. The value is returned with 6 digits of precision, to calculate the USD cost in a human readable format, you first need to find it's raw value and then divide it by 10^6.
// represents the userOperation's max cost in usd (with 6 decimals of precision)
const rawCostInUsd = (maxCostInWei * exchangeRateNativeToUsd) / 10n ** 18n
// represents the userOperation's max cost in usd
// (human readable format after dividing by 6 decimal places)
const costInUsd = Number(rawCostInUsd) / 10 ** 6