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 eip7702, eip8037,
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::transfer_value_cost().as_usize()] = gas::CALLVALUE;
206 table[GasId::cold_account_additional_cost().as_usize()] = 0;
207 table[GasId::new_account_cost().as_usize()] = gas::NEWACCOUNT;
208 table[GasId::warm_storage_read_cost().as_usize()] = 0;
209 table[GasId::sstore_static().as_usize()] = gas::SSTORE_RESET;
211 table[GasId::sstore_set_without_load_cost().as_usize()] =
213 gas::SSTORE_SET - gas::SSTORE_RESET;
214 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = 0;
216 table[GasId::sstore_set_refund().as_usize()] =
218 table[GasId::sstore_set_without_load_cost().as_usize()];
219 table[GasId::sstore_reset_refund().as_usize()] =
221 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
222 table[GasId::sstore_clearing_slot_refund().as_usize()] = 15000;
224 table[GasId::selfdestruct_refund().as_usize()] = 24000;
225 table[GasId::call_stipend().as_usize()] = gas::CALL_STIPEND;
226 table[GasId::cold_storage_additional_cost().as_usize()] = 0;
227 table[GasId::cold_storage_cost().as_usize()] = 0;
228 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
229 table[GasId::code_deposit_cost().as_usize()] = gas::CODEDEPOSIT;
230 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
231 gas::NON_ZERO_BYTE_MULTIPLIER;
232 table[GasId::tx_token_cost().as_usize()] = gas::STANDARD_TOKEN_COST;
233 table[GasId::tx_base_stipend().as_usize()] = 21000;
234
235 if spec.is_enabled_in(SpecId::HOMESTEAD) {
236 table[GasId::tx_create_cost().as_usize()] = gas::CREATE;
237 }
238
239 if spec.is_enabled_in(SpecId::TANGERINE) {
240 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = gas::NEWACCOUNT;
241 }
242
243 if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
244 table[GasId::exp_byte_gas().as_usize()] = 50;
245 }
246
247 if spec.is_enabled_in(SpecId::ISTANBUL) {
248 table[GasId::sstore_static().as_usize()] = gas::ISTANBUL_SLOAD_GAS;
249 table[GasId::sstore_set_without_load_cost().as_usize()] =
250 gas::SSTORE_SET - gas::ISTANBUL_SLOAD_GAS;
251 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
252 gas::SSTORE_RESET - gas::ISTANBUL_SLOAD_GAS;
253 table[GasId::sstore_set_refund().as_usize()] =
254 table[GasId::sstore_set_without_load_cost().as_usize()];
255 table[GasId::sstore_reset_refund().as_usize()] =
256 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
257 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
258 gas::NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
259 }
260
261 if spec.is_enabled_in(SpecId::BERLIN) {
262 table[GasId::sstore_static().as_usize()] = gas::WARM_STORAGE_READ_COST;
263 table[GasId::cold_account_additional_cost().as_usize()] =
264 gas::COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
265 table[GasId::cold_storage_additional_cost().as_usize()] =
266 gas::COLD_SLOAD_COST - gas::WARM_STORAGE_READ_COST;
267 table[GasId::cold_storage_cost().as_usize()] = gas::COLD_SLOAD_COST;
268 table[GasId::warm_storage_read_cost().as_usize()] = gas::WARM_STORAGE_READ_COST;
269
270 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
271 gas::WARM_SSTORE_RESET - gas::WARM_STORAGE_READ_COST;
272 table[GasId::sstore_set_without_load_cost().as_usize()] =
273 gas::SSTORE_SET - gas::WARM_STORAGE_READ_COST;
274 table[GasId::sstore_set_refund().as_usize()] =
275 table[GasId::sstore_set_without_load_cost().as_usize()];
276 table[GasId::sstore_reset_refund().as_usize()] =
277 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
278
279 table[GasId::tx_access_list_address_cost().as_usize()] = gas::ACCESS_LIST_ADDRESS;
280 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
281 gas::ACCESS_LIST_STORAGE_KEY;
282 }
283
284 if spec.is_enabled_in(SpecId::LONDON) {
285 table[GasId::sstore_clearing_slot_refund().as_usize()] =
290 gas::WARM_SSTORE_RESET + gas::ACCESS_LIST_STORAGE_KEY;
291
292 table[GasId::selfdestruct_refund().as_usize()] = 0;
293 }
294
295 if spec.is_enabled_in(SpecId::SHANGHAI) {
296 table[GasId::tx_initcode_cost().as_usize()] = gas::INITCODE_WORD_COST;
297 }
298
299 if spec.is_enabled_in(SpecId::PRAGUE) {
300 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] =
301 eip7702::PER_EMPTY_ACCOUNT_COST;
302
303 table[GasId::tx_eip7702_auth_refund().as_usize()] =
305 eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST;
306
307 table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
308 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
309 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] = 1;
312 }
313
314 if spec.is_enabled_in(SpecId::AMSTERDAM) {
318 table[GasId::create().as_usize()] = 9000;
320 table[GasId::tx_create_cost().as_usize()] = 9000;
321 table[GasId::code_deposit_cost().as_usize()] = 0;
322 table[GasId::new_account_cost().as_usize()] = 0;
323 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
324 table[GasId::sstore_set_without_load_cost().as_usize()] = 2800;
327
328 table[GasId::sstore_set_state_gas().as_usize()] =
330 eip8037::SSTORE_SET_BYTES * eip8037::CPSB_GLAMSTERDAM;
331 table[GasId::new_account_state_gas().as_usize()] =
332 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM;
333 table[GasId::code_deposit_state_gas().as_usize()] =
334 eip8037::CODE_DEPOSIT_PER_BYTE * eip8037::CPSB_GLAMSTERDAM;
335 table[GasId::create_state_gas().as_usize()] =
336 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM;
337 table[GasId::tx_eip7702_state_gas_bytecode().as_usize()] =
338 eip8037::AUTH_BASE_BYTES * eip8037::CPSB_GLAMSTERDAM;
339
340 table[GasId::sstore_set_refund().as_usize()] = 2800;
344
345 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] =
352 eip8037::EIP7702_PER_EMPTY_ACCOUNT_REGULAR;
353 table[GasId::tx_eip7702_auth_refund().as_usize()] = 0;
354
355 table[GasId::tx_floor_cost_per_token().as_usize()] = 16;
360 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] =
361 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()];
362
363 table[GasId::tx_access_list_address_cost().as_usize()] =
369 gas::ACCESS_LIST_ADDRESS + 20 * 64;
370 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
371 gas::ACCESS_LIST_STORAGE_KEY + 32 * 64;
372 table[GasId::tx_access_list_floor_byte_multiplier().as_usize()] = 4;
373 }
374
375 Self::new(Arc::new(table))
376 }
377
378 #[inline]
380 pub fn get(&self, id: GasId) -> u64 {
381 self.table[id.as_usize()]
382 }
383
384 #[inline]
386 pub fn exp_cost(&self, power: U256) -> u64 {
387 if power.is_zero() {
388 return 0;
389 }
390 self.get(GasId::exp_byte_gas())
392 .saturating_mul(log2floor(power) / 8 + 1)
393 }
394
395 #[inline]
397 pub fn selfdestruct_refund(&self) -> i64 {
398 self.get(GasId::selfdestruct_refund()) as i64
399 }
400
401 #[inline]
404 pub fn selfdestruct_cold_cost(&self) -> u64 {
405 self.cold_account_additional_cost() + self.warm_storage_read_cost()
406 }
407
408 #[inline]
410 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
411 let mut gas = 0;
412
413 if should_charge_topup {
415 gas += self.new_account_cost_for_selfdestruct();
416 }
417
418 if is_cold {
419 gas += self.selfdestruct_cold_cost();
425 }
426 gas
427 }
428
429 #[inline]
431 pub fn extcodecopy(&self, len: usize) -> u64 {
432 self.get(GasId::extcodecopy_per_word())
433 .saturating_mul(num_words(len) as u64)
434 }
435
436 #[inline]
438 pub fn mcopy_cost(&self, len: usize) -> u64 {
439 self.get(GasId::mcopy_per_word())
440 .saturating_mul(num_words(len) as u64)
441 }
442
443 #[inline]
445 pub fn sstore_static_gas(&self) -> u64 {
446 self.get(GasId::sstore_static())
447 }
448
449 #[inline]
451 pub fn sstore_set_without_load_cost(&self) -> u64 {
452 self.get(GasId::sstore_set_without_load_cost())
453 }
454
455 #[inline]
457 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
458 self.get(GasId::sstore_reset_without_cold_load_cost())
459 }
460
461 #[inline]
463 pub fn sstore_clearing_slot_refund(&self) -> u64 {
464 self.get(GasId::sstore_clearing_slot_refund())
465 }
466
467 #[inline]
469 pub fn sstore_set_refund(&self) -> u64 {
470 self.get(GasId::sstore_set_refund())
471 }
472
473 #[inline]
475 pub fn sstore_reset_refund(&self) -> u64 {
476 self.get(GasId::sstore_reset_refund())
477 }
478
479 #[inline]
483 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
484 if !is_istanbul {
487 if vals.is_present_zero() && !vals.is_new_zero() {
488 return self.sstore_set_without_load_cost();
489 } else {
490 return self.sstore_reset_without_cold_load_cost();
491 }
492 }
493
494 let mut gas = 0;
495
496 if is_cold {
498 gas += self.cold_storage_cost();
499 }
500
501 if vals.new_values_changes_present() && vals.is_original_eq_present() {
503 gas += if vals.is_original_zero() {
504 self.sstore_set_without_load_cost()
507 } else {
508 self.sstore_reset_without_cold_load_cost()
510 };
511 }
512 gas
513 }
514
515 #[inline]
517 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
518 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
520
521 if !is_istanbul {
522 if !vals.is_present_zero() && vals.is_new_zero() {
524 return sstore_clearing_slot_refund;
525 }
526 return 0;
527 }
528
529 if vals.is_new_eq_present() {
531 return 0;
532 }
533
534 if vals.is_original_eq_present() && vals.is_new_zero() {
537 return sstore_clearing_slot_refund;
538 }
539
540 let mut refund = 0;
541 if !vals.is_original_zero() {
543 if vals.is_present_zero() {
545 refund -= sstore_clearing_slot_refund;
547 } else if vals.is_new_zero() {
549 refund += sstore_clearing_slot_refund;
551 }
552 }
553
554 if vals.is_original_eq_new() {
556 if vals.is_original_zero() {
558 refund += self.sstore_set_refund() as i64;
560 } else {
562 refund += self.sstore_reset_refund() as i64;
564 }
565 }
566 refund
567 }
568
569 #[inline]
571 pub fn log_cost(&self, n: u8, len: u64) -> u64 {
572 self.get(GasId::logdata())
573 .saturating_mul(len)
574 .saturating_add(self.get(GasId::logtopic()) * n as u64)
575 }
576
577 #[inline]
579 pub fn keccak256_cost(&self, len: usize) -> u64 {
580 self.get(GasId::keccak256_per_word())
581 .saturating_mul(num_words(len) as u64)
582 }
583
584 #[inline]
586 pub fn memory_cost(&self, len: usize) -> u64 {
587 let len = len as u64;
588 self.get(GasId::memory_linear_cost())
589 .saturating_mul(len)
590 .saturating_add(
591 (len.saturating_mul(len))
592 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
593 )
594 }
595
596 #[inline]
598 pub fn initcode_cost(&self, len: usize) -> u64 {
599 self.get(GasId::initcode_per_word())
600 .saturating_mul(num_words(len) as u64)
601 }
602
603 #[inline]
605 pub fn create_cost(&self) -> u64 {
606 self.get(GasId::create())
607 }
608
609 #[inline]
611 pub fn create2_cost(&self, len: usize) -> u64 {
612 self.get(GasId::create()).saturating_add(
613 self.get(GasId::keccak256_per_word())
614 .saturating_mul(num_words(len) as u64),
615 )
616 }
617
618 #[inline]
620 pub fn call_stipend(&self) -> u64 {
621 self.get(GasId::call_stipend())
622 }
623
624 #[inline]
626 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
627 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
628 }
629
630 #[inline]
632 pub fn transfer_value_cost(&self) -> u64 {
633 self.get(GasId::transfer_value_cost())
634 }
635
636 #[inline]
638 pub fn cold_account_additional_cost(&self) -> u64 {
639 self.get(GasId::cold_account_additional_cost())
640 }
641
642 #[inline]
644 pub fn cold_storage_additional_cost(&self) -> u64 {
645 self.get(GasId::cold_storage_additional_cost())
646 }
647
648 #[inline]
650 pub fn cold_storage_cost(&self) -> u64 {
651 self.get(GasId::cold_storage_cost())
652 }
653
654 #[inline]
656 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
657 if !is_spurious_dragon || transfers_value {
661 return self.get(GasId::new_account_cost());
662 }
663 0
664 }
665
666 #[inline]
668 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
669 self.get(GasId::new_account_cost_for_selfdestruct())
670 }
671
672 #[inline]
674 pub fn warm_storage_read_cost(&self) -> u64 {
675 self.get(GasId::warm_storage_read_cost())
676 }
677
678 #[inline]
680 pub fn copy_cost(&self, len: usize) -> u64 {
681 self.copy_per_word_cost(num_words(len))
682 }
683
684 #[inline]
686 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
687 self.get(GasId::copy_per_word())
688 .saturating_mul(word_num as u64)
689 }
690
691 #[inline]
693 pub fn code_deposit_cost(&self, len: usize) -> u64 {
694 self.get(GasId::code_deposit_cost())
695 .saturating_mul(len as u64)
696 }
697
698 #[inline]
700 pub fn sstore_state_gas(&self, vals: &SStoreResult) -> u64 {
701 if vals.new_values_changes_present()
702 && vals.is_original_eq_present()
703 && vals.is_original_zero()
704 {
705 self.get(GasId::sstore_set_state_gas())
706 } else {
707 0
708 }
709 }
710
711 #[inline]
719 pub fn sstore_state_gas_refill(&self, vals: &SStoreResult) -> u64 {
720 if !vals.is_new_eq_present() && vals.is_original_eq_new() && vals.is_original_zero() {
721 self.get(GasId::sstore_set_state_gas())
722 } else {
723 0
724 }
725 }
726
727 #[inline]
729 pub fn new_account_state_gas(&self) -> u64 {
730 self.get(GasId::new_account_state_gas())
731 }
732
733 #[inline]
735 pub fn code_deposit_state_gas(&self, len: usize) -> u64 {
736 self.get(GasId::code_deposit_state_gas())
737 .saturating_mul(len as u64)
738 }
739
740 #[inline]
742 pub fn create_state_gas(&self) -> u64 {
743 self.get(GasId::create_state_gas())
744 }
745
746 #[inline]
752 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
753 let regular = self.get(GasId::tx_eip7702_per_empty_account_cost());
754 let state = self.tx_eip7702_state_gas();
755 regular.saturating_add(state)
756 }
757
758 #[inline]
764 pub fn tx_eip7702_auth_refund(&self) -> u64 {
765 let regular = self.get(GasId::tx_eip7702_auth_refund());
766 let state = self.new_account_state_gas();
767 regular.saturating_add(state)
768 }
769
770 #[inline]
775 pub fn tx_eip7702_state_gas(&self) -> u64 {
776 let new_account = self.get(GasId::new_account_state_gas());
777 let bytecode = self.get(GasId::tx_eip7702_state_gas_bytecode());
778 new_account.saturating_add(bytecode)
779 }
780
781 #[inline]
787 pub fn tx_eip7702_state_refund(&self, refunded_accounts: u64, refunded_bytecodes: u64) -> u64 {
788 let per_account = self
789 .get(GasId::new_account_state_gas())
790 .saturating_mul(refunded_accounts);
791 let per_bytecode = self
792 .get(GasId::tx_eip7702_state_gas_bytecode())
793 .saturating_mul(refunded_bytecodes);
794 per_account.saturating_add(per_bytecode)
795 }
796
797 #[inline]
802 pub fn tx_eip7702_auth_refund_regular(&self) -> u64 {
803 self.get(GasId::tx_eip7702_auth_refund())
804 }
805
806 #[inline]
808 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
809 self.get(GasId::tx_token_non_zero_byte_multiplier())
810 }
811
812 #[inline]
814 pub fn tx_token_cost(&self) -> u64 {
815 self.get(GasId::tx_token_cost())
816 }
817
818 pub fn tx_floor_cost_per_token(&self) -> u64 {
820 self.get(GasId::tx_floor_cost_per_token())
821 }
822
823 pub fn tx_floor_token_zero_byte_multiplier(&self) -> u64 {
831 self.get(GasId::tx_floor_token_zero_byte_multiplier())
832 }
833
834 #[inline]
845 pub fn tx_floor_cost(&self, input: &[u8]) -> u64 {
846 let zero_multiplier = self.tx_floor_token_zero_byte_multiplier();
847 let non_zero_multiplier = self.tx_token_non_zero_byte_multiplier();
848 let floor_tokens = if zero_multiplier == non_zero_multiplier {
849 input.len() as u64 * non_zero_multiplier
850 } else {
851 get_tokens_in_calldata(input, non_zero_multiplier)
852 };
853 self.tx_floor_cost_with_tokens(floor_tokens)
854 }
855
856 #[inline]
858 pub fn tx_floor_cost_with_tokens(&self, tokens: u64) -> u64 {
859 self.tx_floor_cost_per_token() * tokens + self.tx_floor_cost_base_gas()
860 }
861
862 pub fn tx_floor_cost_base_gas(&self) -> u64 {
864 self.get(GasId::tx_floor_cost_base_gas())
865 }
866
867 pub fn tx_access_list_address_cost(&self) -> u64 {
869 self.get(GasId::tx_access_list_address_cost())
870 }
871
872 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
874 self.get(GasId::tx_access_list_storage_key_cost())
875 }
876
877 #[inline]
895 pub fn tx_access_list_cost(&self, accounts: u64, storages: u64) -> u64 {
896 accounts
897 .saturating_mul(self.tx_access_list_address_cost())
898 .saturating_add(storages.saturating_mul(self.tx_access_list_storage_key_cost()))
899 }
900
901 #[inline]
909 pub fn tx_access_list_floor_byte_multiplier(&self) -> u64 {
910 self.get(GasId::tx_access_list_floor_byte_multiplier())
911 }
912
913 #[inline]
918 pub fn tx_floor_tokens_in_access_list(&self, accounts: u64, storages: u64) -> u64 {
919 let bytes = accounts
920 .saturating_mul(20)
921 .saturating_add(storages.saturating_mul(32));
922 bytes.saturating_mul(self.tx_access_list_floor_byte_multiplier())
923 }
924
925 pub fn tx_base_stipend(&self) -> u64 {
927 self.get(GasId::tx_base_stipend())
928 }
929
930 #[inline]
934 pub fn tx_create_cost(&self) -> u64 {
935 self.get(GasId::tx_create_cost())
936 }
937
938 #[inline]
940 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
941 self.get(GasId::tx_initcode_cost())
942 .saturating_mul(num_words(len) as u64)
943 }
944
945 pub fn initial_tx_gas(
961 &self,
962 input: &[u8],
963 is_create: bool,
964 access_list_accounts: u64,
965 access_list_storages: u64,
966 authorization_list_num: u64,
967 ) -> InitialAndFloorGas {
968 let tokens_in_calldata =
970 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
971
972 let auth_total_cost = authorization_list_num * self.tx_eip7702_per_empty_account_cost();
976 let auth_state_gas = authorization_list_num * self.tx_eip7702_state_gas();
977
978 let auth_regular_cost = auth_total_cost - auth_state_gas;
979
980 let mut initial_regular_gas = tokens_in_calldata * self.tx_token_cost()
981 + access_list_accounts * self.tx_access_list_address_cost()
983 + access_list_storages * self.tx_access_list_storage_key_cost()
985 + self.tx_base_stipend()
986 + auth_regular_cost;
988
989 let mut initial_state_gas = auth_state_gas;
991
992 if is_create {
993 initial_regular_gas += self.tx_create_cost();
995
996 initial_regular_gas += self.tx_initcode_cost(input.len());
998
999 initial_state_gas += self.create_state_gas();
1002 }
1003
1004 let access_list_floor_tokens =
1007 self.tx_floor_tokens_in_access_list(access_list_accounts, access_list_storages);
1008 let floor_gas =
1009 self.tx_floor_cost(input) + access_list_floor_tokens * self.tx_floor_cost_per_token();
1010
1011 InitialAndFloorGas::default()
1012 .with_initial_regular_gas(initial_regular_gas)
1013 .with_initial_state_gas(initial_state_gas)
1014 .with_floor_gas(floor_gas)
1015 }
1016
1017 pub fn initial_tx_gas_for_tx(&self, tx: impl Transaction) -> InitialAndFloorGas {
1022 let mut accounts = 0;
1023 let mut storages = 0;
1024 if tx.tx_type() != TransactionType::Legacy {
1026 (accounts, storages) = tx
1027 .access_list()
1028 .map(|al| {
1029 al.fold((0, 0), |(num_accounts, num_storage_slots), item| {
1030 (
1031 num_accounts + 1,
1032 num_storage_slots + item.storage_slots().count() as u64,
1033 )
1034 })
1035 })
1036 .unwrap_or_default();
1037 }
1038
1039 self.initial_tx_gas(
1040 tx.input(),
1041 tx.kind().is_create(),
1042 accounts,
1043 storages,
1044 tx.authorization_list_len() as u64,
1045 )
1046 }
1047}
1048
1049#[inline]
1050pub(crate) const fn log2floor(value: U256) -> u64 {
1051 255u64.saturating_sub(value.leading_zeros() as u64)
1052}
1053
1054#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1056pub struct GasId(u8);
1057
1058impl GasId {
1059 #[inline]
1061 pub const fn new(id: u8) -> Self {
1062 Self(id)
1063 }
1064
1065 #[inline]
1067 pub const fn as_u8(&self) -> u8 {
1068 self.0
1069 }
1070
1071 #[inline]
1073 pub const fn as_usize(&self) -> usize {
1074 self.0 as usize
1075 }
1076
1077 pub const fn name(&self) -> &'static str {
1089 match self.0 {
1090 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
1091 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
1092 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
1093 x if x == Self::logdata().as_u8() => "logdata",
1094 x if x == Self::logtopic().as_u8() => "logtopic",
1095 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
1096 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
1097 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
1098 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
1099 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
1100 x if x == Self::create().as_u8() => "create",
1101 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
1102 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
1103 x if x == Self::cold_account_additional_cost().as_u8() => {
1104 "cold_account_additional_cost"
1105 }
1106 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
1107 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
1108 x if x == Self::sstore_static().as_u8() => "sstore_static",
1109 x if x == Self::sstore_set_without_load_cost().as_u8() => {
1110 "sstore_set_without_load_cost"
1111 }
1112 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
1113 "sstore_reset_without_cold_load_cost"
1114 }
1115 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
1116 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
1117 x if x == Self::call_stipend().as_u8() => "call_stipend",
1118 x if x == Self::cold_storage_additional_cost().as_u8() => {
1119 "cold_storage_additional_cost"
1120 }
1121 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
1122 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
1123 "new_account_cost_for_selfdestruct"
1124 }
1125 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
1126 x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => {
1127 "tx_eip7702_per_empty_account_cost"
1128 }
1129 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
1130 "tx_token_non_zero_byte_multiplier"
1131 }
1132 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
1133 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
1134 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
1135 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
1136 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
1137 "tx_access_list_storage_key_cost"
1138 }
1139 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
1140 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
1141 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
1142 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
1143 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
1144 x if x == Self::tx_eip7702_auth_refund().as_u8() => "tx_eip7702_auth_refund",
1145 x if x == Self::sstore_set_state_gas().as_u8() => "sstore_set_state_gas",
1146 x if x == Self::new_account_state_gas().as_u8() => "new_account_state_gas",
1147 x if x == Self::code_deposit_state_gas().as_u8() => "code_deposit_state_gas",
1148 x if x == Self::create_state_gas().as_u8() => "create_state_gas",
1149 x if x == Self::tx_eip7702_state_gas_bytecode().as_u8() => {
1150 "tx_eip7702_state_gas_bytecode"
1151 }
1152 x if x == Self::tx_floor_token_zero_byte_multiplier().as_u8() => {
1153 "tx_floor_token_zero_byte_multiplier"
1154 }
1155 x if x == Self::tx_access_list_floor_byte_multiplier().as_u8() => {
1156 "tx_access_list_floor_byte_multiplier"
1157 }
1158 _ => "unknown",
1159 }
1160 }
1161
1162 pub fn from_name(s: &str) -> Option<GasId> {
1176 match s {
1177 "exp_byte_gas" => Some(Self::exp_byte_gas()),
1178 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
1179 "copy_per_word" => Some(Self::copy_per_word()),
1180 "logdata" => Some(Self::logdata()),
1181 "logtopic" => Some(Self::logtopic()),
1182 "mcopy_per_word" => Some(Self::mcopy_per_word()),
1183 "keccak256_per_word" => Some(Self::keccak256_per_word()),
1184 "memory_linear_cost" => Some(Self::memory_linear_cost()),
1185 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
1186 "initcode_per_word" => Some(Self::initcode_per_word()),
1187 "create" => Some(Self::create()),
1188 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
1189 "transfer_value_cost" => Some(Self::transfer_value_cost()),
1190 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
1191 "new_account_cost" => Some(Self::new_account_cost()),
1192 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
1193 "sstore_static" => Some(Self::sstore_static()),
1194 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
1195 "sstore_reset_without_cold_load_cost" => {
1196 Some(Self::sstore_reset_without_cold_load_cost())
1197 }
1198 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
1199 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
1200 "call_stipend" => Some(Self::call_stipend()),
1201 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
1202 "cold_storage_cost" => Some(Self::cold_storage_cost()),
1203 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
1204 "code_deposit_cost" => Some(Self::code_deposit_cost()),
1205 "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()),
1206 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
1207 "tx_token_cost" => Some(Self::tx_token_cost()),
1208 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
1209 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
1210 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
1211 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
1212 "tx_base_stipend" => Some(Self::tx_base_stipend()),
1213 "tx_create_cost" => Some(Self::tx_create_cost()),
1214 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
1215 "sstore_set_refund" => Some(Self::sstore_set_refund()),
1216 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
1217 "tx_eip7702_auth_refund" => Some(Self::tx_eip7702_auth_refund()),
1218 "sstore_set_state_gas" => Some(Self::sstore_set_state_gas()),
1219 "new_account_state_gas" => Some(Self::new_account_state_gas()),
1220 "code_deposit_state_gas" => Some(Self::code_deposit_state_gas()),
1221 "create_state_gas" => Some(Self::create_state_gas()),
1222 "tx_eip7702_state_gas_bytecode" => Some(Self::tx_eip7702_state_gas_bytecode()),
1223 "tx_floor_token_zero_byte_multiplier" => {
1224 Some(Self::tx_floor_token_zero_byte_multiplier())
1225 }
1226 "tx_access_list_floor_byte_multiplier" => {
1227 Some(Self::tx_access_list_floor_byte_multiplier())
1228 }
1229 _ => None,
1230 }
1231 }
1232
1233 pub const fn exp_byte_gas() -> GasId {
1235 Self::new(1)
1236 }
1237
1238 pub const fn extcodecopy_per_word() -> GasId {
1240 Self::new(2)
1241 }
1242
1243 pub const fn copy_per_word() -> GasId {
1245 Self::new(3)
1246 }
1247
1248 pub const fn logdata() -> GasId {
1250 Self::new(4)
1251 }
1252
1253 pub const fn logtopic() -> GasId {
1255 Self::new(5)
1256 }
1257
1258 pub const fn mcopy_per_word() -> GasId {
1260 Self::new(6)
1261 }
1262
1263 pub const fn keccak256_per_word() -> GasId {
1265 Self::new(7)
1266 }
1267
1268 pub const fn memory_linear_cost() -> GasId {
1270 Self::new(8)
1271 }
1272
1273 pub const fn memory_quadratic_reduction() -> GasId {
1275 Self::new(9)
1276 }
1277
1278 pub const fn initcode_per_word() -> GasId {
1280 Self::new(10)
1281 }
1282
1283 pub const fn create() -> GasId {
1285 Self::new(11)
1286 }
1287
1288 pub const fn call_stipend_reduction() -> GasId {
1290 Self::new(12)
1291 }
1292
1293 pub const fn transfer_value_cost() -> GasId {
1295 Self::new(13)
1296 }
1297
1298 pub const fn cold_account_additional_cost() -> GasId {
1300 Self::new(14)
1301 }
1302
1303 pub const fn new_account_cost() -> GasId {
1305 Self::new(15)
1306 }
1307
1308 pub const fn warm_storage_read_cost() -> GasId {
1312 Self::new(16)
1313 }
1314
1315 pub const fn sstore_static() -> GasId {
1318 Self::new(17)
1319 }
1320
1321 pub const fn sstore_set_without_load_cost() -> GasId {
1323 Self::new(18)
1324 }
1325
1326 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1328 Self::new(19)
1329 }
1330
1331 pub const fn sstore_clearing_slot_refund() -> GasId {
1333 Self::new(20)
1334 }
1335
1336 pub const fn selfdestruct_refund() -> GasId {
1338 Self::new(21)
1339 }
1340
1341 pub const fn call_stipend() -> GasId {
1343 Self::new(22)
1344 }
1345
1346 pub const fn cold_storage_additional_cost() -> GasId {
1348 Self::new(23)
1349 }
1350
1351 pub const fn cold_storage_cost() -> GasId {
1353 Self::new(24)
1354 }
1355
1356 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1358 Self::new(25)
1359 }
1360
1361 pub const fn code_deposit_cost() -> GasId {
1363 Self::new(26)
1364 }
1365
1366 pub const fn tx_eip7702_per_empty_account_cost() -> GasId {
1368 Self::new(27)
1369 }
1370
1371 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1373 Self::new(28)
1374 }
1375
1376 pub const fn tx_token_cost() -> GasId {
1378 Self::new(29)
1379 }
1380
1381 pub const fn tx_floor_cost_per_token() -> GasId {
1383 Self::new(30)
1384 }
1385
1386 pub const fn tx_floor_cost_base_gas() -> GasId {
1388 Self::new(31)
1389 }
1390
1391 pub const fn tx_access_list_address_cost() -> GasId {
1393 Self::new(32)
1394 }
1395
1396 pub const fn tx_access_list_storage_key_cost() -> GasId {
1398 Self::new(33)
1399 }
1400
1401 pub const fn tx_base_stipend() -> GasId {
1403 Self::new(34)
1404 }
1405
1406 pub const fn tx_create_cost() -> GasId {
1408 Self::new(35)
1409 }
1410
1411 pub const fn tx_initcode_cost() -> GasId {
1413 Self::new(36)
1414 }
1415
1416 pub const fn sstore_set_refund() -> GasId {
1418 Self::new(37)
1419 }
1420
1421 pub const fn sstore_reset_refund() -> GasId {
1423 Self::new(38)
1424 }
1425
1426 pub const fn tx_eip7702_auth_refund() -> GasId {
1430 Self::new(39)
1431 }
1432
1433 pub const fn sstore_set_state_gas() -> GasId {
1435 Self::new(40)
1436 }
1437
1438 pub const fn new_account_state_gas() -> GasId {
1440 Self::new(41)
1441 }
1442
1443 pub const fn code_deposit_state_gas() -> GasId {
1445 Self::new(42)
1446 }
1447
1448 pub const fn create_state_gas() -> GasId {
1450 Self::new(43)
1451 }
1452
1453 pub const fn tx_eip7702_state_gas_bytecode() -> GasId {
1457 Self::new(44)
1458 }
1459
1460 pub const fn tx_floor_token_zero_byte_multiplier() -> GasId {
1467 Self::new(45)
1468 }
1469
1470 pub const fn tx_access_list_floor_byte_multiplier() -> GasId {
1476 Self::new(46)
1477 }
1478}
1479
1480#[cfg(test)]
1481mod tests {
1482 use super::*;
1483 use std::collections::HashSet;
1484
1485 #[cfg(test)]
1486 mod log2floor_tests {
1487 use super::*;
1488
1489 #[test]
1490 fn test_log2floor_edge_cases() {
1491 assert_eq!(log2floor(U256::ZERO), 0);
1493
1494 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);
1508 assert_eq!(log2floor(U256::from(u64::MAX) + U256::from(1u64)), 64);
1509 assert_eq!(log2floor(U256::MAX), 255);
1510 }
1511 }
1512
1513 #[test]
1514 fn test_gas_id_name_and_from_str_coverage() {
1515 let mut unique_names = HashSet::new();
1516 let mut known_gas_ids = 0;
1517
1518 for i in 0..=255 {
1520 let gas_id = GasId::new(i);
1521 let name = gas_id.name();
1522
1523 if name != "unknown" {
1525 unique_names.insert(name);
1526 }
1527 }
1528
1529 for name in &unique_names {
1531 if let Some(gas_id) = GasId::from_name(name) {
1532 known_gas_ids += 1;
1533 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1535 }
1536 }
1537
1538 println!("Total unique named GasIds: {}", unique_names.len());
1539 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1540
1541 assert_eq!(
1543 unique_names.len(),
1544 known_gas_ids,
1545 "Not all unique names are resolvable via from_str"
1546 );
1547
1548 assert_eq!(
1550 unique_names.len(),
1551 46,
1552 "Expected 46 unique GasIds, found {}",
1553 unique_names.len()
1554 );
1555 }
1556
1557 #[test]
1558 fn test_tx_access_list_cost() {
1559 use crate::cfg::gas;
1560
1561 let gas_params = GasParams::new_spec(SpecId::BERLIN);
1563
1564 assert_eq!(gas_params.tx_access_list_cost(0, 0), 0);
1566
1567 assert_eq!(
1569 gas_params.tx_access_list_cost(1, 0),
1570 gas::ACCESS_LIST_ADDRESS
1571 );
1572
1573 assert_eq!(
1575 gas_params.tx_access_list_cost(0, 1),
1576 gas::ACCESS_LIST_STORAGE_KEY
1577 );
1578
1579 assert_eq!(
1581 gas_params.tx_access_list_cost(2, 5),
1582 2 * gas::ACCESS_LIST_ADDRESS + 5 * gas::ACCESS_LIST_STORAGE_KEY
1583 );
1584
1585 assert_eq!(
1587 gas_params.tx_access_list_cost(100, 200),
1588 100 * gas::ACCESS_LIST_ADDRESS + 200 * gas::ACCESS_LIST_STORAGE_KEY
1589 );
1590
1591 let gas_params_pre_berlin = GasParams::new_spec(SpecId::ISTANBUL);
1593 assert_eq!(gas_params_pre_berlin.tx_access_list_cost(10, 20), 0);
1594 }
1595
1596 #[test]
1597 fn test_initial_state_gas_for_create() {
1598 let gas_params = GasParams::new_spec(SpecId::AMSTERDAM);
1600 let create_gas = gas_params.initial_tx_gas(b"", true, 0, 0, 0);
1602 let expected_state_gas = gas_params.create_state_gas();
1603
1604 assert_eq!(create_gas.initial_state_gas_final(), expected_state_gas);
1605 assert_eq!(
1606 create_gas.initial_state_gas_final(),
1607 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM
1608 );
1609
1610 let create_cost = gas_params.tx_create_cost();
1612 let initcode_cost = gas_params.tx_initcode_cost(0);
1613 assert_eq!(
1614 create_gas.initial_total_gas(),
1615 gas_params.tx_base_stipend() + create_cost + initcode_cost + expected_state_gas
1616 );
1617
1618 let call_gas = gas_params.initial_tx_gas(b"", false, 0, 0, 0);
1620 assert_eq!(call_gas.initial_state_gas_final(), 0);
1621 assert_eq!(call_gas.initial_total_gas(), gas_params.tx_base_stipend());
1623 }
1624
1625 #[test]
1626 fn test_eip7981_access_list_cost_amsterdam() {
1627 let params = GasParams::new_spec(SpecId::AMSTERDAM);
1630
1631 assert_eq!(params.tx_access_list_address_cost(), 2400 + 20 * 64);
1633 assert_eq!(params.tx_access_list_storage_key_cost(), 1900 + 32 * 64);
1634 assert_eq!(params.tx_access_list_cost(1, 0), 2400 + 20 * 64);
1635 assert_eq!(params.tx_access_list_cost(0, 1), 1900 + 32 * 64);
1636
1637 assert_eq!(params.tx_access_list_floor_byte_multiplier(), 4);
1639 assert_eq!(params.tx_floor_tokens_in_access_list(2, 3), (40 + 96) * 4);
1641
1642 let gas = params.initial_tx_gas(b"", false, 2, 3, 0);
1644 let expected_al_floor = (40 + 96) * 4 * params.tx_floor_cost_per_token();
1645 assert_eq!(
1646 gas.floor_gas(),
1647 params.tx_floor_cost_base_gas() + expected_al_floor,
1648 );
1649
1650 let prague = GasParams::new_spec(SpecId::PRAGUE);
1652 assert_eq!(prague.tx_access_list_floor_byte_multiplier(), 0);
1653 assert_eq!(prague.tx_floor_tokens_in_access_list(2, 3), 0);
1654 let prague_gas = prague.initial_tx_gas(b"", false, 2, 3, 0);
1655 assert_eq!(prague_gas.floor_gas(), prague.tx_floor_cost_base_gas());
1656 }
1657}