Define your sponsorship logic

The architecture of your paymaster contract hinges on a single decision: who pays for the gas, and under what conditions? ERC-4337 paymasters enable dApps and wallets to sponsor user operations, pay for gas in ERC-20 tokens, or cover fees in stablecoins [1]. This flexibility allows you to design a sponsorship model that aligns with your product's user experience goals.

You must choose one of three primary modes before writing the core validation logic:

  • Full Sponsorship: The paymaster covers all gas costs for every transaction executed through your smart account. This approach removes friction for new users but requires a reliable funding mechanism to keep the paymaster wallet solvent.
  • Action-Based Sponsorship: The paymaster only covers gas for specific, high-value actions, such as onboarding, first-time trades, or critical security updates. Other transactions are paid for by the user. This balances cost efficiency with user acquisition.
  • ERC-20 Fee Payment: The paymaster allows users to pay gas fees using an ERC-20 token (like USDC or DAI) rather than the native chain token (like ETH or MATIC). This requires integrating a price oracle to convert the token value to the equivalent native gas cost.

Choosing your mode early dictates how you structure the validatePaymasterUserOp function. If you opt for action-based sponsorship, your contract must parse the callData to identify the intended function call. If you choose ERC-20 payments, you must implement logic to verify token allowances and handle slippage.

A common mistake is attempting to support all modes simultaneously in the initial build. This complicates the validation logic and increases the attack surface. Start with the simplest model that solves your core user problem, then iterate. For example, if your goal is onboarding, full sponsorship is often the most effective starting point.

Set up the development environment

This section establishes the tooling stack for building and compiling your ERC-4337 Paymaster contract. We will use Hardhat as our primary development environment due to its extensive ecosystem and TypeScript support, which aligns well with the complexity of Account Abstraction logic.

Initialize the Hardhat Project

Start by creating a new directory for your project and initializing it with npm. Install Hardhat and the necessary TypeScript dependencies to ensure type safety during development.

Shell
mkdir paymaster-kit
cd paymaster-kit
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init

During the init process, select "Create a TypeScript project" and install the suggested dependencies. This configuration provides a robust base for writing and testing Solidity contracts with full type inference.

Install ERC-4337 Dependencies

To interact with the ERC-4337 standard, you need the official reference implementations and utility libraries. Install the account-abstraction package, which contains the core contract interfaces and helper utilities for bundlers and paymasters.

Shell
npm install @account-abstraction/contracts

This package includes the IPaymaster interface and the PaymasterHelper contract, which simplifies the verification and payment logic. It also pulls in solmate and openzeppelin contracts, ensuring you are using audited, standard-compliant primitives.

Configure Hardhat Network

Update your hardhat.config.ts to include the necessary network configurations for local testing and deployment. You will need to define at least one local network (e.g., Hardhat Network) for rapid iteration and testnets for validation.

TypeScript
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

const config: HardhatUserConfig = {
  solidity: "0.8.24",
  networks: {
    hardhat: {
      chainId: 31337,
    },
    // Add testnet configurations here
  },
};

export default config;

Ensure your Solidity version matches the requirements of the ERC-4337 contracts. Version 0.8.24 is recommended for its stability and compatibility with the latest optimization flags. With this setup, you are ready to write your first Paymaster contract in the next section.

Implement the paymaster contract

To enable gasless transactions, your smart contract must inherit from IPaymasterOperations and implement the required EntryPoint hooks. This contract acts as the sponsor, validating user intent and handling the gas payment logic on-chain.

Paymaster Kits
1
Inherit IPaymasterOperations

Begin by importing the EntryPoint interface and inheriting from IPaymasterOperations. This provides the necessary function signatures for validatePaymasterUserOp and postOp, ensuring your contract can interact correctly with the ERC-4337 bundler and EntryPoint contract.

2
Implement validatePaymasterUserOp

This function runs before the user operation executes. It must validate that the user has paid the paymaster (e.g., via an allowance or signature) and return a context string. This context is passed to the postOp function for settlement. If validation fails, the operation is reverted immediately.

3
Handle deposit and refund logic

Paymasters must manage their ETH balance on the EntryPoint. Implement logic to deposit ETH using entryPoint.depositTo{value: amount}(address(this)). When the balance is low, the contract should trigger a withdrawal or alert the owner to refill, ensuring operations do not fail due to insufficient funds.

4
Implement postOp

The postOp function runs after the user operation completes. Use the context from validation to settle payments (e.g., transferring ERC-20 tokens from the user to the paymaster). This is also where you can handle refunds or charge excess gas if the user operation used more gas than estimated.

By following this sequence, you create a robust Paymaster that can sponsor gas for your users. Ensure you test thoroughly on a testnet to verify that the EntryPoint correctly calls your validation and post-operation hooks.

Fund the paymaster deposit

Your paymaster contract requires an ETH balance to pay gas fees on behalf of users. Without this liquidity, sponsored transactions will revert, and your application will fail to execute. Think of the deposit as the fuel tank for your gas station; if it's empty, no cars (transactions) can leave.

