Skip to content

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