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::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_per_empty_account_cost().as_usize()] =
303 eip7702::PER_EMPTY_ACCOUNT_COST;
304
305 table[GasId::tx_eip7702_auth_refund().as_usize()] =
307 eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST;
308
309 table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
310 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
311 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] = 1;
314 }
315
316 if spec.is_enabled_in(SpecId::AMSTERDAM) {
320 table[GasId::create().as_usize()] = 9000;
322 table[GasId::tx_create_cost().as_usize()] = 9000;
323 table[GasId::code_deposit_cost().as_usize()] = 0;
324 table[GasId::new_account_cost().as_usize()] = 0;
325 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
326 table[GasId::sstore_set_without_load_cost().as_usize()] = 2800;
329
330 table[GasId::sstore_set_state_gas().as_usize()] =
332 eip8037::SSTORE_SET_BYTES * eip8037::CPSB_GLAMSTERDAM;
333 table[GasId::new_account_state_gas().as_usize()] =
334 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM;
335 table[GasId::code_deposit_state_gas().as_usize()] =
336 eip8037::CODE_DEPOSIT_PER_BYTE * eip8037::CPSB_GLAMSTERDAM;
337 table[GasId::create_state_gas().as_usize()] =
338 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM;
339 table[GasId::tx_eip7702_state_gas_bytecode().as_usize()] =
340 eip8037::AUTH_BASE_BYTES * eip8037::CPSB_GLAMSTERDAM;
341
342 table[GasId::sstore_set_refund().as_usize()] = 2800;
346
347 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] =
354 eip8037::EIP7702_PER_EMPTY_ACCOUNT_REGULAR;
355 table[GasId::tx_eip7702_auth_refund().as_usize()] = 0;
356
357 table[GasId::tx_floor_cost_per_token().as_usize()] = 16;
362 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] =
363 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()];
364
365 table[GasId::tx_access_list_address_cost().as_usize()] =
371 gas::ACCESS_LIST_ADDRESS + 20 * 64;
372 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
373 gas::ACCESS_LIST_STORAGE_KEY + 32 * 64;
374 table[GasId::tx_access_list_floor_byte_multiplier().as_usize()] = 4;
375 }
376
377 Self::new(Arc::new(table))
378 }
379
380 #[inline]
382 pub fn get(&self, id: GasId) -> u64 {
383 self.table[id.as_usize()]
384 }
385
386 #[inline]
388 pub fn exp_cost(&self, power: U256) -> u64 {
389 if power.is_zero() {
390 return 0;
391 }
392 self.get(GasId::exp_byte_gas())
394 .saturating_mul(log2floor(power) / 8 + 1)
395 }
396
397 #[inline]
399 pub fn selfdestruct_refund(&self) -> i64 {
400 self.get(GasId::selfdestruct_refund()) as i64
401 }
402
403 #[inline]
406 pub fn selfdestruct_cold_cost(&self) -> u64 {
407 self.cold_account_additional_cost() + self.warm_storage_read_cost()
408 }
409
410 #[inline]
412 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
413 let mut gas = 0;
414
415 if should_charge_topup {
417 gas += self.new_account_cost_for_selfdestruct();
418 }
419
420 if is_cold {
421 gas += self.selfdestruct_cold_cost();
427 }
428 gas
429 }
430
431 #[inline]
433 pub fn extcodecopy(&self, len: usize) -> u64 {
434 self.get(GasId::extcodecopy_per_word())
435 .saturating_mul(num_words(len) as u64)
436 }
437
438 #[inline]
440 pub fn mcopy_cost(&self, len: usize) -> u64 {
441 self.get(GasId::mcopy_per_word())
442 .saturating_mul(num_words(len) as u64)
443 }
444
445 #[inline]
447 pub fn sstore_static_gas(&self) -> u64 {
448 self.get(GasId::sstore_static())
449 }
450
451 #[inline]
453 pub fn sstore_set_without_load_cost(&self) -> u64 {
454 self.get(GasId::sstore_set_without_load_cost())
455 }
456
457 #[inline]
459 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
460 self.get(GasId::sstore_reset_without_cold_load_cost())
461 }
462
463 #[inline]
465 pub fn sstore_clearing_slot_refund(&self) -> u64 {
466 self.get(GasId::sstore_clearing_slot_refund())
467 }
468
469 #[inline]
471 pub fn sstore_set_refund(&self) -> u64 {
472 self.get(GasId::sstore_set_refund())
473 }
474
475 #[inline]
477 pub fn sstore_reset_refund(&self) -> u64 {
478 self.get(GasId::sstore_reset_refund())
479 }
480
481 #[inline]
485 pub fn max_refund_quotient(&self) -> u64 {
486 self.get(GasId::max_refund_quotient())
487 }
488
489 #[inline]
493 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
494 if !is_istanbul {
497 if vals.is_present_zero() && !vals.is_new_zero() {
498 return self.sstore_set_without_load_cost();
499 } else {
500 return self.sstore_reset_without_cold_load_cost();
501 }
502 }
503
504 let mut gas = 0;
505
506 if is_cold {
508 gas += self.cold_storage_cost();
509 }
510
511 if vals.new_values_changes_present() && vals.is_original_eq_present() {
513 gas += if vals.is_original_zero() {
514 self.sstore_set_without_load_cost()
517 } else {
518 self.sstore_reset_without_cold_load_cost()
520 };
521 }
522 gas
523 }
524
525 #[inline]
527 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
528 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
530
531 if !is_istanbul {
532 if !vals.is_present_zero() && vals.is_new_zero() {
534 return sstore_clearing_slot_refund;
535 }
536 return 0;
537 }
538
539 if vals.is_new_eq_present() {
541 return 0;
542 }
543
544 if vals.is_original_eq_present() && vals.is_new_zero() {
547 return sstore_clearing_slot_refund;
548 }
549
550 let mut refund = 0;
551 if !vals.is_original_zero() {
553 if vals.is_present_zero() {
555 refund -= sstore_clearing_slot_refund;
557 } else if vals.is_new_zero() {
559 refund += sstore_clearing_slot_refund;
561 }
562 }
563
564 if vals.is_original_eq_new() {
566 if vals.is_original_zero() {
568 refund += self.sstore_set_refund() as i64;
570 } else {
572 refund += self.sstore_reset_refund() as i64;
574 }
575 }
576 refund
577 }
578
579 #[inline]
581 pub fn log_cost(&self, n: u8, len: u64) -> u64 {
582 self.get(GasId::logdata())
583 .saturating_mul(len)
584 .saturating_add(self.get(GasId::logtopic()) * n as u64)
585 }
586
587 #[inline]
589 pub fn keccak256_cost(&self, len: usize) -> u64 {
590 self.get(GasId::keccak256_per_word())
591 .saturating_mul(num_words(len) as u64)
592 }
593
594 #[inline]
596 pub fn memory_cost(&self, len: usize) -> u64 {
597 let len = len as u64;
598 self.get(GasId::memory_linear_cost())
599 .saturating_mul(len)
600 .saturating_add(
601 (len.saturating_mul(len))
602 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
603 )
604 }
605
606 #[inline]
608 pub fn initcode_cost(&self, len: usize) -> u64 {
609 self.get(GasId::initcode_per_word())
610 .saturating_mul(num_words(len) as u64)
611 }
612
613 #[inline]
615 pub fn create_cost(&self) -> u64 {
616 self.get(GasId::create())
617 }
618
619 #[inline]
621 pub fn create2_cost(&self, len: usize) -> u64 {
622 self.get(GasId::create()).saturating_add(
623 self.get(GasId::keccak256_per_word())
624 .saturating_mul(num_words(len) as u64),
625 )
626 }
627
628 #[inline]
630 pub fn call_stipend(&self) -> u64 {
631 self.get(GasId::call_stipend())
632 }
633
634 #[inline]
636 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
637 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
638 }
639
640 #[inline]
642 pub fn transfer_value_cost(&self) -> u64 {
643 self.get(GasId::transfer_value_cost())
644 }
645
646 #[inline]
648 pub fn cold_account_additional_cost(&self) -> u64 {
649 self.get(GasId::cold_account_additional_cost())
650 }
651
652 #[inline]
654 pub fn cold_storage_additional_cost(&self) -> u64 {
655 self.get(GasId::cold_storage_additional_cost())
656 }
657
658 #[inline]
660 pub fn cold_storage_cost(&self) -> u64 {
661 self.get(GasId::cold_storage_cost())
662 }
663
664 #[inline]
666 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
667 if !is_spurious_dragon || transfers_value {
671 return self.get(GasId::new_account_cost());
672 }
673 0
674 }
675
676 #[inline]
678 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
679 self.get(GasId::new_account_cost_for_selfdestruct())
680 }
681
682 #[inline]
684 pub fn warm_storage_read_cost(&self) -> u64 {
685 self.get(GasId::warm_storage_read_cost())
686 }
687
688 #[inline]
690 pub fn copy_cost(&self, len: usize) -> u64 {
691 self.copy_per_word_cost(num_words(len))
692 }
693
694 #[inline]
696 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
697 self.get(GasId::copy_per_word())
698 .saturating_mul(word_num as u64)
699 }
700
701 #[inline]
703 pub fn code_deposit_cost(&self, len: usize) -> u64 {
704 self.get(GasId::code_deposit_cost())
705 .saturating_mul(len as u64)
706 }
707
708 #[inline]
710 pub fn sstore_state_gas(&self, vals: &SStoreResult) -> u64 {
711 if vals.new_values_changes_present()
712 && vals.is_original_eq_present()
713 && vals.is_original_zero()
714 {
715 self.get(GasId::sstore_set_state_gas())
716 } else {
717 0
718 }
719 }
720
721 #[inline]
729 pub fn sstore_state_gas_refill(&self, vals: &SStoreResult) -> u64 {
730 if !vals.is_new_eq_present() && vals.is_original_eq_new() && vals.is_original_zero() {
731 self.get(GasId::sstore_set_state_gas())
732 } else {
733 0
734 }
735 }
736
737 #[inline]
739 pub fn new_account_state_gas(&self) -> u64 {
740 self.get(GasId::new_account_state_gas())
741 }
742
743 #[inline]
745 pub fn code_deposit_state_gas(&self, len: usize) -> u64 {
746 self.get(GasId::code_deposit_state_gas())
747 .saturating_mul(len as u64)
748 }
749
750 #[inline]
752 pub fn create_state_gas(&self) -> u64 {
753 self.get(GasId::create_state_gas())
754 }
755
756 #[inline]
762 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
763 let regular = self.get(GasId::tx_eip7702_per_empty_account_cost());
764 let state = self.tx_eip7702_state_gas();
765 regular.saturating_add(state)
766 }
767
768 #[inline]
774 pub fn tx_eip7702_auth_refund(&self) -> u64 {
775 let regular = self.get(GasId::tx_eip7702_auth_refund());
776 let state = self.new_account_state_gas();
777 regular.saturating_add(state)
778 }
779
780 #[inline]
785 pub fn tx_eip7702_state_gas(&self) -> u64 {
786 let new_account = self.get(GasId::new_account_state_gas());
787 let bytecode = self.get(GasId::tx_eip7702_state_gas_bytecode());
788 new_account.saturating_add(bytecode)
789 }
790
791 #[inline]
797 pub fn tx_eip7702_state_refund(&self, refunded_accounts: u64, refunded_bytecodes: u64) -> u64 {
798 let per_account = self
799 .get(GasId::new_account_state_gas())
800 .saturating_mul(refunded_accounts);
801 let per_bytecode = self
802 .get(GasId::tx_eip7702_state_gas_bytecode())
803 .saturating_mul(refunded_bytecodes);
804 per_account.saturating_add(per_bytecode)
805 }
806
807 #[inline]
812 pub fn tx_eip7702_auth_refund_regular(&self) -> u64 {
813 self.get(GasId::tx_eip7702_auth_refund())
814 }
815
816 #[inline]
818 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
819 self.get(GasId::tx_token_non_zero_byte_multiplier())
820 }
821
822 #[inline]
824 pub fn tx_token_cost(&self) -> u64 {
825 self.get(GasId::tx_token_cost())
826 }
827
828 pub fn tx_floor_cost_per_token(&self) -> u64 {
830 self.get(GasId::tx_floor_cost_per_token())
831 }
832
833 pub fn tx_floor_token_zero_byte_multiplier(&self) -> u64 {
841 self.get(GasId::tx_floor_token_zero_byte_multiplier())
842 }
843
844 #[inline]
855 pub fn tx_floor_cost(&self, input: &[u8]) -> u64 {
856 let zero_multiplier = self.tx_floor_token_zero_byte_multiplier();
857 let non_zero_multiplier = self.tx_token_non_zero_byte_multiplier();
858 let floor_tokens = if zero_multiplier == non_zero_multiplier {
859 input.len() as u64 * non_zero_multiplier
860 } else {
861 get_tokens_in_calldata(input, non_zero_multiplier)
862 };
863 self.tx_floor_cost_with_tokens(floor_tokens)
864 }
865
866 #[inline]
868 pub fn tx_floor_cost_with_tokens(&self, tokens: u64) -> u64 {
869 self.tx_floor_cost_per_token() * tokens + self.tx_floor_cost_base_gas()
870 }
871
872 pub fn tx_floor_cost_base_gas(&self) -> u64 {
874 self.get(GasId::tx_floor_cost_base_gas())
875 }
876
877 pub fn tx_access_list_address_cost(&self) -> u64 {
879 self.get(GasId::tx_access_list_address_cost())
880 }
881
882 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
884 self.get(GasId::tx_access_list_storage_key_cost())
885 }
886
887 #[inline]
905 pub fn tx_access_list_cost(&self, accounts: u64, storages: u64) -> u64 {
906 accounts
907 .saturating_mul(self.tx_access_list_address_cost())
908 .saturating_add(storages.saturating_mul(self.tx_access_list_storage_key_cost()))
909 }
910
911 #[inline]
919 pub fn tx_access_list_floor_byte_multiplier(&self) -> u64 {
920 self.get(GasId::tx_access_list_floor_byte_multiplier())
921 }
922
923 #[inline]
928 pub fn tx_floor_tokens_in_access_list(&self, accounts: u64, storages: u64) -> u64 {
929 let bytes = accounts
930 .saturating_mul(20)
931 .saturating_add(storages.saturating_mul(32));
932 bytes.saturating_mul(self.tx_access_list_floor_byte_multiplier())
933 }
934
935 pub fn tx_base_stipend(&self) -> u64 {
937 self.get(GasId::tx_base_stipend())
938 }
939
940 #[inline]
944 pub fn tx_create_cost(&self) -> u64 {
945 self.get(GasId::tx_create_cost())
946 }
947
948 #[inline]
950 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
951 self.get(GasId::tx_initcode_cost())
952 .saturating_mul(num_words(len) as u64)
953 }
954
955 pub fn initial_tx_gas(
971 &self,
972 input: &[u8],
973 is_create: bool,
974 access_list_accounts: u64,
975 access_list_storages: u64,
976 authorization_list_num: u64,
977 ) -> InitialAndFloorGas {
978 let tokens_in_calldata =
980 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
981
982 let auth_total_cost = authorization_list_num * self.tx_eip7702_per_empty_account_cost();
986 let auth_state_gas = authorization_list_num * self.tx_eip7702_state_gas();
987
988 let auth_regular_cost = auth_total_cost - auth_state_gas;
989
990 let mut initial_regular_gas = tokens_in_calldata * self.tx_token_cost()
991 + access_list_accounts * self.tx_access_list_address_cost()
993 + access_list_storages * self.tx_access_list_storage_key_cost()
995 + self.tx_base_stipend()
996 + auth_regular_cost;
998
999 let mut initial_state_gas = auth_state_gas;
1001
1002 if is_create {
1003 initial_regular_gas += self.tx_create_cost();
1005
1006 initial_regular_gas += self.tx_initcode_cost(input.len());
1008
1009 initial_state_gas += self.create_state_gas();
1012 }
1013
1014 let access_list_floor_tokens =
1017 self.tx_floor_tokens_in_access_list(access_list_accounts, access_list_storages);
1018 let floor_gas =
1019 self.tx_floor_cost(input) + access_list_floor_tokens * self.tx_floor_cost_per_token();
1020
1021 InitialAndFloorGas::default()
1022 .with_initial_regular_gas(initial_regular_gas)
1023 .with_initial_state_gas(initial_state_gas)
1024 .with_floor_gas(floor_gas)
1025 }
1026
1027 pub fn initial_tx_gas_for_tx(&self, tx: impl Transaction) -> InitialAndFloorGas {
1032 let mut accounts = 0;
1033 let mut storages = 0;
1034 if tx.tx_type() != TransactionType::Legacy {
1036 (accounts, storages) = tx
1037 .access_list()
1038 .map(|al| {
1039 al.fold((0, 0), |(num_accounts, num_storage_slots), item| {
1040 (
1041 num_accounts + 1,
1042 num_storage_slots + item.storage_slots().count() as u64,
1043 )
1044 })
1045 })
1046 .unwrap_or_default();
1047 }
1048
1049 self.initial_tx_gas(
1050 tx.input(),
1051 tx.kind().is_create(),
1052 accounts,
1053 storages,
1054 tx.authorization_list_len() as u64,
1055 )
1056 }
1057}
1058
1059#[inline]
1060pub(crate) const fn log2floor(value: U256) -> u64 {
1061 255u64.saturating_sub(value.leading_zeros() as u64)
1062}
1063
1064#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1066pub struct GasId(u8);
1067
1068impl GasId {
1069 #[inline]
1071 pub const fn new(id: u8) -> Self {
1072 Self(id)
1073 }
1074
1075 #[inline]
1077 pub const fn as_u8(&self) -> u8 {
1078 self.0
1079 }
1080
1081 #[inline]
1083 pub const fn as_usize(&self) -> usize {
1084 self.0 as usize
1085 }
1086
1087 pub const fn name(&self) -> &'static str {
1099 match self.0 {
1100 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
1101 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
1102 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
1103 x if x == Self::logdata().as_u8() => "logdata",
1104 x if x == Self::logtopic().as_u8() => "logtopic",
1105 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
1106 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
1107 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
1108 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
1109 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
1110 x if x == Self::create().as_u8() => "create",
1111 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
1112 x if x == Self::max_refund_quotient().as_u8() => "max_refund_quotient",
1113 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
1114 x if x == Self::cold_account_additional_cost().as_u8() => {
1115 "cold_account_additional_cost"
1116 }
1117 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
1118 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
1119 x if x == Self::sstore_static().as_u8() => "sstore_static",
1120 x if x == Self::sstore_set_without_load_cost().as_u8() => {
1121 "sstore_set_without_load_cost"
1122 }
1123 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
1124 "sstore_reset_without_cold_load_cost"
1125 }
1126 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
1127 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
1128 x if x == Self::call_stipend().as_u8() => "call_stipend",
1129 x if x == Self::cold_storage_additional_cost().as_u8() => {
1130 "cold_storage_additional_cost"
1131 }
1132 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
1133 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
1134 "new_account_cost_for_selfdestruct"
1135 }
1136 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
1137 x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => {
1138 "tx_eip7702_per_empty_account_cost"
1139 }
1140 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
1141 "tx_token_non_zero_byte_multiplier"
1142 }
1143 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
1144 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
1145 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
1146 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
1147 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
1148 "tx_access_list_storage_key_cost"
1149 }
1150 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
1151 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
1152 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
1153 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
1154 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
1155 x if x == Self::tx_eip7702_auth_refund().as_u8() => "tx_eip7702_auth_refund",
1156 x if x == Self::sstore_set_state_gas().as_u8() => "sstore_set_state_gas",
1157 x if x == Self::new_account_state_gas().as_u8() => "new_account_state_gas",
1158 x if x == Self::code_deposit_state_gas().as_u8() => "code_deposit_state_gas",
1159 x if x == Self::create_state_gas().as_u8() => "create_state_gas",
1160 x if x == Self::tx_eip7702_state_gas_bytecode().as_u8() => {
1161 "tx_eip7702_state_gas_bytecode"
1162 }
1163 x if x == Self::tx_floor_token_zero_byte_multiplier().as_u8() => {
1164 "tx_floor_token_zero_byte_multiplier"
1165 }
1166 x if x == Self::tx_access_list_floor_byte_multiplier().as_u8() => {
1167 "tx_access_list_floor_byte_multiplier"
1168 }
1169 _ => "unknown",
1170 }
1171 }
1172
1173 pub fn from_name(s: &str) -> Option<GasId> {
1187 match s {
1188 "exp_byte_gas" => Some(Self::exp_byte_gas()),
1189 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
1190 "copy_per_word" => Some(Self::copy_per_word()),
1191 "logdata" => Some(Self::logdata()),
1192 "logtopic" => Some(Self::logtopic()),
1193 "mcopy_per_word" => Some(Self::mcopy_per_word()),
1194 "keccak256_per_word" => Some(Self::keccak256_per_word()),
1195 "memory_linear_cost" => Some(Self::memory_linear_cost()),
1196 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
1197 "initcode_per_word" => Some(Self::initcode_per_word()),
1198 "create" => Some(Self::create()),
1199 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
1200 "max_refund_quotient" => Some(Self::max_refund_quotient()),
1201 "transfer_value_cost" => Some(Self::transfer_value_cost()),
1202 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
1203 "new_account_cost" => Some(Self::new_account_cost()),
1204 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
1205 "sstore_static" => Some(Self::sstore_static()),
1206 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
1207 "sstore_reset_without_cold_load_cost" => {
1208 Some(Self::sstore_reset_without_cold_load_cost())
1209 }
1210 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
1211 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
1212 "call_stipend" => Some(Self::call_stipend()),
1213 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
1214 "cold_storage_cost" => Some(Self::cold_storage_cost()),
1215 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
1216 "code_deposit_cost" => Some(Self::code_deposit_cost()),
1217 "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()),
1218 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
1219 "tx_token_cost" => Some(Self::tx_token_cost()),
1220 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
1221 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
1222 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
1223 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
1224 "tx_base_stipend" => Some(Self::tx_base_stipend()),
1225 "tx_create_cost" => Some(Self::tx_create_cost()),
1226 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
1227 "sstore_set_refund" => Some(Self::sstore_set_refund()),
1228 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
1229 "tx_eip7702_auth_refund" => Some(Self::tx_eip7702_auth_refund()),
1230 "sstore_set_state_gas" => Some(Self::sstore_set_state_gas()),
1231 "new_account_state_gas" => Some(Self::new_account_state_gas()),
1232 "code_deposit_state_gas" => Some(Self::code_deposit_state_gas()),
1233 "create_state_gas" => Some(Self::create_state_gas()),
1234 "tx_eip7702_state_gas_bytecode" => Some(Self::tx_eip7702_state_gas_bytecode()),
1235 "tx_floor_token_zero_byte_multiplier" => {
1236 Some(Self::tx_floor_token_zero_byte_multiplier())
1237 }
1238 "tx_access_list_floor_byte_multiplier" => {
1239 Some(Self::tx_access_list_floor_byte_multiplier())
1240 }
1241 _ => None,
1242 }
1243 }
1244
1245 pub const fn exp_byte_gas() -> GasId {
1247 Self::new(1)
1248 }
1249
1250 pub const fn extcodecopy_per_word() -> GasId {
1252 Self::new(2)
1253 }
1254
1255 pub const fn copy_per_word() -> GasId {
1257 Self::new(3)
1258 }
1259
1260 pub const fn logdata() -> GasId {
1262 Self::new(4)
1263 }
1264
1265 pub const fn logtopic() -> GasId {
1267 Self::new(5)
1268 }
1269
1270 pub const fn mcopy_per_word() -> GasId {
1272 Self::new(6)
1273 }
1274
1275 pub const fn keccak256_per_word() -> GasId {
1277 Self::new(7)
1278 }
1279
1280 pub const fn memory_linear_cost() -> GasId {
1282 Self::new(8)
1283 }
1284
1285 pub const fn memory_quadratic_reduction() -> GasId {
1287 Self::new(9)
1288 }
1289
1290 pub const fn initcode_per_word() -> GasId {
1292 Self::new(10)
1293 }
1294
1295 pub const fn create() -> GasId {
1297 Self::new(11)
1298 }
1299
1300 pub const fn call_stipend_reduction() -> GasId {
1302 Self::new(12)
1303 }
1304
1305 pub const fn max_refund_quotient() -> GasId {
1307 Self::new(47)
1308 }
1309
1310 pub const fn transfer_value_cost() -> GasId {
1312 Self::new(13)
1313 }
1314
1315 pub const fn cold_account_additional_cost() -> GasId {
1317 Self::new(14)
1318 }
1319
1320 pub const fn new_account_cost() -> GasId {
1322 Self::new(15)
1323 }
1324
1325 pub const fn warm_storage_read_cost() -> GasId {
1329 Self::new(16)
1330 }
1331
1332 pub const fn sstore_static() -> GasId {
1335 Self::new(17)
1336 }
1337
1338 pub const fn sstore_set_without_load_cost() -> GasId {
1340 Self::new(18)
1341 }
1342
1343 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1345 Self::new(19)
1346 }
1347
1348 pub const fn sstore_clearing_slot_refund() -> GasId {
1350 Self::new(20)
1351 }
1352
1353 pub const fn selfdestruct_refund() -> GasId {
1355 Self::new(21)
1356 }
1357
1358 pub const fn call_stipend() -> GasId {
1360 Self::new(22)
1361 }
1362
1363 pub const fn cold_storage_additional_cost() -> GasId {
1365 Self::new(23)
1366 }
1367
1368 pub const fn cold_storage_cost() -> GasId {
1370 Self::new(24)
1371 }
1372
1373 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1375 Self::new(25)
1376 }
1377
1378 pub const fn code_deposit_cost() -> GasId {
1380 Self::new(26)
1381 }
1382
1383 pub const fn tx_eip7702_per_empty_account_cost() -> GasId {
1385 Self::new(27)
1386 }
1387
1388 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1390 Self::new(28)
1391 }
1392
1393 pub const fn tx_token_cost() -> GasId {
1395 Self::new(29)
1396 }
1397
1398 pub const fn tx_floor_cost_per_token() -> GasId {
1400 Self::new(30)
1401 }
1402
1403 pub const fn tx_floor_cost_base_gas() -> GasId {
1405 Self::new(31)
1406 }
1407
1408 pub const fn tx_access_list_address_cost() -> GasId {
1410 Self::new(32)
1411 }
1412
1413 pub const fn tx_access_list_storage_key_cost() -> GasId {
1415 Self::new(33)
1416 }
1417
1418 pub const fn tx_base_stipend() -> GasId {
1420 Self::new(34)
1421 }
1422
1423 pub const fn tx_create_cost() -> GasId {
1425 Self::new(35)
1426 }
1427
1428 pub const fn tx_initcode_cost() -> GasId {
1430 Self::new(36)
1431 }
1432
1433 pub const fn sstore_set_refund() -> GasId {
1435 Self::new(37)
1436 }
1437
1438 pub const fn sstore_reset_refund() -> GasId {
1440 Self::new(38)
1441 }
1442
1443 pub const fn tx_eip7702_auth_refund() -> GasId {
1447 Self::new(39)
1448 }
1449
1450 pub const fn sstore_set_state_gas() -> GasId {
1452 Self::new(40)
1453 }
1454
1455 pub const fn new_account_state_gas() -> GasId {
1457 Self::new(41)
1458 }
1459
1460 pub const fn code_deposit_state_gas() -> GasId {
1462 Self::new(42)
1463 }
1464
1465 pub const fn create_state_gas() -> GasId {
1467 Self::new(43)
1468 }
1469
1470 pub const fn tx_eip7702_state_gas_bytecode() -> GasId {
1474 Self::new(44)
1475 }
1476
1477 pub const fn tx_floor_token_zero_byte_multiplier() -> GasId {
1484 Self::new(45)
1485 }
1486
1487 pub const fn tx_access_list_floor_byte_multiplier() -> GasId {
1493 Self::new(46)
1494 }
1495}
1496
1497#[cfg(test)]
1498mod tests {
1499 use super::*;
1500 use std::collections::HashSet;
1501
1502 #[cfg(test)]
1503 mod log2floor_tests {
1504 use super::*;
1505
1506 #[test]
1507 fn test_log2floor_edge_cases() {
1508 assert_eq!(log2floor(U256::ZERO), 0);
1510
1511 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);
1525 assert_eq!(log2floor(U256::from(u64::MAX) + U256::from(1u64)), 64);
1526 assert_eq!(log2floor(U256::MAX), 255);
1527 }
1528 }
1529
1530 #[test]
1531 fn test_gas_id_name_and_from_str_coverage() {
1532 let mut unique_names = HashSet::new();
1533 let mut known_gas_ids = 0;
1534
1535 for i in 0..=255 {
1537 let gas_id = GasId::new(i);
1538 let name = gas_id.name();
1539
1540 if name != "unknown" {
1542 unique_names.insert(name);
1543 }
1544 }
1545
1546 for name in &unique_names {
1548 if let Some(gas_id) = GasId::from_name(name) {
1549 known_gas_ids += 1;
1550 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1552 }
1553 }
1554
1555 println!("Total unique named GasIds: {}", unique_names.len());
1556 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1557
1558 assert_eq!(
1560 unique_names.len(),
1561 known_gas_ids,
1562 "Not all unique names are resolvable via from_str"
1563 );
1564
1565 assert_eq!(
1567 unique_names.len(),
1568 47,
1569 "Expected 47 unique GasIds, found {}",
1570 unique_names.len()
1571 );
1572 }
1573
1574 #[test]
1575 fn test_max_refund_quotient_defaults_and_override() {
1576 let frontier = GasParams::new_spec(SpecId::FRONTIER);
1577 assert_eq!(frontier.max_refund_quotient(), 2);
1578 assert_eq!(frontier.get(GasId::max_refund_quotient()), 2);
1579
1580 let london = GasParams::new_spec(SpecId::LONDON);
1581 assert_eq!(london.max_refund_quotient(), 5);
1582 assert_eq!(
1583 GasId::from_name("max_refund_quotient"),
1584 Some(GasId::max_refund_quotient())
1585 );
1586 assert_eq!(GasId::max_refund_quotient().name(), "max_refund_quotient");
1587
1588 let mut custom = london;
1589 custom.override_gas([(GasId::max_refund_quotient(), 10)]);
1590 assert_eq!(custom.max_refund_quotient(), 10);
1591 }
1592
1593 #[test]
1594 fn test_tx_access_list_cost() {
1595 use crate::cfg::gas;
1596
1597 let gas_params = GasParams::new_spec(SpecId::BERLIN);
1599
1600 assert_eq!(gas_params.tx_access_list_cost(0, 0), 0);
1602
1603 assert_eq!(
1605 gas_params.tx_access_list_cost(1, 0),
1606 gas::ACCESS_LIST_ADDRESS
1607 );
1608
1609 assert_eq!(
1611 gas_params.tx_access_list_cost(0, 1),
1612 gas::ACCESS_LIST_STORAGE_KEY
1613 );
1614
1615 assert_eq!(
1617 gas_params.tx_access_list_cost(2, 5),
1618 2 * gas::ACCESS_LIST_ADDRESS + 5 * gas::ACCESS_LIST_STORAGE_KEY
1619 );
1620
1621 assert_eq!(
1623 gas_params.tx_access_list_cost(100, 200),
1624 100 * gas::ACCESS_LIST_ADDRESS + 200 * gas::ACCESS_LIST_STORAGE_KEY
1625 );
1626
1627 let gas_params_pre_berlin = GasParams::new_spec(SpecId::ISTANBUL);
1629 assert_eq!(gas_params_pre_berlin.tx_access_list_cost(10, 20), 0);
1630 }
1631
1632 #[test]
1633 fn test_initial_state_gas_for_create() {
1634 let gas_params = GasParams::new_spec(SpecId::AMSTERDAM);
1636 let create_gas = gas_params.initial_tx_gas(b"", true, 0, 0, 0);
1638 let expected_state_gas = gas_params.create_state_gas();
1639
1640 assert_eq!(create_gas.initial_state_gas_final(), expected_state_gas);
1641 assert_eq!(
1642 create_gas.initial_state_gas_final(),
1643 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM
1644 );
1645
1646 let create_cost = gas_params.tx_create_cost();
1648 let initcode_cost = gas_params.tx_initcode_cost(0);
1649 assert_eq!(
1650 create_gas.initial_total_gas(),
1651 gas_params.tx_base_stipend() + create_cost + initcode_cost + expected_state_gas
1652 );
1653
1654 let call_gas = gas_params.initial_tx_gas(b"", false, 0, 0, 0);
1656 assert_eq!(call_gas.initial_state_gas_final(), 0);
1657 assert_eq!(call_gas.initial_total_gas(), gas_params.tx_base_stipend());
1659 }
1660
1661 #[test]
1662 fn test_eip7981_access_list_cost_amsterdam() {
1663 let params = GasParams::new_spec(SpecId::AMSTERDAM);
1666
1667 assert_eq!(params.tx_access_list_address_cost(), 2400 + 20 * 64);
1669 assert_eq!(params.tx_access_list_storage_key_cost(), 1900 + 32 * 64);
1670 assert_eq!(params.tx_access_list_cost(1, 0), 2400 + 20 * 64);
1671 assert_eq!(params.tx_access_list_cost(0, 1), 1900 + 32 * 64);
1672
1673 assert_eq!(params.tx_access_list_floor_byte_multiplier(), 4);
1675 assert_eq!(params.tx_floor_tokens_in_access_list(2, 3), (40 + 96) * 4);
1677
1678 let gas = params.initial_tx_gas(b"", false, 2, 3, 0);
1680 let expected_al_floor = (40 + 96) * 4 * params.tx_floor_cost_per_token();
1681 assert_eq!(
1682 gas.floor_gas(),
1683 params.tx_floor_cost_base_gas() + expected_al_floor,
1684 );
1685
1686 let prague = GasParams::new_spec(SpecId::PRAGUE);
1688 assert_eq!(prague.tx_access_list_floor_byte_multiplier(), 0);
1689 assert_eq!(prague.tx_floor_tokens_in_access_list(2, 3), 0);
1690 let prague_gas = prague.initial_tx_gas(b"", false, 2, 3, 0);
1691 assert_eq!(prague_gas.floor_gas(), prague.tx_floor_cost_base_gas());
1692 }
1693}