Related to the earlier discussion here about using the nonce key’s uint192 component: under Glamsterdam, now live on Sepolia, the first use of any nonce key costs about 98,700 more gas than reusing one, and that cost lands inside verificationGasLimit. Numbers below.
EntryPoint v0.7 on Glamsterdam: the first use of a nonce key now costs ~99k gas inside verificationGasLimit
What changed
EIP-8037 reprices state creation. Writing a storage slot that was zero now costs 97,920 state gas (20,000 before). On the Sepolia node, each additional new slot in a call measures 110,026 gas in total, execution included.
Why it lands in verificationGasLimit
In EntryPoint v0.7 (0x0000000071727De22E5E9d8BAf0edAc6f37da032), _validatePrepayment measures gas from before account validation until after the nonce update, and only then checks the limit:
uint256 preGas = gasleft();
...
validationData = _validateAccountPrepayment(opIndex, userOp, outOpInfo, requiredPreFund, verificationGasLimit);
if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) {
revert FailedOp(opIndex, "AA25 invalid account nonce");
}
unchecked {
if (preGas - gasleft() > verificationGasLimit) {
revert FailedOp(opIndex, "AA26 over verificationGasLimit");
}
}
_validateAndUpdateNonce increments nonceSequenceNumber[sender][key]. The first userOp that uses a given 192-bit key (sequence 0) writes that slot for the first time. So any userOp that opens a new nonce key now needs about 98k more verification gas than its account’s own validation, and fails with AA26 if its limit doesn’t include it.
What I saw on Sepolia
8 smart accounts on one paymaster failed 164 bundles with AA26. Each op used a fresh nonce key, so after the fork every op paid for a new nonce slot inside verificationGasLimit. Their limits had no room for it. They recovered at 17:05 UTC when the limits went up.
- 164
handleOps transactions reverted with FailedOp(0, "AA26 over verificationGasLimit") from one bundler’s executors. They were 73 distinct userOps from 8 smart accounts (Kernel via EIP-7702, Safe, SimpleAccount), resubmitted 1 to 14 times each (median 2); the executors paid about 0.045 Sepolia ETH for the failed bundles.
- Every one of the 73 used a non-zero nonce key with sequence 0: the first use of that key. None had initCode, so account deployment was not the cause.
- The failing ops carried verificationGasLimit values of 41,810 to 79,833, below the cost of the nonce write alone.
- Ops from the same accounts with limits of 233k to 259k succeeded after the fork. 228 of 350 bundles failed between 13:54 and 17:02 UTC, 1 of 31 between 17:05 and 17:45 UTC.
- Fresh bundler estimates were not the problem: the estimators I read (alto, rundler) simulate the EntryPoint’s own
_validatePrepayment on the node, nonce update included, so on an Amsterdam node they cover the new slot.
What a new nonce key costs now
Measured on the Sepolia node with eth_estimateGas of EntryPoint.incrementNonce(key) (the same storage write a userOp’s validation makes), a fresh key against a reused one:
| rules |
fresh key |
reused key |
extra for a fresh key |
| after the fork |
127,095 |
28,438 |
98,657 |
| before the fork (block 11856335) |
44,370 |
27,236 |
17,134 |
Opening a new key now costs about 81,500 more gas than it did before the fork.
Who is affected
- Apps that set verificationGasLimit without a fresh estimate (a fixed or cached value) and open a new nonce key per userOp: they fail with AA26 until the limit includes the slot.
- Apps and SDKs that open a new key per op but estimate fresh: no failure, but every userOp pays the higher cost. viem’s
toSmartAccount defaults the key source to Date.now(), and several account implementations built on it pass that key through, so their default is a new key on every op.
Fix
- When building a userOp whose nonce key has sequence 0 (
EntryPoint.getNonce(sender, key) returns key << 64), add the cost of the new nonce slot to verificationGasLimit (98,657 more than a reused key, measured on Sepolia), or re-estimate on an Amsterdam node with the exact nonce you will sign.
- If parallel nonces are not needed, reuse a key: only the first use of a key pays for the slot.
- Bundlers: an op that failed AA26 will fail again with the same limits; drop it or re-estimate instead of resubmitting.
How to check your own ops
Trace a reverted bundle on a Glamsterdam node (debug_traceTransaction with the callTracer): the output decodes to FailedOp(opIndex, "AA26 over verificationGasLimit"). Decode the bundle as handleOps(PackedUserOperation[],address) and read ops[opIndex].nonce: the high 192 bits are the key, the low 64 bits the sequence. Sequence 0 with a non-zero key is this case.
cc @dror @alex-forshtat-tbk, since this lands in EntryPoint v0.7’s validation path.
Write-up drafted with help from an AI assistant; every number comes from my own runs and the commands in the repo.