Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ee5f210
feat: implement EIP-8037 state gas (reservoir model)
rakita Mar 13, 2026
d154685
Merge remote-tracking branch 'origin/main' into state-gas
rakita Mar 13, 2026
556602b
rm statetest jsons
rakita Mar 13, 2026
1e9fe4a
Rename spent to regular_gas_spent in ResultGas and simplify reservoir…
rakita Mar 16, 2026
257d261
Simplify EIP-8037 state gas: remove redundant fields from ResultGas a…
rakita Mar 18, 2026
4534164
Move EIP-7702 state gas refund split into pre_execution
rakita Mar 18, 2026
7a826db
Add PrecompileFailure type for state gas propagation on error
rakita Mar 18, 2026
a9b3634
Merge remote-tracking branch 'origin/main' into state-gas
rakita Mar 18, 2026
03b7c8d
Convert precompile errors to PrecompileFailure via .into()
rakita Mar 18, 2026
72b376c
Fix EIP-8037 state gas accounting regression
rakita Mar 18, 2026
3a29466
Move state gas deduction into first_frame_input
rakita Mar 18, 2026
b6ccdff
Fix EIP-8037 gas accounting: floor gas, reservoir, inspector, validation
rakita Mar 21, 2026
242243f
Fix EIP-8037 reservoir accounting: tx_gas_used, child revert refunds,…
rakita Mar 22, 2026
712dac7
Fix CREATE/CREATE2 static call check ordering for EIP-8037 state gas …
rakita Mar 22, 2026
7a999fc
Simplify last_frame_result: remove initial_reservoir parameter and fi…
rakita Mar 23, 2026
5f94ddb
Revert CREATE/CREATE2 static call check to before gas charging
rakita Mar 24, 2026
1e1d64d
Fix reservoir handling: restore state gas on revert/halt
rakita Mar 24, 2026
5001497
Fix EIP-8037 state_gas_spent accounting for EIP-7702 auth list
rakita Mar 24, 2026
319fe0a
Merge remote-tracking branch 'origin/main' into rakita/state-gas
rakita Mar 24, 2026
5aef2ce
Improve precompile reservoir handling and apply formatting fixes
rakita Mar 24, 2026
033d0ef
Fix precompile_provider compilation errors against current precompile…
rakita Mar 25, 2026
81d3b17
Simplify precompile interface: remove gas_limit from PrecompileOutput…
rakita Mar 25, 2026
94e0305
Fix EIP-8037 reservoir refill tests and complete precompile interface…
rakita Mar 25, 2026
03986fc
temporary remove all ee-tests
rakita Mar 25, 2026
9d5d898
Fix state gas accounting: include reservoir in total_gas_spent and pr…
rakita Mar 25, 2026
f77d65f
Apply formatting fixes and use saturating arithmetic for state gas spent
rakita Mar 25, 2026
ef18e6a
Revert "temporary remove all ee-tests"
rakita Mar 25, 2026
be396f7
Fix total_gas_spent to exclude reservoir and use saturating arithmetic
rakita Mar 25, 2026
7e01885
Use build_result_gas helper in system call gas handling
rakita Mar 27, 2026
4b975de
Merge remote-tracking branch 'origin/main' into rakita/state-gas
rakita Mar 27, 2026
8891655
bump rust to 1.91
rakita Mar 29, 2026
44a4233
Remove redundant gas_limit from Gas, delegate to GasTracker
rakita Apr 1, 2026
2cffb90
refactor(precompile): restructure PrecompileOutput for state gas and …
rakita Apr 7, 2026
a357a60
Merge remote-tracking branch 'origin/main' into rakita/state-gas
rakita Apr 7, 2026
d7267ab
refactor(precompile): move reservoir into PrecompileOutput and add Pr…
rakita Apr 7, 2026
28c73bb
fix: update bench and test code for new Precompile API
rakita Apr 8, 2026
d3b4555
docs: fix ResultGas doc comments to match current API
rakita Apr 9, 2026
74b2541
docs: fix broken doc links and typos in ResultGas
rakita Apr 9, 2026
0064a83
Merge remote-tracking branch 'origin/main' into state-gas
rakita Apr 9, 2026
99b3589
feat: track state_gas_spent and reservoir in GasInspector
rakita Apr 9, 2026
c8460de
fix: validate max(intrinsic_gas, floor_gas) against TX_MAX_GAS_LIMIT cap
rakita Apr 9, 2026
df67e80
chore: add must_use annotations to gas recording methods and minor cl…
rakita Apr 9, 2026
0634dfa
refactor: update custom_precompile_journal example to use PrecompileO…
rakita Apr 9, 2026
577c903
refactor: make Precompile::execute return Result<PrecompileOutput, Pr…
rakita Apr 9, 2026
3f340d8
chore: bump MSRV to 1.91 in CI
rakita Apr 9, 2026
f410840
fix: exclude EIP-8037 reservoir gas from op-revm fee settlement
rakita Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions crates/context/interface/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ pub trait Cfg {

/// Returns the gas params for the EVM.
fn gas_params(&self) -> &GasParams;

/// Returns whether EIP-8037 (Amsterdam) state creation gas cost increase is enabled.
///
/// When enabled, storage creation gas is tracked separately from regular gas
/// via the reservoir model. EIP-8037 specifies concrete gas values based on
/// `cost_per_state_byte` and adds a hash cost for deployed bytecode.
fn is_amsterdam_eip8037_enabled(&self) -> bool;
}

/// What bytecode analysis to perform
Expand Down
170 changes: 167 additions & 3 deletions crates/context/interface/src/cfg/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,149 @@
use crate::{cfg::GasParams, transaction::AccessListItemTr as _, Transaction, TransactionType};
use primitives::hardfork::SpecId;

