Skip to content

Update EIP-8037: Refund state gas on all frame failures including top level#11476

Merged
eth-bot merged 2 commits into
ethereum:masterfrom
qu0b:qu0b/state-gas-refund-on-all-failure
Apr 17, 2026
Merged

Update EIP-8037: Refund state gas on all frame failures including top level#11476
eth-bot merged 2 commits into
ethereum:masterfrom
qu0b:qu0b/state-gas-refund-on-all-failure

Conversation

@qu0b

@qu0b qu0b commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Alternative to #11468. Both PRs clarify child frame behavior identically — the difference is top-level failure:

This PR is a spec change, not just a clarification. It aligns top-level behavior with child frame behavior: state gas is refunded on failure at all levels because no state was actually grown.

Context from #11468 discussion

@rakita called the current top-level behavior a "spec code bug." @misilva73 (EIP author) said restoring state gas "would make more sense," noting:

  1. The depth-based asymmetry is hard to justify — at depth 1 state gas is reclaimed, at depth 0 it's lost
  2. block_state_gas_used should reflect reality — a block that reverted all state-touching txs would hit the state gas limit despite growing zero state

The main counter-argument is pre-EIP-8037 precedent (GAS_NEW_ACCOUNT consumed on revert). This PR explicitly departs from that in favor of internal consistency with the stated design principle: "state gas pays for long-term state growth which does not occur on failure."

Line-by-line explanation

Bullet 5 (child frame behavior) — identical to #11468

Old: On child success, the remaining state_gas_reservoir is returned to the parent.
New: Adds that the child's execution_state_gas_used is accumulated into the parent's counter on success, and explicitly not accumulated on failure.

Old: State gas is fully preserved on failure because state changes are reverted, so no state was actually grown.
Removed. "Fully preserved" was ambiguous (preserved = still counted, or preserved = returned?). Replaced by explicit "not added" language.

Bullet 6 (top-level behavior) — WHERE THE TWO PRs DIVERGE

Old: On exceptional halt, remaining gas_left is attributed to execution_regular_gas_used...
New: Covers both revert and exceptional halt at the top level (the old spec only mentioned exceptional halt — top-level revert was unaddressed). Explicitly describes the gas movement: all state gas consumed during execution (from the reservoir AND any that spilled into gas_left) is restored to the state_gas_reservoir, mirroring the child frame restoration mechanism. execution_state_gas_used is reset to zero. On exceptional halt, gas_left is additionally zeroed.

Why the spillover matters: When the reservoir is exhausted, state gas charges spill into gas_left. Simply resetting execution_state_gas_used without moving that gas back would leave the sender paying for state gas as if it were regular gas — invisible to both counters.

Compare #11468: execution_state_gas_used is not reset — still counts toward block_state_gas_used. Top-level revert is also not explicitly addressed (inherits from the "State gas on frame failure" section which says state gas counts at top level).

Section "State gas on frame failure" (was "Revert behavior for state gas")

Old: State gas charged for account creation... is consumed even if the frame reverts — state changes are rolled back but gas is not refunded.
New: Describes the full restoration at top level: all consumed execution state gas (reservoir + spillover from gas_left) is moved back into the reservoir, execution_state_gas_used reset to zero. Acknowledges departure from pre-EIP-8037 precedent.
Compare #11468: Top level "has no parent to reclaim it" → state gas counts. Consistent with pre-EIP-8037 behavior.

🤖 Generated with Claude Code

Clarify that state gas is refunded on failure at every level, not just
child frames. When the top-level frame reverts or halts, execution_state_gas_used
is reset to zero and the reservoir is returned to the sender, consistent
with child frame behavior and the principle that state gas pays exclusively
for state that is actually grown.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@qu0b qu0b requested a review from eth-bot as a code owner April 1, 2026 13:34
@github-actions github-actions Bot added c-update Modifies an existing proposal s-draft This EIP is a Draft t-core labels Apr 1, 2026
@eth-bot

