1use crate::{
4 cfg::gas::{self, get_tokens_in_calldata, InitialAndFloorGas},
5 context::SStoreResult,
6 transaction::AccessListItemTr as _,
7 Transaction, TransactionType,
8};
9use core::hash::{Hash, Hasher};
10use primitives::{
11 eip2780, eip7702, eip8037, eip8038,
12 hardfork::SpecId::{self},
13 OnceLock, U256,
14};
15use std::sync::Arc;
16
17#[derive(Clone)]
19pub struct GasParams {
20 table: Arc<[u64; 256]>,
22}
23
24impl PartialEq<GasParams> for GasParams {
25 fn eq(&self, other: &GasParams) -> bool {
26 self.table == other.table
27 }
28}
29
30impl Hash for GasParams {
31 fn hash<H: Hasher>(&self, hasher: &mut H) {
32 self.table.hash(hasher);
33 }
34}
35
36impl core::fmt::Debug for GasParams {
37 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 write!(f, "GasParams {{ table: {:?} }}", self.table)
39 }
40}
41
42#[inline]
45pub const fn num_words(len: usize) -> usize {
46 len.div_ceil(32)
47}
48
49impl Eq for GasParams {}
50#[cfg(feature = "serde")]
51mod serde {
52 use super::{Arc, GasParams};
53 use std::vec::Vec;
54
55 #[derive(serde::Serialize, serde::Deserialize)]
56 struct GasParamsSerde {
57 table: Vec<u64>,
58 }
59
60 #[cfg(feature = "serde")]
61 impl serde::Serialize for GasParams {
62 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63 where
64 S: serde::Serializer,
65 {
66 GasParamsSerde {
67 table: self.table.to_vec(),
68 }
69 .serialize(serializer)
70 }
71 }
72
73 impl<'de> serde::Deserialize<'de> for GasParams {
74 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75 where
76 D: serde::Deserializer<'de>,
77 {
78 let table = GasParamsSerde::deserialize(deserializer)?;
79 if table.table.len() != 256 {
80 return Err(serde::de::Error::custom("Invalid gas params length"));
81 }
82 Ok(Self::new(Arc::new(table.table.try_into().unwrap())))
83 }
84 }
85}
86
87impl Default for GasParams {
88 #[inline]
89 fn default() -> Self {
90 Self::new_spec(SpecId::default())
91 }
92}
93
94impl GasParams {
95 #[inline]
97 pub const fn new(table: Arc<[u64; 256]>) -> Self {
98 Self { table }
99 }
100
101 pub fn override_gas(&mut self, values: impl IntoIterator<Item = (GasId, u64)>) {
117 let mut table = *self.table.clone();
118 for (id, value) in values.into_iter() {
119 table[id.as_usize()] = value;
120 }
121 *self = Self::new(Arc::new(table));
122 }
123
124 #[inline]
126 pub fn table(&self) -> &[u64; 256] {
127 &self.table
128 }
129
130 #[inline(never)]
132 pub fn new_spec(spec: SpecId) -> Self {
133 use SpecId::*;
134 let gas_params = match spec {
135 FRONTIER => {
136 static TABLE: OnceLock<GasParams> = OnceLock::new();
137 TABLE.get_or_init(|| Self::new_spec_inner(spec))
138 }
139 HOMESTEAD => {
141 static TABLE: OnceLock<GasParams> = OnceLock::new();
142 TABLE.get_or_init(|| Self::new_spec_inner(spec))
143 }
144 TANGERINE => {
146 static TABLE: OnceLock<GasParams> = OnceLock::new();
147 TABLE.get_or_init(|| Self::new_spec_inner(spec))
148 }
149 SPURIOUS_DRAGON | BYZANTIUM | PETERSBURG => {
151 static TABLE: OnceLock<GasParams> = OnceLock::new();
152 TABLE.get_or_init(|| Self::new_spec_inner(spec))
153 }
154 ISTANBUL => {
156 static TABLE: OnceLock<GasParams> = OnceLock::new();
157 TABLE.get_or_init(|| Self::new_spec_inner(spec))
158 }
159 BERLIN => {
161 static TABLE: OnceLock<GasParams> = OnceLock::new();
162 TABLE.get_or_init(|| Self::new_spec_inner(spec))
163 }
164 LONDON | MERGE => {
166 static TABLE: OnceLock<GasParams> = OnceLock::new();
167 TABLE.get_or_init(|| Self::new_spec_inner(spec))
168 }
169 SHANGHAI | CANCUN => {
171 static TABLE: OnceLock<GasParams> = OnceLock::new();
172 TABLE.get_or_init(|| Self::new_spec_inner(spec))
173 }
174 PRAGUE | OSAKA => {
176 static TABLE: OnceLock<GasParams> = OnceLock::new();
177 TABLE.get_or_init(|| Self::new_spec_inner(spec))
178 }
179 SpecId::AMSTERDAM => {
181 static TABLE: OnceLock<GasParams> = OnceLock::new();
182 TABLE.get_or_init(|| Self::new_spec_inner(spec))
183 }
184 };
185 gas_params.clone()
186 }
187
188 #[inline]
190 fn new_spec_inner(spec: SpecId) -> Self {
191 let mut table = [0; 256];
192
193 table[GasId::exp_byte_gas().as_usize()] = 10;
194 table[GasId::logdata().as_usize()] = gas::LOGDATA;
195 table[GasId::logtopic().as_usize()] = gas::LOGTOPIC;
196 table[GasId::copy_per_word().as_usize()] = gas::COPY;
197 table[GasId::extcodecopy_per_word().as_usize()] = gas::COPY;
198 table[GasId::mcopy_per_word().as_usize()] = gas::COPY;
199 table[GasId::keccak256_per_word().as_usize()] = gas::KECCAK256WORD;
200 table[GasId::memory_linear_cost().as_usize()] = gas::MEMORY;
201 table[GasId::memory_quadratic_reduction().as_usize()] = 512;
202 table[GasId::initcode_per_word().as_usize()] = gas::INITCODE_WORD_COST;
203 table[GasId::create().as_usize()] = gas::CREATE;
204 table[GasId::call_stipend_reduction().as_usize()] = 64;
205 table[GasId::max_refund_quotient().as_usize()] = 2;
206 table[GasId::transfer_value_cost().as_usize()] = gas::CALLVALUE;
207 table[GasId::cold_account_additional_cost().as_usize()] = 0;
208 table[GasId::new_account_cost().as_usize()] = gas::NEWACCOUNT;
209 table[GasId::warm_storage_read_cost().as_usize()] = 0;
210 table[GasId::sstore_static().as_usize()] = gas::SSTORE_RESET;
212 table[GasId::sstore_set_without_load_cost().as_usize()] =
214 gas::SSTORE_SET - gas::SSTORE_RESET;
215 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = 0;
217 table[GasId::sstore_set_refund().as_usize()] =
219 table[GasId::sstore_set_without_load_cost().as_usize()];
220 table[GasId::sstore_reset_refund().as_usize()] =
222 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
223 table[GasId::sstore_clearing_slot_refund().as_usize()] = 15000;
225 table[GasId::selfdestruct_refund().as_usize()] = 24000;
226 table[GasId::call_stipend().as_usize()] = gas::CALL_STIPEND;
227 table[GasId::cold_storage_additional_cost().as_usize()] = 0;
228 table[GasId::cold_storage_cost().as_usize()] = 0;
229 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
230 table[GasId::code_deposit_cost().as_usize()] = gas::CODEDEPOSIT;
231 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
232 gas::NON_ZERO_BYTE_MULTIPLIER;
233 table[GasId::tx_token_cost().as_usize()] = gas::STANDARD_TOKEN_COST;
234 table[GasId::tx_base_stipend().as_usize()] = 21000;
235
236 if spec.is_enabled_in(SpecId::HOMESTEAD) {
237 table[GasId::tx_create_cost().as_usize()] = gas::CREATE;
238 }
239
240 if spec.is_enabled_in(SpecId::TANGERINE) {
241 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = gas::NEWACCOUNT;
242 }
243
244 if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
245 table[GasId::exp_byte_gas().as_usize()] = 50;
246 }
247
248 if spec.is_enabled_in(SpecId::ISTANBUL) {
249 table[GasId::sstore_static().as_usize()] = gas::ISTANBUL_SLOAD_GAS;
250 table[GasId::sstore_set_without_load_cost().as_usize()] =
251 gas::SSTORE_SET - gas::ISTANBUL_SLOAD_GAS;
252 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
253 gas::SSTORE_RESET - gas::ISTANBUL_SLOAD_GAS;
254 table[GasId::sstore_set_refund().as_usize()] =
255 table[GasId::sstore_set_without_load_cost().as_usize()];
256 table[GasId::sstore_reset_refund().as_usize()] =
257 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
258 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
259 gas::NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
260 }
261
262 if spec.is_enabled_in(SpecId::BERLIN) {
263 table[GasId::sstore_static().as_usize()] = gas::WARM_STORAGE_READ_COST;
264 table[GasId::cold_account_additional_cost().as_usize()] =
265 gas::COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
266 table[GasId::cold_storage_additional_cost().as_usize()] =
267 gas::COLD_SLOAD_COST - gas::WARM_STORAGE_READ_COST;
268 table[GasId::cold_storage_cost().as_usize()] = gas::COLD_SLOAD_COST;
269 table[GasId::warm_storage_read_cost().as_usize()] = gas::WARM_STORAGE_READ_COST;
270
271 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
272 gas::WARM_SSTORE_RESET - gas::WARM_STORAGE_READ_COST;
273 table[GasId::sstore_set_without_load_cost().as_usize()] =
274 gas::SSTORE_SET - gas::WARM_STORAGE_READ_COST;
275 table[GasId::sstore_set_refund().as_usize()] =
276 table[GasId::sstore_set_without_load_cost().as_usize()];
277 table[GasId::sstore_reset_refund().as_usize()] =
278 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
279
280 table[GasId::tx_access_list_address_cost().as_usize()] = gas::ACCESS_LIST_ADDRESS;
281 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
282 gas::ACCESS_LIST_STORAGE_KEY;
283 }
284
285 if spec.is_enabled_in(SpecId::LONDON) {
286 table[GasId::sstore_clearing_slot_refund().as_usize()] =
291 gas::WARM_SSTORE_RESET + gas::ACCESS_LIST_STORAGE_KEY;
292
293 table[GasId::selfdestruct_refund().as_usize()] = 0;
294 table[GasId::max_refund_quotient().as_usize()] = 5;
295 }
296
297 if spec.is_enabled_in(SpecId::SHANGHAI) {
298 table[GasId::tx_initcode_cost().as_usize()] = gas::INITCODE_WORD_COST;
299 }
300
301 if spec.is_enabled_in(SpecId::PRAGUE) {
302 table[GasId::tx_eip7702_regular_gas().as_usize()] = eip7702::PER_EMPTY_ACCOUNT_COST;
303
304 table[GasId::tx_eip7702_regular_refund().as_usize()] =
306 eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST;
307
308 table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
309 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
310 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] = 1;
313 }
314
315 if spec.is_enabled_in(SpecId::AMSTERDAM) {
319 table[GasId::create().as_usize()] = 9000;
321 table[GasId::tx_create_cost().as_usize()] = 9000;
322 table[GasId::code_deposit_cost().as_usize()] = 0;
323 table[GasId::new_account_cost().as_usize()] = 0;
324 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
325 table[GasId::sstore_set_without_load_cost().as_usize()] = 2800;
328
329 table[GasId::sstore_set_state_gas().as_usize()] =
331 eip8037::SSTORE_SET_BYTES * eip8037::CPSB_GLAMSTERDAM;
332 table[GasId::new_account_state_gas().as_usize()] =
333 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM;
334 table[GasId::code_deposit_state_gas().as_usize()] =
335 eip8037::CODE_DEPOSIT_PER_BYTE * eip8037::CPSB_GLAMSTERDAM;
336 table[GasId::create_state_gas().as_usize()] =
337 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM;
338 table[GasId::tx_eip7702_state_gas_bytecode().as_usize()] =
339 eip8037::AUTH_BASE_BYTES * eip8037::CPSB_GLAMSTERDAM;
340
341 table[GasId::sstore_set_refund().as_usize()] = 2800;
345
346 table[GasId::tx_eip7702_regular_gas().as_usize()] =
356 eip8038::EIP7702_PER_EMPTY_ACCOUNT_REGULAR;
357 table[GasId::tx_eip7702_regular_refund().as_usize()] = eip8038::ACCOUNT_WRITE;
358
359 table[GasId::tx_floor_cost_base_gas().as_usize()] = eip2780::TX_BASE_COST;
361
362 table[GasId::tx_floor_cost_per_token().as_usize()] = 16;
367 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] =
368 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()];
369
370 table[GasId::tx_access_list_address_cost().as_usize()] =
376 gas::ACCESS_LIST_ADDRESS + 20 * 64;
377 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
378 gas::ACCESS_LIST_STORAGE_KEY + 32 * 64;
379 table[GasId::tx_access_list_floor_byte_multiplier().as_usize()] = 4;
380
381 table[GasId::warm_storage_read_cost().as_usize()] = eip8038::WARM_ACCESS;
395 table[GasId::cold_account_additional_cost().as_usize()] =
396 eip8038::COLD_ACCOUNT_ACCESS_ADDITIONAL;
397 table[GasId::cold_storage_additional_cost().as_usize()] =
398 eip8038::COLD_STORAGE_ACCESS_ADDITIONAL;
399 table[GasId::cold_storage_cost().as_usize()] = eip8038::COLD_STORAGE_ACCESS_ADDITIONAL;
405 table[GasId::transfer_value_cost().as_usize()] = eip8038::CALL_VALUE;
413 table[GasId::new_account_cost().as_usize()] = 0;
414 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = eip8038::ACCOUNT_WRITE;
415
416 table[GasId::sstore_static().as_usize()] = eip8038::WARM_ACCESS;
421 table[GasId::sstore_set_without_load_cost().as_usize()] = eip8038::STORAGE_WRITE;
422 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = eip8038::STORAGE_WRITE;
423 table[GasId::sstore_set_refund().as_usize()] = eip8038::STORAGE_WRITE;
424 table[GasId::sstore_reset_refund().as_usize()] = eip8038::STORAGE_WRITE;
425 table[GasId::sstore_clearing_slot_refund().as_usize()] = eip8038::STORAGE_CLEAR_REFUND;
426
427 table[GasId::create().as_usize()] = eip8038::CREATE_ACCESS;
431 table[GasId::tx_create_cost().as_usize()] = eip8038::CREATE_ACCESS;
432
433 table[GasId::tx_access_list_address_cost().as_usize()] =
436 eip8038::ACCESS_LIST_ADDRESS_COST + 20 * 64;
437 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
438 eip8038::ACCESS_LIST_STORAGE_KEY_COST + 32 * 64;
439
440 table[GasId::tx_eip7702_regular_gas().as_usize()] =
444 eip8038::EIP7702_PER_EMPTY_ACCOUNT_REGULAR;
445
446 table[GasId::tx_transfer_log_cost().as_usize()] = eip2780::TRANSFER_LOG_COST;
452 table[GasId::tx_account_write_cost().as_usize()] = eip8038::ACCOUNT_WRITE;
453 table[GasId::tx_create_access_cost().as_usize()] = eip8038::CREATE_ACCESS;
454 }
455
456 Self::new(Arc::new(table))
457 }
458
459 #[inline]
461 pub fn get(&self, id: GasId) -> u64 {
462 self.table[id.as_usize()]
463 }
464
465 #[inline]
467 pub fn exp_cost(&self, power: U256) -> u64 {
468 if power.is_zero() {
469 return 0;
470 }
471 self.get(GasId::exp_byte_gas())
473 .saturating_mul(log2floor(power) / 8 + 1)
474 }
475
476 #[inline]
478 pub fn selfdestruct_refund(&self) -> i64 {
479 self.get(GasId::selfdestruct_refund()) as i64
480 }
481
482 #[inline]
485 pub fn selfdestruct_cold_cost(&self) -> u64 {
486 self.cold_account_additional_cost() + self.warm_storage_read_cost()
487 }
488
489 #[inline]
491 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
492 let mut gas = 0;
493
494 if should_charge_topup {
496 gas += self.new_account_cost_for_selfdestruct();
497 }
498
499 if is_cold {
500 gas += self.selfdestruct_cold_cost();
506 }
507 gas
508 }
509
510 #[inline]
512 pub fn extcodecopy(&self, len: usize) -> u64 {
513 self.get(GasId::extcodecopy_per_word())
514 .saturating_mul(num_words(len) as u64)
515 }
516
517 #[inline]
519 pub fn mcopy_cost(&self, len: usize) -> u64 {
520 self.get(GasId::mcopy_per_word())
521 .saturating_mul(num_words(len) as u64)
522 }
523
524 #[inline]
526 pub fn sstore_static_gas(&self) -> u64 {
527 self.get(GasId::sstore_static())
528 }
529
530 #[inline]
532 pub fn sstore_set_without_load_cost(&self) -> u64 {
533 self.get(GasId::sstore_set_without_load_cost())
534 }
535
536 #[inline]
538 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
539 self.get(GasId::sstore_reset_without_cold_load_cost())
540 }
541
542 #[inline]
544 pub fn sstore_clearing_slot_refund(&self) -> u64 {
545 self.get(GasId::sstore_clearing_slot_refund())
546 }
547
548 #[inline]
550 pub fn sstore_set_refund(&self) -> u64 {
551 self.get(GasId::sstore_set_refund())
552 }
553
554 #[inline]
556 pub fn sstore_reset_refund(&self) -> u64 {
557 self.get(GasId::sstore_reset_refund())
558 }
559
560 #[inline]
564 pub fn max_refund_quotient(&self) -> u64 {
565 self.get(GasId::max_refund_quotient())
566 }
567
568 #[inline]
572 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
573 if !is_istanbul {
576 if vals.is_present_zero() && !vals.is_new_zero() {
577 return self.sstore_set_without_load_cost();
578 } else {
579 return self.sstore_reset_without_cold_load_cost();
580 }
581 }
582
583 let mut gas = 0;
584
585 if is_cold {
587 gas += self.cold_storage_cost();
588 }
589
590 if vals.new_values_changes_present() && vals.is_original_eq_present() {
592 gas += if vals.is_original_zero() {
593 self.sstore_set_without_load_cost()
596 } else {
597 self.sstore_reset_without_cold_load_cost()
599 };
600 }
601 gas
602 }
603
604 #[inline]
606 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
607 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
609
610 if !is_istanbul {
611 if !vals.is_present_zero() && vals.is_new_zero() {
613 return sstore_clearing_slot_refund;
614 }
615 return 0;
616 }
617
618 if vals.is_new_eq_present() {
620 return 0;
621 }
622
623 if vals.is_original_eq_present() && vals.is_new_zero() {
626 return sstore_clearing_slot_refund;
627 }
628
629 let mut refund = 0;
630 if !vals.is_original_zero() {
632 if vals.is_present_zero() {
634 refund -= sstore_clearing_slot_refund;
636 } else if vals.is_new_zero() {
638 refund += sstore_clearing_slot_refund;
640 }
641 }
642
643 if vals.is_original_eq_new() {
645 if vals.is_original_zero() {
647 refund += self.sstore_set_refund() as i64;
649 } else {
651 refund += self.sstore_reset_refund() as i64;
653 }
654 }
655 refund
656 }
657
658 #[inline]
660 pub fn log_cost(&self, n: u8, len: u64) -> u64 {
661 self.get(GasId::logdata())
662 .saturating_mul(len)
663 .saturating_add(self.get(GasId::logtopic()) * n as u64)
664 }
665
666 #[inline]
668 pub fn keccak256_cost(&self, len: usize) -> u64 {
669 self.get(GasId::keccak256_per_word())
670 .saturating_mul(num_words(len) as u64)
671 }
672
673 #[inline]
675 pub fn memory_cost(&self, len: usize) -> u64 {
676 let len = len as u64;
677 self.get(GasId::memory_linear_cost())
678 .saturating_mul(len)
679 .saturating_add(
680 (len.saturating_mul(len))
681 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
682 )
683 }
684
685 #[inline]
687 pub fn initcode_cost(&self, len: usize) -> u64 {
688 self.get(GasId::initcode_per_word())
689 .saturating_mul(num_words(len) as u64)
690 }
691
692 #[inline]
694 pub fn create_cost(&self) -> u64 {
695 self.get(GasId::create())
696 }
697
698 #[inline]
700 pub fn create2_cost(&self, len: usize) -> u64 {
701 self.get(GasId::create()).saturating_add(
702 self.get(GasId::keccak256_per_word())
703 .saturating_mul(num_words(len) as u64),
704 )
705 }
706
707 #[inline]
709 pub fn call_stipend(&self) -> u64 {
710 self.get(GasId::call_stipend())
711 }
712
713 #[inline]
715 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
716 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
717 }
718
719 #[inline]
721 pub fn transfer_value_cost(&self) -> u64 {
722 self.get(GasId::transfer_value_cost())
723 }
724
725 #[inline]
727 pub fn cold_account_additional_cost(&self) -> u64 {
728 self.get(GasId::cold_account_additional_cost())
729 }
730
731 #[inline]
733 pub fn cold_storage_additional_cost(&self) -> u64 {
734 self.get(GasId::cold_storage_additional_cost())
735 }
736
737 #[inline]
739 pub fn cold_storage_cost(&self) -> u64 {
740 self.get(GasId::cold_storage_cost())
741 }
742
743 #[inline]
745 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
746 if !is_spurious_dragon || transfers_value {
750 return self.get(GasId::new_account_cost());
751 }
752 0
753 }
754
755 #[inline]
757 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
758 self.get(GasId::new_account_cost_for_selfdestruct())
759 }
760
761 #[inline]
763 pub fn warm_storage_read_cost(&self) -> u64 {
764 self.get(GasId::warm_storage_read_cost())
765 }
766
767 #[inline]
769 pub fn copy_cost(&self, len: usize) -> u64 {
770 self.copy_per_word_cost(num_words(len))
771 }
772
773 #[inline]
775 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
776 self.get(GasId::copy_per_word())
777 .saturating_mul(word_num as u64)
778 }
779
780 #[inline]
782 pub fn code_deposit_cost(&self, len: usize) -> u64 {
783 self.get(GasId::code_deposit_cost())
784 .saturating_mul(len as u64)
785 }
786
787 #[inline]
789 pub fn sstore_state_gas(&self, vals: &SStoreResult) -> u64 {
790 if vals.new_values_changes_present()
791 && vals.is_original_eq_present()
792 && vals.is_original_zero()
793 {
794 self.get(GasId::sstore_set_state_gas())
795 } else {
796 0
797 }
798 }
799
800 #[inline]
808 pub fn sstore_state_gas_refill(&self, vals: &SStoreResult) -> u64 {
809 if !vals.is_new_eq_present() && vals.is_original_eq_new() && vals.is_original_zero() {
810 self.get(GasId::sstore_set_state_gas())
811 } else {
812 0
813 }
814 }
815
816 #[inline]
818 pub fn new_account_state_gas(&self) -> u64 {
819 self.get(GasId::new_account_state_gas())
820 }
821
822 #[inline]
824 pub fn code_deposit_state_gas(&self, len: usize) -> u64 {
825 self.get(GasId::code_deposit_state_gas())
826 .saturating_mul(len as u64)
827 }
828
829 #[inline]
831 pub fn create_state_gas(&self) -> u64 {
832 self.get(GasId::create_state_gas())
833 }
834
835 #[inline]
841 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
842 let regular = self.get(GasId::tx_eip7702_regular_gas());
843 let state = self.tx_eip7702_state_gas();
844 regular.saturating_add(state)
845 }
846
847 #[inline]
853 pub fn tx_eip7702_auth_refund(&self) -> u64 {
854 let regular = self.get(GasId::tx_eip7702_regular_refund());
855 let state = self.new_account_state_gas();
856 regular.saturating_add(state)
857 }
858
859 #[inline]
864 pub fn tx_eip7702_state_gas(&self) -> u64 {
865 self.tx_eip7702_state_refund(1, 1)
867 }
868
869 #[inline]
878 pub fn tx_eip7702_state_refund(&self, num_accounts: u64, num_bytecodes: u64) -> u64 {
879 let per_account = self
880 .get(GasId::new_account_state_gas())
881 .saturating_mul(num_accounts);
882 let per_bytecode = self
883 .get(GasId::tx_eip7702_state_gas_bytecode())
884 .saturating_mul(num_bytecodes);
885 per_account.saturating_add(per_bytecode)
886 }
887
888 #[inline]
893 pub fn tx_eip7702_auth_refund_regular(&self) -> u64 {
894 self.get(GasId::tx_eip7702_regular_refund())
895 }
896
897 #[inline]
899 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
900 self.get(GasId::tx_token_non_zero_byte_multiplier())
901 }
902
903 #[inline]
905 pub fn tx_token_cost(&self) -> u64 {
906 self.get(GasId::tx_token_cost())
907 }
908
909 pub fn tx_floor_cost_per_token(&self) -> u64 {
911 self.get(GasId::tx_floor_cost_per_token())
912 }
913
914 pub fn tx_floor_token_zero_byte_multiplier(&self) -> u64 {
922 self.get(GasId::tx_floor_token_zero_byte_multiplier())
923 }
924
925 #[inline]
936 pub fn tx_floor_cost(&self, input: &[u8]) -> u64 {
937 let zero_multiplier = self.tx_floor_token_zero_byte_multiplier();
938 let non_zero_multiplier = self.tx_token_non_zero_byte_multiplier();
939 let floor_tokens = if zero_multiplier == non_zero_multiplier {
940 input.len() as u64 * non_zero_multiplier
941 } else {
942 get_tokens_in_calldata(input, non_zero_multiplier)
943 };
944 self.tx_floor_cost_with_tokens(floor_tokens)
945 }
946
947 #[inline]
949 pub fn tx_floor_cost_with_tokens(&self, tokens: u64) -> u64 {
950 self.tx_floor_cost_per_token() * tokens + self.tx_floor_cost_base_gas()
951 }
952
953 pub fn tx_floor_cost_base_gas(&self) -> u64 {
955 self.get(GasId::tx_floor_cost_base_gas())
956 }
957
958 pub fn tx_access_list_address_cost(&self) -> u64 {
960 self.get(GasId::tx_access_list_address_cost())
961 }
962
963 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
965 self.get(GasId::tx_access_list_storage_key_cost())
966 }
967
968 #[inline]
986 pub fn tx_access_list_cost(&self, accounts: u64, storages: u64) -> u64 {
987 accounts
988 .saturating_mul(self.tx_access_list_address_cost())
989 .saturating_add(storages.saturating_mul(self.tx_access_list_storage_key_cost()))
990 }
991
992 #[inline]
1000 pub fn tx_access_list_floor_byte_multiplier(&self) -> u64 {
1001 self.get(GasId::tx_access_list_floor_byte_multiplier())
1002 }
1003
1004 #[inline]
1009 pub fn tx_floor_tokens_in_access_list(&self, accounts: u64, storages: u64) -> u64 {
1010 let bytes = accounts
1011 .saturating_mul(20)
1012 .saturating_add(storages.saturating_mul(32));
1013 bytes.saturating_mul(self.tx_access_list_floor_byte_multiplier())
1014 }
1015
1016 pub fn tx_base_stipend(&self) -> u64 {
1018 self.get(GasId::tx_base_stipend())
1019 }
1020
1021 #[inline]
1024 pub fn tx_transfer_log_cost(&self) -> u64 {
1025 self.get(GasId::tx_transfer_log_cost())
1026 }
1027
1028 #[inline]
1032 pub fn tx_account_write_cost(&self) -> u64 {
1033 self.get(GasId::tx_account_write_cost())
1034 }
1035
1036 #[inline]
1040 pub fn tx_create_access_cost(&self) -> u64 {
1041 self.get(GasId::tx_create_access_cost())
1042 }
1043
1044 #[inline]
1048 pub fn tx_create_cost(&self) -> u64 {
1049 self.get(GasId::tx_create_cost())
1050 }
1051
1052 #[inline]
1054 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
1055 self.get(GasId::tx_initcode_cost())
1056 .saturating_mul(num_words(len) as u64)
1057 }
1058
1059 #[allow(clippy::too_many_arguments)]
1080 pub fn initial_tx_gas(
1081 &self,
1082 input: &[u8],
1083 is_create: bool,
1084 access_list_accounts: u64,
1085 access_list_storages: u64,
1086 authorization_list_num: u64,
1087 eip2780: Option<Eip2780TxInfo>,
1088 ) -> InitialAndFloorGas {
1089 let tokens_in_calldata =
1091 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
1092
1093 let auth_total_cost = authorization_list_num * self.tx_eip7702_per_empty_account_cost();
1097 let auth_state_gas = authorization_list_num * self.tx_eip7702_state_gas();
1098
1099 let auth_regular_cost = auth_total_cost - auth_state_gas;
1100
1101 let base_and_to_and_value_gas = match eip2780 {
1102 None => {
1103 let mut base = self.tx_base_stipend();
1104 if is_create {
1105 base += self.tx_create_cost();
1107 }
1108 base
1109 }
1110 Some(info) => self.eip2780_base_to_value_gas(is_create, &info),
1111 };
1112
1113 let mut initial_regular_gas = tokens_in_calldata * self.tx_token_cost()
1114 + access_list_accounts * self.tx_access_list_address_cost()
1116 + access_list_storages * self.tx_access_list_storage_key_cost()
1118 + base_and_to_and_value_gas
1119 + auth_regular_cost;
1121
1122 let mut initial_state_gas = auth_state_gas;
1124
1125 if is_create {
1126 initial_regular_gas += self.tx_initcode_cost(input.len());
1128
1129 initial_state_gas += self.create_state_gas();
1132 }
1133
1134 let access_list_floor_tokens =
1137 self.tx_floor_tokens_in_access_list(access_list_accounts, access_list_storages);
1138 let floor_gas =
1139 self.tx_floor_cost(input) + access_list_floor_tokens * self.tx_floor_cost_per_token();
1140
1141 InitialAndFloorGas::default()
1142 .with_initial_regular_gas(initial_regular_gas)
1143 .with_initial_state_gas(initial_state_gas)
1144 .with_floor_gas(floor_gas)
1145 }
1146
1147 fn eip2780_base_to_value_gas(&self, is_create: bool, info: &Eip2780TxInfo) -> u64 {
1156 let mut gas = eip2780::TX_BASE_COST;
1157
1158 if is_create {
1159 gas += self.tx_create_access_cost();
1161 if !info.value.is_zero() {
1162 gas += self.tx_transfer_log_cost();
1163 }
1164 } else if !info.is_self_transfer {
1165 gas += eip8038::COLD_ACCOUNT_ACCESS;
1167 if !info.value.is_zero() {
1168 gas += self.tx_transfer_log_cost() + eip2780::TX_VALUE_COST;
1169 }
1170 }
1171
1172 gas
1173 }
1174
1175 pub fn initial_tx_gas_for_tx(
1180 &self,
1181 tx: impl Transaction,
1182 eip2780: Option<Eip2780TxInfo>,
1183 ) -> InitialAndFloorGas {
1184 let mut accounts = 0;
1185 let mut storages = 0;
1186 if tx.tx_type() != TransactionType::Legacy {
1188 (accounts, storages) = tx
1189 .access_list()
1190 .map(|al| {
1191 al.fold((0, 0), |(num_accounts, num_storage_slots), item| {
1192 (
1193 num_accounts + 1,
1194 num_storage_slots + item.storage_slots().count() as u64,
1195 )
1196 })
1197 })
1198 .unwrap_or_default();
1199 }
1200
1201 self.initial_tx_gas(
1202 tx.input(),
1203 tx.kind().is_create(),
1204 accounts,
1205 storages,
1206 tx.authorization_list_len() as u64,
1207 eip2780,
1208 )
1209 }
1210}
1211
1212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1220pub struct Eip2780TxInfo {
1221 pub value: U256,
1223 pub is_self_transfer: bool,
1225}
1226
1227#[inline]
1228pub(crate) const fn log2floor(value: U256) -> u64 {
1229 255u64.saturating_sub(value.leading_zeros() as u64)
1230}
1231
1232#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1234pub struct GasId(u8);
1235
1236impl GasId {
1237 #[inline]
1239 pub const fn new(id: u8) -> Self {
1240 Self(id)
1241 }
1242
1243 #[inline]
1245 pub const fn as_u8(&self) -> u8 {
1246 self.0
1247 }
1248
1249 #[inline]
1251 pub const fn as_usize(&self) -> usize {
1252 self.0 as usize
1253 }
1254
1255 pub const fn name(&self) -> &'static str {
1267 match self.0 {
1268 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
1269 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
1270 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
1271 x if x == Self::logdata().as_u8() => "logdata",
1272 x if x == Self::logtopic().as_u8() => "logtopic",
1273 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
1274 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
1275 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
1276 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
1277 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
1278 x if x == Self::create().as_u8() => "create",
1279 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
1280 x if x == Self::max_refund_quotient().as_u8() => "max_refund_quotient",
1281 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
1282 x if x == Self::cold_account_additional_cost().as_u8() => {
1283 "cold_account_additional_cost"
1284 }
1285 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
1286 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
1287 x if x == Self::sstore_static().as_u8() => "sstore_static",
1288 x if x == Self::sstore_set_without_load_cost().as_u8() => {
1289 "sstore_set_without_load_cost"
1290 }
1291 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
1292 "sstore_reset_without_cold_load_cost"
1293 }
1294 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
1295 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
1296 x if x == Self::call_stipend().as_u8() => "call_stipend",
1297 x if x == Self::cold_storage_additional_cost().as_u8() => {
1298 "cold_storage_additional_cost"
1299 }
1300 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
1301 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
1302 "new_account_cost_for_selfdestruct"
1303 }
1304 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
1305 x if x == Self::tx_eip7702_regular_gas().as_u8() => "tx_eip7702_regular_gas",
1306 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
1307 "tx_token_non_zero_byte_multiplier"
1308 }
1309 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
1310 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
1311 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
1312 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
1313 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
1314 "tx_access_list_storage_key_cost"
1315 }
1316 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
1317 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
1318 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
1319 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
1320 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
1321 x if x == Self::tx_eip7702_regular_refund().as_u8() => "tx_eip7702_regular_refund",
1322 x if x == Self::sstore_set_state_gas().as_u8() => "sstore_set_state_gas",
1323 x if x == Self::new_account_state_gas().as_u8() => "new_account_state_gas",
1324 x if x == Self::code_deposit_state_gas().as_u8() => "code_deposit_state_gas",
1325 x if x == Self::create_state_gas().as_u8() => "create_state_gas",
1326 x if x == Self::tx_eip7702_state_gas_bytecode().as_u8() => {
1327 "tx_eip7702_state_gas_bytecode"
1328 }
1329 x if x == Self::tx_floor_token_zero_byte_multiplier().as_u8() => {
1330 "tx_floor_token_zero_byte_multiplier"
1331 }
1332 x if x == Self::tx_access_list_floor_byte_multiplier().as_u8() => {
1333 "tx_access_list_floor_byte_multiplier"
1334 }
1335 x if x == Self::tx_transfer_log_cost().as_u8() => "tx_transfer_log_cost",
1336 x if x == Self::tx_account_write_cost().as_u8() => "tx_account_write_cost",
1337 x if x == Self::tx_create_access_cost().as_u8() => "tx_create_access_cost",
1338 _ => "unknown",
1339 }
1340 }
1341
1342 pub fn from_name(s: &str) -> Option<GasId> {
1356 match s {
1357 "exp_byte_gas" => Some(Self::exp_byte_gas()),
1358 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
1359 "copy_per_word" => Some(Self::copy_per_word()),
1360 "logdata" => Some(Self::logdata()),
1361 "logtopic" => Some(Self::logtopic()),
1362 "mcopy_per_word" => Some(Self::mcopy_per_word()),
1363 "keccak256_per_word" => Some(Self::keccak256_per_word()),
1364 "memory_linear_cost" => Some(Self::memory_linear_cost()),
1365 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
1366 "initcode_per_word" => Some(Self::initcode_per_word()),
1367 "create" => Some(Self::create()),
1368 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
1369 "max_refund_quotient" => Some(Self::max_refund_quotient()),
1370 "transfer_value_cost" => Some(Self::transfer_value_cost()),
1371 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
1372 "new_account_cost" => Some(Self::new_account_cost()),
1373 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
1374 "sstore_static" => Some(Self::sstore_static()),
1375 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
1376 "sstore_reset_without_cold_load_cost" => {
1377 Some(Self::sstore_reset_without_cold_load_cost())
1378 }
1379 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
1380 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
1381 "call_stipend" => Some(Self::call_stipend()),
1382 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
1383 "cold_storage_cost" => Some(Self::cold_storage_cost()),
1384 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
1385 "code_deposit_cost" => Some(Self::code_deposit_cost()),
1386 "tx_eip7702_regular_gas" => Some(Self::tx_eip7702_regular_gas()),
1387 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
1388 "tx_token_cost" => Some(Self::tx_token_cost()),
1389 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
1390 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
1391 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
1392 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
1393 "tx_base_stipend" => Some(Self::tx_base_stipend()),
1394 "tx_create_cost" => Some(Self::tx_create_cost()),
1395 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
1396 "sstore_set_refund" => Some(Self::sstore_set_refund()),
1397 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
1398 "tx_eip7702_regular_refund" => Some(Self::tx_eip7702_regular_refund()),
1399 "sstore_set_state_gas" => Some(Self::sstore_set_state_gas()),
1400 "new_account_state_gas" => Some(Self::new_account_state_gas()),
1401 "code_deposit_state_gas" => Some(Self::code_deposit_state_gas()),
1402 "create_state_gas" => Some(Self::create_state_gas()),
1403 "tx_eip7702_state_gas_bytecode" => Some(Self::tx_eip7702_state_gas_bytecode()),
1404 "tx_floor_token_zero_byte_multiplier" => {
1405 Some(Self::tx_floor_token_zero_byte_multiplier())
1406 }
1407 "tx_access_list_floor_byte_multiplier" => {
1408 Some(Self::tx_access_list_floor_byte_multiplier())
1409 }
1410 "tx_transfer_log_cost" => Some(Self::tx_transfer_log_cost()),
1411 "tx_account_write_cost" => Some(Self::tx_account_write_cost()),
1412 "tx_create_access_cost" => Some(Self::tx_create_access_cost()),
1413 _ => None,
1414 }
1415 }
1416
1417 pub const fn exp_byte_gas() -> GasId {
1419 Self::new(1)
1420 }
1421
1422 pub const fn extcodecopy_per_word() -> GasId {
1424 Self::new(2)
1425 }
1426
1427 pub const fn copy_per_word() -> GasId {
1429 Self::new(3)
1430 }
1431
1432 pub const fn logdata() -> GasId {
1434 Self::new(4)
1435 }
1436
1437 pub const fn logtopic() -> GasId {
1439 Self::new(5)
1440 }
1441
1442 pub const fn mcopy_per_word() -> GasId {
1444 Self::new(6)
1445 }
1446
1447 pub const fn keccak256_per_word() -> GasId {
1449 Self::new(7)
1450 }
1451
1452 pub const fn memory_linear_cost() -> GasId {
1454 Self::new(8)
1455 }
1456
1457 pub const fn memory_quadratic_reduction() -> GasId {
1459 Self::new(9)
1460 }
1461
1462 pub const fn initcode_per_word() -> GasId {
1464 Self::new(10)
1465 }
1466
1467 pub const fn create() -> GasId {
1469 Self::new(11)
1470 }
1471
1472 pub const fn call_stipend_reduction() -> GasId {
1474 Self::new(12)
1475 }
1476
1477 pub const fn max_refund_quotient() -> GasId {
1479 Self::new(47)
1480 }
1481
1482 pub const fn transfer_value_cost() -> GasId {
1484 Self::new(13)
1485 }
1486
1487 pub const fn cold_account_additional_cost() -> GasId {
1489 Self::new(14)
1490 }
1491
1492 pub const fn new_account_cost() -> GasId {
1494 Self::new(15)
1495 }
1496
1497 pub const fn warm_storage_read_cost() -> GasId {
1501 Self::new(16)
1502 }
1503
1504 pub const fn sstore_static() -> GasId {
1507 Self::new(17)
1508 }
1509
1510 pub const fn sstore_set_without_load_cost() -> GasId {
1512 Self::new(18)
1513 }
1514
1515 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1517 Self::new(19)
1518 }
1519
1520 pub const fn sstore_clearing_slot_refund() -> GasId {
1522 Self::new(20)
1523 }
1524
1525 pub const fn selfdestruct_refund() -> GasId {
1527 Self::new(21)
1528 }
1529
1530 pub const fn call_stipend() -> GasId {
1532 Self::new(22)
1533 }
1534
1535 pub const fn cold_storage_additional_cost() -> GasId {
1537 Self::new(23)
1538 }
1539
1540 pub const fn cold_storage_cost() -> GasId {
1542 Self::new(24)
1543 }
1544
1545 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1547 Self::new(25)
1548 }
1549
1550 pub const fn code_deposit_cost() -> GasId {
1552 Self::new(26)
1553 }
1554
1555 pub const fn tx_eip7702_regular_gas() -> GasId {
1562 Self::new(27)
1563 }
1564
1565 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1567 Self::new(28)
1568 }
1569
1570 pub const fn tx_token_cost() -> GasId {
1572 Self::new(29)
1573 }
1574
1575 pub const fn tx_floor_cost_per_token() -> GasId {
1577 Self::new(30)
1578 }
1579
1580 pub const fn tx_floor_cost_base_gas() -> GasId {
1582 Self::new(31)
1583 }
1584
1585 pub const fn tx_access_list_address_cost() -> GasId {
1587 Self::new(32)
1588 }
1589
1590 pub const fn tx_access_list_storage_key_cost() -> GasId {
1592 Self::new(33)
1593 }
1594
1595 pub const fn tx_base_stipend() -> GasId {
1597 Self::new(34)
1598 }
1599
1600 pub const fn tx_create_cost() -> GasId {
1602 Self::new(35)
1603 }
1604
1605 pub const fn tx_initcode_cost() -> GasId {
1607 Self::new(36)
1608 }
1609
1610 pub const fn sstore_set_refund() -> GasId {
1612 Self::new(37)
1613 }
1614
1615 pub const fn sstore_reset_refund() -> GasId {
1617 Self::new(38)
1618 }
1619
1620 pub const fn tx_eip7702_regular_refund() -> GasId {
1628 Self::new(39)
1629 }
1630
1631 pub const fn sstore_set_state_gas() -> GasId {
1633 Self::new(40)
1634 }
1635
1636 pub const fn new_account_state_gas() -> GasId {
1638 Self::new(41)
1639 }
1640
1641 pub const fn code_deposit_state_gas() -> GasId {
1643 Self::new(42)
1644 }
1645
1646 pub const fn create_state_gas() -> GasId {
1648 Self::new(43)
1649 }
1650
1651 pub const fn tx_eip7702_state_gas_bytecode() -> GasId {
1655 Self::new(44)
1656 }
1657
1658 pub const fn tx_floor_token_zero_byte_multiplier() -> GasId {
1665 Self::new(45)
1666 }
1667
1668 pub const fn tx_access_list_floor_byte_multiplier() -> GasId {
1674 Self::new(46)
1675 }
1676
1677 pub const fn tx_transfer_log_cost() -> GasId {
1680 Self::new(48)
1681 }
1682
1683 pub const fn tx_account_write_cost() -> GasId {
1687 Self::new(49)
1688 }
1689
1690 pub const fn tx_create_access_cost() -> GasId {
1694 Self::new(50)
1695 }
1696}
1697
1698#[cfg(test)]
1699mod tests {
1700 use super::*;
1701 use std::collections::HashSet;
1702
1703 #[cfg(test)]
1704 mod log2floor_tests {
1705 use super::*;
1706
1707 #[test]
1708 fn test_log2floor_edge_cases() {
1709 assert_eq!(log2floor(U256::ZERO), 0);
1711
1712 assert_eq!(log2floor(U256::from(1u64)), 0); assert_eq!(log2floor(U256::from(2u64)), 1); assert_eq!(log2floor(U256::from(4u64)), 2); assert_eq!(log2floor(U256::from(8u64)), 3); assert_eq!(log2floor(U256::from(256u64)), 8); assert_eq!(log2floor(U256::from(3u64)), 1); assert_eq!(log2floor(U256::from(5u64)), 2); assert_eq!(log2floor(U256::from(255u64)), 7); assert_eq!(log2floor(U256::from(u64::MAX)), 63);
1726 assert_eq!(log2floor(U256::from(u64::MAX) + U256::from(1u64)), 64);
1727 assert_eq!(log2floor(U256::MAX), 255);
1728 }
1729 }
1730
1731 #[test]
1732 fn test_gas_id_name_and_from_str_coverage() {
1733 let mut unique_names = HashSet::new();
1734 let mut known_gas_ids = 0;
1735
1736 for i in 0..=255 {
1738 let gas_id = GasId::new(i);
1739 let name = gas_id.name();
1740
1741 if name != "unknown" {
1743 unique_names.insert(name);
1744 }
1745 }
1746
1747 for name in &unique_names {
1749 if let Some(gas_id) = GasId::from_name(name) {
1750 known_gas_ids += 1;
1751 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1753 }
1754 }
1755
1756 println!("Total unique named GasIds: {}", unique_names.len());
1757 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1758
1759 assert_eq!(
1761 unique_names.len(),
1762 known_gas_ids,
1763 "Not all unique names are resolvable via from_str"
1764 );
1765
1766 assert_eq!(
1768 unique_names.len(),
1769 50,
1770 "Expected 50 unique GasIds, found {}",
1771 unique_names.len()
1772 );
1773 }
1774
1775 #[test]
1776 fn test_max_refund_quotient_defaults_and_override() {
1777 let frontier = GasParams::new_spec(SpecId::FRONTIER);
1778 assert_eq!(frontier.max_refund_quotient(), 2);
1779 assert_eq!(frontier.get(GasId::max_refund_quotient()), 2);
1780
1781 let london = GasParams::new_spec(SpecId::LONDON);
1782 assert_eq!(london.max_refund_quotient(), 5);
1783 assert_eq!(
1784 GasId::from_name("max_refund_quotient"),
1785 Some(GasId::max_refund_quotient())
1786 );
1787 assert_eq!(GasId::max_refund_quotient().name(), "max_refund_quotient");
1788
1789 let mut custom = london;
1790 custom.override_gas([(GasId::max_refund_quotient(), 10)]);
1791 assert_eq!(custom.max_refund_quotient(), 10);
1792 }
1793
1794 #[test]
1795 fn test_tx_access_list_cost() {
1796 use crate::cfg::gas;
1797
1798 let gas_params = GasParams::new_spec(SpecId::BERLIN);
1800
1801 assert_eq!(gas_params.tx_access_list_cost(0, 0), 0);
1803
1804 assert_eq!(
1806 gas_params.tx_access_list_cost(1, 0),
1807 gas::ACCESS_LIST_ADDRESS
1808 );
1809
1810 assert_eq!(
1812 gas_params.tx_access_list_cost(0, 1),
1813 gas::ACCESS_LIST_STORAGE_KEY
1814 );
1815
1816 assert_eq!(
1818 gas_params.tx_access_list_cost(2, 5),
1819 2 * gas::ACCESS_LIST_ADDRESS + 5 * gas::ACCESS_LIST_STORAGE_KEY
1820 );
1821
1822 assert_eq!(
1824 gas_params.tx_access_list_cost(100, 200),
1825 100 * gas::ACCESS_LIST_ADDRESS + 200 * gas::ACCESS_LIST_STORAGE_KEY
1826 );
1827
1828 let gas_params_pre_berlin = GasParams::new_spec(SpecId::ISTANBUL);
1830 assert_eq!(gas_params_pre_berlin.tx_access_list_cost(10, 20), 0);
1831 }
1832
1833 #[test]
1834 fn test_initial_state_gas_for_create() {
1835 let gas_params = GasParams::new_spec(SpecId::AMSTERDAM);
1837 let create_gas = gas_params.initial_tx_gas(b"", true, 0, 0, 0, None);
1839 let expected_state_gas = gas_params.create_state_gas();
1840
1841 assert_eq!(create_gas.initial_state_gas_final(), expected_state_gas);
1842 assert_eq!(
1843 create_gas.initial_state_gas_final(),
1844 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM
1845 );
1846
1847 let create_cost = gas_params.tx_create_cost();
1849 let initcode_cost = gas_params.tx_initcode_cost(0);
1850 assert_eq!(
1851 create_gas.initial_total_gas(),
1852 gas_params.tx_base_stipend() + create_cost + initcode_cost + expected_state_gas
1853 );
1854
1855 let call_gas = gas_params.initial_tx_gas(b"", false, 0, 0, 0, None);
1857 assert_eq!(call_gas.initial_state_gas_final(), 0);
1858 assert_eq!(call_gas.initial_total_gas(), gas_params.tx_base_stipend());
1860 }
1861
1862 #[test]
1863 fn test_eip7981_access_list_cost_amsterdam() {
1864 let params = GasParams::new_spec(SpecId::AMSTERDAM);
1869
1870 assert_eq!(params.tx_access_list_address_cost(), 3000 + 20 * 64);
1872 assert_eq!(params.tx_access_list_storage_key_cost(), 3000 + 32 * 64);
1873 assert_eq!(params.tx_access_list_cost(1, 0), 3000 + 20 * 64);
1874 assert_eq!(params.tx_access_list_cost(0, 1), 3000 + 32 * 64);
1875
1876 assert_eq!(params.tx_access_list_floor_byte_multiplier(), 4);
1878 assert_eq!(params.tx_floor_tokens_in_access_list(2, 3), (40 + 96) * 4);
1880
1881 let gas = params.initial_tx_gas(b"", false, 2, 3, 0, None);
1883 let expected_al_floor = (40 + 96) * 4 * params.tx_floor_cost_per_token();
1884 assert_eq!(
1885 gas.floor_gas(),
1886 params.tx_floor_cost_base_gas() + expected_al_floor,
1887 );
1888
1889 let prague = GasParams::new_spec(SpecId::PRAGUE);
1891 assert_eq!(prague.tx_access_list_floor_byte_multiplier(), 0);
1892 assert_eq!(prague.tx_floor_tokens_in_access_list(2, 3), 0);
1893 let prague_gas = prague.initial_tx_gas(b"", false, 2, 3, 0, None);
1894 assert_eq!(prague_gas.floor_gas(), prague.tx_floor_cost_base_gas());
1895 }
1896}