Follow these steps to fund the deposit on the target network.

Paymaster Kits
1
Verify the EntryPoint address

Before sending any funds, confirm the correct EntryPoint contract address for your target network (e.g., Sepolia, Mainnet, or a testnet). Using the wrong address will result in lost funds. Refer to the official documentation for the specific EntryPoint version you are integrating with.

2
Send ETH to the deposit function

Call the deposit() function on the EntryPoint contract, passing your paymaster contract address as the beneficiary. This transfers ETH from your wallet into the EntryPoint's internal balance for your paymaster. Ensure your wallet has enough ETH to cover both the deposit amount and the gas cost of the transaction itself.

3
Monitor the balance

Verify the deposit succeeded by checking the paymaster's balance on a block explorer. You can query the balanceOf() method on the EntryPoint contract with your paymaster address. If the balance is zero, the transaction likely failed or was sent to the wrong contract.

Integrate with your frontend

Connecting the smart contract to the user interface requires passing the paymaster data correctly in the UserOperation. The frontend must construct this data structure to ensure the wallet can relay the transaction to the bundler. This section outlines the sequence for building the UserOperation payload.

Paymaster Kits
1
Initialize the smart account instance

Load your wallet library (such as Viem or Ethers) with the smart account address. Ensure the account is initialized with the correct paymaster and paymaster data fields. This sets the context for all subsequent transaction signing requests.

2
Construct the UserOperation payload

Build the UserOperation object. This includes the callData (encoded function call), callGasLimit, preVerificationGas, gasLimits, verificationGasLimit, paymasterAndData, and signature. The paymasterAndData field is critical; it must contain the paymaster address and any required authentication data.

3
Sign the UserOperation hash

Use the user’s private key or a wallet provider to sign the hash of the UserOperation (excluding the signature field). This produces the user’s signature, which must be appended to the UserOperation object before submission.

4
Submit to the bundler

Send the completed UserOperation to the bundler via the eth_sendUserOperation RPC method. The bundler will validate the operation, including the paymaster’s pre-verification checks, and relay it to the mempool.

For a detailed implementation guide, refer to MetaMask’s documentation on using an ERC-20 paymaster with a smart account. This resource provides code examples for handling ERC-20 fee payments, which is a common use case for gasless transactions.

Test on a local bundler

Before deploying your paymaster to a public testnet, you must verify that user operations are correctly bundled and submitted to the EntryPoint contract. A local bunder allows you to inspect the mempool, debug signature verification, and confirm gas estimation without risking real funds or waiting for network congestion.

1
Spin up the bundler service

Initialize a local bundler instance, such as the Holesky bundler or a local Geth node configured for ERC-4337. This service listens for sendUserOperation calls and aggregates them into bundles. Ensure your node is synced and ready to receive user operations from your local development environment.

2
Send a test user operation

Use your paymaster kit to submit a user operation to the local bundler. Include a valid signature and ensure the paymaster’s payForUserOperation method is called correctly. The bundler should accept the operation and add it to its pending pool.

3
Verify EntryPoint interaction

Check that the bundler successfully includes your operation in a bundle and submits it to the EntryPoint contract. Verify that the EntryPoint executes the paymaster’s payment logic and that the user’s signature is validated. Look for successful execution logs in your node’s output.

4
Debug gas and limits

If the operation fails, inspect the error logs. Common issues include incorrect gas limits, signature mismatches, or paymaster balance constraints. Adjust your paymaster’s gas parameters and retry. Ensure the bundler is correctly estimating gas for the user operation.

Once you confirm that the bundler correctly processes your user operations and interacts with the EntryPoint, you can proceed to test on a public testnet like Holesky. This final step ensures your paymaster performs as expected in a live environment.

Common paymaster mistakes

Even with ERC-4337 standardizing user operations, implementation errors can break gasless experiences or expose your protocol to exploitation. The following sections outline the most frequent pitfalls and how to avoid them.

Ignoring signature expiry

A paymaster must validate that the user’s signature is still valid at execution time. If you omit timestamp checks or rely on weak nonce management, attackers can replay old transactions indefinitely. Always enforce a strict expiry window in your validatePaymasterUserOp function.

Hardcoding gas limits

Setting fixed gas caps for sponsorship ignores network volatility. If the base fee spikes, your pre-paid limit may be insufficient, causing the bundler to drop the transaction or the user to face partial failures. Use dynamic gas estimation based on current block conditions rather than static constants.

Weak validation logic

Your validation logic is the first line of defense. Failing to verify the sender’s address or skipping checks on the paymasterData field can allow unauthorized actors to trigger sponsorship for invalid operations. Ensure every field in the UserOperation struct is rigorously checked against your business rules before returning a valid signature.

Paymaster implementation FAQ

The term "paymaster" spans legal, financial, and blockchain contexts. In ERC-4337, a paymaster is a smart contract that sponsors user operations. It differs from traditional payroll services or legal escrow agents. This section clarifies the technical role of smart contract paymasters.