eth-bot commented Apr 1, 2026

Copy link
Copy Markdown
Collaborator

✅ All reviewers have approved.

@eth-bot eth-bot added the a-review Waiting on author to review label Apr 1, 2026
@eth-bot eth-bot changed the title EIP-8037: Refund state gas on all frame failures including top level Update EIP-8037: Refund state gas on all frame failures including top level Apr 1, 2026
The previous version had three gaps:
1. Only covered exceptional halt at top level, not revert
2. Reset execution_state_gas_used without restoring the actual gas
   that spilled from the reservoir into gas_left
3. Didn't explicitly describe the gas movement mechanism at top level

Now the top-level restoration mirrors child frame restoration exactly:
all consumed state gas (reservoir + spillover) is moved back into the
state_gas_reservoir, and execution_state_gas_used is reset to zero.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
On top-level revert or exceptional halt, restore all execution
state gas to the reservoir and reset execution_state_gas_used to
zero. State changes are fully reverted so no state was actually
grown — the sender should not pay for state gas.

This mirrors incorporate_child_on_error for child frames, where
state_gas_used is restored to the parent's state_gas_left and not
accumulated into the parent's state_gas_used.

Implements the spec change proposed in ethereum/EIPs#11476 (the
alternative to #11468 where state gas IS counted at top level).

Includes test: test_code_deposit_fail_excludes_initcode_state_gas
which validates that initcode state gas does NOT count in
block_state_gas_used when code deposit triggers exceptional halt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
On top-level revert or exceptional halt, restore all execution
state gas to the reservoir and reset execution_state_gas_used to
zero. State changes are fully reverted so no state was actually
grown — the sender should not pay for state gas.

This mirrors incorporate_child_on_error for child frames, where
state_gas_used is restored to the parent's state_gas_left and not
accumulated into the parent's state_gas_used.

Spec change: ethereum/EIPs#11476
Aligns with nethermind's current behavior.

Includes test: test_code_deposit_fail_excludes_initcode_state_gas
which validates that initcode state gas does NOT count in
block_state_gas_used when code deposit triggers exceptional halt.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
EELS patch: on top-level revert or exceptional halt, restore all
execution state gas to the reservoir and reset execution_state_gas_used
to zero. Mirrors incorporate_child_on_error for child frames.

Test: 2-tx blockchain test at 100M gas limit. TX1 deploys 14 KiB
contract pushing block_state above block_regular. TX2 is a failing
CREATE whose initcode state gas (GAS_NEW_ACCOUNT = 131,488) becomes
the observable delta in header.gas_used = max(regular, state).

Unpatched EELS: gasUsed = 0x100D000 (state gas counted)
Patched EELS:   gasUsed = 0xFECE60 (state gas refunded)
Delta: 131,488 = 112 * cpsb = GAS_NEW_ACCOUNT

Spec change: ethereum/EIPs#11476

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
EELS patch: on top-level revert or exceptional halt, restore all
execution state gas to the reservoir and reset state_gas_used to
zero. Mirrors incorporate_child_on_error for child frames.

Test from PR ethereum#2595: single-tx at 60M, 3 parametrized cases
(oversized code x2, OOG deposit).

Spec change: ethereum/EIPs#11476

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
EELS patch: on top-level revert or exceptional halt, restore all
execution state gas to the reservoir and reset state_gas_used to
zero. Mirrors incorporate_child_on_error for child frames.

Test: 2-tx blockchain test at 100M where state gas is the binding
dimension. TX1 deploys 14 KiB contract (high state gas). TX2 is a
failing CREATE whose initcode state gas (GAS_NEW_ACCOUNT) is the
observable delta in header.gas_used.

