Full Report
ERC-4337 for Account Abstraction is great for UX improvements, but it has added significant complexity to smart contract interactions. Ethereum natively only allows transactions that originate by EOAs. By using smart wallets and gas sponsors, this becomes cheaper via batching and "free" for users to execute a transaction. User recovery can also be done. There are several roles in this: UserOperation: A transaction-like object for representing the users intent. Smart Contract Account: The sender is a contract that implements logic via validateUserOp() and executeUserOp(). Bundler: Off-chain service that acts as an alternative mempool. Collects user ops, packages them and pays the gas. Entrypoint: Central on-chain gateway for ERC4337. This validates and routes each user operation. Paymaster: Felixible gas payment options. Use the native token or ERC20 tokens. Calls validatePaymasterUserOp and postOp(). The Entrypoint contract first validates that all operations are valid before executing them. This includes checking gas payments, nonce checks and more. In the execution phase, the entrypoint calls innerHandleOp() to forward the operation to the intended destination. Next, it calls postOp() on the Paymaster (if provided). Finally, the bundler is compensated for gas costs. The author of this post has audited several implementations of ERC-4337 and has noticed two common issues. The first one is undercalculated gas costs. If the execution gas limit exceeds what's actually used during execution, a 10% penalty is charged, paid to the bundler/deducted from the user's deposit. In the example code, the returned funds includes the penalty - this allows for more funds than put it to be taken from the paymaster. For ERC20 token transfers, there are two types: pre-payment + refund and post-payment. The pre-payment path is more secure; the issues stem from the post-payment method. If postOp() fails, then the error is just handled. If it's not one of two specific errors, then the state isn't rolled back. In practice, this means that the bundler will still get paid for the failed transaction. Why does this matter? Even if the postOp() call fails because it can't collect funds from the user, the paymaster still needs to pay the bundler's gas costs. This is how the attack would work: Create a UserOperation with a high gas price. Revoke the paymaster's allowance before postOp() executes. postOp() fails. The paymaster pays Bundler for their high gas costs without receiving any funds. The paymaster loses money since they paid the bundler but couldn't collect from the user. The bundler profits as long as the actual gas costs less than what they charged. This bug allows for bundlers to drain paymaster deposits. Some paymasters try to protect against this by simulating the UserOperation execution before signing. However, this can be bypassed by approving the transfer during simulation, but rejecting it on the actual call. So, what's the solution? Just don't use the post-payment strategy. If you absolutely have to, restrict usage to a whitelist of trusted bundlers. Just use pre-funding instead. Overall, a great article explaining how ERC-4337 works and bugs around it.
Analysis Summary
# Vulnerability: ERC-4337 Paymaster Post-Payment Drain
## CVE Details
- **CVE ID**: N/A (Design pattern flaw in decentralized applications)
- **CVSS Score**: 7.5 (High)
- **CWE**: CWE-639: Bypass of Authorization through User-Controlled Key; CWE-755: Improper Handling of Exceptional Conditions.
## Affected Systems
- **Products**: Smart Contract Paymasters implementing ERC-4337 (Account Abstraction).
- **Versions**: Implementations using "Post-payment" (collecting fees after execution) rather than "Pre-payment" (locking funds during validation).
- **Configurations**: Paymasters that allow arbitrary users to pay gas fees using ERC20 tokens via the `postOp()` hook without a trusted whitelist.
## Vulnerability Description
The vulnerability exists in the logic flow of the ERC-4337 `EntryPoint` and `Paymaster` interaction. In the "post-payment" strategy, a Paymaster attempts to charge the user for gas spent *after* the main transaction execution occurs (inside the `postOp()` function).
The flaw arises because if `postOp()` fails (reverts) for reasons other than specific out-of-gas errors, the `EntryPoint` contract does not roll back the entire transaction. Instead, it completes the Bundler's reimbursement using the Paymaster’s deposited funds. An attacker can intentionally cause `postOp()` to fail (e.g., by revoking the token allowance or emptying their balance during the execution phase), ensuring the Paymaster pays the Bundler while the Paymaster fails to recover those costs from the attacker.
## Exploitation
- **Status**: PoC available (Logic described in audit findings).
- **Complexity**: Medium
- **Attack Vector**: Network
- **Mechanism**:
1. Attacker submits a `UserOperation` with a high gas price.
2. The Paymaster validates the operation.
3. During `executeUserOp()`, the attacker's contract revokes the Paymaster's ERC20 allowance.
4. `postOp()` is called to collect the fee, but reverts due to "Insufficient Allowance."
5. The `EntryPoint` pays the Bundler from the Paymaster's deposit.
6. The Paymaster loses the gas cost; the Bundler (which could be the attacker) profits from the high gas price.
## Impact
- **Confidentiality**: None
- **Integrity**: Low (Token balances for the Paymaster are incorrectly depleted).
- **Availability**: High (A Paymaster’s deposit can be fully drained, preventing legitimate users from sponsoring transactions).
## Remediation
### Patches
- Not a software bug in the ERC-4337 protocol itself, but a logic error in specific Paymaster implementations. Developers must update their contract logic to Version 0.6+ compatible "Pre-payment" flows.
### Workarounds
- **Abandon Post-payment**: Switch to a "Pre-payment + Refund" strategy where funds are pulled during the `validatePaymasterUserOp` phase.
- **Whitelisting**: If post-payment is mandatory, restrict the Paymaster to interact only with a whitelist of trusted, known Bundlers.
- **Gas Penalty Fix**: Ensure gas calculation logic does not inadvertently refund the 10% unused gas penalty to the user when it should be retained or handled by the Paymaster.
## Detection
- **Indicators of Compromise**: Frequent `postOp()` reverts on the blockchain involving the same `sender` or `Bundler` addresses.
- **Detection Methods**: Monitor Paymaster events for discrepancies between `actualGasCost` and the actual ERC20 tokens collected from users.
## References
- ERC-4337 Documentation: [https://docs.erc4337.io/](https://docs.erc4337.io/)
- Original Report: [https://osec.io/blog/2025-12-02-paymasters-evm/](https://osec.io/blog/2025-12-02-paymasters-evm/)