From 0938565b15b2845ecf5273563706d7bf3fbe2ff0 Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Thu, 22 Jan 2026 16:55:09 +0000 Subject: [PATCH 1/3] feat: introduce gas_floor_base_cost for EIP-7623 compliance --- evm/src/gasometer/mod.rs | 42 ++++++++++++++++++---------------------- evm/src/runtime/mod.rs | 12 ++++++++++++ 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/evm/src/gasometer/mod.rs b/evm/src/gasometer/mod.rs index 02d2091d..e5868844 100644 --- a/evm/src/gasometer/mod.rs +++ b/evm/src/gasometer/mod.rs @@ -343,10 +343,24 @@ impl<'config> Gasometer<'config> { /// - [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028) /// - [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) #[must_use] + #[allow(clippy::as_conversions)] // NOTE: in that context usize->u64 `as_conversions` is safe pub const fn intrinsic_gas_and_gas_floor(cost: TransactionCost, config: &Config) -> (u64, u64) { + const fn floor_gas_calc( + config: &Config, + zero_data_len: usize, + non_zero_data_len: usize, + ) -> u64 { + if config.has_floor_gas { + // According to EIP-2028: non-zero byte = 16, zero-byte = 4 + // According to EIP-7623: tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4 + let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64; + tokens_in_calldata * config.total_cost_floor_per_token + config.gas_floor_base_cost + } else { + 0 + } + } + match cost { - // NOTE: in that context usize->u64 `as_conversions` is safe - #[allow(clippy::as_conversions)] TransactionCost::Call { zero_data_len, non_zero_data_len, @@ -361,21 +375,10 @@ impl<'config> Gasometer<'config> { + access_list_address_len as u64 * config.gas_access_list_address + access_list_storage_len as u64 * config.gas_access_list_storage_key + authorization_list_len as u64 * config.gas_per_empty_account_cost; - - let floor_gas = if config.has_floor_gas { - // According to EIP-2028: non-zero byte = 16, zero-byte = 4 - // According to EIP-7623: tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4 - let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64; - tokens_in_calldata * config.total_cost_floor_per_token - + config.gas_transaction_call - } else { - 0 - }; + let floor_gas = floor_gas_calc(config, zero_data_len, non_zero_data_len); (cost, floor_gas) } - // NOTE: in that context usize->u64 `as_conversions` is safe - #[allow(clippy::as_conversions)] TransactionCost::Create { zero_data_len, non_zero_data_len, @@ -388,19 +391,12 @@ impl<'config> Gasometer<'config> { + 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; } - let floor_gas = if config.has_floor_gas { - // According to EIP-2028: non-zero byte = 16, zero-byte = 4 - // According to EIP-7623: tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4 - let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64; - tokens_in_calldata * config.total_cost_floor_per_token - + config.gas_transaction_call - } else { - 0 - }; + let floor_gas = floor_gas_calc(config, zero_data_len, non_zero_data_len); (cost, floor_gas) } diff --git a/evm/src/runtime/mod.rs b/evm/src/runtime/mod.rs index fe886aed..c1f22503 100644 --- a/evm/src/runtime/mod.rs +++ b/evm/src/runtime/mod.rs @@ -261,6 +261,8 @@ pub struct Config { /// EIP-7623 pub has_floor_gas: bool, /// EIP-7623 + pub gas_floor_base_cost: u64, + /// EIP-7623 pub total_cost_floor_per_token: u64, } @@ -326,6 +328,7 @@ impl Config { gas_per_empty_account_cost: 0, gas_per_auth_base_cost: 0, has_floor_gas: false, + gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -391,6 +394,7 @@ impl Config { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, + gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -451,6 +455,7 @@ impl Config { gas_per_empty_account_cost, gas_per_auth_base_cost, has_floor_gas, + gas_floor_base_cost, total_cost_floor_per_token, } = inputs; @@ -526,6 +531,7 @@ impl Config { gas_per_empty_account_cost, gas_per_auth_base_cost, has_floor_gas, + gas_floor_base_cost, total_cost_floor_per_token, } } @@ -554,6 +560,7 @@ struct DerivedConfigInputs { gas_per_empty_account_cost: u64, gas_per_auth_base_cost: u64, has_floor_gas: bool, + gas_floor_base_cost: u64, total_cost_floor_per_token: u64, } @@ -578,6 +585,7 @@ impl DerivedConfigInputs { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, + gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -602,6 +610,7 @@ impl DerivedConfigInputs { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, + gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -626,6 +635,7 @@ impl DerivedConfigInputs { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, + gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -651,6 +661,7 @@ impl DerivedConfigInputs { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, + gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -671,6 +682,7 @@ impl DerivedConfigInputs { config.gas_per_empty_account_cost = 25000; config.gas_per_auth_base_cost = 12500; config.has_floor_gas = true; + config.gas_floor_base_cost = 21000; config.total_cost_floor_per_token = 10; config } From 4810147b779bda36469a8ff358b992333162c452 Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Thu, 22 Jan 2026 17:19:45 +0000 Subject: [PATCH 2/3] fix: replace simple arithmetic operations with saturating_* analogs for gas calculations --- evm/src/gasometer/mod.rs | 71 ++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/evm/src/gasometer/mod.rs b/evm/src/gasometer/mod.rs index e5868844..cb6ee36d 100644 --- a/evm/src/gasometer/mod.rs +++ b/evm/src/gasometer/mod.rs @@ -353,8 +353,13 @@ impl<'config> Gasometer<'config> { if config.has_floor_gas { // According to EIP-2028: non-zero byte = 16, zero-byte = 4 // According to EIP-7623: tokens_in_calldata = zero_bytes_in_calldata + nonzero_bytes_in_calldata * 4 - let tokens_in_calldata = (zero_data_len + non_zero_data_len * 4) as u64; - tokens_in_calldata * config.total_cost_floor_per_token + config.gas_floor_base_cost + let tokens_in_calldata = non_zero_data_len + .saturating_mul(4) + .saturating_add(zero_data_len) as u64; + + tokens_in_calldata + .saturating_mul(config.total_cost_floor_per_token) + .saturating_add(config.gas_floor_base_cost) } else { 0 } @@ -369,12 +374,33 @@ impl<'config> Gasometer<'config> { authorization_list_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 - + authorization_list_len as u64 * config.gas_per_empty_account_cost; + let cost = config + .gas_transaction_call + .saturating_add( + config + .gas_transaction_zero_data + .saturating_mul(zero_data_len as u64), + ) + .saturating_add( + config + .gas_transaction_non_zero_data + .saturating_mul(non_zero_data_len as u64), + ) + .saturating_add( + config + .gas_access_list_address + .saturating_mul(access_list_address_len as u64), + ) + .saturating_add( + config + .gas_access_list_storage_key + .saturating_mul(access_list_storage_len as u64), + ) + .saturating_add( + config + .gas_per_empty_account_cost + .saturating_mul(authorization_list_len as u64), + ); let floor_gas = floor_gas_calc(config, zero_data_len, non_zero_data_len); (cost, floor_gas) @@ -386,14 +412,31 @@ impl<'config> Gasometer<'config> { access_list_storage_len, initcode_cost, } => { - let mut cost = 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; + let mut cost = config + .gas_transaction_create + .saturating_add( + config + .gas_transaction_zero_data + .saturating_mul(zero_data_len as u64), + ) + .saturating_add( + config + .gas_transaction_non_zero_data + .saturating_mul(non_zero_data_len as u64), + ) + .saturating_add( + config + .gas_access_list_address + .saturating_mul(access_list_address_len as u64), + ) + .saturating_add( + config + .gas_access_list_storage_key + .saturating_mul(access_list_storage_len as u64), + ); if config.max_initcode_size.is_some() { - cost += initcode_cost; + cost = cost.saturating_add(initcode_cost); } let floor_gas = floor_gas_calc(config, zero_data_len, non_zero_data_len); From 210297658366d303398357fee4735ece7414ebcd Mon Sep 17 00:00:00 2001 From: Oleksandr Anyshchenko Date: Fri, 23 Jan 2026 10:10:17 +0000 Subject: [PATCH 3/3] chore: remove gas_floor_base_cost constant --- evm/src/gasometer/mod.rs | 2 +- evm/src/runtime/mod.rs | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/evm/src/gasometer/mod.rs b/evm/src/gasometer/mod.rs index cb6ee36d..4280c186 100644 --- a/evm/src/gasometer/mod.rs +++ b/evm/src/gasometer/mod.rs @@ -359,7 +359,7 @@ impl<'config> Gasometer<'config> { tokens_in_calldata .saturating_mul(config.total_cost_floor_per_token) - .saturating_add(config.gas_floor_base_cost) + .saturating_add(config.gas_transaction_call) } else { 0 } diff --git a/evm/src/runtime/mod.rs b/evm/src/runtime/mod.rs index c1f22503..4b645056 100644 --- a/evm/src/runtime/mod.rs +++ b/evm/src/runtime/mod.rs @@ -173,7 +173,7 @@ pub struct Config { /// Gas paid for a contract creation transaction. pub gas_transaction_create: u64, /// Gas paid for a message call transaction. - pub gas_transaction_call: u64, + pub gas_transaction_call: u64, // TODO: Rename in 3.0.0 because the constant is related not to transaction call only. /// Gas paid for zero data in a transaction. pub gas_transaction_zero_data: u64, /// Gas paid for non-zero data in a transaction. @@ -261,8 +261,6 @@ pub struct Config { /// EIP-7623 pub has_floor_gas: bool, /// EIP-7623 - pub gas_floor_base_cost: u64, - /// EIP-7623 pub total_cost_floor_per_token: u64, } @@ -328,7 +326,6 @@ impl Config { gas_per_empty_account_cost: 0, gas_per_auth_base_cost: 0, has_floor_gas: false, - gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -394,7 +391,6 @@ impl Config { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, - gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -455,7 +451,6 @@ impl Config { gas_per_empty_account_cost, gas_per_auth_base_cost, has_floor_gas, - gas_floor_base_cost, total_cost_floor_per_token, } = inputs; @@ -531,7 +526,6 @@ impl Config { gas_per_empty_account_cost, gas_per_auth_base_cost, has_floor_gas, - gas_floor_base_cost, total_cost_floor_per_token, } } @@ -560,7 +554,6 @@ struct DerivedConfigInputs { gas_per_empty_account_cost: u64, gas_per_auth_base_cost: u64, has_floor_gas: bool, - gas_floor_base_cost: u64, total_cost_floor_per_token: u64, } @@ -585,7 +578,6 @@ impl DerivedConfigInputs { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, - gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -610,7 +602,6 @@ impl DerivedConfigInputs { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, - gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -635,7 +626,6 @@ impl DerivedConfigInputs { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, - gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -661,7 +651,6 @@ impl DerivedConfigInputs { gas_per_auth_base_cost: 0, gas_per_empty_account_cost: 0, has_floor_gas: false, - gas_floor_base_cost: 0, total_cost_floor_per_token: 0, } } @@ -682,7 +671,6 @@ impl DerivedConfigInputs { config.gas_per_empty_account_cost = 25000; config.gas_per_auth_base_cost = 12500; config.has_floor_gas = true; - config.gas_floor_base_cost = 21000; config.total_cost_floor_per_token = 10; config }