This is the opposite of ethereum#2595 which tests that state gas IS counted
at the top level (#11468). This test verifies state gas is NOT
counted at the top level (#11476).

Spec change: ethereum/EIPs#11476

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
EELS patch: on top-level revert or exceptional halt, restore all
execution state gas to the reservoir and reset state_gas_used to
zero. Mirrors incorporate_child_on_error for child frames.

Test: 2-tx blockchain test at 100M where state gas is the binding
dimension. TX1 deploys 14 KiB contract (high state gas). TX2 is a
failing CREATE whose initcode state gas (GAS_NEW_ACCOUNT) is the
observable delta in header.gas_used.

This is the opposite of ethereum#2595 which tests that state gas IS counted
at the top level (#11468). This test verifies state gas is NOT
counted at the top level (#11476).

Spec change: ethereum/EIPs#11476

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
EELS patch: on top-level revert or exceptional halt, restore all
execution state gas to the reservoir and reset state_gas_used to
zero. Mirrors incorporate_child_on_error for child frames.

Test: 2-tx blockchain test at 100M where state gas is the binding
dimension. TX1 deploys 14 KiB contract (high state gas). TX2 is a
failing CREATE whose initcode state gas (GAS_NEW_ACCOUNT) is the
observable delta in header.gas_used.

This is the opposite of ethereum#2595 which tests that state gas IS counted
at the top level (#11468). This test verifies state gas is NOT
counted at the top level (#11476).

Spec change: ethereum/EIPs#11476

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
spencer-tb added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
If EIP-8037 adopts the top-level state gas refund
(ethereum/EIPs#11476), block gas accounting
in these tests will need updating.
spencer-tb added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
If EIP-8037 adopts the top-level state gas refund
(ethereum/EIPs#11476), block gas accounting
in these tests will need updating.
spencer-tb added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
If EIP-8037 adopts the top-level state gas refund
(ethereum/EIPs#11476), block gas accounting
in these tests will need updating.
spencer-tb added a commit to qu0b/execution-specs that referenced this pull request Apr 1, 2026
If EIP-8037 adopts the top-level state gas refund
(ethereum/EIPs#11476), block gas accounting
in these tests will need updating.

@spencer-tb spencer-tb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@misilva73 misilva73 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@eth-bot eth-bot enabled auto-merge (squash) April 17, 2026 07:23

@eth-bot eth-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All Reviewers Have Approved; Performing Automatic Merge...

@eth-bot eth-bot merged commit 26d056f into ethereum:master Apr 17, 2026
12 of 17 checks passed
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 17, 2026
EELS patch: on top-level revert or exceptional halt, restore all
execution state gas to the reservoir and reset state_gas_used to
zero. Mirrors incorporate_child_on_error for child frames.

Test: 2-tx blockchain test at 100M where state gas is the binding
dimension. TX1 deploys 14 KiB contract (high state gas). TX2 is a
failing CREATE whose initcode state gas (GAS_NEW_ACCOUNT) is the
observable delta in header.gas_used.

This is the opposite of ethereum#2595 which tests that state gas IS counted
at the top level (#11468). This test verifies state gas is NOT
counted at the top level (#11476).

Spec change: ethereum/EIPs#11476

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
qu0b added a commit to qu0b/execution-specs that referenced this pull request Apr 17, 2026
EELS patch: on top-level revert or exceptional halt, restore all
execution state gas to the reservoir and reset state_gas_used to
zero. Mirrors incorporate_child_on_error for child frames.

Test: 2-tx blockchain test at 100M where state gas is the binding
dimension. TX1 deploys 14 KiB contract (high state gas). TX2 is a
failing CREATE whose initcode state gas (GAS_NEW_ACCOUNT) is the
observable delta in header.gas_used.

This is the opposite of ethereum#2595 which tests that state gas IS counted
at the top level (#11468). This test verifies state gas is NOT
counted at the top level (#11476).

Spec change: ethereum/EIPs#11476

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a-review Waiting on author to review c-update Modifies an existing proposal s-draft This EIP is a Draft t-core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants