Skip to content

How to use the ERC-20 Paymaster without unlimited token approvals

This guide will show you how to use the prepareUserOperationForErc20Paymaster function in permissionless.

This function lets you approve only the required amount of tokens for your userOperation. The approval call will be injected into the userOperation's calldata before it is sent.

Steps

Define imports and create the clients

import { prepareUserOperationForErc20Paymaster } from "permissionless/experimental/pimlico";
import { createSmartAccountClient } from "permissionless";
import { toSafeSmartAccount } from "permissionless/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { createPublicClient, Hex, http } 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(
			process.env.PRIVATE_KEY as Hex,
		),
	],
	entryPoint: {
		address: entryPoint07Address,
		version: "0.7",
	},
	version: "1.4.1",
});

Add the prepareUserOperationForErc20Paymaster override to createSmartAccountClient

This override will replace viem's prepareUserOperation action.

const smartAccountClient = createSmartAccountClient({
	account,
	chain,
	bundlerTransport: http(pimlicoUrl),
	paymaster: pimlicoClient,
	userOperation: {
		estimateFeesPerGas: async () => {
			return (await pimlicoClient.getUserOperationGasPrice()).fast;
		},
		prepareUserOperation:
			prepareUserOperationForErc20Paymaster(pimlicoClient),
	},
});

Call sendTransaction

Calling sendTransaction will now automatically approve the required amount of tokens for your userOperation.

If the sender has a sufficient existing approval, then the additional approval call will not be added to the calldata.

const token = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
 
const hash = await smartAccountClient.sendTransaction({
	calls: [
		{
			to: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
			value: 0n,
			data: "0x1234",
		},
	],
	paymasterContext: {
		token,
	},
});