Skip to content

About

ERC-4337 EntryPoint v0.8: paymaster charged for postOp overhead beyond paymasterPostOpGasLimit (EIP-4337 §4.3.1). Foundry PoCs + fix PR.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

4 Commits

Folders and files

Repository files navigation

ERC-4337 EntryPoint v0.8 Paymaster charged for postOp overhead beyond its declared limit

What it is: the canonical ERC-4337 EntryPoint charges a paymaster for postOp-phase execution above the paymasterPostOpGasLimit it declared — the value EIP-4337 §4.3.1 defines as a strict upper bound. The overcharge occurs even when the paymaster's postOp is empty and paymasterPostOpGasLimit = 0.

Proven: deterministic Foundry PoC against the real EntryPoint on a mainnet fork (0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f). 13/13 tests pass.


TL;DR

  • Spec: EIP-4337 §4.3.1 — a paymaster's postOp gas is bounded above by paymasterPostOpGasLimit; the paymaster must never pay for postOp beyond it.
  • Reality: on a postOp OOG revert, EntryPoint still bills the paymaster for internal overhead surrounding the postOp call (context handling + call machinery) — a nonzero charge with the limit set to 0.
  • Consequence: a paymaster's declared gas budget is not the real ceiling. A party that controls op inclusion (a malicious/colluding bundler) can force a sponsored op into a postOp OOG path and make the paymaster overpay, per op, every block, bundled N-at-a-time.
  • Severity framing (honest): this is a spec-conformance + griefing/overcharge bug, not a one-transaction paymaster drain. The per-op overcharge is small (~1.18e14 wei measured); the risk is systematic — it breaks the gas-accounting invariant every paymaster relies on and bleeds deposits at scale.

Background — the postOp gas contract

An ERC-4337 paymaster sponsors a user operation and optionally runs postOp() after the account call. When it does, it declares paymasterPostOpGasLimit inside paymasterAndData. EIP-4337 §4.3.1 treats this as a strict upper bound: the paymaster may be charged for at most that much postOp gas.

Paymasters rely on this to price sponsorship. If the real charge can exceed the declared limit, every paymaster's accounting is off by an amount it never agreed to — including paymasters that run no postOp logic at all (limit = 0).

The bug

When postOp reverts out-of-gas, EntryPoint's post-execution accounting still attributes the surrounding internal overhead (copying the paymaster context, the call frame, bookkeeping) to the paymaster's bill, without capping that attribution at paymasterPostOpGasLimit.

Result, measured with an empty postOp and paymasterPostOpGasLimit = 0:

Perfect Paymaster Deposit Before: 2000000000000000000 wei
Perfect Paymaster Deposit After:  1999881513781656571 wei
Overcharge (this op):                  118486218343429 wei   (~0.000118 ETH)
CONFIRMED ON MAINNET FORK with empty postOp, limit = 0.

The declared ceiling was 0. The paymaster paid > 0. That is the violation.

Attack flow

sequenceDiagram
    participant B as Bundler (attacker / colluder)
    participant EP as EntryPoint (mainnet)
    participant PM as Paymaster (limit = 0)

    B->>EP: handleOps([ userOp sponsored by PM ])
    EP->>PM: validatePaymasterUserOp  ->  context, postOpGasLimit = 0
    EP->>EP: execute account call
    EP->>PM: postOp() (empty) -> OOG revert
    Note over EP,PM: 4.3.1 bound = 0, so charge must be 0
    EP->>PM: charge deposit for internal postOp overhead (> 0)
    B->>EP: repeat, bundled N ops per tx, every block
Loading

Impact — stated honestly

  • What's exposed: paymaster deposits held in the EntryPoint. This is not "drainable in one transaction"; the per-op overcharge is ~1.18e14 wei.
  • Why it still matters:
    • It is a provable spec violation (§4.3.1) in the reference EntryPoint used across the ecosystem.
    • The charge is repeatable and bundlable — a 50-op bundle multiplies it ~50x per transaction, and it recurs every block.
    • It breaks the invariant paymasters price against, so a sponsor's cost model silently under-estimates real spend.
  • Who can trigger it: anyone who controls op inclusion — a malicious or colluding bundler — using a legitimately sponsored op steered into a postOp OOG path. It does not require the attacker to forge paymasterAndData (see the Pimlico exchange below).

The fix

Cap the postOp-phase gas attributed to the paymaster at the declared limit, so internal overhead can never be billed beyond paymasterPostOpGasLimit:

// concept: clamp the charged postOp gas to the declared upper bound
uint256 chargedPostOpGas = Math.min(actualPostOpGasUsed, paymasterPostOpGasLimit);

test/ExploitForkTestFixed.sol demonstrates the patched path: with the overhead capped, the overcharge collapses and the drain is no longer profitable.

➡️ Fix PR: Tejanadh/account-abstraction#1

Reproduce

forge test -vvv                                                # full suite, 13/13 pass on fork
forge test --match-path test/MainnetEmptyPostOpTest.sol -vvv   # empty-postOp overcharge on mainnet fork
forge test --match-path test/Safe4337OverchargeTest.t.sol -vvv # same bug via the Safe 4337 Module (v0.3.0)
forge test --match-path test/ExploitForkTestFixed.sol -vvv     # patched path - overcharge collapses
Test Shows
MainnetEmptyPostOpTest.sol overcharge with empty postOp, limit = 0, on a mainnet fork
PerfectPaymasterTest.sol overcharge against a minimal, well-behaved paymaster
BundleDrainTest.sol ~50x amplification via a bundled handleOps
Safe4337OverchargeTest.t.sol the bug reaches the Safe 4337 Module ecosystem
ExploitForkTestFixed.sol the capped-overhead fix removes profitability

Disclosure

Reported to: HackenProof (Account Abstraction program) · Cantina (via Alchemy) · Safe Bug Bounty.

Pimlico exchange (kept for transparency). Pimlico assessed their paymaster as not affected, because users cannot set paymasterAndData (it is returned by their off-chain API and signature-checked). That mitigation is real for user-initiated manipulation.

Counterpoint: the underlying EntryPoint overcharge is independent of who authored paymasterAndData. A malicious bundler can include a legitimately Pimlico-sponsored op known to hit a postOp OOG revert; the EntryPoint then overcharges the paymaster for internal overhead even though the context was generated and signed by Pimlico. Offer to adapt the PoC to a specific paymaster implementation stands.

Severity note

I report this as a spec-conformance / paymaster-overcharge issue. It is high-confidence and provable, with a bounded per-op economic impact amplified by bundling and repetition — I state that bound explicitly rather than headline the paymaster's total TVL. Programs that received it may grade it differently; the technical claim and its limits are exactly as measured above.


Contact: tejanadh927@gmail.com · @TEJANadh10 · audit portfolio

About

ERC-4337 EntryPoint v0.8: paymaster charged for postOp overhead beyond paymasterPostOpGasLimit (EIP-4337 §4.3.1). Foundry PoCs + fix PR.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages