Glamsterdam changes what happens after a failed call. Gas refills and repricing can let failure handlers complete where they once reverted, undermining assumptions behind existing gas checks.
Contracts often catch failed external calls so they can record a failure, continue a batch, or choose a fallback. But if a call fails because it was given too little gas, continuing can turn a retryable operation into a permanent failure. Preventing that can depend on assumptions about how much gas the call needs, how much comes back, and what the remaining gas allows the contract to do.
Glamsterdam changes those assumptions. State-gas accounting and opcode repricing can change both the gas needed by the target and the gas available to the contract after failure. Even with unchanged contract code, a failure path that previously reverted can now complete.
This article builds on two proposals: EIP-8037, which changes how state creation is charged and introduces a separate state-gas reservoir, and EIP-8038, which reprices state access.
Notation and assumptions
Unless stated otherwise, calls send no ETH and forward the maximum allowed by EIP-150. Examples and diagrams ignore integer rounding and incidental opcode costs.
Under Glamsterdam, gas_left is the frame’s execution-gas balance and the value reported by the GAS opcode, exposed in Solidity as gasleft(). This balance can also pay state creation charges when the separate state_gas_reservoir is exhausted. Gas requirements therefore depend on the reservoir funding available along the call path.
This article uses the specifications reviewed on October 13, 2026. At the time of that review, EIP-8037 and EIP-8038 were scheduled for Glamsterdam, and their public pages carried Last Call status.
Why contracts try to detect out-of-gas failures
A failed external call does not by itself tell the contract whether the operation failed due to a logic error or simply ran out of gas (OOG). That distinction matters when the contract records a permanent failure or removes the option to retry. A malicious caller may deliberately supply too little gas for an otherwise valid operation to succeed, while leaving enough for the contract to record the failure. This is known as insufficient gas griefing.
Contracts try to prevent this in three ways:
-
A. Check before the call. Ensure a chosen gas budget can actually be forwarded, accounting for EIP-150’s forwarding limit. This only rules out OOG if that budget is enough for the target’s work.
require(canForwardGas(gasleft(), gasBudget)); (bool ok,) = target.call{gas: gasBudget}(data);Here,
canForwardGasis shorthand for a check that accounts for call overhead and EIP-150. -
B. Check after the call. Use the remaining gas to detect an underfunded attempt and revert instead of recording a permanent failure. The check takes two common forms:
-
B1. Against a fixed requirement. Forward a chosen budget and treat a small remainder as a sign that the call was underfunded.
(bool ok,) = target.call{gas: gasReq}(data); if (!ok && gasleft() < gasReq / 63) revert InsufficientGas(); -
B2. Against the gas available before the call. Forward the maximum and treat a remainder below 1/64 of the pre-call gas as a sign that the target consumed everything it was given.
uint256 gasInitial = gasleft(); (bool ok,) = target.call(data); if (!ok && gasleft() < gasInitial / 64) revert InsufficientGas();
-
-
C. Rely on reverts. If the failed call leaves too little gas to finish the failure handler, the contract also runs out of gas and reverts. This can provide protection even without an explicit check.
(bool ok,) = target.call(data); if (!ok) failed[id] = true; // A fresh-slot write may itself run OOG.
Approaches B and C rely on how much gas remains after failure. Under EIP-150, a call can forward at most approximately 63/64 of the available gas. If it forwards that maximum and the immediate target runs OOG, only the contract’s small retained share remains. A gas check can reject that remainder, or the failure handler may be too expensive to finish with it.
Both forms of approach B already have a limitation: after a nested OOG, an intermediary can return retained gas through REVERT, leaving enough to pass the post-call check. Glamsterdam adds further risks.
1. The reservoir changes what the remainder can buy
EIP-8037 separates gas for state creation from gas for execution. A transaction still specifies one gas limit, but gas above TX_MAX_GAS_LIMIT—16,777,216—is initially placed in a state-gas reservoir. State creation draws from that reservoir first. Once it is exhausted, state creation charges draw from gas_left.
The reservoir travels with calls in full; EIP-150’s 63/64 rule applies only to gas_left. The GAS opcode reports only gas_left, so a small gasleft() value does not mean that there is not enough gas remaining to pay for state creation.
If the target reverts or runs OOG, its reservoir is restored to the value it had at call entry, so gas it used from that reservoir remains available to the caller’s failure handler.
Consider a write to a slot whose original and current values are zero. Under EIP-8037, creating the slot costs:
state creation charge = 64 bytes * 1,530 gas/byte = 97,920 state gas
EIP-8038 adds execution charges for access and writing. Together, the two proposals give us:
| Fresh-slot write | Osaka gas charge | Glamsterdam execution charge | Glamsterdam state creation charge |
|---|---|---|---|
| Warm slot | 20,000 | 10,100 | 97,920 |
| Cold slot | 22,100 | 12,100 | 97,920 |
The total price rises, but the amount that must come from execution gas falls when the reservoir pays the state creation charge.
Consider approach C’s failure handler, which writes to a slot that was zero at transaction start and is still zero.
With enough gas in the reservoir to cover the state creation charge, around 15,000 remaining execution gas could pay for a cold fresh-slot write and a short continuation under Glamsterdam. The same execution-gas balance cannot cover the corresponding 22,100-gas write under Osaka.
Because the reservoir is always forwarded in full, an outer wrapper can pair it with a limited execution budget. The reservoir can be funded by a transaction gas limit above 16,777,216 or by a refill across frames.
The reservoir does not make writes possible with near-zero execution gas. It changes which pool pays the expensive part.
Affected approaches
- C: Undermines reliance on reverts. A contract that relied on the write forcing a revert can now commit a failure it previously could not record.
2. Clearing a slot can fund the failure handler
Under Osaka, clearing a slot allocated earlier in the transaction grants a refund through the transaction’s refund counter. That refund is settled at the end, subject to the 20% cap. It cannot pay for the next instruction.
Under Glamsterdam, undoing that state creation triggers an immediate state-gas refill. The refill first credits gas_left, up to the frame’s outstanding state creation charges funded from that balance. Any remainder goes into the reservoir. The accounting reverses the order in which the two pools funded the charges: last in, first out (LIFO).
Suppose a contract uses a persistent storage slot with a 0 → 1 → 0 lifecycle. It sets the slot, calls a target, catches a failure, and clears the slot before continuing:
Clearing the slot can restore up to 97,920 execution gas, capped by the frame’s outstanding state creation charges paid from execution gas. Those charges can come from the frame itself or successful subcalls. The target has still failed, but the contract can now have enough gas to finish a failure path that previously would have reverted.
There are three conditions to check:
- The slot must have been zero at transaction start for clearing it to trigger a refill.
- The frame must have outstanding state creation charges funded from
gas_leftfor the refill to restore execution gas. - The contract must have enough execution gas to complete the clear.
SSTOREstill requires more than 2,300 execution gas at its stipend check, regardless of the reservoir.
Affected approaches
- B1, B2: Can bypass a check after the refill. Clearing the slot can push the remaining gas above the check’s threshold despite the failed call running OOG.
- B1, B2: Can amplify an earlier bypass. If gas returned after a nested OOG bypasses the check and lets the contract clear the slot, the refill can fund the remaining failure handler.
- C: Undermines reliance on reverts. A failure handler that would previously have run OOG can now finish using the refilled gas.
A check that rejects the failure before the clear remains unaffected by the refill.
3. A reverting intermediary can return gas it already spent
Gas can also be restored before control returns to the contract. Consider the call path C → D → E, where C is the defending contract, D is an intermediary, and E is the target. D creates state, then calls E. E runs OOG, and D propagates the failure with REVERT.
Under Osaka, D returns whatever gas it retained and did not spend handling the failure. Its state creation charges remain spent.
Under Glamsterdam, reverting restores D’s state-gas accounting to its entry baseline. Its outstanding state_gas_from_gas_left is credited back to gas_left before the remaining execution gas returns to C. That counter may also include charges inherited from successful descendants.
This restored gas comes on top of whatever gas the intermediary retained and did not spend. If D has an outstanding execution-funded state creation charge for one new slot, reverting can restore 97,920 gas. Multiple such charges can return more, bounded by the outstanding amount.
REVERT is essential to this path. If D itself runs OOG or otherwise halts exceptionally, its accounting is restored but its resulting gas_left is consumed. It returns no execution gas. D must retain enough gas to propagate the failure with REVERT for the restored gas to reach C.
Affected approaches
- B1, B2: Can bypass the post-call check. The returned gas can push the remainder above the failure threshold despite the nested OOG.
- C: Undermines reliance on reverts. The returned gas can let the failure handler finish instead of running OOG.
4. A budget that used to be enough can become too small
Pre-call checks avoid relying on how much gas comes back. With a valid bound on call overhead, they can guarantee delivery of the requested amount of gas. But delivering the requested gas and delivering enough gas are separate guarantees.
To rule out OOG, the forwarded budget must cover every permitted execution path. A budget that was sufficient under Osaka may no longer be sufficient under Glamsterdam. Two changes can push the target’s cost above an old budget:
- EIP-8037 makes state creation much more expensive. When the reservoir is empty or insufficient, the state creation charge falls on execution gas. A cold fresh-slot write then costs 12,100 + 97,920 = 110,020 execution gas, compared with 22,100 gas under Osaka.
- EIP-8038 raises execution costs even for operations that create no state, for example:
| Operation or charge | Osaka gas | Glamsterdam execution gas |
|---|---|---|
| First change to an existing non-zero slot, warm | 2,900 | 10,100 |
| First change to an existing non-zero slot, cold | 5,000 | 12,100 |
| Cold account access | 2,600 | 3,000 |
Value-transfer charge (CALL_VALUE), for calls sending ETH | 9,000 | 11,300 |
If a protocol treats its pre-call check as proof that any subsequent failure must be an application error, the old budget can now lead it down the wrong failure path. A large historical margin does not establish that the budget remains sufficient after repricing.
Affected approaches
- A: Can invalidate both the budget and the forwarding check. Even if the check still guarantees delivery, the chosen amount may no longer cover the target’s work. A check using outdated call overhead may also fail to guarantee delivery.
- B1: Invalidates the budget behind the threshold. The
gasReq / 63check relies ongasReqbeing sufficient; repricing can make that assumption false. - C: Can undermine reliance on reverts. The target may now run OOG with a larger gas allocation, leaving enough retained gas for the failure handler to finish.
What still holds?
Glamsterdam does not make every gas check ineffective. A correctly bounded pre-call check can still guarantee gas delivery. To use that guarantee to rule out OOG, the requested budget must cover every permitted execution path under the new gas schedule. Assessing sufficiency without reservoir assistance avoids depending on state gas that might not be available. Forwarding overhead must also be revalidated.
We nevertheless advise against relying on approaches B1 or B2. Both infer the cause of a failure from gasleft() after the call, which holds only if the callee halted directly and nothing returned gas before the check: an intermediary that reverts or a refill can break either condition, as shown above. Approach C offers even less: its protection is an implicit side effect of gas costs, which Glamsterdam changes and later forks may change again.
Reviewing failure handling before the fork
Start with calls that allow execution to continue after failure. Trace both the target’s complete execution path, including proxies, and everything the calling contract executes before committing its failure response.
-
Revisit implicit revert assumptions. Calculate the full continuation cost under both schedules. If a fresh-slot write was expected to force OOG, we need to include the reservoir and EIP-8038’s remaining execution charges.
-
Locate refills relative to gas checks. Look for persistent slots allocated and cleared in the same transaction, and state creation reverted by an intermediary. Identify which pool funded the charge and whether failure propagates with
REVERTor an exceptional halt. -
Revalidate fixed budgets and forwarding overhead. A bound needs to cover all permitted paths, including the case where the reservoir cannot help. Where bounds are adjustable, update them as dependencies and gas schedules change.
-
Test the outcome of the failure handler. Force the subcall to fail and check whether the outer operation disables retries, removes a claim, or records an incorrect result. Compare Osaka and Glamsterdam with the same relevant pre-state and explicit gas assumptions.
Where possible, propagate call failures so that the whole operation reverts. If failure handling is required, preserve a recovery path rather than making an irreversible decision based only on the remaining gas. Updating a numeric threshold does not establish safety without validating the assumptions behind it.
References
- Glamsterdam upgrade specification
- EIP-8037: State Creation Gas Cost Increase
- EIP-8038: State-access gas cost update
- EIP-150: Gas cost changes for IO-heavy operations
For help assessing a call path, contact glamsterdam@chainsecurity.com.
