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, 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