Analyzing ERC-4337 Paymaster Vulnerabilities: Why Most Are Broken Today
The rise of AI-driven agentic payments backed by Visa, Mastercard, and Ripple is pushing ERC-4337 into the mainstream. But amid this hype, a troubling reality persists: most deployed paymaster contracts suffer from critical security flaws that expose users to replay attacks, flash loan exploits, and faulty access control. Let’s deep dive into these vulnerabilities with concrete Solidity examples you can run in Foundry today, so you can diagnose and harden your own paymaster implementations.
ERC-4337 Paymasters: A Quick Recap
ERC-4337 enables account abstraction by offloading gas payment logic to external paymasters. These contracts agree to cover gas costs for user operations (UserOps) under customizable terms. The paymaster’s validatePaymasterUserOp hook gives it a last chance to verify if it should pay for a given UserOp.
The challenge: paymasters get entrusted with substantial financial risk, and their validation logic must be rock solid. Missteps here let attackers exploit replayability, unauthorized payments, or flash loans to drain funds.
Common Vulnerabilities in Paymaster Logic
1. Naive Replay Protection Fails on Cross-Chain or Relayed UserOps
Many paymasters attempt replay prevention by caching a unique identifier (e.g., user nonce) on-chain. However, this is insufficient when UserOps are relayed through multiple bundlers or cross-shard chains that represent the same transaction differently, causing replay checks to fail.
mapping(bytes32 => bool) public usedUserOps;
function validatePaymasterUserOp(UserOperation calldata userOp) external returns (bytes memory context, uint256 validationData) {
bytes32 userOpHash = keccak256(abi.encode(userOp.sender, userOp.nonce));
require(!usedUserOps[userOpHash], "Replay detected");
usedUserOps[userOpHash] = true;
// Additional validation...
}
This seems secure but is brittle without canonical replay identifiers, and attackers can replay the same UserOp on different chains or with different bundlers.
2. Insufficient Access Control Enables Unauthorized Payments
Some paymasters authorize payments based purely on whitelisted senders but do not verify that the paymaster itself was called legitimately via ERC-4337 entry points. Attackers can trick the paymaster into paying for arbitrary calls without going through proper bundler validation.
mapping(address => bool) public whitelist;
function validatePaymasterUserOp(UserOperation calldata userOp) external returns (bytes memory context, uint256 validationData) {
require(whitelist[userOp.sender], "Sender not whitelisted");
// Missing validation that msg.sender == entryPoint
// Risk: Anyone can call and drain funds
}
The missing check require(msg.sender == entryPoint) leaves a large attack surface for unauthorized payment triggers.
3. Flash Loan Attacks Exploit Callbacks Not Properly Accounted For
Flash loan-based reentrancy or nested calls can exploit paymasters that validate UserOps without checking state changes atomically or without guarding against reentrancy.
A canonical flash loan exploit looks like this:
- Loan tokens from an external liquidity pool
- Use tokens to trigger
validatePaymasterUserOp - During the callback, re-enter and withdraw more funds before balance checkpoints
bool internal locked;
function validatePaymasterUserOp(UserOperation calldata userOp) external returns (bytes memory context, uint256 validationData) {
require(!locked, "Reentrancy detected");
locked = true;
// Validate userOp conditions, check token balances
// ...
locked = false;
}
Many paymasters lack such reentrancy guards or atomic state checks, making them flash loan attack vectors.
Running Vulnerability Demos with Foundry
To help internalize these flaws, here’s how you can quickly test a naive replay vulnerability:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "forge-std/Test.sol";
contract NaivePaymaster {
mapping(bytes32 => bool) public usedUserOps;
function validatePaymasterUserOp(address sender, uint256 nonce) external returns (bool) {
bytes32 userOpHash = keccak256(abi.encode(sender, nonce));
require(!usedUserOps[userOpHash], "Replay detected");
usedUserOps[userOpHash] = true;
return true;
}
}
contract ReplayAttackTest is Test {
NaivePaymaster pm;
address user = address(0xBEEF);
function setUp() public {
pm = new NaivePaymaster();
}
function testReplayAttack() public {
// First call passes
assertTrue(pm.validatePaymasterUserOp(user, 1));
// Replay same UserOp hash on another chain/bundler simulation
// Here test calls again, simulating replay on new bundler whose tx hash may differ
vm.expectRevert("Replay detected");
pm.validatePaymasterUserOp(user, 1);
}
}
Though simplistic, this code reproduces the core replay problem — without canonical replay protection across chains or relayers, the usedUserOps single mapping is insufficient.
Here’s a quick table comparing common paymaster validation patterns and their failure modes:
| Pattern | Weakness | Exploit Scenario | Mitigation |
|---|---|---|---|
| Simple nonce replay check | Cross-relayer replay | Replay UserOps on other bundlers | Use chain-specific context or embedded signatures |
| Sender whitelist w/o entryPoint check | Unauthorized caller triggers payments | Anyone invoking paymaster drains funds | Enforce strict msg.sender == entryPoint checks |
| No reentrancy guard | Flash loan nested UserOps exploits | Reentrant UserOps drain funds | Use reentrancy locks or atomic state validation |
Next Steps for Your Paymaster Audits
If you’ve deployed or inherited a paymaster contract, immediately audit it for:
- Proper
msg.sendervalidation against the ERC-4337 entryPoint to ensure legit invocation - Full replay-proof nonce schemes that uniquely bind UserOps to your execution context
- Reentrancy guards, especially if your contract interacts with external liquidity pools or flash loan providers
Experiment with Foundry tests like the snippet above to reproduce and patch these vulnerabilities. Security flaws here directly affect user balances and platform trust, so hitting these checks before mainnet usage is essential.
In audit practice, these weaknesses repeatedly show up across paymasters in DeFi protocols adopting ERC-4337. Addressing them reduces risks of replay attacks and flash loan drains, protecting your users and your treasury.
A deep dive from the team I work with highlights why these paymaster pitfalls remain so common and how to address them thoughtfully. For further research and robust auditing insights, see Soken security audits.
Top comments (0)