1use crate::{
4 cfg::gas::{self, get_tokens_in_calldata, InitialAndFloorGas},
5 context::SStoreResult,
6};
7use core::hash::{Hash, Hasher};
8use primitives::{
9 eip7702, eip8037,
10 hardfork::SpecId::{self},
11 OnceLock, U256,
12};
13use std::sync::Arc;
14
15#[derive(Clone)]
17pub struct GasParams {
18 table: Arc<[u64; 256]>,
20}
21
22impl PartialEq<GasParams> for GasParams {
23 fn eq(&self, other: &GasParams) -> bool {
24 self.table == other.table
25 }
26}
27
28impl Hash for GasParams {
29 fn hash<H: Hasher>(&self, hasher: &mut H) {
30 self.table.hash(hasher);
31 }
32}
33
34impl core::fmt::Debug for GasParams {
35 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36 write!(f, "GasParams {{ table: {:?} }}", self.table)
37 }
38}
39
40#[inline]
43pub const fn num_words(len: usize) -> usize {
44 len.div_ceil(32)
45}
46
47impl Eq for GasParams {}
48#[cfg(feature = "serde")]
49mod serde {
50 use super::{Arc, GasParams};
51 use std::vec::Vec;
52
53 #[derive(serde::Serialize, serde::Deserialize)]
54 struct GasParamsSerde {
55 table: Vec<u64>,
56 }
57
58 #[cfg(feature = "serde")]
59 impl serde::Serialize for GasParams {
60 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
61 where
62 S: serde::Serializer,
63 {
64 GasParamsSerde {
65 table: self.table.to_vec(),
66 }
67 .serialize(serializer)
68 }
69 }
70
71 impl<'de> serde::Deserialize<'de> for GasParams {
72 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73 where
74 D: serde::Deserializer<'de>,
75 {
76 let table = GasParamsSerde::deserialize(deserializer)?;
77 if table.table.len() != 256 {
78 return Err(serde::de::Error::custom("Invalid gas params length"));
79 }
80 Ok(Self::new(Arc::new(table.table.try_into().unwrap())))
81 }
82 }
83}
84
85impl Default for GasParams {
86 #[inline]
87 fn default() -> Self {
88 Self::new_spec(SpecId::default())
89 }
90}
91
92impl GasParams {
93 #[inline]
95 pub const fn new(table: Arc<[u64; 256]>) -> Self {
96 Self { table }
97 }
98
99 pub fn override_gas(&mut self, values: impl IntoIterator<Item = (GasId, u64)>) {
115 let mut table = *self.table.clone();
116 for (id, value) in values.into_iter() {
117 table[id.as_usize()] = value;
118 }
119 *self = Self::new(Arc::new(table));
120 }
121
122 #[inline]
124 pub fn table(&self) -> &[u64; 256] {
125 &self.table
126 }
127
128 #[inline(never)]
130 pub fn new_spec(spec: SpecId) -> Self {
131 use SpecId::*;
132 let gas_params = match spec {
133 FRONTIER => {
134 static TABLE: OnceLock<GasParams> = OnceLock::new();
135 TABLE.get_or_init(|| Self::new_spec_inner(spec))
136 }
137 HOMESTEAD => {
139 static TABLE: OnceLock<GasParams> = OnceLock::new();
140 TABLE.get_or_init(|| Self::new_spec_inner(spec))
141 }
142 TANGERINE => {
144 static TABLE: OnceLock<GasParams> = OnceLock::new();
145 TABLE.get_or_init(|| Self::new_spec_inner(spec))
146 }
147 SPURIOUS_DRAGON | BYZANTIUM | PETERSBURG => {
149 static TABLE: OnceLock<GasParams> = OnceLock::new();
150 TABLE.get_or_init(|| Self::new_spec_inner(spec))
151 }
152 ISTANBUL => {
154 static TABLE: OnceLock<GasParams> = OnceLock::new();
155 TABLE.get_or_init(|| Self::new_spec_inner(spec))
156 }
157 BERLIN => {
159 static TABLE: OnceLock<GasParams> = OnceLock::new();
160 TABLE.get_or_init(|| Self::new_spec_inner(spec))
161 }
162 LONDON | MERGE => {
164 static TABLE: OnceLock<GasParams> = OnceLock::new();
165 TABLE.get_or_init(|| Self::new_spec_inner(spec))
166 }
167 SHANGHAI | CANCUN => {
169 static TABLE: OnceLock<GasParams> = OnceLock::new();
170 TABLE.get_or_init(|| Self::new_spec_inner(spec))
171 }
172 PRAGUE | OSAKA => {
174 static TABLE: OnceLock<GasParams> = OnceLock::new();
175 TABLE.get_or_init(|| Self::new_spec_inner(spec))
176 }
177 SpecId::AMSTERDAM => {
179 static TABLE: OnceLock<GasParams> = OnceLock::new();
180 TABLE.get_or_init(|| Self::new_spec_inner(spec))
181 }
182 };
183 gas_params.clone()
184 }
185
186 #[inline]
188 fn new_spec_inner(spec: SpecId) -> Self {
189 let mut table = [0; 256];
190
191 table[GasId::exp_byte_gas().as_usize()] = 10;
192 table[GasId::logdata().as_usize()] = gas::LOGDATA;
193 table[GasId::logtopic().as_usize()] = gas::LOGTOPIC;
194 table[GasId::copy_per_word().as_usize()] = gas::COPY;
195 table[GasId::extcodecopy_per_word().as_usize()] = gas::COPY;
196 table[GasId::mcopy_per_word().as_usize()] = gas::COPY;
197 table[GasId::keccak256_per_word().as_usize()] = gas::KECCAK256WORD;
198 table[GasId::memory_linear_cost().as_usize()] = gas::MEMORY;
199 table[GasId::memory_quadratic_reduction().as_usize()] = 512;
200 table[GasId::initcode_per_word().as_usize()] = gas::INITCODE_WORD_COST;
201 table[GasId::create().as_usize()] = gas::CREATE;
202 table[GasId::call_stipend_reduction().as_usize()] = 64;
203 table[GasId::transfer_value_cost().as_usize()] = gas::CALLVALUE;
204 table[GasId::cold_account_additional_cost().as_usize()] = 0;
205 table[GasId::new_account_cost().as_usize()] = gas::NEWACCOUNT;
206 table[GasId::warm_storage_read_cost().as_usize()] = 0;
207 table[GasId::sstore_static().as_usize()] = gas::SSTORE_RESET;
209 table[GasId::sstore_set_without_load_cost().as_usize()] =
211 gas::SSTORE_SET - gas::SSTORE_RESET;
212 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = 0;
214 table[GasId::sstore_set_refund().as_usize()] =
216 table[GasId::sstore_set_without_load_cost().as_usize()];
217 table[GasId::sstore_reset_refund().as_usize()] =
219 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
220 table[GasId::sstore_clearing_slot_refund().as_usize()] = 15000;
222 table[GasId::selfdestruct_refund().as_usize()] = 24000;
223 table[GasId::call_stipend().as_usize()] = gas::CALL_STIPEND;
224 table[GasId::cold_storage_additional_cost().as_usize()] = 0;
225 table[GasId::cold_storage_cost().as_usize()] = 0;
226 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
227 table[GasId::code_deposit_cost().as_usize()] = gas::CODEDEPOSIT;
228 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
229 gas::NON_ZERO_BYTE_MULTIPLIER;
230 table[GasId::tx_token_cost().as_usize()] = gas::STANDARD_TOKEN_COST;
231 table[GasId::tx_base_stipend().as_usize()] = 21000;
232
233 if spec.is_enabled_in(SpecId::HOMESTEAD) {
234 table[GasId::tx_create_cost().as_usize()] = gas::CREATE;
235 }
236
237 if spec.is_enabled_in(SpecId::TANGERINE) {
238 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = gas::NEWACCOUNT;
239 }
240
241 if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
242 table[GasId::exp_byte_gas().as_usize()] = 50;
243 }
244
245 if spec.is_enabled_in(SpecId::ISTANBUL) {
246 table[GasId::sstore_static().as_usize()] = gas::ISTANBUL_SLOAD_GAS;
247 table[GasId::sstore_set_without_load_cost().as_usize()] =
248 gas::SSTORE_SET - gas::ISTANBUL_SLOAD_GAS;
249 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
250 gas::SSTORE_RESET - gas::ISTANBUL_SLOAD_GAS;
251 table[GasId::sstore_set_refund().as_usize()] =
252 table[GasId::sstore_set_without_load_cost().as_usize()];
253 table[GasId::sstore_reset_refund().as_usize()] =
254 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
255 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
256 gas::NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
257 }
258
259 if spec.is_enabled_in(SpecId::BERLIN) {
260 table[GasId::sstore_static().as_usize()] = gas::WARM_STORAGE_READ_COST;
261 table[GasId::cold_account_additional_cost().as_usize()] =
262 gas::COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
263 table[GasId::cold_storage_additional_cost().as_usize()] =
264 gas::COLD_SLOAD_COST - gas::WARM_STORAGE_READ_COST;
265 table[GasId::cold_storage_cost().as_usize()] = gas::COLD_SLOAD_COST;
266 table[GasId::warm_storage_read_cost().as_usize()] = gas::WARM_STORAGE_READ_COST;
267
268 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
269 gas::WARM_SSTORE_RESET - gas::WARM_STORAGE_READ_COST;
270 table[GasId::sstore_set_without_load_cost().as_usize()] =
271 gas::SSTORE_SET - gas::WARM_STORAGE_READ_COST;
272 table[GasId::sstore_set_refund().as_usize()] =
273 table[GasId::sstore_set_without_load_cost().as_usize()];
274 table[GasId::sstore_reset_refund().as_usize()] =
275 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
276
277 table[GasId::tx_access_list_address_cost().as_usize()] = gas::ACCESS_LIST_ADDRESS;
278 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
279 gas::ACCESS_LIST_STORAGE_KEY;
280 }
281
282 if spec.is_enabled_in(SpecId::LONDON) {
283 table[GasId::sstore_clearing_slot_refund().as_usize()] =
288 gas::WARM_SSTORE_RESET + gas::ACCESS_LIST_STORAGE_KEY;
289
290 table[GasId::selfdestruct_refund().as_usize()] = 0;
291 }
292
293 if spec.is_enabled_in(SpecId::SHANGHAI) {
294 table[GasId::tx_initcode_cost().as_usize()] = gas::INITCODE_WORD_COST;
295 }
296
297 if spec.is_enabled_in(SpecId::PRAGUE) {
298 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] =
299 eip7702::PER_EMPTY_ACCOUNT_COST;
300
301 table[GasId::tx_eip7702_auth_refund().as_usize()] =
303 eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST;
304
305 table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
306 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
307 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] = 1;
310 }
311
312 if spec.is_enabled_in(SpecId::AMSTERDAM) {
317 table[GasId::create().as_usize()] = 9000;
319 table[GasId::tx_create_cost().as_usize()] = 9000;
320 table[GasId::code_deposit_cost().as_usize()] = 0;
321 table[GasId::new_account_cost().as_usize()] = 0;
322 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
323 table[GasId::sstore_set_without_load_cost().as_usize()] = 2800;
326
327 table[GasId::sstore_set_state_gas().as_usize()] = eip8037::SSTORE_SET_BYTES;
329 table[GasId::new_account_state_gas().as_usize()] = eip8037::NEW_ACCOUNT_BYTES;
330 table[GasId::code_deposit_state_gas().as_usize()] = eip8037::CODE_DEPOSIT_PER_BYTE;
331 table[GasId::create_state_gas().as_usize()] = eip8037::NEW_ACCOUNT_BYTES;
332 table[GasId::tx_eip7702_state_gas_bytecode().as_usize()] = eip8037::AUTH_BASE_BYTES;
333
334 table[GasId::sstore_set_refund().as_usize()] = 2800;
338
339 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] =
346 eip8037::EIP7702_PER_EMPTY_ACCOUNT_REGULAR;
347 table[GasId::tx_eip7702_auth_refund().as_usize()] = 0;
348
349 table[GasId::tx_floor_cost_per_token().as_usize()] = 16;
354 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] =
355 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()];
356
357 table[GasId::tx_access_list_address_cost().as_usize()] =
363 gas::ACCESS_LIST_ADDRESS + 20 * 64;
364 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
365 gas::ACCESS_LIST_STORAGE_KEY + 32 * 64;
366 table[GasId::tx_access_list_floor_byte_multiplier().as_usize()] = 4;
367 }
368
369 Self::new(Arc::new(table))
370 }
371
372 #[inline]
374 pub fn get(&self, id: GasId) -> u64 {
375 self.table[id.as_usize()]
376 }
377
378 #[inline]
380 pub fn exp_cost(&self, power: U256) -> u64 {
381 if power.is_zero() {
382 return 0;
383 }
384 self.get(GasId::exp_byte_gas())
386 .saturating_mul(log2floor(power) / 8 + 1)
387 }
388
389 #[inline]
391 pub fn selfdestruct_refund(&self) -> i64 {
392 self.get(GasId::selfdestruct_refund()) as i64
393 }
394
395 #[inline]
398 pub fn selfdestruct_cold_cost(&self) -> u64 {
399 self.cold_account_additional_cost() + self.warm_storage_read_cost()
400 }
401
402 #[inline]
404 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
405 let mut gas = 0;
406
407 if should_charge_topup {
409 gas += self.new_account_cost_for_selfdestruct();
410 }
411
412 if is_cold {
413 gas += self.selfdestruct_cold_cost();
419 }
420 gas
421 }
422
423 #[inline]
425 pub fn extcodecopy(&self, len: usize) -> u64 {
426 self.get(GasId::extcodecopy_per_word())
427 .saturating_mul(num_words(len) as u64)
428 }
429
430 #[inline]
432 pub fn mcopy_cost(&self, len: usize) -> u64 {
433 self.get(GasId::mcopy_per_word())
434 .saturating_mul(num_words(len) as u64)
435 }
436
437 #[inline]
439 pub fn sstore_static_gas(&self) -> u64 {
440 self.get(GasId::sstore_static())
441 }
442
443 #[inline]
445 pub fn sstore_set_without_load_cost(&self) -> u64 {
446 self.get(GasId::sstore_set_without_load_cost())
447 }
448
449 #[inline]
451 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
452 self.get(GasId::sstore_reset_without_cold_load_cost())
453 }
454
455 #[inline]
457 pub fn sstore_clearing_slot_refund(&self) -> u64 {
458 self.get(GasId::sstore_clearing_slot_refund())
459 }
460
461 #[inline]
463 pub fn sstore_set_refund(&self) -> u64 {
464 self.get(GasId::sstore_set_refund())
465 }
466
467 #[inline]
469 pub fn sstore_reset_refund(&self) -> u64 {
470 self.get(GasId::sstore_reset_refund())
471 }
472
473 #[inline]
477 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
478 if !is_istanbul {
481 if vals.is_present_zero() && !vals.is_new_zero() {
482 return self.sstore_set_without_load_cost();
483 } else {
484 return self.sstore_reset_without_cold_load_cost();
485 }
486 }
487
488 let mut gas = 0;
489
490 if is_cold {
492 gas += self.cold_storage_cost();
493 }
494
495 if vals.new_values_changes_present() && vals.is_original_eq_present() {
497 gas += if vals.is_original_zero() {
498 self.sstore_set_without_load_cost()
501 } else {
502 self.sstore_reset_without_cold_load_cost()
504 };
505 }
506 gas
507 }
508
509 #[inline]
511 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
512 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
514
515 if !is_istanbul {
516 if !vals.is_present_zero() && vals.is_new_zero() {
518 return sstore_clearing_slot_refund;
519 }
520 return 0;
521 }
522
523 if vals.is_new_eq_present() {
525 return 0;
526 }
527
528 if vals.is_original_eq_present() && vals.is_new_zero() {
531 return sstore_clearing_slot_refund;
532 }
533
534 let mut refund = 0;
535 if !vals.is_original_zero() {
537 if vals.is_present_zero() {
539 refund -= sstore_clearing_slot_refund;
541 } else if vals.is_new_zero() {
543 refund += sstore_clearing_slot_refund;
545 }
546 }
547
548 if vals.is_original_eq_new() {
550 if vals.is_original_zero() {
552 refund += self.sstore_set_refund() as i64;
554 } else {
556 refund += self.sstore_reset_refund() as i64;
558 }
559 }
560 refund
561 }
562
563 #[inline]
565 pub fn log_cost(&self, n: u8, len: u64) -> u64 {
566 self.get(GasId::logdata())
567 .saturating_mul(len)
568 .saturating_add(self.get(GasId::logtopic()) * n as u64)
569 }
570
571 #[inline]
573 pub fn keccak256_cost(&self, len: usize) -> u64 {
574 self.get(GasId::keccak256_per_word())
575 .saturating_mul(num_words(len) as u64)
576 }
577
578 #[inline]
580 pub fn memory_cost(&self, len: usize) -> u64 {
581 let len = len as u64;
582 self.get(GasId::memory_linear_cost())
583 .saturating_mul(len)
584 .saturating_add(
585 (len.saturating_mul(len))
586 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
587 )
588 }
589
590 #[inline]
592 pub fn initcode_cost(&self, len: usize) -> u64 {
593 self.get(GasId::initcode_per_word())
594 .saturating_mul(num_words(len) as u64)
595 }
596
597 #[inline]
599 pub fn create_cost(&self) -> u64 {
600 self.get(GasId::create())
601 }
602
603 #[inline]
605 pub fn create2_cost(&self, len: usize) -> u64 {
606 self.get(GasId::create()).saturating_add(
607 self.get(GasId::keccak256_per_word())
608 .saturating_mul(num_words(len) as u64),
609 )
610 }
611
612 #[inline]
614 pub fn call_stipend(&self) -> u64 {
615 self.get(GasId::call_stipend())
616 }
617
618 #[inline]
620 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
621 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
622 }
623
624 #[inline]
626 pub fn transfer_value_cost(&self) -> u64 {
627 self.get(GasId::transfer_value_cost())
628 }
629
630 #[inline]
632 pub fn cold_account_additional_cost(&self) -> u64 {
633 self.get(GasId::cold_account_additional_cost())
634 }
635
636 #[inline]
638 pub fn cold_storage_additional_cost(&self) -> u64 {
639 self.get(GasId::cold_storage_additional_cost())
640 }
641
642 #[inline]
644 pub fn cold_storage_cost(&self) -> u64 {
645 self.get(GasId::cold_storage_cost())
646 }
647
648 #[inline]
650 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
651 if !is_spurious_dragon || transfers_value {
655 return self.get(GasId::new_account_cost());
656 }
657 0
658 }
659
660 #[inline]
662 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
663 self.get(GasId::new_account_cost_for_selfdestruct())
664 }
665
666 #[inline]
668 pub fn warm_storage_read_cost(&self) -> u64 {
669 self.get(GasId::warm_storage_read_cost())
670 }
671
672 #[inline]
674 pub fn copy_cost(&self, len: usize) -> u64 {
675 self.copy_per_word_cost(num_words(len))
676 }
677
678 #[inline]
680 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
681 self.get(GasId::copy_per_word())
682 .saturating_mul(word_num as u64)
683 }
684
685 #[inline]
687 pub fn code_deposit_cost(&self, len: usize) -> u64 {
688 self.get(GasId::code_deposit_cost())
689 .saturating_mul(len as u64)
690 }
691
692 #[inline]
694 pub fn sstore_state_gas(&self, vals: &SStoreResult, cpsb: u64) -> u64 {
695 if vals.new_values_changes_present()
696 && vals.is_original_eq_present()
697 && vals.is_original_zero()
698 {
699 self.get(GasId::sstore_set_state_gas()).saturating_mul(cpsb)
700 } else {
701 0
702 }
703 }
704
705 #[inline]
714 pub fn sstore_state_gas_refill(&self, vals: &SStoreResult, cpsb: u64) -> u64 {
715 if !vals.is_new_eq_present() && vals.is_original_eq_new() && vals.is_original_zero() {
716 self.get(GasId::sstore_set_state_gas()).saturating_mul(cpsb)
717 } else {
718 0
719 }
720 }
721
722 #[inline]
724 pub fn new_account_state_gas(&self, cpsb: u64) -> u64 {
725 self.get(GasId::new_account_state_gas())
726 .saturating_mul(cpsb)
727 }
728
729 #[inline]
731 pub fn code_deposit_state_gas(&self, len: usize, cpsb: u64) -> u64 {
732 self.get(GasId::code_deposit_state_gas())
733 .saturating_mul(len as u64)
734 .saturating_mul(cpsb)
735 }
736
737 #[inline]
739 pub fn create_state_gas(&self, cpsb: u64) -> u64 {
740 self.get(GasId::create_state_gas()).saturating_mul(cpsb)
741 }
742
743 #[inline]
749 pub fn tx_eip7702_per_empty_account_cost(&self, cpsb: u64) -> u64 {
750 let regular = self.get(GasId::tx_eip7702_per_empty_account_cost());
751 let state = self.tx_eip7702_state_gas(cpsb);
752 regular.saturating_add(state)
753 }
754
755 #[inline]
761 pub fn tx_eip7702_auth_refund(&self, cpsb: u64) -> u64 {
762 let regular = self.get(GasId::tx_eip7702_auth_refund());
763 let state = self.new_account_state_gas(cpsb);
764 regular.saturating_add(state)
765 }
766
767 #[inline]
772 pub fn tx_eip7702_state_gas(&self, cpsb: u64) -> u64 {
773 let new_account = self.get(GasId::new_account_state_gas());
774 let bytecode = self.get(GasId::tx_eip7702_state_gas_bytecode());
775 new_account.saturating_add(bytecode).saturating_mul(cpsb)
776 }
777
778 #[inline]
786 pub fn tx_eip7702_state_refund(
787 &self,
788 refunded_accounts: u64,
789 refunded_bytecodes: u64,
790 cpsb: u64,
791 ) -> u64 {
792 let per_account = self
793 .get(GasId::new_account_state_gas())
794 .saturating_mul(refunded_accounts);
795 let per_bytecode = self
796 .get(GasId::tx_eip7702_state_gas_bytecode())
797 .saturating_mul(refunded_bytecodes);
798 per_account
799 .saturating_add(per_bytecode)
800 .saturating_mul(cpsb)
801 }
802
803 #[inline]
808 pub fn tx_eip7702_auth_refund_regular(&self) -> u64 {
809 self.get(GasId::tx_eip7702_auth_refund())
810 }
811
812 #[inline]
814 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
815 self.get(GasId::tx_token_non_zero_byte_multiplier())
816 }
817
818 #[inline]
820 pub fn tx_token_cost(&self) -> u64 {
821 self.get(GasId::tx_token_cost())
822 }
823
824 pub fn tx_floor_cost_per_token(&self) -> u64 {
826 self.get(GasId::tx_floor_cost_per_token())
827 }
828
829 pub fn tx_floor_token_zero_byte_multiplier(&self) -> u64 {
837 self.get(GasId::tx_floor_token_zero_byte_multiplier())
838 }
839
840 #[inline]
851 pub fn tx_floor_cost(&self, input: &[u8]) -> u64 {
852 let zero_multiplier = self.tx_floor_token_zero_byte_multiplier();
853 let non_zero_multiplier = self.tx_token_non_zero_byte_multiplier();
854 let floor_tokens = if zero_multiplier == non_zero_multiplier {
855 input.len() as u64 * non_zero_multiplier
856 } else {
857 get_tokens_in_calldata(input, non_zero_multiplier)
858 };
859 self.tx_floor_cost_with_tokens(floor_tokens)
860 }
861
862 #[inline]
864 pub fn tx_floor_cost_with_tokens(&self, tokens: u64) -> u64 {
865 self.tx_floor_cost_per_token() * tokens + self.tx_floor_cost_base_gas()
866 }
867
868 pub fn tx_floor_cost_base_gas(&self) -> u64 {
870 self.get(GasId::tx_floor_cost_base_gas())
871 }
872
873 pub fn tx_access_list_address_cost(&self) -> u64 {
875 self.get(GasId::tx_access_list_address_cost())
876 }
877
878 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
880 self.get(GasId::tx_access_list_storage_key_cost())
881 }
882
883 #[inline]
901 pub fn tx_access_list_cost(&self, accounts: u64, storages: u64) -> u64 {
902 accounts
903 .saturating_mul(self.tx_access_list_address_cost())
904 .saturating_add(storages.saturating_mul(self.tx_access_list_storage_key_cost()))
905 }
906
907 #[inline]
915 pub fn tx_access_list_floor_byte_multiplier(&self) -> u64 {
916 self.get(GasId::tx_access_list_floor_byte_multiplier())
917 }
918
919 #[inline]
924 pub fn tx_floor_tokens_in_access_list(&self, accounts: u64, storages: u64) -> u64 {
925 let bytes = accounts
926 .saturating_mul(20)
927 .saturating_add(storages.saturating_mul(32));
928 bytes.saturating_mul(self.tx_access_list_floor_byte_multiplier())
929 }
930
931 pub fn tx_base_stipend(&self) -> u64 {
933 self.get(GasId::tx_base_stipend())
934 }
935
936 #[inline]
940 pub fn tx_create_cost(&self) -> u64 {
941 self.get(GasId::tx_create_cost())
942 }
943
944 #[inline]
946 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
947 self.get(GasId::tx_initcode_cost())
948 .saturating_mul(num_words(len) as u64)
949 }
950
951 pub fn initial_tx_gas(
967 &self,
968 input: &[u8],
969 is_create: bool,
970 access_list_accounts: u64,
971 access_list_storages: u64,
972 authorization_list_num: u64,
973 cpsb: u64,
974 ) -> InitialAndFloorGas {
975 let tokens_in_calldata =
977 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
978
979 let auth_total_cost = authorization_list_num * self.tx_eip7702_per_empty_account_cost(cpsb);
983 let auth_state_gas = authorization_list_num * self.tx_eip7702_state_gas(cpsb);
984
985 let auth_regular_cost = auth_total_cost - auth_state_gas;
986
987 let mut initial_regular_gas = tokens_in_calldata * self.tx_token_cost()
988 + access_list_accounts * self.tx_access_list_address_cost()
990 + access_list_storages * self.tx_access_list_storage_key_cost()
992 + self.tx_base_stipend()
993 + auth_regular_cost;
995
996 let mut initial_state_gas = auth_state_gas;
998
999 if is_create {
1000 initial_regular_gas += self.tx_create_cost();
1002
1003 initial_regular_gas += self.tx_initcode_cost(input.len());
1005
1006 initial_state_gas += self.create_state_gas(cpsb);
1009 }
1010
1011 let access_list_floor_tokens =
1014 self.tx_floor_tokens_in_access_list(access_list_accounts, access_list_storages);
1015 let floor_gas =
1016 self.tx_floor_cost(input) + access_list_floor_tokens * self.tx_floor_cost_per_token();
1017
1018 InitialAndFloorGas::default()
1019 .with_initial_regular_gas(initial_regular_gas)
1020 .with_initial_state_gas(initial_state_gas)
1021 .with_floor_gas(floor_gas)
1022 }
1023}
1024
1025#[inline]
1026pub(crate) const fn log2floor(value: U256) -> u64 {
1027 255u64.saturating_sub(value.leading_zeros() as u64)
1028}
1029
1030#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1032pub struct GasId(u8);
1033
1034impl GasId {
1035 #[inline]
1037 pub const fn new(id: u8) -> Self {
1038 Self(id)
1039 }
1040
1041 #[inline]
1043 pub const fn as_u8(&self) -> u8 {
1044 self.0
1045 }
1046
1047 #[inline]
1049 pub const fn as_usize(&self) -> usize {
1050 self.0 as usize
1051 }
1052
1053 pub const fn name(&self) -> &'static str {
1065 match self.0 {
1066 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
1067 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
1068 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
1069 x if x == Self::logdata().as_u8() => "logdata",
1070 x if x == Self::logtopic().as_u8() => "logtopic",
1071 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
1072 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
1073 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
1074 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
1075 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
1076 x if x == Self::create().as_u8() => "create",
1077 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
1078 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
1079 x if x == Self::cold_account_additional_cost().as_u8() => {
1080 "cold_account_additional_cost"
1081 }
1082 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
1083 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
1084 x if x == Self::sstore_static().as_u8() => "sstore_static",
1085 x if x == Self::sstore_set_without_load_cost().as_u8() => {
1086 "sstore_set_without_load_cost"
1087 }
1088 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
1089 "sstore_reset_without_cold_load_cost"
1090 }
1091 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
1092 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
1093 x if x == Self::call_stipend().as_u8() => "call_stipend",
1094 x if x == Self::cold_storage_additional_cost().as_u8() => {
1095 "cold_storage_additional_cost"
1096 }
1097 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
1098 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
1099 "new_account_cost_for_selfdestruct"
1100 }
1101 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
1102 x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => {
1103 "tx_eip7702_per_empty_account_cost"
1104 }
1105 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
1106 "tx_token_non_zero_byte_multiplier"
1107 }
1108 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
1109 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
1110 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
1111 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
1112 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
1113 "tx_access_list_storage_key_cost"
1114 }
1115 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
1116 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
1117 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
1118 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
1119 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
1120 x if x == Self::tx_eip7702_auth_refund().as_u8() => "tx_eip7702_auth_refund",
1121 x if x == Self::sstore_set_state_gas().as_u8() => "sstore_set_state_gas",
1122 x if x == Self::new_account_state_gas().as_u8() => "new_account_state_gas",
1123 x if x == Self::code_deposit_state_gas().as_u8() => "code_deposit_state_gas",
1124 x if x == Self::create_state_gas().as_u8() => "create_state_gas",
1125 x if x == Self::tx_eip7702_state_gas_bytecode().as_u8() => {
1126 "tx_eip7702_state_gas_bytecode"
1127 }
1128 x if x == Self::tx_floor_token_zero_byte_multiplier().as_u8() => {
1129 "tx_floor_token_zero_byte_multiplier"
1130 }
1131 x if x == Self::tx_access_list_floor_byte_multiplier().as_u8() => {
1132 "tx_access_list_floor_byte_multiplier"
1133 }
1134 _ => "unknown",
1135 }
1136 }
1137
1138 pub fn from_name(s: &str) -> Option<GasId> {
1152 match s {
1153 "exp_byte_gas" => Some(Self::exp_byte_gas()),
1154 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
1155 "copy_per_word" => Some(Self::copy_per_word()),
1156 "logdata" => Some(Self::logdata()),
1157 "logtopic" => Some(Self::logtopic()),
1158 "mcopy_per_word" => Some(Self::mcopy_per_word()),
1159 "keccak256_per_word" => Some(Self::keccak256_per_word()),
1160 "memory_linear_cost" => Some(Self::memory_linear_cost()),
1161 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
1162 "initcode_per_word" => Some(Self::initcode_per_word()),
1163 "create" => Some(Self::create()),
1164 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
1165 "transfer_value_cost" => Some(Self::transfer_value_cost()),
1166 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
1167 "new_account_cost" => Some(Self::new_account_cost()),
1168 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
1169 "sstore_static" => Some(Self::sstore_static()),
1170 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
1171 "sstore_reset_without_cold_load_cost" => {
1172 Some(Self::sstore_reset_without_cold_load_cost())
1173 }
1174 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
1175 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
1176 "call_stipend" => Some(Self::call_stipend()),
1177 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
1178 "cold_storage_cost" => Some(Self::cold_storage_cost()),
1179 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
1180 "code_deposit_cost" => Some(Self::code_deposit_cost()),
1181 "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()),
1182 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
1183 "tx_token_cost" => Some(Self::tx_token_cost()),
1184 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
1185 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
1186 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
1187 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
1188 "tx_base_stipend" => Some(Self::tx_base_stipend()),
1189 "tx_create_cost" => Some(Self::tx_create_cost()),
1190 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
1191 "sstore_set_refund" => Some(Self::sstore_set_refund()),
1192 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
1193 "tx_eip7702_auth_refund" => Some(Self::tx_eip7702_auth_refund()),
1194 "sstore_set_state_gas" => Some(Self::sstore_set_state_gas()),
1195 "new_account_state_gas" => Some(Self::new_account_state_gas()),
1196 "code_deposit_state_gas" => Some(Self::code_deposit_state_gas()),
1197 "create_state_gas" => Some(Self::create_state_gas()),
1198 "tx_eip7702_state_gas_bytecode" => Some(Self::tx_eip7702_state_gas_bytecode()),
1199 "tx_floor_token_zero_byte_multiplier" => {
1200 Some(Self::tx_floor_token_zero_byte_multiplier())
1201 }
1202 "tx_access_list_floor_byte_multiplier" => {
1203 Some(Self::tx_access_list_floor_byte_multiplier())
1204 }
1205 _ => None,
1206 }
1207 }
1208
1209 pub const fn exp_byte_gas() -> GasId {
1211 Self::new(1)
1212 }
1213
1214 pub const fn extcodecopy_per_word() -> GasId {
1216 Self::new(2)
1217 }
1218
1219 pub const fn copy_per_word() -> GasId {
1221 Self::new(3)
1222 }
1223
1224 pub const fn logdata() -> GasId {
1226 Self::new(4)
1227 }
1228
1229 pub const fn logtopic() -> GasId {
1231 Self::new(5)
1232 }
1233
1234 pub const fn mcopy_per_word() -> GasId {
1236 Self::new(6)
1237 }
1238
1239 pub const fn keccak256_per_word() -> GasId {
1241 Self::new(7)
1242 }
1243
1244 pub const fn memory_linear_cost() -> GasId {
1246 Self::new(8)
1247 }
1248
1249 pub const fn memory_quadratic_reduction() -> GasId {
1251 Self::new(9)
1252 }
1253
1254 pub const fn initcode_per_word() -> GasId {
1256 Self::new(10)
1257 }
1258
1259 pub const fn create() -> GasId {
1261 Self::new(11)
1262 }
1263
1264 pub const fn call_stipend_reduction() -> GasId {
1266 Self::new(12)
1267 }
1268
1269 pub const fn transfer_value_cost() -> GasId {
1271 Self::new(13)
1272 }
1273
1274 pub const fn cold_account_additional_cost() -> GasId {
1276 Self::new(14)
1277 }
1278
1279 pub const fn new_account_cost() -> GasId {
1281 Self::new(15)
1282 }
1283
1284 pub const fn warm_storage_read_cost() -> GasId {
1288 Self::new(16)
1289 }
1290
1291 pub const fn sstore_static() -> GasId {
1294 Self::new(17)
1295 }
1296
1297 pub const fn sstore_set_without_load_cost() -> GasId {
1299 Self::new(18)
1300 }
1301
1302 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1304 Self::new(19)
1305 }
1306
1307 pub const fn sstore_clearing_slot_refund() -> GasId {
1309 Self::new(20)
1310 }
1311
1312 pub const fn selfdestruct_refund() -> GasId {
1314 Self::new(21)
1315 }
1316
1317 pub const fn call_stipend() -> GasId {
1319 Self::new(22)
1320 }
1321
1322 pub const fn cold_storage_additional_cost() -> GasId {
1324 Self::new(23)
1325 }
1326
1327 pub const fn cold_storage_cost() -> GasId {
1329 Self::new(24)
1330 }
1331
1332 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1334 Self::new(25)
1335 }
1336
1337 pub const fn code_deposit_cost() -> GasId {
1339 Self::new(26)
1340 }
1341
1342 pub const fn tx_eip7702_per_empty_account_cost() -> GasId {
1344 Self::new(27)
1345 }
1346
1347 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1349 Self::new(28)
1350 }
1351
1352 pub const fn tx_token_cost() -> GasId {
1354 Self::new(29)
1355 }
1356
1357 pub const fn tx_floor_cost_per_token() -> GasId {
1359 Self::new(30)
1360 }
1361
1362 pub const fn tx_floor_cost_base_gas() -> GasId {
1364 Self::new(31)
1365 }
1366
1367 pub const fn tx_access_list_address_cost() -> GasId {
1369 Self::new(32)
1370 }
1371
1372 pub const fn tx_access_list_storage_key_cost() -> GasId {
1374 Self::new(33)
1375 }
1376
1377 pub const fn tx_base_stipend() -> GasId {
1379 Self::new(34)
1380 }
1381
1382 pub const fn tx_create_cost() -> GasId {
1384 Self::new(35)
1385 }
1386
1387 pub const fn tx_initcode_cost() -> GasId {
1389 Self::new(36)
1390 }
1391
1392 pub const fn sstore_set_refund() -> GasId {
1394 Self::new(37)
1395 }
1396
1397 pub const fn sstore_reset_refund() -> GasId {
1399 Self::new(38)
1400 }
1401
1402 pub const fn tx_eip7702_auth_refund() -> GasId {
1406 Self::new(39)
1407 }
1408
1409 pub const fn sstore_set_state_gas() -> GasId {
1411 Self::new(40)
1412 }
1413
1414 pub const fn new_account_state_gas() -> GasId {
1416 Self::new(41)
1417 }
1418
1419 pub const fn code_deposit_state_gas() -> GasId {
1421 Self::new(42)
1422 }
1423
1424 pub const fn create_state_gas() -> GasId {
1426 Self::new(43)
1427 }
1428
1429 pub const fn tx_eip7702_state_gas_bytecode() -> GasId {
1433 Self::new(44)
1434 }
1435
1436 pub const fn tx_floor_token_zero_byte_multiplier() -> GasId {
1443 Self::new(45)
1444 }
1445
1446 pub const fn tx_access_list_floor_byte_multiplier() -> GasId {
1452 Self::new(46)
1453 }
1454}
1455
1456#[cfg(test)]
1457mod tests {
1458 use super::*;
1459 use std::collections::HashSet;
1460
1461 #[cfg(test)]
1462 mod log2floor_tests {
1463 use super::*;
1464
1465 #[test]
1466 fn test_log2floor_edge_cases() {
1467 assert_eq!(log2floor(U256::ZERO), 0);
1469
1470 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);
1484 assert_eq!(log2floor(U256::from(u64::MAX) + U256::from(1u64)), 64);
1485 assert_eq!(log2floor(U256::MAX), 255);
1486 }
1487 }
1488
1489 #[test]
1490 fn test_gas_id_name_and_from_str_coverage() {
1491 let mut unique_names = HashSet::new();
1492 let mut known_gas_ids = 0;
1493
1494 for i in 0..=255 {
1496 let gas_id = GasId::new(i);
1497 let name = gas_id.name();
1498
1499 if name != "unknown" {
1501 unique_names.insert(name);
1502 }
1503 }
1504
1505 for name in &unique_names {
1507 if let Some(gas_id) = GasId::from_name(name) {
1508 known_gas_ids += 1;
1509 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1511 }
1512 }
1513
1514 println!("Total unique named GasIds: {}", unique_names.len());
1515 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1516
1517 assert_eq!(
1519 unique_names.len(),
1520 known_gas_ids,
1521 "Not all unique names are resolvable via from_str"
1522 );
1523
1524 assert_eq!(
1526 unique_names.len(),
1527 46,
1528 "Expected 46 unique GasIds, found {}",
1529 unique_names.len()
1530 );
1531 }
1532
1533 #[test]
1534 fn test_tx_access_list_cost() {
1535 use crate::cfg::gas;
1536
1537 let gas_params = GasParams::new_spec(SpecId::BERLIN);
1539
1540 assert_eq!(gas_params.tx_access_list_cost(0, 0), 0);
1542
1543 assert_eq!(
1545 gas_params.tx_access_list_cost(1, 0),
1546 gas::ACCESS_LIST_ADDRESS
1547 );
1548
1549 assert_eq!(
1551 gas_params.tx_access_list_cost(0, 1),
1552 gas::ACCESS_LIST_STORAGE_KEY
1553 );
1554
1555 assert_eq!(
1557 gas_params.tx_access_list_cost(2, 5),
1558 2 * gas::ACCESS_LIST_ADDRESS + 5 * gas::ACCESS_LIST_STORAGE_KEY
1559 );
1560
1561 assert_eq!(
1563 gas_params.tx_access_list_cost(100, 200),
1564 100 * gas::ACCESS_LIST_ADDRESS + 200 * gas::ACCESS_LIST_STORAGE_KEY
1565 );
1566
1567 let gas_params_pre_berlin = GasParams::new_spec(SpecId::ISTANBUL);
1569 assert_eq!(gas_params_pre_berlin.tx_access_list_cost(10, 20), 0);
1570 }
1571
1572 #[test]
1573 fn test_initial_state_gas_for_create() {
1574 let gas_params = GasParams::new_spec(SpecId::AMSTERDAM);
1576 let cpsb = eip8037::CPSB_GLAMSTERDAM;
1577
1578 let create_gas = gas_params.initial_tx_gas(b"", true, 0, 0, 0, cpsb);
1580 let expected_state_gas = gas_params.create_state_gas(cpsb);
1581
1582 assert_eq!(create_gas.initial_state_gas_final(), expected_state_gas);
1583 assert_eq!(
1584 create_gas.initial_state_gas_final(),
1585 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM
1586 );
1587
1588 let create_cost = gas_params.tx_create_cost();
1590 let initcode_cost = gas_params.tx_initcode_cost(0);
1591 assert_eq!(
1592 create_gas.initial_total_gas(),
1593 gas_params.tx_base_stipend() + create_cost + initcode_cost + expected_state_gas
1594 );
1595
1596 let call_gas = gas_params.initial_tx_gas(b"", false, 0, 0, 0, cpsb);
1598 assert_eq!(call_gas.initial_state_gas_final(), 0);
1599 assert_eq!(call_gas.initial_total_gas(), gas_params.tx_base_stipend());
1601 }
1602
1603 #[test]
1604 fn test_eip7981_access_list_cost_amsterdam() {
1605 let params = GasParams::new_spec(SpecId::AMSTERDAM);
1608
1609 assert_eq!(params.tx_access_list_address_cost(), 2400 + 20 * 64);
1611 assert_eq!(params.tx_access_list_storage_key_cost(), 1900 + 32 * 64);
1612 assert_eq!(params.tx_access_list_cost(1, 0), 2400 + 20 * 64);
1613 assert_eq!(params.tx_access_list_cost(0, 1), 1900 + 32 * 64);
1614
1615 assert_eq!(params.tx_access_list_floor_byte_multiplier(), 4);
1617 assert_eq!(params.tx_floor_tokens_in_access_list(2, 3), (40 + 96) * 4);
1619
1620 let gas = params.initial_tx_gas(b"", false, 2, 3, 0, 0);
1622 let expected_al_floor = (40 + 96) * 4 * params.tx_floor_cost_per_token();
1623 assert_eq!(
1624 gas.floor_gas(),
1625 params.tx_floor_cost_base_gas() + expected_al_floor,
1626 );
1627
1628 let prague = GasParams::new_spec(SpecId::PRAGUE);
1630 assert_eq!(prague.tx_access_list_floor_byte_multiplier(), 0);
1631 assert_eq!(prague.tx_floor_tokens_in_access_list(2, 3), 0);
1632 let prague_gas = prague.initial_tx_gas(b"", false, 2, 3, 0, 0);
1633 assert_eq!(prague_gas.floor_gas(), prague.tx_floor_cost_base_gas());
1634 }
1635}