feat: add EIP-8037 / TIP-1016 state gas support#3406
Merged
Conversation
Merging this PR will degrade performance by 5.58%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | JUMP_50 |
17.9 µs | 18.6 µs | -3.34% |
| ❌ | GASLIMIT_50 |
18.4 µs | 19 µs | -3.11% |
| ❌ | DELEGATECALL_50 |
86.6 µs | 90 µs | -3.79% |
| ❌ | GAS_50 |
18.3 µs | 18.9 µs | -3.12% |
| ❌ | JUMPDEST_50 |
15.6 µs | 16.3 µs | -4.33% |
| ❌ | MCOPY_50 |
20.8 µs | 21.5 µs | -3.01% |
| ❌ | MSIZE_50 |
18.4 µs | 19 µs | -3.11% |
| ❌ | MSTORE_50 |
19.7 µs | 20.4 µs | -3.31% |
| ❌ | PUSH0_50 |
18.3 µs | 18.9 µs | -3.27% |
| ❌ | PUSH1_50 |
18.4 µs | 19 µs | -3.26% |
| ❌ | PUSH2_50 |
18.5 µs | 19.1 µs | -3.24% |
| ❌ | PUSH8_50 |
18.6 µs | 19.2 µs | -3.07% |
| ❌ | PUSH7_50 |
18.8 µs | 19.4 µs | -3.05% |
| ❌ | STATICCALL_50 |
89.8 µs | 92.9 µs | -3.33% |
| ❌ | subcall_1000_nested |
2.1 ms | 2.3 ms | -5.58% |
| ❌ | subcall_1000_transfer_1wei |
1.2 ms | 1.3 ms | -4.15% |
| ❌ | subcall_1000_same_account |
1.1 ms | 1.2 ms | -5.34% |
| ⚡ | blake2/12_rounds |
4.7 µs | 4.5 µs | +4.06% |
| ⚡ | blake2/2_rounds |
3.2 µs | 3.1 µs | +4.12% |
| ⚡ | blake2/4_rounds |
3.6 µs | 3.4 µs | +5.49% |
Comparing rakita/state-gas (f410840) with main (d060498)
rakita
force-pushed
the
rakita/state-gas
branch
from
February 10, 2026 16:38
2a4830a to
2eb6654
Compare
rakita
force-pushed
the
rakita/state-gas
branch
from
February 11, 2026 13:30
6ec22a9 to
cf91d0f
Compare
rakita
force-pushed
the
rakita/state-gas
branch
from
February 16, 2026 11:48
2ade6e8 to
f88aa3c
Compare
rakita
commented
Feb 16, 2026
rakita
commented
Feb 17, 2026
rakita
commented
Feb 17, 2026
Add state gas accounting with reservoir model across interpreter, handler, context, and precompile crates. Includes comprehensive EIP-8037 test suite with 35+ test vectors covering SSTORE, CALL, CREATE, SELFDESTRUCT, reservoir refill, and gas opcode behavior.
rakita
force-pushed
the
rakita/state-gas
branch
from
March 13, 2026 10:50
f904eac to
ee5f210
Compare
rakita
commented
Mar 13, 2026
rakita
commented
Mar 13, 2026
… refill - Rename ResultGas::spent to regular_gas_spent to distinguish from state gas after EIP-8037 - Add total_gas_spent() method and deprecate old spent()/set_spent()/with_spent() accessors - Simplify reservoir refill logic: replace handler_reservoir_refill() with inline max() - Remove unnecessary reservoir manipulation in precompile path - Update op-revm handler to use simplified reservoir refill - Update Cargo.lock dependencies
…nd update test data
Consolidate the EIP-8037 auth list refund splitting logic from run_without_catch_error into apply_eip7702_auth_list, avoiding duplication between handler and inspector. pre_execution now takes init_and_floor_gas as a mutable reference and returns only the regular refund portion.
Introduce PrecompileFailure wrapper that carries both the error and an optional GasTracker, allowing precompiles that charge state gas (EIP-8037) to propagate gas info back to the caller on failure for proper reservoir refill.
Update all precompile Err returns to use .into() for converting PrecompileError to PrecompileFailure, enabling state gas tracking on precompile failures.
Commit 257d261 introduced a regression in gas accounting by incorrectly erasing both remaining and reservoir gas from total_gas_spent. The reservoir should not be erased since it's tracked separately via state_gas_spent. This fix reverts the erase_cost line from erase_cost(remaining + reservoir) to erase_cost(remaining), which correctly accounts for: - Regular gas: deducted from remaining calculation - State gas: tracked separately in state_gas_spent field Results: - State test failures reduced from 20 to 4 (pre-regression count) - All 408 workspace tests now pass - Updated EIP-8037 test data and assertions to match correct values
Refactor to improve code organization by moving the state gas deduction logic from the execution method into the first_frame_input method, where frame initialization concerns are naturally grouped together. This keeps frame setup logic cohesive and simplifies the execution method to focus on the execution loop rather than frame setup details. No functional change - same gas accounting and execution behavior.
Four fixes for EIP-8037 state gas interactions: 1. post_execution: Fix EIP-7623 floor gas check to account for EIP-8037 reservoir. Per EIP-8037, gas_used = tx.gas - gas_left - reservoir. The floor check now subtracts reservoir when computing gas used. 2. handler: Simplify reservoir handling in last_frame_result to always use the frame's final reservoir value directly. It already reflects child frame restorations via handle_reservoir_remaining_gas. 3. handler: Add EIP-8037 floor gas validation when gas_limit exceeds TX_MAX_GAS_LIMIT. The maximum gas_used is capped at TX_MAX_GAS_LIMIT since excess goes to reservoir, so floor_gas must not exceed that cap. 4. inspector: Remove double deduction of initial_state_gas in inspect_execution. The first_frame_input method already handles the deduction; the explicit deduction in the inspector was redundant.
… and EIP-7702 - tx_gas_used now subtracts unused reservoir gas (tx.gas - gas_left - state_gas_left) - Child revert/halt returns all state gas to parent reservoir (matching Python spec) - EIP-7702 state gas refund goes to reservoir instead of reducing initial_state_gas - Refund cap uses gas_used excluding reservoir - Precompile provider preserves reservoir across gas tracker replacement - Updated EIP-8037 test expectations and test data
…recovery Move require_non_staticcall! after gas charging in the CREATE/CREATE2 instruction handler. In the execution-specs, the static check is inside generic_create() after all gas (including state gas) has been charged. The previous ordering prevented state gas from being recorded on the frame, so on child error the parent could not recover it via handle_reservoir_remaining_gas.
This reverts commit 03986fc.
Compute total_gas_spent as limit - remaining - reservoir in build_result_gas to correctly exclude unused state gas. Use saturating_sub in Gas::spent/total_gas_spent to prevent underflow. Add std feature to ee-tests revm dependency.
Replace manual ResultGas construction with the shared build_result_gas function for consistency with the main execution path.
Gas had its own `limit` field duplicating `GasTracker::gas_limit`. Remove it and delegate `Gas::limit()` to `self.tracker.limit()`. Also fixes `Gas::new_spent` which previously set tracker gas_limit to 0 instead of the actual limit.
…reservoir support (#3541) * refactor(precompile): split PrecompileOutput and PrecompileError for state gas support Rename PrecompileOutput to PrecompileOutputEth (simple gas_used + bytes) for individual precompile functions. Introduce new PrecompileOutput with halt, gas_used, state_gas_used, reservoir, and bytes fields for provider-level results. Split PrecompileError: non-fatal errors become PrecompileHaltReason (expressed via PrecompileOutput::halt), while PrecompileError now only holds fatal errors (Fatal/FatalAny) that abort EVM execution. New type aliases: - PrecompileEthResult = Result<PrecompileOutputEth, PrecompileHaltReason> - PrecompileResult = Result<PrecompileOutput, PrecompileError> * refactor(precompile): replace Option<PrecompileHalt> with PrecompileStatus enum Introduce PrecompileStatus enum (Success, Revert, Halt) to replace the Option<PrecompileHalt> field in PrecompileOutput. Add PrecompileEthFn type alias for legacy precompile function signatures, with Precompile::execute() now returning PrecompileOutput via a From<PrecompileEthResult> conversion. Add helper methods: is_success, is_revert, halt_reason, into_halt_reason. * refactor(handler): extract precompile_output_to_interpreter_result helper Add a standalone function that converts PrecompileOutput into InterpreterResult, and use it in EthPrecompiles::run. Fmt and clippy fixes. * refactor(precompile): thread reservoir through PrecompileOutput and revert execute signature - Add `reservoir` parameter to PrecompileOutput::new, halt, revert constructors - Add `from_eth_result` constructor that takes reservoir - Revert Precompile::execute to return PrecompileEthResult - Move PrecompileOutput conversion to call site in EthPrecompiles - Remove unused into_halt_reason and From<PrecompileEthResult> impl * refactor(precompile): change Precompile fn_ from PrecompileEthFn to PrecompileFn (#3546) Change the Precompile struct's fn_ field from PrecompileEthFn (fn(&[u8], u64) -> PrecompileEthResult) to PrecompileFn (fn(&[u8], u64, u64) -> PrecompileOutput) so precompiles receive reservoir directly and return PrecompileOutput. Add call_eth_precompile helper to wrap existing PrecompileEthFn implementations into the new PrecompileFn signature. Each precompile module gets a thin wrapper using this helper. * refactor(precompile): add eth_precompile_fn! macro to eliminate wrapper boilerplate Replace 17 hand-written PrecompileEthFn-to-PrecompileFn wrapper functions with the new eth_precompile_fn! macro that generates them uniformly. * refactor(op-revm): use eth_precompile_fn! macro for precompile wrappers Replace 8 hand-written wrapper functions in op-revm with the eth_precompile_fn! macro. * style: apply cargo fmt * Rename Eth precompile structs * fix(op-revm): add missing reservoir argument to execute calls in tests
…ecompileStatus helpers PrecompileOutput now carries reservoir and state_gas_used, so precompile_output_to_interpreter_result no longer needs a separate reservoir parameter. Added convenience methods on PrecompileStatus and fixed unused import warning.
Update eip2537 benchmarks to use Precompile::execute() instead of removed precompile() method, and fix manual div_ceil clippy warning.
Remove references to removed getters (limit, spent, intrinsic_gas, remaining) and update table and derived values to reflect the actual methods (total_gas_spent, tx_gas_used, block_regular_gas_used, etc.).
- Fix unresolved links to non-existent `regular_gas_spent` and `with_regular_gas_spent` methods in deprecation notes - Fix typos: "substract" → "subtract", "enought" → "enough" - Fix stale comments referencing `regular_gas_spent` field
tempoxyz-bot
reviewed
Apr 9, 2026
tempoxyz-bot
left a comment
There was a problem hiding this comment.
👁️ Cyclops Review
EIP-8037 dual gas model implementation. Core reservoir accounting matches the spec, but several downstream integration gaps exist — particularly in op-revm fee settlement, Amsterdam validation coverage, inspector gas reporting, and the precompile API boundary.
7 findings (2 high, 4 medium, 1 low)
Reviewer Callouts
- ⚡ Downstream reth/alloy-evm gas accounting:
ExecutionResult::gas_used()is now receipt-domain only. Block builders usingalloy-evmandrethstill collapse receipt and block gas into a single accumulator. Once Amsterdam is enabled, valid blocks with state-gas transactions will produce incorrectheader.gas_used. The in-tree harness was updated, but downstream integration needsblock_regular_gas_used()/block_state_gas_used(). - ⚡ reth precompile cache:
PrecompileOutputnow carries halt status and live reservoir. reth's unchanged cache treats everyOk(output)as context-free success keyed by calldata/spec. Cached OOG halts (which setgas_used = 0) will always hit on later calls with sufficient gas, and cached success outputs replay stale reservoirs. - ⚡ Gas accounting complexity: The
saturating_sub→0pattern infirst_frame_input()silently allows impossible negative regular-gas budgets instead of failing validation.
…utput API Use `PrecompileOutput::from_eth_result` and `precompile_output_to_interpreter_result` instead of manually constructing InterpreterResult, aligning with the new gas handling.
…ecompileError> Distinguish fatal/unrecoverable precompile errors from non-fatal halts by returning a Result. PrecompileFn signature updated accordingly, and all call sites now propagate or unwrap the error.
The core revm handler already excludes reservoir gas when calculating beneficiary rewards and caller reimbursement, but op-revm's fee settlement did not account for it. Once Osaka/EIP-8037 is enabled, transactions with leftover reservoir gas would over-credit BASE_FEE_RECIPIENT and OPERATOR_FEE_RECIPIENT, and under-refund operator fees to the caller. - reward_beneficiary: use effective_used (gas.used() - reservoir) for base_fee_amount and operator_fee_cost calculations - operator_fee_refund: include gas.reservoir() when computing gas_used so the refund correctly covers unused reservoir gas - handler validation: use initial_regular_gas (excluding state gas) when checking floor_gas against TX_MAX_GAS_LIMIT cap
Merged
theochap
pushed a commit
to ethereum-optimism/optimism
that referenced
this pull request
Apr 15, 2026
* feat: implement EIP-8037 state gas (reservoir model) Add state gas accounting with reservoir model across interpreter, handler, context, and precompile crates. Includes comprehensive EIP-8037 test suite with 35+ test vectors covering SSTORE, CALL, CREATE, SELFDESTRUCT, reservoir refill, and gas opcode behavior. * rm statetest jsons * Rename spent to regular_gas_spent in ResultGas and simplify reservoir refill - Rename ResultGas::spent to regular_gas_spent to distinguish from state gas after EIP-8037 - Add total_gas_spent() method and deprecate old spent()/set_spent()/with_spent() accessors - Simplify reservoir refill logic: replace handler_reservoir_refill() with inline max() - Remove unnecessary reservoir manipulation in precompile path - Update op-revm handler to use simplified reservoir refill - Update Cargo.lock dependencies * Simplify EIP-8037 state gas: remove redundant fields from ResultGas and update test data * Move EIP-7702 state gas refund split into pre_execution Consolidate the EIP-8037 auth list refund splitting logic from run_without_catch_error into apply_eip7702_auth_list, avoiding duplication between handler and inspector. pre_execution now takes init_and_floor_gas as a mutable reference and returns only the regular refund portion. * Add PrecompileFailure type for state gas propagation on error Introduce PrecompileFailure wrapper that carries both the error and an optional GasTracker, allowing precompiles that charge state gas (EIP-8037) to propagate gas info back to the caller on failure for proper reservoir refill. * Convert precompile errors to PrecompileFailure via .into() Update all precompile Err returns to use .into() for converting PrecompileError to PrecompileFailure, enabling state gas tracking on precompile failures. * Fix EIP-8037 state gas accounting regression Commit 14bd5a61 introduced a regression in gas accounting by incorrectly erasing both remaining and reservoir gas from total_gas_spent. The reservoir should not be erased since it's tracked separately via state_gas_spent. This fix reverts the erase_cost line from erase_cost(remaining + reservoir) to erase_cost(remaining), which correctly accounts for: - Regular gas: deducted from remaining calculation - State gas: tracked separately in state_gas_spent field Results: - State test failures reduced from 20 to 4 (pre-regression count) - All 408 workspace tests now pass - Updated EIP-8037 test data and assertions to match correct values * Move state gas deduction into first_frame_input Refactor to improve code organization by moving the state gas deduction logic from the execution method into the first_frame_input method, where frame initialization concerns are naturally grouped together. This keeps frame setup logic cohesive and simplifies the execution method to focus on the execution loop rather than frame setup details. No functional change - same gas accounting and execution behavior. * Fix EIP-8037 gas accounting: floor gas, reservoir, inspector, validation Four fixes for EIP-8037 state gas interactions: 1. post_execution: Fix EIP-7623 floor gas check to account for EIP-8037 reservoir. Per EIP-8037, gas_used = tx.gas - gas_left - reservoir. The floor check now subtracts reservoir when computing gas used. 2. handler: Simplify reservoir handling in last_frame_result to always use the frame's final reservoir value directly. It already reflects child frame restorations via handle_reservoir_remaining_gas. 3. handler: Add EIP-8037 floor gas validation when gas_limit exceeds TX_MAX_GAS_LIMIT. The maximum gas_used is capped at TX_MAX_GAS_LIMIT since excess goes to reservoir, so floor_gas must not exceed that cap. 4. inspector: Remove double deduction of initial_state_gas in inspect_execution. The first_frame_input method already handles the deduction; the explicit deduction in the inspector was redundant. * Fix EIP-8037 reservoir accounting: tx_gas_used, child revert refunds, and EIP-7702 - tx_gas_used now subtracts unused reservoir gas (tx.gas - gas_left - state_gas_left) - Child revert/halt returns all state gas to parent reservoir (matching Python spec) - EIP-7702 state gas refund goes to reservoir instead of reducing initial_state_gas - Refund cap uses gas_used excluding reservoir - Precompile provider preserves reservoir across gas tracker replacement - Updated EIP-8037 test expectations and test data * Fix CREATE/CREATE2 static call check ordering for EIP-8037 state gas recovery Move require_non_staticcall! after gas charging in the CREATE/CREATE2 instruction handler. In the execution-specs, the static check is inside generic_create() after all gas (including state gas) has been charged. The previous ordering prevented state gas from being recorded on the frame, so on child error the parent could not recover it via handle_reservoir_remaining_gas. * Simplify last_frame_result: remove initial_reservoir parameter and fix reservoir handling - Remove unused `initial_reservoir` parameter from `last_frame_result` across all handlers - Fix precompile provider to save reservoir before overwriting gas tracker - Add deprecated `gas_used()` method on ExecutionResult pointing to `tx_gas_used()` * Revert CREATE/CREATE2 static call check to before gas charging Static call check doesn't need to be after gas charging - CREATE in a static context is always an error regardless of gas accounting. * Fix reservoir handling: restore state gas on revert/halt On revert/halt, state changes are rolled back so state gas should be refunded back to the reservoir. Only on success should the frame's final reservoir be used as-is. * Fix EIP-8037 state_gas_spent accounting for EIP-7702 auth list Two bugs in state_gas_spent reporting: 1. build_result_gas added full initial_state_gas without subtracting eip7702_reservoir_refund, overcounting state gas for existing authority accounts. This caused block_state_gas_used to be too high and block_regular_gas_used to be too low. 2. On revert/halt, last_frame_result set state_gas_spent before restoring it to the reservoir, leaving a stale execution value. Now correctly zeroed since state changes are rolled back. * Improve precompile reservoir handling and apply formatting fixes Refactor precompile provider to properly handle reservoir refill logic for both success and error/halt cases, ensuring state gas accounting is correct when precompiles don't track reservoir. Also apply rustfmt formatting across multiple files. * Fix precompile_provider compilation errors against current precompile API PrecompileOutput has gas_used (not gas: GasTracker) and PrecompileError is a flat enum (no .error/.gas subfields). Use record_regular_cost() to avoid deprecation warning. * Simplify precompile interface: remove gas_limit from PrecompileOutput, flatten PrecompileError Remove gas_limit field from PrecompileOutput (only gas_used needed) and eliminate the PrecompileError wrapper layer so errors are returned directly as the enum variant without .into() conversion. * Fix EIP-8037 reservoir refill tests and complete precompile interface cleanup Update 4 EIP-8037 tests to match current reservoir refill behavior: on revert/halt at the top level, state_gas_spent is zeroed and state gas is restored to the reservoir (state changes rolled back). Regenerate testdata. Also complete the PrecompileOutput/PrecompileError simplification from the previous commit: remove reverted field, flatten error matching in tests. * temporary remove all ee-tests * Fix state gas accounting: include reservoir in total_gas_spent and prevent underflow - Remove saturating_sub of reservoir from total_gas_spent in build_result_gas - Use saturating_sub in block_regular_gas_used to prevent underflow * Apply formatting fixes and use saturating arithmetic for state gas spent * Revert "temporary remove all ee-tests" This reverts commit 03986fcd15d861b32ce2e56186ffd9ebdbfa3871. * Fix total_gas_spent to exclude reservoir and use saturating arithmetic Compute total_gas_spent as limit - remaining - reservoir in build_result_gas to correctly exclude unused state gas. Use saturating_sub in Gas::spent/total_gas_spent to prevent underflow. Add std feature to ee-tests revm dependency. * Use build_result_gas helper in system call gas handling Replace manual ResultGas construction with the shared build_result_gas function for consistency with the main execution path. * bump rust to 1.91 * Remove redundant gas_limit from Gas, delegate to GasTracker Gas had its own `limit` field duplicating `GasTracker::gas_limit`. Remove it and delegate `Gas::limit()` to `self.tracker.limit()`. Also fixes `Gas::new_spent` which previously set tracker gas_limit to 0 instead of the actual limit. * refactor(precompile): restructure PrecompileOutput for state gas and reservoir support (bluealloy/revm#3541) * refactor(precompile): split PrecompileOutput and PrecompileError for state gas support Rename PrecompileOutput to PrecompileOutputEth (simple gas_used + bytes) for individual precompile functions. Introduce new PrecompileOutput with halt, gas_used, state_gas_used, reservoir, and bytes fields for provider-level results. Split PrecompileError: non-fatal errors become PrecompileHaltReason (expressed via PrecompileOutput::halt), while PrecompileError now only holds fatal errors (Fatal/FatalAny) that abort EVM execution. New type aliases: - PrecompileEthResult = Result<PrecompileOutputEth, PrecompileHaltReason> - PrecompileResult = Result<PrecompileOutput, PrecompileError> * refactor(precompile): replace Option<PrecompileHalt> with PrecompileStatus enum Introduce PrecompileStatus enum (Success, Revert, Halt) to replace the Option<PrecompileHalt> field in PrecompileOutput. Add PrecompileEthFn type alias for legacy precompile function signatures, with Precompile::execute() now returning PrecompileOutput via a From<PrecompileEthResult> conversion. Add helper methods: is_success, is_revert, halt_reason, into_halt_reason. * refactor(handler): extract precompile_output_to_interpreter_result helper Add a standalone function that converts PrecompileOutput into InterpreterResult, and use it in EthPrecompiles::run. Fmt and clippy fixes. * refactor(precompile): thread reservoir through PrecompileOutput and revert execute signature - Add `reservoir` parameter to PrecompileOutput::new, halt, revert constructors - Add `from_eth_result` constructor that takes reservoir - Revert Precompile::execute to return PrecompileEthResult - Move PrecompileOutput conversion to call site in EthPrecompiles - Remove unused into_halt_reason and From<PrecompileEthResult> impl * refactor(precompile): change Precompile fn_ from PrecompileEthFn to PrecompileFn (bluealloy/revm#3546) Change the Precompile struct's fn_ field from PrecompileEthFn (fn(&[u8], u64) -> PrecompileEthResult) to PrecompileFn (fn(&[u8], u64, u64) -> PrecompileOutput) so precompiles receive reservoir directly and return PrecompileOutput. Add call_eth_precompile helper to wrap existing PrecompileEthFn implementations into the new PrecompileFn signature. Each precompile module gets a thin wrapper using this helper. * refactor(precompile): add eth_precompile_fn! macro to eliminate wrapper boilerplate Replace 17 hand-written PrecompileEthFn-to-PrecompileFn wrapper functions with the new eth_precompile_fn! macro that generates them uniformly. * refactor(op-revm): use eth_precompile_fn! macro for precompile wrappers Replace 8 hand-written wrapper functions in op-revm with the eth_precompile_fn! macro. * style: apply cargo fmt * Rename Eth precompile structs * fix(op-revm): add missing reservoir argument to execute calls in tests * refactor(precompile): move reservoir into PrecompileOutput and add PrecompileStatus helpers PrecompileOutput now carries reservoir and state_gas_used, so precompile_output_to_interpreter_result no longer needs a separate reservoir parameter. Added convenience methods on PrecompileStatus and fixed unused import warning. * fix: update bench and test code for new Precompile API Update eip2537 benchmarks to use Precompile::execute() instead of removed precompile() method, and fix manual div_ceil clippy warning. * docs: fix ResultGas doc comments to match current API Remove references to removed getters (limit, spent, intrinsic_gas, remaining) and update table and derived values to reflect the actual methods (total_gas_spent, tx_gas_used, block_regular_gas_used, etc.). * docs: fix broken doc links and typos in ResultGas - Fix unresolved links to non-existent `regular_gas_spent` and `with_regular_gas_spent` methods in deprecation notes - Fix typos: "substract" → "subtract", "enought" → "enough" - Fix stale comments referencing `regular_gas_spent` field * feat: track state_gas_spent and reservoir in GasInspector * fix: validate max(intrinsic_gas, floor_gas) against TX_MAX_GAS_LIMIT cap * chore: add must_use annotations to gas recording methods and minor cleanups * refactor: update custom_precompile_journal example to use PrecompileOutput API Use `PrecompileOutput::from_eth_result` and `precompile_output_to_interpreter_result` instead of manually constructing InterpreterResult, aligning with the new gas handling. * refactor: make Precompile::execute return Result<PrecompileOutput, PrecompileError> Distinguish fatal/unrecoverable precompile errors from non-fatal halts by returning a Result. PrecompileFn signature updated accordingly, and all call sites now propagate or unwrap the error. * chore: bump MSRV to 1.91 in CI * fix: exclude EIP-8037 reservoir gas from op-revm fee settlement The core revm handler already excludes reservoir gas when calculating beneficiary rewards and caller reimbursement, but op-revm's fee settlement did not account for it. Once Osaka/EIP-8037 is enabled, transactions with leftover reservoir gas would over-credit BASE_FEE_RECIPIENT and OPERATOR_FEE_RECIPIENT, and under-refund operator fees to the caller. - reward_beneficiary: use effective_used (gas.used() - reservoir) for base_fee_amount and operator_fee_cost calculations - operator_fee_refund: include gas.reservoir() when computing gas_used so the refund correctly covers unused reservoir gas - handler validation: use initial_regular_gas (excluding state gas) when checking floor_gas against TX_MAX_GAS_LIMIT cap
pull Bot
pushed a commit
to Poya0073/optimism
that referenced
this pull request
Apr 24, 2026
* feat: add EIP-8037 / TIP-1016 state gas support (bluealloy/revm#3406) * feat: implement EIP-8037 state gas (reservoir model) Add state gas accounting with reservoir model across interpreter, handler, context, and precompile crates. Includes comprehensive EIP-8037 test suite with 35+ test vectors covering SSTORE, CALL, CREATE, SELFDESTRUCT, reservoir refill, and gas opcode behavior. * rm statetest jsons * Rename spent to regular_gas_spent in ResultGas and simplify reservoir refill - Rename ResultGas::spent to regular_gas_spent to distinguish from state gas after EIP-8037 - Add total_gas_spent() method and deprecate old spent()/set_spent()/with_spent() accessors - Simplify reservoir refill logic: replace handler_reservoir_refill() with inline max() - Remove unnecessary reservoir manipulation in precompile path - Update op-revm handler to use simplified reservoir refill - Update Cargo.lock dependencies * Simplify EIP-8037 state gas: remove redundant fields from ResultGas and update test data * Move EIP-7702 state gas refund split into pre_execution Consolidate the EIP-8037 auth list refund splitting logic from run_without_catch_error into apply_eip7702_auth_list, avoiding duplication between handler and inspector. pre_execution now takes init_and_floor_gas as a mutable reference and returns only the regular refund portion. * Add PrecompileFailure type for state gas propagation on error Introduce PrecompileFailure wrapper that carries both the error and an optional GasTracker, allowing precompiles that charge state gas (EIP-8037) to propagate gas info back to the caller on failure for proper reservoir refill. * Convert precompile errors to PrecompileFailure via .into() Update all precompile Err returns to use .into() for converting PrecompileError to PrecompileFailure, enabling state gas tracking on precompile failures. * Fix EIP-8037 state gas accounting regression Commit 14bd5a61 introduced a regression in gas accounting by incorrectly erasing both remaining and reservoir gas from total_gas_spent. The reservoir should not be erased since it's tracked separately via state_gas_spent. This fix reverts the erase_cost line from erase_cost(remaining + reservoir) to erase_cost(remaining), which correctly accounts for: - Regular gas: deducted from remaining calculation - State gas: tracked separately in state_gas_spent field Results: - State test failures reduced from 20 to 4 (pre-regression count) - All 408 workspace tests now pass - Updated EIP-8037 test data and assertions to match correct values * Move state gas deduction into first_frame_input Refactor to improve code organization by moving the state gas deduction logic from the execution method into the first_frame_input method, where frame initialization concerns are naturally grouped together. This keeps frame setup logic cohesive and simplifies the execution method to focus on the execution loop rather than frame setup details. No functional change - same gas accounting and execution behavior. * Fix EIP-8037 gas accounting: floor gas, reservoir, inspector, validation Four fixes for EIP-8037 state gas interactions: 1. post_execution: Fix EIP-7623 floor gas check to account for EIP-8037 reservoir. Per EIP-8037, gas_used = tx.gas - gas_left - reservoir. The floor check now subtracts reservoir when computing gas used. 2. handler: Simplify reservoir handling in last_frame_result to always use the frame's final reservoir value directly. It already reflects child frame restorations via handle_reservoir_remaining_gas. 3. handler: Add EIP-8037 floor gas validation when gas_limit exceeds TX_MAX_GAS_LIMIT. The maximum gas_used is capped at TX_MAX_GAS_LIMIT since excess goes to reservoir, so floor_gas must not exceed that cap. 4. inspector: Remove double deduction of initial_state_gas in inspect_execution. The first_frame_input method already handles the deduction; the explicit deduction in the inspector was redundant. * Fix EIP-8037 reservoir accounting: tx_gas_used, child revert refunds, and EIP-7702 - tx_gas_used now subtracts unused reservoir gas (tx.gas - gas_left - state_gas_left) - Child revert/halt returns all state gas to parent reservoir (matching Python spec) - EIP-7702 state gas refund goes to reservoir instead of reducing initial_state_gas - Refund cap uses gas_used excluding reservoir - Precompile provider preserves reservoir across gas tracker replacement - Updated EIP-8037 test expectations and test data * Fix CREATE/CREATE2 static call check ordering for EIP-8037 state gas recovery Move require_non_staticcall! after gas charging in the CREATE/CREATE2 instruction handler. In the execution-specs, the static check is inside generic_create() after all gas (including state gas) has been charged. The previous ordering prevented state gas from being recorded on the frame, so on child error the parent could not recover it via handle_reservoir_remaining_gas. * Simplify last_frame_result: remove initial_reservoir parameter and fix reservoir handling - Remove unused `initial_reservoir` parameter from `last_frame_result` across all handlers - Fix precompile provider to save reservoir before overwriting gas tracker - Add deprecated `gas_used()` method on ExecutionResult pointing to `tx_gas_used()` * Revert CREATE/CREATE2 static call check to before gas charging Static call check doesn't need to be after gas charging - CREATE in a static context is always an error regardless of gas accounting. * Fix reservoir handling: restore state gas on revert/halt On revert/halt, state changes are rolled back so state gas should be refunded back to the reservoir. Only on success should the frame's final reservoir be used as-is. * Fix EIP-8037 state_gas_spent accounting for EIP-7702 auth list Two bugs in state_gas_spent reporting: 1. build_result_gas added full initial_state_gas without subtracting eip7702_reservoir_refund, overcounting state gas for existing authority accounts. This caused block_state_gas_used to be too high and block_regular_gas_used to be too low. 2. On revert/halt, last_frame_result set state_gas_spent before restoring it to the reservoir, leaving a stale execution value. Now correctly zeroed since state changes are rolled back. * Improve precompile reservoir handling and apply formatting fixes Refactor precompile provider to properly handle reservoir refill logic for both success and error/halt cases, ensuring state gas accounting is correct when precompiles don't track reservoir. Also apply rustfmt formatting across multiple files. * Fix precompile_provider compilation errors against current precompile API PrecompileOutput has gas_used (not gas: GasTracker) and PrecompileError is a flat enum (no .error/.gas subfields). Use record_regular_cost() to avoid deprecation warning. * Simplify precompile interface: remove gas_limit from PrecompileOutput, flatten PrecompileError Remove gas_limit field from PrecompileOutput (only gas_used needed) and eliminate the PrecompileError wrapper layer so errors are returned directly as the enum variant without .into() conversion. * Fix EIP-8037 reservoir refill tests and complete precompile interface cleanup Update 4 EIP-8037 tests to match current reservoir refill behavior: on revert/halt at the top level, state_gas_spent is zeroed and state gas is restored to the reservoir (state changes rolled back). Regenerate testdata. Also complete the PrecompileOutput/PrecompileError simplification from the previous commit: remove reverted field, flatten error matching in tests. * temporary remove all ee-tests * Fix state gas accounting: include reservoir in total_gas_spent and prevent underflow - Remove saturating_sub of reservoir from total_gas_spent in build_result_gas - Use saturating_sub in block_regular_gas_used to prevent underflow * Apply formatting fixes and use saturating arithmetic for state gas spent * Revert "temporary remove all ee-tests" This reverts commit 03986fcd15d861b32ce2e56186ffd9ebdbfa3871. * Fix total_gas_spent to exclude reservoir and use saturating arithmetic Compute total_gas_spent as limit - remaining - reservoir in build_result_gas to correctly exclude unused state gas. Use saturating_sub in Gas::spent/total_gas_spent to prevent underflow. Add std feature to ee-tests revm dependency. * Use build_result_gas helper in system call gas handling Replace manual ResultGas construction with the shared build_result_gas function for consistency with the main execution path. * bump rust to 1.91 * Remove redundant gas_limit from Gas, delegate to GasTracker Gas had its own `limit` field duplicating `GasTracker::gas_limit`. Remove it and delegate `Gas::limit()` to `self.tracker.limit()`. Also fixes `Gas::new_spent` which previously set tracker gas_limit to 0 instead of the actual limit. * refactor(precompile): restructure PrecompileOutput for state gas and reservoir support (bluealloy/revm#3541) * refactor(precompile): split PrecompileOutput and PrecompileError for state gas support Rename PrecompileOutput to PrecompileOutputEth (simple gas_used + bytes) for individual precompile functions. Introduce new PrecompileOutput with halt, gas_used, state_gas_used, reservoir, and bytes fields for provider-level results. Split PrecompileError: non-fatal errors become PrecompileHaltReason (expressed via PrecompileOutput::halt), while PrecompileError now only holds fatal errors (Fatal/FatalAny) that abort EVM execution. New type aliases: - PrecompileEthResult = Result<PrecompileOutputEth, PrecompileHaltReason> - PrecompileResult = Result<PrecompileOutput, PrecompileError> * refactor(precompile): replace Option<PrecompileHalt> with PrecompileStatus enum Introduce PrecompileStatus enum (Success, Revert, Halt) to replace the Option<PrecompileHalt> field in PrecompileOutput. Add PrecompileEthFn type alias for legacy precompile function signatures, with Precompile::execute() now returning PrecompileOutput via a From<PrecompileEthResult> conversion. Add helper methods: is_success, is_revert, halt_reason, into_halt_reason. * refactor(handler): extract precompile_output_to_interpreter_result helper Add a standalone function that converts PrecompileOutput into InterpreterResult, and use it in EthPrecompiles::run. Fmt and clippy fixes. * refactor(precompile): thread reservoir through PrecompileOutput and revert execute signature - Add `reservoir` parameter to PrecompileOutput::new, halt, revert constructors - Add `from_eth_result` constructor that takes reservoir - Revert Precompile::execute to return PrecompileEthResult - Move PrecompileOutput conversion to call site in EthPrecompiles - Remove unused into_halt_reason and From<PrecompileEthResult> impl * refactor(precompile): change Precompile fn_ from PrecompileEthFn to PrecompileFn (bluealloy/revm#3546) Change the Precompile struct's fn_ field from PrecompileEthFn (fn(&[u8], u64) -> PrecompileEthResult) to PrecompileFn (fn(&[u8], u64, u64) -> PrecompileOutput) so precompiles receive reservoir directly and return PrecompileOutput. Add call_eth_precompile helper to wrap existing PrecompileEthFn implementations into the new PrecompileFn signature. Each precompile module gets a thin wrapper using this helper. * refactor(precompile): add eth_precompile_fn! macro to eliminate wrapper boilerplate Replace 17 hand-written PrecompileEthFn-to-PrecompileFn wrapper functions with the new eth_precompile_fn! macro that generates them uniformly. * refactor(op-revm): use eth_precompile_fn! macro for precompile wrappers Replace 8 hand-written wrapper functions in op-revm with the eth_precompile_fn! macro. * style: apply cargo fmt * Rename Eth precompile structs * fix(op-revm): add missing reservoir argument to execute calls in tests * refactor(precompile): move reservoir into PrecompileOutput and add PrecompileStatus helpers PrecompileOutput now carries reservoir and state_gas_used, so precompile_output_to_interpreter_result no longer needs a separate reservoir parameter. Added convenience methods on PrecompileStatus and fixed unused import warning. * fix: update bench and test code for new Precompile API Update eip2537 benchmarks to use Precompile::execute() instead of removed precompile() method, and fix manual div_ceil clippy warning. * docs: fix ResultGas doc comments to match current API Remove references to removed getters (limit, spent, intrinsic_gas, remaining) and update table and derived values to reflect the actual methods (total_gas_spent, tx_gas_used, block_regular_gas_used, etc.). * docs: fix broken doc links and typos in ResultGas - Fix unresolved links to non-existent `regular_gas_spent` and `with_regular_gas_spent` methods in deprecation notes - Fix typos: "substract" → "subtract", "enought" → "enough" - Fix stale comments referencing `regular_gas_spent` field * feat: track state_gas_spent and reservoir in GasInspector * fix: validate max(intrinsic_gas, floor_gas) against TX_MAX_GAS_LIMIT cap * chore: add must_use annotations to gas recording methods and minor cleanups * refactor: update custom_precompile_journal example to use PrecompileOutput API Use `PrecompileOutput::from_eth_result` and `precompile_output_to_interpreter_result` instead of manually constructing InterpreterResult, aligning with the new gas handling. * refactor: make Precompile::execute return Result<PrecompileOutput, PrecompileError> Distinguish fatal/unrecoverable precompile errors from non-fatal halts by returning a Result. PrecompileFn signature updated accordingly, and all call sites now propagate or unwrap the error. * chore: bump MSRV to 1.91 in CI * fix: exclude EIP-8037 reservoir gas from op-revm fee settlement The core revm handler already excludes reservoir gas when calculating beneficiary rewards and caller reimbursement, but op-revm's fee settlement did not account for it. Once Osaka/EIP-8037 is enabled, transactions with leftover reservoir gas would over-credit BASE_FEE_RECIPIENT and OPERATOR_FEE_RECIPIENT, and under-refund operator fees to the caller. - reward_beneficiary: use effective_used (gas.used() - reservoir) for base_fee_amount and operator_fee_cost calculations - operator_fee_refund: include gas.reservoir() when computing gas_used so the refund correctly covers unused reservoir gas - handler validation: use initial_regular_gas (excluding state gas) when checking floor_gas against TX_MAX_GAS_LIMIT cap * refactor: move EIP-8037 gas cap validation into validate_initial_tx_gas (bluealloy/revm#3552) Extract `initial_regular_gas()` helper on InitialAndFloorGas and move the EIP-8037 TX_MAX_GAS_LIMIT validation from the handler into `validate_initial_tx_gas`, consolidating validation logic. Also clean up formatting across precompile and op-revm crates. * chore: release (bluealloy/revm#3472) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore: v106 release prep (bluealloy/revm#3554) * docs: add v106 migration guide entry * chore: v106 release prep — version bumps and changelogs * add revm version * fix(op-revm): update validate_against_state_and_deduct_caller signature Adds the new `&mut InitialAndFloorGas` parameter introduced in ethereum-optimism#3577 to the OpHandler trait impl, and passes `&mut Default::default()` at all test call sites. * chore: v107 release prep * chore(op-revm): fmt --------- Co-authored-by: rakita <rakita@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: rakita <dragan0rakita@gmail.com>
ClaytonNorthey92
pushed a commit
to hemilabs/optimism
that referenced
this pull request
Jun 25, 2026
* feat: add EIP-8037 / TIP-1016 state gas support (bluealloy/revm#3406) * feat: implement EIP-8037 state gas (reservoir model) Add state gas accounting with reservoir model across interpreter, handler, context, and precompile crates. Includes comprehensive EIP-8037 test suite with 35+ test vectors covering SSTORE, CALL, CREATE, SELFDESTRUCT, reservoir refill, and gas opcode behavior. * rm statetest jsons * Rename spent to regular_gas_spent in ResultGas and simplify reservoir refill - Rename ResultGas::spent to regular_gas_spent to distinguish from state gas after EIP-8037 - Add total_gas_spent() method and deprecate old spent()/set_spent()/with_spent() accessors - Simplify reservoir refill logic: replace handler_reservoir_refill() with inline max() - Remove unnecessary reservoir manipulation in precompile path - Update op-revm handler to use simplified reservoir refill - Update Cargo.lock dependencies * Simplify EIP-8037 state gas: remove redundant fields from ResultGas and update test data * Move EIP-7702 state gas refund split into pre_execution Consolidate the EIP-8037 auth list refund splitting logic from run_without_catch_error into apply_eip7702_auth_list, avoiding duplication between handler and inspector. pre_execution now takes init_and_floor_gas as a mutable reference and returns only the regular refund portion. * Add PrecompileFailure type for state gas propagation on error Introduce PrecompileFailure wrapper that carries both the error and an optional GasTracker, allowing precompiles that charge state gas (EIP-8037) to propagate gas info back to the caller on failure for proper reservoir refill. * Convert precompile errors to PrecompileFailure via .into() Update all precompile Err returns to use .into() for converting PrecompileError to PrecompileFailure, enabling state gas tracking on precompile failures. * Fix EIP-8037 state gas accounting regression Commit 14bd5a61 introduced a regression in gas accounting by incorrectly erasing both remaining and reservoir gas from total_gas_spent. The reservoir should not be erased since it's tracked separately via state_gas_spent. This fix reverts the erase_cost line from erase_cost(remaining + reservoir) to erase_cost(remaining), which correctly accounts for: - Regular gas: deducted from remaining calculation - State gas: tracked separately in state_gas_spent field Results: - State test failures reduced from 20 to 4 (pre-regression count) - All 408 workspace tests now pass - Updated EIP-8037 test data and assertions to match correct values * Move state gas deduction into first_frame_input Refactor to improve code organization by moving the state gas deduction logic from the execution method into the first_frame_input method, where frame initialization concerns are naturally grouped together. This keeps frame setup logic cohesive and simplifies the execution method to focus on the execution loop rather than frame setup details. No functional change - same gas accounting and execution behavior. * Fix EIP-8037 gas accounting: floor gas, reservoir, inspector, validation Four fixes for EIP-8037 state gas interactions: 1. post_execution: Fix EIP-7623 floor gas check to account for EIP-8037 reservoir. Per EIP-8037, gas_used = tx.gas - gas_left - reservoir. The floor check now subtracts reservoir when computing gas used. 2. handler: Simplify reservoir handling in last_frame_result to always use the frame's final reservoir value directly. It already reflects child frame restorations via handle_reservoir_remaining_gas. 3. handler: Add EIP-8037 floor gas validation when gas_limit exceeds TX_MAX_GAS_LIMIT. The maximum gas_used is capped at TX_MAX_GAS_LIMIT since excess goes to reservoir, so floor_gas must not exceed that cap. 4. inspector: Remove double deduction of initial_state_gas in inspect_execution. The first_frame_input method already handles the deduction; the explicit deduction in the inspector was redundant. * Fix EIP-8037 reservoir accounting: tx_gas_used, child revert refunds, and EIP-7702 - tx_gas_used now subtracts unused reservoir gas (tx.gas - gas_left - state_gas_left) - Child revert/halt returns all state gas to parent reservoir (matching Python spec) - EIP-7702 state gas refund goes to reservoir instead of reducing initial_state_gas - Refund cap uses gas_used excluding reservoir - Precompile provider preserves reservoir across gas tracker replacement - Updated EIP-8037 test expectations and test data * Fix CREATE/CREATE2 static call check ordering for EIP-8037 state gas recovery Move require_non_staticcall! after gas charging in the CREATE/CREATE2 instruction handler. In the execution-specs, the static check is inside generic_create() after all gas (including state gas) has been charged. The previous ordering prevented state gas from being recorded on the frame, so on child error the parent could not recover it via handle_reservoir_remaining_gas. * Simplify last_frame_result: remove initial_reservoir parameter and fix reservoir handling - Remove unused `initial_reservoir` parameter from `last_frame_result` across all handlers - Fix precompile provider to save reservoir before overwriting gas tracker - Add deprecated `gas_used()` method on ExecutionResult pointing to `tx_gas_used()` * Revert CREATE/CREATE2 static call check to before gas charging Static call check doesn't need to be after gas charging - CREATE in a static context is always an error regardless of gas accounting. * Fix reservoir handling: restore state gas on revert/halt On revert/halt, state changes are rolled back so state gas should be refunded back to the reservoir. Only on success should the frame's final reservoir be used as-is. * Fix EIP-8037 state_gas_spent accounting for EIP-7702 auth list Two bugs in state_gas_spent reporting: 1. build_result_gas added full initial_state_gas without subtracting eip7702_reservoir_refund, overcounting state gas for existing authority accounts. This caused block_state_gas_used to be too high and block_regular_gas_used to be too low. 2. On revert/halt, last_frame_result set state_gas_spent before restoring it to the reservoir, leaving a stale execution value. Now correctly zeroed since state changes are rolled back. * Improve precompile reservoir handling and apply formatting fixes Refactor precompile provider to properly handle reservoir refill logic for both success and error/halt cases, ensuring state gas accounting is correct when precompiles don't track reservoir. Also apply rustfmt formatting across multiple files. * Fix precompile_provider compilation errors against current precompile API PrecompileOutput has gas_used (not gas: GasTracker) and PrecompileError is a flat enum (no .error/.gas subfields). Use record_regular_cost() to avoid deprecation warning. * Simplify precompile interface: remove gas_limit from PrecompileOutput, flatten PrecompileError Remove gas_limit field from PrecompileOutput (only gas_used needed) and eliminate the PrecompileError wrapper layer so errors are returned directly as the enum variant without .into() conversion. * Fix EIP-8037 reservoir refill tests and complete precompile interface cleanup Update 4 EIP-8037 tests to match current reservoir refill behavior: on revert/halt at the top level, state_gas_spent is zeroed and state gas is restored to the reservoir (state changes rolled back). Regenerate testdata. Also complete the PrecompileOutput/PrecompileError simplification from the previous commit: remove reverted field, flatten error matching in tests. * temporary remove all ee-tests * Fix state gas accounting: include reservoir in total_gas_spent and prevent underflow - Remove saturating_sub of reservoir from total_gas_spent in build_result_gas - Use saturating_sub in block_regular_gas_used to prevent underflow * Apply formatting fixes and use saturating arithmetic for state gas spent * Revert "temporary remove all ee-tests" This reverts commit 03986fcd15d861b32ce2e56186ffd9ebdbfa3871. * Fix total_gas_spent to exclude reservoir and use saturating arithmetic Compute total_gas_spent as limit - remaining - reservoir in build_result_gas to correctly exclude unused state gas. Use saturating_sub in Gas::spent/total_gas_spent to prevent underflow. Add std feature to ee-tests revm dependency. * Use build_result_gas helper in system call gas handling Replace manual ResultGas construction with the shared build_result_gas function for consistency with the main execution path. * bump rust to 1.91 * Remove redundant gas_limit from Gas, delegate to GasTracker Gas had its own `limit` field duplicating `GasTracker::gas_limit`. Remove it and delegate `Gas::limit()` to `self.tracker.limit()`. Also fixes `Gas::new_spent` which previously set tracker gas_limit to 0 instead of the actual limit. * refactor(precompile): restructure PrecompileOutput for state gas and reservoir support (bluealloy/revm#3541) * refactor(precompile): split PrecompileOutput and PrecompileError for state gas support Rename PrecompileOutput to PrecompileOutputEth (simple gas_used + bytes) for individual precompile functions. Introduce new PrecompileOutput with halt, gas_used, state_gas_used, reservoir, and bytes fields for provider-level results. Split PrecompileError: non-fatal errors become PrecompileHaltReason (expressed via PrecompileOutput::halt), while PrecompileError now only holds fatal errors (Fatal/FatalAny) that abort EVM execution. New type aliases: - PrecompileEthResult = Result<PrecompileOutputEth, PrecompileHaltReason> - PrecompileResult = Result<PrecompileOutput, PrecompileError> * refactor(precompile): replace Option<PrecompileHalt> with PrecompileStatus enum Introduce PrecompileStatus enum (Success, Revert, Halt) to replace the Option<PrecompileHalt> field in PrecompileOutput. Add PrecompileEthFn type alias for legacy precompile function signatures, with Precompile::execute() now returning PrecompileOutput via a From<PrecompileEthResult> conversion. Add helper methods: is_success, is_revert, halt_reason, into_halt_reason. * refactor(handler): extract precompile_output_to_interpreter_result helper Add a standalone function that converts PrecompileOutput into InterpreterResult, and use it in EthPrecompiles::run. Fmt and clippy fixes. * refactor(precompile): thread reservoir through PrecompileOutput and revert execute signature - Add `reservoir` parameter to PrecompileOutput::new, halt, revert constructors - Add `from_eth_result` constructor that takes reservoir - Revert Precompile::execute to return PrecompileEthResult - Move PrecompileOutput conversion to call site in EthPrecompiles - Remove unused into_halt_reason and From<PrecompileEthResult> impl * refactor(precompile): change Precompile fn_ from PrecompileEthFn to PrecompileFn (bluealloy/revm#3546) Change the Precompile struct's fn_ field from PrecompileEthFn (fn(&[u8], u64) -> PrecompileEthResult) to PrecompileFn (fn(&[u8], u64, u64) -> PrecompileOutput) so precompiles receive reservoir directly and return PrecompileOutput. Add call_eth_precompile helper to wrap existing PrecompileEthFn implementations into the new PrecompileFn signature. Each precompile module gets a thin wrapper using this helper. * refactor(precompile): add eth_precompile_fn! macro to eliminate wrapper boilerplate Replace 17 hand-written PrecompileEthFn-to-PrecompileFn wrapper functions with the new eth_precompile_fn! macro that generates them uniformly. * refactor(op-revm): use eth_precompile_fn! macro for precompile wrappers Replace 8 hand-written wrapper functions in op-revm with the eth_precompile_fn! macro. * style: apply cargo fmt * Rename Eth precompile structs * fix(op-revm): add missing reservoir argument to execute calls in tests * refactor(precompile): move reservoir into PrecompileOutput and add PrecompileStatus helpers PrecompileOutput now carries reservoir and state_gas_used, so precompile_output_to_interpreter_result no longer needs a separate reservoir parameter. Added convenience methods on PrecompileStatus and fixed unused import warning. * fix: update bench and test code for new Precompile API Update eip2537 benchmarks to use Precompile::execute() instead of removed precompile() method, and fix manual div_ceil clippy warning. * docs: fix ResultGas doc comments to match current API Remove references to removed getters (limit, spent, intrinsic_gas, remaining) and update table and derived values to reflect the actual methods (total_gas_spent, tx_gas_used, block_regular_gas_used, etc.). * docs: fix broken doc links and typos in ResultGas - Fix unresolved links to non-existent `regular_gas_spent` and `with_regular_gas_spent` methods in deprecation notes - Fix typos: "substract" → "subtract", "enought" → "enough" - Fix stale comments referencing `regular_gas_spent` field * feat: track state_gas_spent and reservoir in GasInspector * fix: validate max(intrinsic_gas, floor_gas) against TX_MAX_GAS_LIMIT cap * chore: add must_use annotations to gas recording methods and minor cleanups * refactor: update custom_precompile_journal example to use PrecompileOutput API Use `PrecompileOutput::from_eth_result` and `precompile_output_to_interpreter_result` instead of manually constructing InterpreterResult, aligning with the new gas handling. * refactor: make Precompile::execute return Result<PrecompileOutput, PrecompileError> Distinguish fatal/unrecoverable precompile errors from non-fatal halts by returning a Result. PrecompileFn signature updated accordingly, and all call sites now propagate or unwrap the error. * chore: bump MSRV to 1.91 in CI * fix: exclude EIP-8037 reservoir gas from op-revm fee settlement The core revm handler already excludes reservoir gas when calculating beneficiary rewards and caller reimbursement, but op-revm's fee settlement did not account for it. Once Osaka/EIP-8037 is enabled, transactions with leftover reservoir gas would over-credit BASE_FEE_RECIPIENT and OPERATOR_FEE_RECIPIENT, and under-refund operator fees to the caller. - reward_beneficiary: use effective_used (gas.used() - reservoir) for base_fee_amount and operator_fee_cost calculations - operator_fee_refund: include gas.reservoir() when computing gas_used so the refund correctly covers unused reservoir gas - handler validation: use initial_regular_gas (excluding state gas) when checking floor_gas against TX_MAX_GAS_LIMIT cap * refactor: move EIP-8037 gas cap validation into validate_initial_tx_gas (bluealloy/revm#3552) Extract `initial_regular_gas()` helper on InitialAndFloorGas and move the EIP-8037 TX_MAX_GAS_LIMIT validation from the handler into `validate_initial_tx_gas`, consolidating validation logic. Also clean up formatting across precompile and op-revm crates. * chore: release (bluealloy/revm#3472) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore: v106 release prep (bluealloy/revm#3554) * docs: add v106 migration guide entry * chore: v106 release prep — version bumps and changelogs * add revm version * fix(op-revm): update validate_against_state_and_deduct_caller signature Adds the new `&mut InitialAndFloorGas` parameter introduced in ethereum-optimism#3577 to the OpHandler trait impl, and passes `&mut Default::default()` at all test call sites. * chore: v107 release prep * chore(op-revm): fmt --------- Co-authored-by: rakita <rakita@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: rakita <dragan0rakita@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements EIP-8037 (TIP-1016) dual-limit gas accounting, separating execution gas (CPU work) from state gas (storage/account creation, code deposit). This introduces a reservoir model where state gas draws from a separate pool before spilling into regular gas.
Key Changes
interpreter/gas.rs): Addedreservoir,regular_gas_budget,state_gas_spentfields withrecord_state_cost()that draws from reservoir first, then spills to regular gas.gas_left()excludes reservoir per EIP-8037.context/interface/cfg/gas_params.rs): New state gas cost IDs —sstore_set_state_gas,new_account_state_gas,code_deposit_state_gas,create_state_gas.SSTORE— new slot creation (host.rs)CREATE/CREATE2— contract creation (contract.rs)Code deposit— per-byte code storage (frame.rs)CALL— new account with value transfer (call_helpers.rs)SELFDESTRUCT— new beneficiary account (host.rs)handler/frame.rs): Child frame state gas accumulates to parent on success; reservoir refill on halt/revert prevents state gas from permanently consuming regular gas budget.record_regular_cost()— no state gas for precompiles.system.rs): Returnsremaining - reservoirper spec.last_frame_result.Architecture
remaining=gas_left+reservoir;regular_gas_budgetis implicit cap for regular gasgas_leftonly viarecord_cost(), cannot draw from reservoirreservoirfirst viarecord_state_cost(), spills togas_leftwhen exhaustedgas_left()only, excluding the state gas reservoirregular_gas_budgetandreservoirdirectlyCfgEnvCfg Integration
Cfg::state_gas_ids()returnsOption<GasParams>—Nonedisables state gas (backward compatible).CfgEnvstores state gas config behind#[cfg(feature = "optional_state_gas_params")].Test Suite
35+ test vectors in
crates/ee-tests/src/eip8037.rscovering 7 categories:Test Plan
cargo build --workspacepassescargo nextest run --workspace— all tests passcargo nextest run -p ee-tests— all 35+ EIP-8037 tests passcargo clippy --workspace --all-targets --all-features— cleancargo fmt --all— cleanno_stdcompatibility checkrevme statetest