How ERC-4337 actually works: bundler, paymaster, entrypoint
Every Ethereum account has one job it cannot delegate: pay gas in ETH. That constraint shapes every onboarding flow in the ecosystem. You cannot send a user to a dapp before they have ETH, and you cannot get them ETH without a fiat ramp that asks for ID and takes three business days. The friction is real, and it is structural.
ERC-4337 breaks that constraint. Not by changing how Ethereum nodes work, but by adding a parallel execution layer on top — one that lets smart contracts act as first-class accounts, sponsors pay gas on behalf of users, and wallets define their own signature schemes. A wallet can require two-of-three signatures. A paymaster can cover gas costs for new users and charge in USDC instead. A bundler aggregates a hundred operations into one transaction. None of this requires a hard fork.
The spec was finalized in 2023. The EntryPoint contract was deployed to every major EVM chain at the same address. Understanding how the three primitives fit together — the EntryPoint, the Bundler, and the Paymaster — makes the rest of the design legible.
The Problem with EOAs
An externally-owned account (EOA) is just a private key. Its validation logic is hardcoded into the protocol: ECDSA signature, the correct nonce, enough ETH for gas. You cannot change any of that. You cannot say "accept a passkey signature" or "require two approvals" or "let someone else pay the fee."
Smart contracts can hold ETH and execute arbitrary logic, but the protocol never lets them initiate transactions. Only EOAs can do that. So every smart contract wallet in the pre-4337 world needed an EOA relay — a "relayer" or "executor" — to submit transactions on behalf of the wallet. These systems worked, but they were custom-built per project, incompatible with each other, and required off-chain infrastructure that every team had to run themselves.
ERC-4337 standardizes the relay layer. Instead of each project rolling its own, the ecosystem shares a single trusted coordinator contract — the EntryPoint — and a permissionless off-chain network of bundlers to feed it.
The UserOperation
The core data type in ERC-4337 is the UserOperation — not a regular Ethereum transaction, but a struct that describes what a smart wallet wants to do.
struct UserOperation {
address sender; // the smart wallet, not the signer
uint256 nonce;
bytes initCode; // factory + salt, empty if wallet exists
bytes callData; // the actual intent: transfer, swap, mint
uint256 callGasLimit;
uint256 verificationGasLimit;
uint256 preVerificationGas;
uint256 maxFeePerGas;
uint256 maxPriorityFeePerGas;
bytes paymasterAndData; // empty = user pays; non-empty = sponsor pays
bytes signature; // wallet-defined: ECDSA, multisig, passkey…
}
sender is the smart wallet address — the contract that will execute the operation. The private key that signed the signature field is the owner of that wallet, but the wallet itself is what appears on-chain as the originating account.
initCode is non-empty only when the wallet does not exist yet. It encodes the factory contract address and the salt used to deploy the wallet. The EntryPoint deploys the wallet during the same transaction as the first operation. Users do not need a separate deployment step.
paymasterAndData is what makes gas sponsorship work. When it is empty, the smart wallet must have deposited ETH into the EntryPoint to cover gas. When it contains a paymaster address (plus optional context bytes the paymaster uses for its own logic), the paymaster takes responsibility for the gas costs instead.
signature is opaque to the EntryPoint. It passes the bytes directly to the wallet's validateUserOp function. The wallet decides what a valid signature means — ECDSA, Schnorr, a multisig threshold, a WebAuthn passkey. The EntryPoint does not care.
The Bundler
A bundler is an off-chain node — typically an EOA with ETH — that watches a separate peer-to-peer mempool for UserOperation objects, selects a profitable batch, and submits them to the EntryPoint as a single regular Ethereum transaction by calling handleOps.
The bundler earns the difference between the gas the user (or paymaster) agreed to pay and the actual gas cost of execution. This incentive structure mirrors how miners and validators work: the bundler is taking on execution risk in exchange for a fee.
Before submitting, a responsible bundler simulates each UserOperation locally to verify it will not revert. This matters because a reverted handleOps call still costs gas — the bundler eats that loss. The spec includes rules about what wallet validation logic is allowed to do during simulation (no storage reads from arbitrary contracts, no calls to addresses that could change behavior between simulation and execution) to make simulation results trustworthy.
Bundlers are permissionless. Anyone can run one. In practice, most production traffic goes through a handful of bundler-as-a-service APIs (Alchemy, Pimlico, Biconomy), but the architecture does not require trusting any of them — a user can submit to any bundler, or run their own.
The EntryPoint
The EntryPoint is a singleton contract deployed at 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 on every supported chain. It is the only contract that all three parties — wallet, bundler, and paymaster — need to trust. Its source is immutable and has been audited by multiple firms.
handleOps is its main entry point. The bundler calls it with an array of UserOperation structs. The EntryPoint runs them in two strict phases.
Phase 1 — Validate all ops first. For each UserOperation, the EntryPoint calls validateUserOp on the sender wallet and, if a paymaster is specified, validatePaymasterUserOp on the paymaster. Both calls must succeed. The wallet confirms the signature is valid and increments its nonce. The paymaster confirms it is willing to sponsor and optionally returns context data that gets passed back in the post-operation call. Gas limits and prefunding are verified here. No actual business logic runs in this phase.
Phase 2 — Execute all ops. For each validated op, the EntryPoint calls execute on the sender wallet with the callData. This is where the actual intent runs — the token transfer, the swap, the NFT mint. If the call reverts, the EntryPoint catches the revert and continues processing the remaining ops in the batch. After execution, if a paymaster was involved, the EntryPoint calls postOp on the paymaster so it can finalize accounting (charge the user in ERC-20, log usage, etc.).
Splitting validation and execution is deliberate. It prevents a malicious wallet from using the validation phase to consume excessive gas and then reverting — that attack pattern is economically blocked because validation gas is always billed.
The Paymaster
The paymaster is a smart contract that tells the EntryPoint "I will cover the gas for this operation." It must have ETH deposited in the EntryPoint's internal accounting system to back that promise.
function validatePaymasterUserOp(
UserOperation calldata userOp,
bytes32 userOpHash,
uint256 maxCost
) external returns (bytes memory context, uint256 validationData);
The return value of validatePaymasterUserOp is a context blob that gets passed to postOp. This is how ERC-20 gas payment works in practice: during validation, the paymaster checks that the user has approved enough tokens. During postOp, after the actual gas cost is known, the paymaster pulls the exact token amount from the user.
The two most common paymaster patterns are:
Sponsoring paymaster — the operator simply pays all gas costs for qualifying users. The AAPM project uses this pattern: new users sign in with Google via Web3Auth, get a smart wallet deployed automatically, and their first transactions are sponsored entirely. No ETH, no fiat ramp.
ERC-20 paymaster — the paymaster holds an ETH deposit but charges users in a token (USDC, MATIC, whatever). The paymaster runs a token-to-ETH conversion in postOp to replenish itself. Users experience "pay with USDC" while the network still settles in ETH.
Both patterns require the paymaster operator to pre-deposit ETH into the EntryPoint. The deposit is slashed to reimburse the bundler after execution. The paymaster can set a stake separately to unlock the ability to access storage across multiple validation calls (unstaked paymasters face tighter simulation restrictions).
A Concrete Flow
To make this concrete, here is what happens when a user in the AAPM prediction market places a bet without holding ETH:
- The frontend builds a
UserOperationwithcallDataencoding the bet,paymasterAndDataset to the AAPM sponsoring paymaster, and a signature from the user's Web3Auth-managed key. - The UserOp is submitted to a bundler's RPC endpoint.
- The bundler simulates the op, confirms both validation calls succeed, and includes it in the next batch.
- The bundler calls
EntryPoint.handleOps([userOp], bundlerAddress). - Phase 1: EntryPoint calls
wallet.validateUserOp— signature checks out, nonce increments. Then callspaymaster.validatePaymasterUserOp— paymaster confirms it is willing to sponsor, returns empty context. - Phase 2: EntryPoint calls
wallet.execute(predictionMarketAddress, 0, placeBetCalldata). The prediction market records the bet. - EntryPoint calls
paymaster.postOp(PostOpMode.opSucceeded, "", actualGasCost). The paymaster's ETH deposit is debited. Bundler is reimbursed.
The user placed a bet, never saw a gas prompt, never held ETH. To them it was indistinguishable from a Web2 form submission.
What This Enables
The shift from EOAs to smart wallets is not cosmetic. When validation logic lives in a contract that you control, the design space opens up in ways that were previously impossible at the protocol level.
Session keys let users authorize a dapp to sign a bounded set of operations without approving every individual transaction — useful for games or any flow with repeated low-value actions. Batch operations let a wallet execute multiple contract calls atomically in a single UserOp: approve and swap in one step, no two-transaction dance. Social recovery lets a wallet define a recovery mechanism — trusted friends, hardware backup, time-locked guardian — without relying on the user to safeguard a seed phrase. Spending limits enforce per-day or per-recipient ETH caps directly in the wallet's validation logic.
None of these are features you add to ERC-4337. They are consequences of moving validation logic into a smart contract where you can write whatever rules you want.
Conclusion
ERC-4337 is a clever piece of engineering because it achieves a protocol-level goal — flexible account logic — without touching the protocol. The EntryPoint is the anchor of trust. The bundler is the permissionless executor that connects the off-chain UserOp mempool to on-chain settlement. The paymaster is the abstraction that severs the link between "who uses the application" and "who pays the gas."
The three primitives are independent but composable. A wallet does not need a paymaster. A paymaster works with any wallet that implements validateUserOp. A bundler does not care what the wallet's signature scheme is. The separation of concerns is what makes the system extensible — you can upgrade any one piece without rewriting the others.
The hardest part of building with ERC-4337 is not the spec itself. It is choosing your bundler infrastructure, deciding your paymaster's sponsorship policy (who qualifies, how much, what happens on revert), and designing wallet validation logic that is simulation-safe. Those are product decisions as much as engineering ones.