/// Tracker for gas during execution.
///
/// This is used to track the gas during execution.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct GasTracker {
/// Gas Limit,
gas_limit: u64,
/// Regular gas remaining (`gas_left`). Reservoir is tracked separately.
remaining: u64,
/// State gas reservoir (gas exceeding TX_MAX_GAS_LIMIT). Starts as `execution_gas - min(execution_gas, regular_gas_budget)`.
/// When 0, all remaining gas is regular gas with hard cap at `TX_MAX_GAS_LIMIT`.
reservoir: u64,
/// Total state gas spent so far.
state_gas_spent: u64,
/// Refunded gas. Used to refund the gas to the caller at the end of execution.
refunded: i64,
}

impl GasTracker {
/// Creates a new `GasTracker` with the given remaining gas and reservoir.
#[inline]
pub const fn new(gas_limit: u64, remaining: u64, reservoir: u64) -> Self {
Self {
gas_limit,
remaining,
reservoir,
state_gas_spent: 0,
refunded: 0,
}
}

/// Creates a new `GasTracker` with the given used gas and reservoir.
#[inline]
pub const fn new_used_gas(gas_limit: u64, used_gas: u64, reservoir: u64) -> Self {
Self::new(gas_limit, gas_limit - used_gas, reservoir)
}

/// Returns the remaining gas.
#[inline]
pub const fn remaining(&self) -> u64 {
self.remaining
}

/// Sets the remaining gas.
#[inline]
pub fn set_remaining(&mut self, val: u64) {
self.remaining = val;
}

/// Returns the reservoir gas.
#[inline]
pub const fn reservoir(&self) -> u64 {
self.reservoir
}

/// Sets the reservoir gas.
#[inline]
pub fn set_reservoir(&mut self, val: u64) {
self.reservoir = val;
}

/// Returns the state gas spent.
#[inline]
pub const fn state_gas_spent(&self) -> u64 {
self.state_gas_spent
}

/// Sets the state gas spent.
#[inline]
pub fn set_state_gas_spent(&mut self, val: u64) {
self.state_gas_spent = val;
}

/// Returns the refunded gas.
#[inline]
pub const fn refunded(&self) -> i64 {
self.refunded
}

/// Sets the refunded gas.
#[inline]
pub fn set_refunded(&mut self, val: i64) {
self.refunded = val;
}

/// Records a regular gas cost.
///
/// Deducts from `remaining`. Returns `false` if insufficient gas.
#[inline]
#[must_use]
pub fn record_regular_cost(&mut self, cost: u64) -> bool {
if let Some(new_remaining) = self.remaining.checked_sub(cost) {
self.remaining = new_remaining;
return true;
}
false
}

/// Records a state gas cost (EIP-8037 reservoir model).
///
/// State gas charges deduct from the reservoir first. If the reservoir is exhausted,
/// remaining charges spill into `remaining` (requiring `remaining >= cost`).
/// Tracks state gas spent.
///
/// Returns `false` if total remaining gas is insufficient.
#[inline]
pub fn record_state_cost(&mut self, cost: u64) -> bool {
if self.reservoir >= cost {
self.state_gas_spent = self.state_gas_spent.saturating_add(cost);
self.reservoir -= cost;
return true;
}

let spill = cost - self.reservoir;

let success = self.record_regular_cost(spill);
if success {
self.state_gas_spent = self.state_gas_spent.saturating_add(cost);
self.reservoir = 0;
}
success
}

/// Records a refund value.
#[inline]
pub fn record_refund(&mut self, refund: i64) {
self.refunded += refund;
}

/// Erases a gas cost from remaining (returns gas from child frame).
#[inline]
pub fn erase_cost(&mut self, returned: u64) {
self.remaining += returned;
}

/// Spends all remaining gas excluding the reservoir.
#[inline]
pub fn spend_all(&mut self) {
self.remaining = 0;
}
}

/// Gas cost for operations that consume zero gas.
pub const ZERO: u64 = 0;
/// Base gas cost for basic operations.
Expand Down Expand Up @@ -112,7 +255,13 @@ pub const CALL_STIPEND: u64 = 2300;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InitialAndFloorGas {
/// Initial gas for transaction.
pub initial_gas: u64,
pub initial_total_gas: u64,
Comment thread
rakita marked this conversation as resolved.
/// State gas component of initial_gas (subset of initial_total_gas).
/// Under EIP-8037, this includes:
/// - EIP-7702 auth list state gas (per-auth account creation + metadata costs)
/// - For CREATE transactions: `create_state_gas` (account creation + contract metadata)
/// - For CALL transactions: 0 (state gas is unpredictable at validation time)
pub initial_state_gas: u64,
/// If transaction is a Call and Prague is enabled
/// floor_gas is at least amount of gas that is going to be spent.
pub floor_gas: u64,
Expand All @@ -121,9 +270,24 @@ pub struct InitialAndFloorGas {
impl InitialAndFloorGas {
/// Create a new InitialAndFloorGas instance.
#[inline]
pub const fn new(initial_gas: u64, floor_gas: u64) -> Self {
pub const fn new(initial_total_gas: u64, floor_gas: u64) -> Self {
Self {
initial_total_gas,
initial_state_gas: 0,
floor_gas,
}
}

/// Create a new InitialAndFloorGas instance with state gas tracking.
#[inline]
pub const fn new_with_state_gas(
initial_total_gas: u64,
initial_state_gas: u64,
floor_gas: u64,
) -> Self {
Self {
initial_gas,
initial_total_gas,
initial_state_gas,
floor_gas,
}
}
Expand Down
Loading
Loading