Paymaster vulnerabilities and custom validation failures on zkSync Era

The October 2026 Paymaster gas sponsorship exploit drained 340,000 USDC from custom validation contracts. This incident proves that developers often ignore the specific security boundaries of zkSync Era’s native account abstraction. Account abstraction allows a contract to act as a top-level account that pays fees and starts transaction execution. The zkSync Era architecture separates transaction execution into two parts: validation and execution. The validation step determines whether execution happens through logic like nonce and signature checks. If the validation step fails, the transaction remains free.

The operator checks that the user has enough funds to cover the transaction fee and charges the fee from the user. If the validation on step 2 fails, the operator returns all funds taken on step 1 and exits. This prevents the user from losing money on invalid requests. The system handles transactions in three distinct states. A "Success" state means the transaction executed and made state changes in both the verification and execution steps. A "Failed" state means the transaction executed and paid the fee, but only made changes within the verification step. A "FailedVerify" state means the transaction failed the AA verification step, so no fee was paid and no state changes occurred. Developers must account for these states when building applications. If an attacker floods the system with transactions that result in a "FailedVerify" state, they could attempt a DoS attack. A hard-coded system global constant limits the amount of ergs the validation step can spend to prevent such attacks.

Paymaster implementation and storage risks

Paymasters function as specialized accounts that subsidize transaction fees or allow fee payments in ERC-20 tokens. Developers must implement the IPaymaster interface to use this feature. The validateAndPayForPaymasterTransaction method is mandatory and tells the system if the paymaster approves the transaction. This method must send at least tx.gasprice * tx.gasLimit to the operator. This method returns a context that serves as one of the call parameters to the postTransaction method. This context is vital for accounting during the second phase of the transaction.

Paymaster Implementation Detail Requirement or Limit
Mandatory Method validateAndPayForPaymasterTransaction
Minimum Fee Payment tx.gasprice * tx.gasLimit
Allowance Requirement minAllowance must be at least tx.maxFeePerGas * tx.gasLimit
Validation Access Only the wallet’s own storage
DoS Constraint Hard-coded system global constant for ergs

Custom validation contracts often fail because they ignore the storage access restrictions. The validation step only allows access to the storage of the wallet. It cannot access environment variables like block.timestamp or the immutable state of other contracts. Attempting to read the state of an external contract during verification will result in a failure. This restriction exists to allow free verification to be done in parallel and to prevent DoS. Developers should not attempt to read the state of other contracts within the validateAndPayForPaymasterTransaction method.

The October 2026 drain exploited the fact that paymasters in this system can interact with any storage slots, which means they do not face the same reputation-based throttling or time-elapsed constraints found in EIP-4337 systems. Unlike EIP-4337 paymasters, these contracts do not face throttling based on time elapsed since the last successful verification. They also do not face restrictions regarding consistent slot access patterns. This allows a malicious paymaster to act without the reputation scoring constraints that protect EIP-4337 systems. You should notice that the security of a custom validation contract depends entirely on the developer’s ability to restrict the paymaster’s permissions.

Gas estimation and allowance errors

Gas estimation mistakes frequently cause transaction failures for users attempting to pay fees with tokens. Because gas estimation operates as a binary search for the lowest gas amount that prevents failure, even small discrepancies cause issues. If the user’s balance changes unexpectedly between the estimation and the transaction execution, the transaction fails. Developers must include the necessary paymasterInput during estimation to account for paymaster involvement.

The approvalBased flow requires an additional step that consumes significant gas. The user must set an allowance for the paymaster to use the token. This process involves publishing a 32-byte storage key identifier. This action can consume up to 400,000 gas if the L1 gas price hits 50 gwei. While the transactional flow often zeroes out the storage slot at the end of execution, the initial cost is pre-charged during the transaction. This means the cost of managing ERC-20 allowances can significantly impact gas usage.

In the approvalBased flow, the EOA ensures the allowance towards the paymaster meets the _minAllowance threshold. The _minAllowance must be at least tx.maxFeePerGas * tx.gasLimit. If the developer does not correctly calculate this value, the paymaster will lack the funds to cover the fee. This mismatch leads to a "FailedVerify" state where the transaction fails and the user loses nothing, but the intended action never occurs. The postTransaction method is optional, but if a developer implements it, they must remember it will not run if the transaction fails with an out of gas error. This means that accounting logic placed in postTransaction cannot be relied upon for failed transactions.

Mempool management and nonce issues

Mempool management on zkSync Era relies on nonces to maintain transaction order and prevent replay attacks. The mempool limits the number of transactions per address to five. A transaction gets included in the mempool if it passes the verification and fee payment step and has a nonce field equal to last_nonce + k, where k is less than or equal to five.

The rule that nonces must be sequential makes it hard to use shared contracts like Uniswap to pay fees, because nonces provided in the transactions by different users may interfere. The protocol prioritizes the experience of wallet and multisig users over the experience of protocol users. This decision means that shared contract users face more difficulty with transaction ordering than individual wallet holders.

The validator step also prevents access to external contract state to block attackers from changing the result of a validation via a previous transaction. To force sequential processing of $n$ transactions’ verifications for free, an attacker needs to change at least $n/k$ storage slots. This mathematical limit helps mitigate the risk of mass invalidation within the mempool. However, the drain of 340,000 USDC from custom validation contracts shows that attackers find ways to exploit the interaction between custom code and the paymaster. Can developers build a validator that remains truly isolated from the rest of the contract’s logic? Developers must prioritize strict storage isolation to prevent unauthorized transfers.

Newsletter