From 275c7bd533dd60e5b5f9b690e4c2c877b9eb78ca Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Fri, 5 Sep 2025 17:17:04 +0300 Subject: [PATCH 01/10] feat: :sparkles: add EIP-7623 floor cost --- src/standard/config.rs | 10 ++++++++++ src/standard/gasometer/mod.rs | 30 ++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/standard/config.rs b/src/standard/config.rs index a7a65d39..e9cdd50f 100644 --- a/src/standard/config.rs +++ b/src/standard/config.rs @@ -364,6 +364,16 @@ impl Config { } } + /// Floor gas paid for zero data in a transaction. + pub fn gas_floor_transaction_zero_data(&self) -> u64 { + 10 + } + + /// Floor gas paid for non-zero data in a transaction. + pub fn gas_floor_transaction_non_zero_data(&self) -> u64 { + 40 + } + /// Gas paid per address in transaction access list (see EIP-2930). pub fn gas_access_list_address(&self) -> u64 { if self.eip2930_access_list { 2400 } else { 0 } diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index 0cb7ce3f..5fe64a06 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -19,6 +19,7 @@ pub struct GasometerState { gas_limit: u64, memory_gas: u64, used_gas: u64, + floor_gas: u64, refunded_gas: i64, /// Whether the gasometer is in static context. pub is_static: bool, @@ -99,6 +100,7 @@ impl GasometerState { gas_limit, memory_gas: 0, used_gas: 0, + floor_gas: 0, refunded_gas: 0, is_static, } @@ -118,9 +120,10 @@ impl GasometerState { }; let mut s = Self::new(gas_limit, false); - let transaction_cost = TransactionCost::call(data, access_list).cost(config); + let (transaction_cost, floor_cost) = TransactionCost::call(data, access_list).cost(config); s.record_gas64(transaction_cost)?; + s.floor_gas = floor_cost; Ok(s) } @@ -138,9 +141,11 @@ impl GasometerState { }; let mut s = Self::new(gas_limit, false); - let transaction_cost = TransactionCost::create(code, access_list).cost(config); + let (transaction_cost, floor_cost) = + TransactionCost::create(code, access_list).cost(config); s.record_gas64(transaction_cost)?; + s.floor_gas = floor_cost; Ok(s) } @@ -202,6 +207,11 @@ impl GasometerState { MergeStrategy::Discard => {} } } + + /// Apply the floor gas cost for calldata as defined in EIP-7623 + pub fn apply_transaction_floor_cost(&mut self) { + self.used_gas = max(self.used_gas, self.floor_gas); + } } /// The eval function of the entire gasometer. @@ -969,7 +979,7 @@ impl TransactionCost { } } - pub fn cost(&self, config: &Config) -> u64 { + pub fn cost(&self, config: &Config) -> (u64, u64) { match self { TransactionCost::Call { zero_data_len, @@ -977,14 +987,17 @@ impl TransactionCost { access_list_address_len, access_list_storage_len, } => { - #[deny(clippy::let_and_return)] let cost = config.gas_transaction_call() + *zero_data_len as u64 * config.gas_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_transaction_non_zero_data() + *access_list_address_len as u64 * config.gas_access_list_address() + *access_list_storage_len as u64 * config.gas_access_list_storage_key(); - cost + let floor = config.gas_transaction_call() + + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() + + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); + + (cost, floor) } TransactionCost::Create { zero_data_len, @@ -998,11 +1011,16 @@ impl TransactionCost { + *non_zero_data_len as u64 * config.gas_transaction_non_zero_data() + *access_list_address_len as u64 * config.gas_access_list_address() + *access_list_storage_len as u64 * config.gas_access_list_storage_key(); + if config.max_initcode_size().is_some() { cost += initcode_cost; } - cost + let floor = config.gas_transaction_call() + + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() + + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); + + (cost, floor) } } } From 578ddd00251e1e434416d6e29b381bbc5a7de04f Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Fri, 5 Sep 2025 17:39:41 +0300 Subject: [PATCH 02/10] refactor: :recycle: use struct instead of tuple for transaction costs --- src/standard/gasometer/mod.rs | 45 +++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index 5fe64a06..a670da9f 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -79,6 +79,13 @@ impl GasometerState { Ok(()) } + /// Record real and floor costs of a transaction. + pub fn records_transaction_cost(&mut self, cost: TransactionCost) -> Result<(), ExitError> { + self.record_gas64(cost.real)?; + self.floor_gas = cost.floor; + Ok(()) + } + /// Set memory gas usage. pub fn set_memory_gas(&mut self, memory_cost: u64) -> Result<(), ExitError> { let all_gas_cost = self.used_gas.checked_add(memory_cost); @@ -120,10 +127,9 @@ impl GasometerState { }; let mut s = Self::new(gas_limit, false); - let (transaction_cost, floor_cost) = TransactionCost::call(data, access_list).cost(config); + let cost = Transaction::call(data, access_list).cost(config); - s.record_gas64(transaction_cost)?; - s.floor_gas = floor_cost; + s.records_transaction_cost(cost)?; Ok(s) } @@ -141,11 +147,9 @@ impl GasometerState { }; let mut s = Self::new(gas_limit, false); - let (transaction_cost, floor_cost) = - TransactionCost::create(code, access_list).cost(config); + let cost = Transaction::create(code, access_list).cost(config); - s.record_gas64(transaction_cost)?; - s.floor_gas = floor_cost; + s.records_transaction_cost(cost)?; Ok(s) } @@ -923,7 +927,7 @@ impl MemoryCost { /// Transaction cost. #[derive(Debug, Clone, Copy)] -enum TransactionCost { +enum Transaction { /// Call transaction cost. Call { /// Length of zeros in transaction data. @@ -950,13 +954,18 @@ enum TransactionCost { }, } -impl TransactionCost { - pub fn call(data: &[u8], access_list: &[(H160, Vec)]) -> TransactionCost { +pub struct TransactionCost { + real: u64, + floor: u64, +} + +impl Transaction { + pub fn call(data: &[u8], access_list: &[(H160, Vec)]) -> Transaction { let zero_data_len = data.iter().filter(|v| **v == 0).count(); let non_zero_data_len = data.len() - zero_data_len; let (access_list_address_len, access_list_storage_len) = count_access_list(access_list); - TransactionCost::Call { + Transaction::Call { zero_data_len, non_zero_data_len, access_list_address_len, @@ -964,13 +973,13 @@ impl TransactionCost { } } - pub fn create(data: &[u8], access_list: &[(H160, Vec)]) -> TransactionCost { + pub fn create(data: &[u8], access_list: &[(H160, Vec)]) -> Transaction { let zero_data_len = data.iter().filter(|v| **v == 0).count(); let non_zero_data_len = data.len() - zero_data_len; let (access_list_address_len, access_list_storage_len) = count_access_list(access_list); let initcode_cost = init_code_cost(data.len()); - TransactionCost::Create { + Transaction::Create { zero_data_len, non_zero_data_len, access_list_address_len, @@ -979,9 +988,9 @@ impl TransactionCost { } } - pub fn cost(&self, config: &Config) -> (u64, u64) { + pub fn cost(&self, config: &Config) -> TransactionCost { match self { - TransactionCost::Call { + Transaction::Call { zero_data_len, non_zero_data_len, access_list_address_len, @@ -997,9 +1006,9 @@ impl TransactionCost { + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); - (cost, floor) + TransactionCost { real: cost, floor } } - TransactionCost::Create { + Transaction::Create { zero_data_len, non_zero_data_len, access_list_address_len, @@ -1020,7 +1029,7 @@ impl TransactionCost { + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); - (cost, floor) + TransactionCost { real: cost, floor } } } } From d4282ec7b304c64ab5fc17f7aa4f595aa69f96dc Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Fri, 5 Sep 2025 17:41:57 +0300 Subject: [PATCH 03/10] refactor: :recycle: rename "real" to "used" --- src/standard/gasometer/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index a670da9f..c7afec66 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -79,9 +79,9 @@ impl GasometerState { Ok(()) } - /// Record real and floor costs of a transaction. + /// Record used and floor costs of a transaction. pub fn records_transaction_cost(&mut self, cost: TransactionCost) -> Result<(), ExitError> { - self.record_gas64(cost.real)?; + self.record_gas64(cost.used)?; self.floor_gas = cost.floor; Ok(()) } @@ -955,7 +955,7 @@ enum Transaction { } pub struct TransactionCost { - real: u64, + used: u64, floor: u64, } @@ -996,7 +996,7 @@ impl Transaction { access_list_address_len, access_list_storage_len, } => { - let cost = config.gas_transaction_call() + let used = config.gas_transaction_call() + *zero_data_len as u64 * config.gas_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_transaction_non_zero_data() + *access_list_address_len as u64 * config.gas_access_list_address() @@ -1006,7 +1006,7 @@ impl Transaction { + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); - TransactionCost { real: cost, floor } + TransactionCost { used, floor } } Transaction::Create { zero_data_len, @@ -1015,21 +1015,21 @@ impl Transaction { access_list_storage_len, initcode_cost, } => { - let mut cost = config.gas_transaction_create() + let mut used = config.gas_transaction_create() + *zero_data_len as u64 * config.gas_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_transaction_non_zero_data() + *access_list_address_len as u64 * config.gas_access_list_address() + *access_list_storage_len as u64 * config.gas_access_list_storage_key(); if config.max_initcode_size().is_some() { - cost += initcode_cost; + used += initcode_cost; } let floor = config.gas_transaction_call() + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); - TransactionCost { real: cost, floor } + TransactionCost { used, floor } } } } From 5d4183b6ee51947e55dff40e905f0c7d3e92c71c Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Fri, 5 Sep 2025 17:48:15 +0300 Subject: [PATCH 04/10] revert: :rewind: keep older name --- src/standard/gasometer/mod.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index c7afec66..598e54c1 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -80,7 +80,7 @@ impl GasometerState { } /// Record used and floor costs of a transaction. - pub fn records_transaction_cost(&mut self, cost: TransactionCost) -> Result<(), ExitError> { + pub fn records_transaction_cost(&mut self, cost: TransactionGas) -> Result<(), ExitError> { self.record_gas64(cost.used)?; self.floor_gas = cost.floor; Ok(()) @@ -127,7 +127,7 @@ impl GasometerState { }; let mut s = Self::new(gas_limit, false); - let cost = Transaction::call(data, access_list).cost(config); + let cost = TransactionCost::call(data, access_list).cost(config); s.records_transaction_cost(cost)?; Ok(s) @@ -147,7 +147,7 @@ impl GasometerState { }; let mut s = Self::new(gas_limit, false); - let cost = Transaction::create(code, access_list).cost(config); + let cost = TransactionCost::create(code, access_list).cost(config); s.records_transaction_cost(cost)?; Ok(s) @@ -927,7 +927,7 @@ impl MemoryCost { /// Transaction cost. #[derive(Debug, Clone, Copy)] -enum Transaction { +enum TransactionCost { /// Call transaction cost. Call { /// Length of zeros in transaction data. @@ -954,18 +954,18 @@ enum Transaction { }, } -pub struct TransactionCost { +pub struct TransactionGas { used: u64, floor: u64, } -impl Transaction { - pub fn call(data: &[u8], access_list: &[(H160, Vec)]) -> Transaction { +impl TransactionCost { + pub fn call(data: &[u8], access_list: &[(H160, Vec)]) -> TransactionCost { let zero_data_len = data.iter().filter(|v| **v == 0).count(); let non_zero_data_len = data.len() - zero_data_len; let (access_list_address_len, access_list_storage_len) = count_access_list(access_list); - Transaction::Call { + TransactionCost::Call { zero_data_len, non_zero_data_len, access_list_address_len, @@ -973,13 +973,13 @@ impl Transaction { } } - pub fn create(data: &[u8], access_list: &[(H160, Vec)]) -> Transaction { + pub fn create(data: &[u8], access_list: &[(H160, Vec)]) -> TransactionCost { let zero_data_len = data.iter().filter(|v| **v == 0).count(); let non_zero_data_len = data.len() - zero_data_len; let (access_list_address_len, access_list_storage_len) = count_access_list(access_list); let initcode_cost = init_code_cost(data.len()); - Transaction::Create { + TransactionCost::Create { zero_data_len, non_zero_data_len, access_list_address_len, @@ -988,9 +988,9 @@ impl Transaction { } } - pub fn cost(&self, config: &Config) -> TransactionCost { + pub fn cost(&self, config: &Config) -> TransactionGas { match self { - Transaction::Call { + TransactionCost::Call { zero_data_len, non_zero_data_len, access_list_address_len, @@ -1006,9 +1006,9 @@ impl Transaction { + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); - TransactionCost { used, floor } + TransactionGas { used, floor } } - Transaction::Create { + TransactionCost::Create { zero_data_len, non_zero_data_len, access_list_address_len, @@ -1029,7 +1029,7 @@ impl Transaction { + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); - TransactionCost { used, floor } + TransactionGas { used, floor } } } } From bb70b3839a83256d26c27a84cab61f96ecd8d189 Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Fri, 5 Sep 2025 18:09:43 +0300 Subject: [PATCH 05/10] feat: :sparkles: add EIP-7623 to Prague config --- src/standard/config.rs | 14 ++++++++++++-- src/standard/invoker/mod.rs | 7 +++++++ src/standard/invoker/state.rs | 2 ++ src/standard/mod.rs | 4 ++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/standard/config.rs b/src/standard/config.rs index e9cdd50f..4a3fd78c 100644 --- a/src/standard/config.rs +++ b/src/standard/config.rs @@ -86,6 +86,8 @@ pub struct Config { pub eip4844_shard_blob: bool, /// EIP-7516: Blob base fee per gas. pub eip7516_blob_base_fee: bool, + /// EIP-7623: Increase calldata cost with floor. + pub eip7623_calldata_floor: bool, } impl Config { @@ -132,6 +134,7 @@ impl Config { eip2930_access_list: false, eip4844_shard_blob: false, eip7516_blob_base_fee: false, + eip7623_calldata_floor: false, } } @@ -237,6 +240,13 @@ impl Config { config } + /// Prague + pub const fn prague() -> Config { + let mut config = Self::cancun(); + config.eip7623_calldata_floor = true; + config + } + /// Gas paid for extcode. pub fn gas_ext_code(&self) -> u64 { if self.eip150_gas_increase { 700 } else { 20 } @@ -366,12 +376,12 @@ impl Config { /// Floor gas paid for zero data in a transaction. pub fn gas_floor_transaction_zero_data(&self) -> u64 { - 10 + if self.eip7623_calldata_floor { 10 } else { 0 } } /// Floor gas paid for non-zero data in a transaction. pub fn gas_floor_transaction_non_zero_data(&self) -> u64 { - 40 + if self.eip7623_calldata_floor { 40 } else { 0 } } /// Gas paid per address in transaction access list (see EIP-2930). diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index 9c7d5aca..3563c5f0 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -510,6 +510,13 @@ where let result = work(); + // Apply EIP-7623 floor cost before calculating effective gas + if let Some(substate) = exit.substate.as_mut() + && invoke.config.eip7623_calldata_floor + { + substate.apply_transaction_floor_cost(); + } + let effective_gas = match result { Ok(_) => exit .substate diff --git a/src/standard/invoker/state.rs b/src/standard/invoker/state.rs index cfab1072..c22237a9 100644 --- a/src/standard/invoker/state.rs +++ b/src/standard/invoker/state.rs @@ -47,4 +47,6 @@ pub trait InvokerState: GasState + Sized { fn is_static(&self) -> bool; /// Effective gas. The final used gas as reported by the transaction. fn effective_gas(&self, with_refund: bool) -> U256; + /// Apply transaction floor cost for EIP-7623. + fn apply_transaction_floor_cost(&mut self); } diff --git a/src/standard/mod.rs b/src/standard/mod.rs index ce54eee1..8369e2a6 100644 --- a/src/standard/mod.rs +++ b/src/standard/mod.rs @@ -230,4 +230,8 @@ impl<'config> InvokerState for State<'config> { fn effective_gas(&self, with_refund: bool) -> U256 { self.gasometer.effective_gas(with_refund, self.config) } + + fn apply_transaction_floor_cost(&mut self) { + self.gasometer.apply_transaction_floor_cost() + } } From df6458efd61b3e8da70ac0edb1a08f763d9a59b0 Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Fri, 5 Sep 2025 18:16:25 +0300 Subject: [PATCH 06/10] fix: :bug: add check for floor requirement --- src/standard/gasometer/mod.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index 598e54c1..36060940 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -126,9 +126,14 @@ impl GasometerState { gas_limit.as_u64() }; - let mut s = Self::new(gas_limit, false); let cost = TransactionCost::call(data, access_list).cost(config); + // EIP-7623: Check if gas limit meets the floor requirement + if config.eip7623_calldata_floor && gas_limit < cost.floor { + return Err(ExitException::OutOfGas.into()); + } + + let mut s = Self::new(gas_limit, false); s.records_transaction_cost(cost)?; Ok(s) } @@ -146,9 +151,14 @@ impl GasometerState { gas_limit.as_u64() }; - let mut s = Self::new(gas_limit, false); let cost = TransactionCost::create(code, access_list).cost(config); + // EIP-7623: Check if gas limit meets the floor requirement + if config.eip7623_calldata_floor && gas_limit < cost.floor { + return Err(ExitException::OutOfGas.into()); + } + + let mut s = Self::new(gas_limit, false); s.records_transaction_cost(cost)?; Ok(s) } From c1f288aa0893251a0806e3441962b75f9eec42de Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Fri, 5 Sep 2025 18:27:30 +0300 Subject: [PATCH 07/10] test: :test_tube: add pectra tests --- jsontests/src/run.rs | 1 + jsontests/src/types.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/jsontests/src/run.rs b/jsontests/src/run.rs index 1222dc5b..88905556 100644 --- a/jsontests/src/run.rs +++ b/jsontests/src/run.rs @@ -190,6 +190,7 @@ pub fn run_test( Fork::London => Config::london(), Fork::Shanghai => Config::shanghai(), Fork::Cancun => Config::cancun(), + Fork::Prague => Config::prague(), _ => return Err(Error::UnsupportedFork), }; config_change(&mut config); diff --git a/jsontests/src/types.rs b/jsontests/src/types.rs index 40dfbc76..160991e2 100644 --- a/jsontests/src/types.rs +++ b/jsontests/src/types.rs @@ -166,6 +166,7 @@ pub enum Fork { Paris, Berlin, Cancun, + Prague, London, Merge, Shanghai, From 12ebc1954992f525d772aec7a63d631eea53c226 Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Mon, 8 Sep 2025 12:07:38 +0300 Subject: [PATCH 08/10] refactor: :recycle: add with_floor parameter to effective_gas --- src/standard/gasometer/mod.rs | 33 ++++++++++++++------------------- src/standard/invoker/mod.rs | 11 ++--------- src/standard/invoker/state.rs | 4 +--- src/standard/mod.rs | 9 +++------ 4 files changed, 20 insertions(+), 37 deletions(-) diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index 36060940..5f522d5e 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -3,7 +3,7 @@ mod costs; mod utils; use alloc::vec::Vec; -use core::cmp::{max, min}; +use core::cmp::max; use evm_interpreter::{ Control, ExitError, ExitException, Machine, Opcode, Stack, @@ -166,23 +166,23 @@ impl GasometerState { /// The effective used gas at the end of the transaction. /// /// In case of revert, refunded gas are not taken into account. - pub fn effective_gas(&self, with_refund: bool, config: &Config) -> U256 { - let refunded_gas = if self.refunded_gas >= 0 { - self.refunded_gas as u64 + pub fn effective_gas(&self, with_refund: bool, with_floor: bool, config: &Config) -> U256 { + let refunded_gas = self.refunded_gas.max(0) as u64; + + let used_gas = if with_refund { + let max_refund = self.total_used_gas() / config.max_refund_quotient(); + self.total_used_gas() - refunded_gas.min(max_refund) } else { - 0 + self.total_used_gas() }; - U256::from(if with_refund { - self.gas_limit - - (self.total_used_gas() - - min( - self.total_used_gas() / config.max_refund_quotient(), - refunded_gas, - )) + let used_gas = if with_floor { + used_gas.max(self.floor_gas) } else { - self.gas_limit - self.total_used_gas() - }) + used_gas + }; + + U256::from(self.gas_limit - used_gas) } /// Create a submeter. @@ -221,11 +221,6 @@ impl GasometerState { MergeStrategy::Discard => {} } } - - /// Apply the floor gas cost for calldata as defined in EIP-7623 - pub fn apply_transaction_floor_cost(&mut self) { - self.used_gas = max(self.used_gas, self.floor_gas); - } } /// The eval function of the entire gasometer. diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index 3563c5f0..82aa94b4 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -510,23 +510,16 @@ where let result = work(); - // Apply EIP-7623 floor cost before calculating effective gas - if let Some(substate) = exit.substate.as_mut() - && invoke.config.eip7623_calldata_floor - { - substate.apply_transaction_floor_cost(); - } - let effective_gas = match result { Ok(_) => exit .substate .as_ref() - .map(|s| s.effective_gas(true)) + .map(|s| s.effective_gas(true, invoke.config.eip7623_calldata_floor)) .unwrap_or_default(), Err(ExitError::Reverted) => exit .substate .as_ref() - .map(|s| s.effective_gas(false)) + .map(|s| s.effective_gas(false, invoke.config.eip7623_calldata_floor)) .unwrap_or_default(), Err(_) => U256::zero(), }; diff --git a/src/standard/invoker/state.rs b/src/standard/invoker/state.rs index c22237a9..c0c2b4de 100644 --- a/src/standard/invoker/state.rs +++ b/src/standard/invoker/state.rs @@ -46,7 +46,5 @@ pub trait InvokerState: GasState + Sized { /// Whether the current state is in the static frame. fn is_static(&self) -> bool; /// Effective gas. The final used gas as reported by the transaction. - fn effective_gas(&self, with_refund: bool) -> U256; - /// Apply transaction floor cost for EIP-7623. - fn apply_transaction_floor_cost(&mut self); + fn effective_gas(&self, with_refund: bool, with_floor: bool) -> U256; } diff --git a/src/standard/mod.rs b/src/standard/mod.rs index 8369e2a6..ccd7c225 100644 --- a/src/standard/mod.rs +++ b/src/standard/mod.rs @@ -227,11 +227,8 @@ impl<'config> InvokerState for State<'config> { self.gasometer.is_static } - fn effective_gas(&self, with_refund: bool) -> U256 { - self.gasometer.effective_gas(with_refund, self.config) - } - - fn apply_transaction_floor_cost(&mut self) { - self.gasometer.apply_transaction_floor_cost() + fn effective_gas(&self, with_refund: bool, with_floor: bool) -> U256 { + self.gasometer + .effective_gas(with_refund, with_floor, self.config) } } From ee3fb27f995e6bfa5cdf6c4bf50b216bc05aab8a Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Mon, 8 Sep 2025 14:18:25 +0300 Subject: [PATCH 09/10] refactor: :recycle: remove redundant with_floor parameter --- src/standard/gasometer/mod.rs | 4 ++-- src/standard/invoker/mod.rs | 4 ++-- src/standard/invoker/state.rs | 2 +- src/standard/mod.rs | 5 ++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index 5f522d5e..a7bbb988 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -166,7 +166,7 @@ impl GasometerState { /// The effective used gas at the end of the transaction. /// /// In case of revert, refunded gas are not taken into account. - pub fn effective_gas(&self, with_refund: bool, with_floor: bool, config: &Config) -> U256 { + pub fn effective_gas(&self, with_refund: bool, config: &Config) -> U256 { let refunded_gas = self.refunded_gas.max(0) as u64; let used_gas = if with_refund { @@ -176,7 +176,7 @@ impl GasometerState { self.total_used_gas() }; - let used_gas = if with_floor { + let used_gas = if config.eip7623_calldata_floor { used_gas.max(self.floor_gas) } else { used_gas diff --git a/src/standard/invoker/mod.rs b/src/standard/invoker/mod.rs index 82aa94b4..9c7d5aca 100644 --- a/src/standard/invoker/mod.rs +++ b/src/standard/invoker/mod.rs @@ -514,12 +514,12 @@ where Ok(_) => exit .substate .as_ref() - .map(|s| s.effective_gas(true, invoke.config.eip7623_calldata_floor)) + .map(|s| s.effective_gas(true)) .unwrap_or_default(), Err(ExitError::Reverted) => exit .substate .as_ref() - .map(|s| s.effective_gas(false, invoke.config.eip7623_calldata_floor)) + .map(|s| s.effective_gas(false)) .unwrap_or_default(), Err(_) => U256::zero(), }; diff --git a/src/standard/invoker/state.rs b/src/standard/invoker/state.rs index c0c2b4de..cfab1072 100644 --- a/src/standard/invoker/state.rs +++ b/src/standard/invoker/state.rs @@ -46,5 +46,5 @@ pub trait InvokerState: GasState + Sized { /// Whether the current state is in the static frame. fn is_static(&self) -> bool; /// Effective gas. The final used gas as reported by the transaction. - fn effective_gas(&self, with_refund: bool, with_floor: bool) -> U256; + fn effective_gas(&self, with_refund: bool) -> U256; } diff --git a/src/standard/mod.rs b/src/standard/mod.rs index ccd7c225..ce54eee1 100644 --- a/src/standard/mod.rs +++ b/src/standard/mod.rs @@ -227,8 +227,7 @@ impl<'config> InvokerState for State<'config> { self.gasometer.is_static } - fn effective_gas(&self, with_refund: bool, with_floor: bool) -> U256 { - self.gasometer - .effective_gas(with_refund, with_floor, self.config) + fn effective_gas(&self, with_refund: bool) -> U256 { + self.gasometer.effective_gas(with_refund, self.config) } } From 795e8249033f9daf7d72596ef1fd4fb1d5190856 Mon Sep 17 00:00:00 2001 From: Manuel Mauro Date: Mon, 8 Sep 2025 14:21:50 +0300 Subject: [PATCH 10/10] refactor: :lock: use saturating arithmetic for floor calculation --- src/standard/gasometer/mod.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/standard/gasometer/mod.rs b/src/standard/gasometer/mod.rs index a7bbb988..4d3e561a 100644 --- a/src/standard/gasometer/mod.rs +++ b/src/standard/gasometer/mod.rs @@ -1007,9 +1007,16 @@ impl TransactionCost { + *access_list_address_len as u64 * config.gas_access_list_address() + *access_list_storage_len as u64 * config.gas_access_list_storage_key(); - let floor = config.gas_transaction_call() - + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() - + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); + let floor = config + .gas_transaction_call() + .saturating_add( + (*zero_data_len as u64) + .saturating_mul(config.gas_floor_transaction_zero_data()), + ) + .saturating_add( + (*non_zero_data_len as u64) + .saturating_mul(config.gas_floor_transaction_non_zero_data()), + ); TransactionGas { used, floor } } @@ -1030,9 +1037,16 @@ impl TransactionCost { used += initcode_cost; } - let floor = config.gas_transaction_call() - + *zero_data_len as u64 * config.gas_floor_transaction_zero_data() - + *non_zero_data_len as u64 * config.gas_floor_transaction_non_zero_data(); + let floor = config + .gas_transaction_call() + .saturating_add( + (*zero_data_len as u64) + .saturating_mul(config.gas_floor_transaction_zero_data()), + ) + .saturating_add( + (*non_zero_data_len as u64) + .saturating_mul(config.gas_floor_transaction_non_zero_data()), + ); TransactionGas { used, floor } }