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 } 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 tokenDecimals = 6
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 max cost
To find your userOperation's max cost, we will need to sum up all it's gas values. This includes:
preVerificationGas
verificationGasLimit
callGasLimit
paymasterVerificationGasLimit
(v0.7 only)paymasterPostOpGasLimit
(v0.7 only)
Once the gas values are summed up, we multiply it by the maxFeePerGas to get the max cost for the userOperation.
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 userOperationMaxGas =
op.preVerificationGas +
op.verificationGasLimit +
op.callGasLimit +
(op.paymasterVerificationGasLimit || 0n) +
(op.paymasterPostOpGasLimit || 0n)
const userOperationMaxCost = userOperationMaxGas * op.maxFeePerGas
Calculate your userOperation's maxCost in denomination of the ERC-20 token
To find the cost of your userOperation in denomination 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 = userOperationMaxCost + postOpGas * op.maxFeePerGas
// represents the userOperation's max cost in token demoniation (wei)
const maxCostInTokenRaw = (maxCostInWei * exchangeRate) / BigInt(1e18)
// represents the userOperation's max cost in token (human readable format)
const maxCostInToken = Number(maxCostInTokenRaw) / 10 ** tokenDecimals
Note: We divide by
BigInt(1e18)
(constant) due to the ether decimals being encoded into exchangeRate.
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