1use crate::{
4 cfg::gas::{self, get_tokens_in_calldata, InitialAndFloorGas},
5 context::SStoreResult,
6 transaction::AccessListItemTr as _,
7 Transaction, TransactionType,
8};
9use core::hash::{Hash, Hasher};
10use primitives::{
11 eip2780, eip7702, eip8037, eip8038,
12 hardfork::SpecId::{self},
13 OnceLock, U256,
14};
15use std::sync::Arc;
16
17#[derive(Clone)]
19pub struct GasParams {
20 table: Arc<[u64; 256]>,
22}
23
24impl PartialEq<GasParams> for GasParams {
25 fn eq(&self, other: &GasParams) -> bool {
26 self.table == other.table
27 }
28}
29
30impl Hash for GasParams {
31 fn hash<H: Hasher>(&self, hasher: &mut H) {
32 self.table.hash(hasher);
33 }
34}
35
36impl core::fmt::Debug for GasParams {
37 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 write!(f, "GasParams {{ table: {:?} }}", self.table)
39 }
40}
41
42#[inline]
45pub const fn num_words(len: usize) -> usize {
46 len.div_ceil(32)
47}
48
49impl Eq for GasParams {}
50#[cfg(feature = "serde")]
51mod serde {
52 use super::{Arc, GasParams};
53 use std::vec::Vec;
54
55 #[derive(serde::Serialize, serde::Deserialize)]
56 struct GasParamsSerde {
57 table: Vec<u64>,
58 }
59
60 #[cfg(feature = "serde")]
61 impl serde::Serialize for GasParams {
62 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63 where
64 S: serde::Serializer,
65 {
66 GasParamsSerde {
67 table: self.table.to_vec(),
68 }
69 .serialize(serializer)
70 }
71 }
72
73 impl<'de> serde::Deserialize<'de> for GasParams {
74 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75 where
76 D: serde::Deserializer<'de>,
77 {
78 let table = GasParamsSerde::deserialize(deserializer)?;
79 if table.table.len() != 256 {
80 return Err(serde::de::Error::custom("Invalid gas params length"));
81 }
82 Ok(Self::new(Arc::new(table.table.try_into().unwrap())))
83 }
84 }
85}
86
87impl Default for GasParams {
88 #[inline]
89 fn default() -> Self {
90 Self::new_spec(SpecId::default())
91 }
92}
93
94impl GasParams {
95 #[inline]
97 pub const fn new(table: Arc<[u64; 256]>) -> Self {
98 Self { table }
99 }
100
101 pub fn override_gas(&mut self, values: impl IntoIterator<Item = (GasId, u64)>) {
117 let mut table = *self.table.clone();
118 for (id, value) in values.into_iter() {
119 table[id.as_usize()] = value;
120 }
121 *self = Self::new(Arc::new(table));
122 }
123
124 #[inline]
126 pub fn table(&self) -> &[u64; 256] {
127 &self.table
128 }
129
130 #[inline(never)]
132 pub fn new_spec(spec: SpecId) -> Self {
133 use SpecId::*;
134 let gas_params = match spec {
135 FRONTIER => {
136 static TABLE: OnceLock<GasParams> = OnceLock::new();
137 TABLE.get_or_init(|| Self::new_spec_inner(spec))
138 }
139 HOMESTEAD => {
141 static TABLE: OnceLock<GasParams> = OnceLock::new();
142 TABLE.get_or_init(|| Self::new_spec_inner(spec))
143 }
144 TANGERINE => {
146 static TABLE: OnceLock<GasParams> = OnceLock::new();
147 TABLE.get_or_init(|| Self::new_spec_inner(spec))
148 }
149 SPURIOUS_DRAGON | BYZANTIUM | PETERSBURG => {
151 static TABLE: OnceLock<GasParams> = OnceLock::new();
152 TABLE.get_or_init(|| Self::new_spec_inner(spec))
153 }
154 ISTANBUL => {
156 static TABLE: OnceLock<GasParams> = OnceLock::new();
157 TABLE.get_or_init(|| Self::new_spec_inner(spec))
158 }
159 BERLIN => {
161 static TABLE: OnceLock<GasParams> = OnceLock::new();
162 TABLE.get_or_init(|| Self::new_spec_inner(spec))
163 }
164 LONDON | MERGE => {
166 static TABLE: OnceLock<GasParams> = OnceLock::new();
167 TABLE.get_or_init(|| Self::new_spec_inner(spec))
168 }
169 SHANGHAI | CANCUN => {
171 static TABLE: OnceLock<GasParams> = OnceLock::new();
172 TABLE.get_or_init(|| Self::new_spec_inner(spec))
173 }
174 PRAGUE | OSAKA => {
176 static TABLE: OnceLock<GasParams> = OnceLock::new();
177 TABLE.get_or_init(|| Self::new_spec_inner(spec))
178 }
179 SpecId::AMSTERDAM => {
181 static TABLE: OnceLock<GasParams> = OnceLock::new();
182 TABLE.get_or_init(|| Self::new_spec_inner(spec))
183 }
184 };
185 gas_params.clone()
186 }
187
188 #[inline]
190 fn new_spec_inner(spec: SpecId) -> Self {
191 let mut table = [0; 256];
192
193 table[GasId::exp_byte_gas().as_usize()] = 10;
194 table[GasId::logdata().as_usize()] = gas::LOGDATA;
195 table[GasId::logtopic().as_usize()] = gas::LOGTOPIC;
196 table[GasId::copy_per_word().as_usize()] = gas::COPY;
197 table[GasId::extcodecopy_per_word().as_usize()] = gas::COPY;
198 table[GasId::mcopy_per_word().as_usize()] = gas::COPY;
199 table[GasId::keccak256_per_word().as_usize()] = gas::KECCAK256WORD;
200 table[GasId::memory_linear_cost().as_usize()] = gas::MEMORY;
201 table[GasId::memory_quadratic_reduction().as_usize()] = 512;
202 table[GasId::initcode_per_word().as_usize()] = gas::INITCODE_WORD_COST;
203 table[GasId::create().as_usize()] = gas::CREATE;
204 table[GasId::call_stipend_reduction().as_usize()] = 64;
205 table[GasId::max_refund_quotient().as_usize()] = 2;
206 table[GasId::transfer_value_cost().as_usize()] = gas::CALLVALUE;
207 table[GasId::cold_account_additional_cost().as_usize()] = 0;
208 table[GasId::new_account_cost().as_usize()] = gas::NEWACCOUNT;
209 table[GasId::warm_storage_read_cost().as_usize()] = 0;
210 table[GasId::sstore_static().as_usize()] = gas::SSTORE_RESET;
212 table[GasId::sstore_set_without_load_cost().as_usize()] =
214 gas::SSTORE_SET - gas::SSTORE_RESET;
215 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = 0;
217 table[GasId::sstore_set_refund().as_usize()] =
219 table[GasId::sstore_set_without_load_cost().as_usize()];
220 table[GasId::sstore_reset_refund().as_usize()] =
222 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
223 table[GasId::sstore_clearing_slot_refund().as_usize()] = 15000;
225 table[GasId::selfdestruct_refund().as_usize()] = 24000;
226 table[GasId::call_stipend().as_usize()] = gas::CALL_STIPEND;
227 table[GasId::cold_storage_additional_cost().as_usize()] = 0;
228 table[GasId::cold_storage_cost().as_usize()] = 0;
229 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
230 table[GasId::code_deposit_cost().as_usize()] = gas::CODEDEPOSIT;
231 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
232 gas::NON_ZERO_BYTE_MULTIPLIER;
233 table[GasId::tx_token_cost().as_usize()] = gas::STANDARD_TOKEN_COST;
234 table[GasId::tx_base_stipend().as_usize()] = 21000;
235
236 if spec.is_enabled_in(SpecId::HOMESTEAD) {
237 table[GasId::tx_create_cost().as_usize()] = gas::CREATE;
238 }
239
240 if spec.is_enabled_in(SpecId::TANGERINE) {
241 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = gas::NEWACCOUNT;
242 }
243
244 if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
245 table[GasId::exp_byte_gas().as_usize()] = 50;
246 }
247
248 if spec.is_enabled_in(SpecId::ISTANBUL) {
249 table[GasId::sstore_static().as_usize()] = gas::ISTANBUL_SLOAD_GAS;
250 table[GasId::sstore_set_without_load_cost().as_usize()] =
251 gas::SSTORE_SET - gas::ISTANBUL_SLOAD_GAS;
252 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
253 gas::SSTORE_RESET - gas::ISTANBUL_SLOAD_GAS;
254 table[GasId::sstore_set_refund().as_usize()] =
255 table[GasId::sstore_set_without_load_cost().as_usize()];
256 table[GasId::sstore_reset_refund().as_usize()] =
257 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
258 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
259 gas::NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
260 }
261
262 if spec.is_enabled_in(SpecId::BERLIN) {
263 table[GasId::sstore_static().as_usize()] = gas::WARM_STORAGE_READ_COST;
264 table[GasId::cold_account_additional_cost().as_usize()] =
265 gas::COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
266 table[GasId::cold_storage_additional_cost().as_usize()] =
267 gas::COLD_SLOAD_COST - gas::WARM_STORAGE_READ_COST;
268 table[GasId::cold_storage_cost().as_usize()] = gas::COLD_SLOAD_COST;
269 table[GasId::warm_storage_read_cost().as_usize()] = gas::WARM_STORAGE_READ_COST;
270
271 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
272 gas::WARM_SSTORE_RESET - gas::WARM_STORAGE_READ_COST;
273 table[GasId::sstore_set_without_load_cost().as_usize()] =
274 gas::SSTORE_SET - gas::WARM_STORAGE_READ_COST;
275 table[GasId::sstore_set_refund().as_usize()] =
276 table[GasId::sstore_set_without_load_cost().as_usize()];
277 table[GasId::sstore_reset_refund().as_usize()] =
278 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
279
280 table[GasId::tx_access_list_address_cost().as_usize()] = gas::ACCESS_LIST_ADDRESS;
281 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
282 gas::ACCESS_LIST_STORAGE_KEY;
283 }
284
285 if spec.is_enabled_in(SpecId::LONDON) {
286 table[GasId::sstore_clearing_slot_refund().as_usize()] =
291 gas::WARM_SSTORE_RESET + gas::ACCESS_LIST_STORAGE_KEY;
292
293 table[GasId::selfdestruct_refund().as_usize()] = 0;
294 table[GasId::max_refund_quotient().as_usize()] = 5;
295 }
296
297 if spec.is_enabled_in(SpecId::SHANGHAI) {
298 table[GasId::tx_initcode_cost().as_usize()] = gas::INITCODE_WORD_COST;
299 }
300
301 if spec.is_enabled_in(SpecId::PRAGUE) {
302 table[GasId::tx_eip7702_regular_gas().as_usize()] = eip7702::PER_EMPTY_ACCOUNT_COST;
303
304 table[GasId::tx_eip7702_regular_refund().as_usize()] =
306 eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST;
307
308 table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
309 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
310 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] = 1;
313 }
314
315 if spec.is_enabled_in(SpecId::AMSTERDAM) {
319 table[GasId::code_deposit_cost().as_usize()] = 0;
321
322 table[GasId::sstore_set_state_gas().as_usize()] =
324 eip8037::SSTORE_SET_BYTES * eip8037::CPSB_GLAMSTERDAM;
325 table[GasId::new_account_state_gas().as_usize()] =
326 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM;
327 table[GasId::code_deposit_state_gas().as_usize()] =
328 eip8037::CODE_DEPOSIT_PER_BYTE * eip8037::CPSB_GLAMSTERDAM;
329 table[GasId::create_state_gas().as_usize()] =
330 eip8037::NEW_ACCOUNT_BYTES * eip8037::CPSB_GLAMSTERDAM;
331 table[GasId::tx_eip7702_state_gas_bytecode().as_usize()] =
332 eip8037::AUTH_BASE_BYTES * eip8037::CPSB_GLAMSTERDAM;
333
334 table[GasId::tx_floor_cost_base_gas().as_usize()] = eip2780::TX_BASE_COST;
336
337 table[GasId::tx_floor_cost_per_token().as_usize()] = 16;
342 table[GasId::tx_floor_token_zero_byte_multiplier().as_usize()] =
343 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()];
344
345 table[GasId::tx_access_list_floor_byte_multiplier().as_usize()] = 4;
350
351 table[GasId::warm_storage_read_cost().as_usize()] = eip8038::WARM_ACCESS;
365 table[GasId::cold_account_additional_cost().as_usize()] =
366 eip8038::COLD_ACCOUNT_ACCESS_ADDITIONAL;
367 table[GasId::cold_storage_additional_cost().as_usize()] =
368 eip8038::COLD_STORAGE_ACCESS_ADDITIONAL;
369 table[GasId::cold_storage_cost().as_usize()] = eip8038::COLD_STORAGE_ACCESS_ADDITIONAL;
375 table[GasId::transfer_value_cost().as_usize()] = eip8038::CALL_VALUE;
382 table[GasId::new_account_cost().as_usize()] = 0;
383 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = eip8038::ACCOUNT_WRITE;
384
385 table[GasId::sstore_static().as_usize()] = eip8038::WARM_ACCESS;
390 table[GasId::sstore_set_without_load_cost().as_usize()] = eip8038::STORAGE_WRITE;
391 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = eip8038::STORAGE_WRITE;
392 table[GasId::sstore_set_refund().as_usize()] = eip8038::STORAGE_WRITE;
393 table[GasId::sstore_reset_refund().as_usize()] = eip8038::STORAGE_WRITE;
394 table[GasId::sstore_clearing_slot_refund().as_usize()] = eip8038::STORAGE_CLEAR_REFUND;
395
396 table[GasId::create().as_usize()] = eip8038::CREATE_ACCESS;
400 table[GasId::tx_create_cost().as_usize()] = eip8038::CREATE_ACCESS;
401
402 table[GasId::tx_access_list_address_cost().as_usize()] =
405 eip8038::ACCESS_LIST_ADDRESS_COST + 20 * 64;
406 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
407 eip8038::ACCESS_LIST_STORAGE_KEY_COST + 32 * 64;
408
409 table[GasId::tx_eip7702_regular_gas().as_usize()] =
417 eip8038::EIP7702_PER_AUTH_BASE_REGULAR;
418 table[GasId::tx_eip7702_regular_refund().as_usize()] = 0;
419
420 table[GasId::tx_account_write_cost().as_usize()] = eip8038::ACCOUNT_WRITE;
426 table[GasId::tx_create_access_cost().as_usize()] = eip8038::CREATE_ACCESS;
427 }
428
429 Self::new(Arc::new(table))
430 }
431
432 #[inline]
434 pub fn get(&self, id: GasId) -> u64 {
435 self.table[id.as_usize()]
436 }
437
438 #[inline]
440 pub fn exp_cost(&self, power: U256) -> u64 {
441 if power.is_zero() {
442 return 0;
443 }
444 self.get(GasId::exp_byte_gas())
446 .saturating_mul(log2floor(power) / 8 + 1)
447 }
448
449 #[inline]
451 pub fn selfdestruct_refund(&self) -> i64 {
452 self.get(GasId::selfdestruct_refund()) as i64
453 }
454
455 #[inline]
458 pub fn selfdestruct_cold_cost(&self) -> u64 {
459 self.cold_account_additional_cost() + self.warm_storage_read_cost()
460 }
461
462 #[inline]
464 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
465 let mut gas = 0;
466
467 if should_charge_topup {
469 gas += self.new_account_cost_for_selfdestruct();
470 }
471
472 if is_cold {
473 gas += self.selfdestruct_cold_cost();
479 }
480 gas
481 }
482
483 #[inline]
485 pub fn extcodecopy(&self, len: usize) -> u64 {
486 self.get(GasId::extcodecopy_per_word())
487 .saturating_mul(num_words(len) as u64)
488 }
489
490 #[inline]
492 pub fn mcopy_cost(&self, len: usize) -> u64 {
493 self.get(GasId::mcopy_per_word())
494 .saturating_mul(num_words(len) as u64)
495 }
496
497 #[inline]
499 pub fn sstore_static_gas(&self) -> u64 {
500 self.get(GasId::sstore_static())
501 }
502
503 #[inline]
505 pub fn sstore_set_without_load_cost(&self) -> u64 {
506 self.get(GasId::sstore_set_without_load_cost())
507 }
508
509 #[inline]
511 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
512 self.get(GasId::sstore_reset_without_cold_load_cost())
513 }
514
515 #[inline]
517 pub fn sstore_clearing_slot_refund(&self) -> u64 {
518 self.get(GasId::sstore_clearing_slot_refund())
519 }
520
521 #[inline]
523 pub fn sstore_set_refund(&self) -> u64 {
524 self.get(GasId::sstore_set_refund())
525 }
526
527 #[inline]
529 pub fn sstore_reset_refund(&self) -> u64 {
530 self.get(GasId::sstore_reset_refund())
531 }
532
533 #[inline]
537 pub fn max_refund_quotient(&self) -> u64 {
538 self.get(GasId::max_refund_quotient())
539 }
540
541 #[inline]
545 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
546 if !is_istanbul {
549 if vals.is_present_zero() && !vals.is_new_zero() {
550 return self.sstore_set_without_load_cost();
551 } else {
552 return self.sstore_reset_without_cold_load_cost();
553 }
554 }
555
556 let mut gas = 0;
557
558 if is_cold {
560 gas += self.cold_storage_cost();
561 }
562
563 if vals.new_values_changes_present() && vals.is_original_eq_present() {
565 gas += if vals.is_original_zero() {
566 self.sstore_set_without_load_cost()
569 } else {
570 self.sstore_reset_without_cold_load_cost()
572 };
573 }
574 gas
575 }
576
577 #[inline]
579 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
580 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
582
583 if !is_istanbul {
584 if !vals.is_present_zero() && vals.is_new_zero() {
586 return sstore_clearing_slot_refund;
587 }
588 return 0;
589 }
590
591 if vals.is_new_eq_present() {
593 return 0;
594 }
595
596 if vals.is_original_eq_present() && vals.is_new_zero() {
599 return sstore_clearing_slot_refund;
600 }
601
602 let mut refund = 0;
603 if !vals.is_original_zero() {
605 if vals.is_present_zero() {
607 refund -= sstore_clearing_slot_refund;
609 } else if vals.is_new_zero() {
611 refund += sstore_clearing_slot_refund;
613 }
614 }
615
616 if vals.is_original_eq_new() {
618 if vals.is_original_zero() {
620 refund += self.sstore_set_refund() as i64;
622 } else {
624 refund += self.sstore_reset_refund() as i64;
626 }
627 }
628 refund
629 }
630
631 #[inline]
633 pub fn log_cost(&self, n: u8, len: u64) -> u64 {
634 self.get(GasId::logdata())
635 .saturating_mul(len)
636 .saturating_add(self.get(GasId::logtopic()) * n as u64)
637 }
638
639 #[inline]
641 pub fn keccak256_cost(&self, len: usize) -> u64 {
642 self.get(GasId::keccak256_per_word())
643 .saturating_mul(num_words(len) as u64)
644 }
645
646 #[inline]
648 pub fn memory_cost(&self, len: usize) -> u64 {
649 let len = len as u64;
650 self.get(GasId::memory_linear_cost())
651 .saturating_mul(len)
652 .saturating_add(
653 (len.saturating_mul(len))
654 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
655 )
656 }
657
658 #[inline]
660 pub fn initcode_cost(&self, len: usize) -> u64 {
661 self.get(GasId::initcode_per_word())
662 .saturating_mul(num_words(len) as u64)
663 }
664
665 #[inline]
667 pub fn create_cost(&self) -> u64 {
668 self.get(GasId::create())
669 }
670
671 #[inline]
673 pub fn create2_cost(&self, len: usize) -> u64 {
674 self.get(GasId::create()).saturating_add(
675 self.get(GasId::keccak256_per_word())
676 .saturating_mul(num_words(len) as u64),
677 )
678 }
679
680 #[inline]
682 pub fn call_stipend(&self) -> u64 {
683 self.get(GasId::call_stipend())
684 }
685
686 #[inline]
688 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
689 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
690 }
691
692 #[inline]
694 pub fn transfer_value_cost(&self) -> u64 {
695 self.get(GasId::transfer_value_cost())
696 }
697
698 #[inline]
700 pub fn cold_account_additional_cost(&self) -> u64 {
701 self.get(GasId::cold_account_additional_cost())
702 }
703
704 #[inline]
706 pub fn cold_storage_additional_cost(&self) -> u64 {
707 self.get(GasId::cold_storage_additional_cost())
708 }
709
710 #[inline]
712 pub fn cold_storage_cost(&self) -> u64 {
713 self.get(GasId::cold_storage_cost())
714 }
715
716 #[inline]
718 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
719 if !is_spurious_dragon || transfers_value {
723 return self.get(GasId::new_account_cost());
724 }
725 0
726 }
727
728 #[inline]
730 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
731 self.get(GasId::new_account_cost_for_selfdestruct())
732 }
733
734 #[inline]
736 pub fn warm_storage_read_cost(&self) -> u64 {
737 self.get(GasId::warm_storage_read_cost())
738 }
739
740 #[inline]
742 pub fn copy_cost(&self, len: usize) -> u64 {
743 self.copy_per_word_cost(num_words(len))
744 }
745
746 #[inline]
748 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
749 self.get(GasId::copy_per_word())
750 .saturating_mul(word_num as u64)
751 }
752
753 #[inline]
755 pub fn code_deposit_cost(&self, len: usize) -> u64 {
756 self.get(GasId::code_deposit_cost())
757 .saturating_mul(len as u64)
758 }
759
760 #[inline]
762 pub fn sstore_state_gas(&self, vals: &SStoreResult) -> u64 {
763 if vals.new_values_changes_present()
764 && vals.is_original_eq_present()
765 && vals.is_original_zero()
766 {
767 self.get(GasId::sstore_set_state_gas())
768 } else {
769 0
770 }
771 }
772
773 #[inline]
781 pub fn sstore_state_gas_refill(&self, vals: &SStoreResult) -> u64 {
782 if !vals.is_new_eq_present() && vals.is_original_eq_new() && vals.is_original_zero() {
783 self.get(GasId::sstore_set_state_gas())
784 } else {
785 0
786 }
787 }
788
789 #[inline]
791 pub fn new_account_state_gas(&self) -> u64 {
792 self.get(GasId::new_account_state_gas())
793 }
794
795 #[inline]
797 pub fn code_deposit_state_gas(&self, len: usize) -> u64 {
798 self.get(GasId::code_deposit_state_gas())
799 .saturating_mul(len as u64)
800 }
801
802 #[inline]
804 pub fn create_state_gas(&self) -> u64 {
805 self.get(GasId::create_state_gas())
806 }
807
808 #[inline]
816 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
817 self.get(GasId::tx_eip7702_regular_gas())
818 }
819
820 #[inline]
826 pub fn tx_eip7702_auth_refund_regular(&self) -> u64 {
827 self.get(GasId::tx_eip7702_regular_refund())
828 }
829
830 #[inline]
833 pub fn tx_eip7702_state_gas_bytecode(&self) -> u64 {
834 self.get(GasId::tx_eip7702_state_gas_bytecode())
835 }
836
837 #[inline]
839 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
840 self.get(GasId::tx_token_non_zero_byte_multiplier())
841 }
842
843 #[inline]
845 pub fn tx_token_cost(&self) -> u64 {
846 self.get(GasId::tx_token_cost())
847 }
848
849 pub fn tx_floor_cost_per_token(&self) -> u64 {
851 self.get(GasId::tx_floor_cost_per_token())
852 }
853
854 pub fn tx_floor_token_zero_byte_multiplier(&self) -> u64 {
862 self.get(GasId::tx_floor_token_zero_byte_multiplier())
863 }
864
865 #[inline]
876 pub fn tx_floor_cost(&self, input: &[u8]) -> u64 {
877 let zero_multiplier = self.tx_floor_token_zero_byte_multiplier();
878 let non_zero_multiplier = self.tx_token_non_zero_byte_multiplier();
879 let floor_tokens = if zero_multiplier == non_zero_multiplier {
880 input.len() as u64 * non_zero_multiplier
881 } else {
882 get_tokens_in_calldata(input, non_zero_multiplier)
883 };
884 self.tx_floor_cost_with_tokens(floor_tokens)
885 }
886
887 #[inline]
889 pub fn tx_floor_cost_with_tokens(&self, tokens: u64) -> u64 {
890 self.tx_floor_cost_per_token() * tokens + self.tx_floor_cost_base_gas()
891 }
892
893 pub fn tx_floor_cost_base_gas(&self) -> u64 {
895 self.get(GasId::tx_floor_cost_base_gas())
896 }
897
898 pub fn tx_access_list_address_cost(&self) -> u64 {
900 self.get(GasId::tx_access_list_address_cost())
901 }
902
903 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
905 self.get(GasId::tx_access_list_storage_key_cost())
906 }
907
908 #[inline]
926 pub fn tx_access_list_cost(&self, accounts: u64, storages: u64) -> u64 {
927 accounts
928 .saturating_mul(self.tx_access_list_address_cost())
929 .saturating_add(storages.saturating_mul(self.tx_access_list_storage_key_cost()))
930 }
931
932 #[inline]
940 pub fn tx_access_list_floor_byte_multiplier(&self) -> u64 {
941 self.get(GasId::tx_access_list_floor_byte_multiplier())
942 }
943
944 #[inline]
949 pub fn tx_floor_tokens_in_access_list(&self, accounts: u64, storages: u64) -> u64 {
950 let bytes = accounts
951 .saturating_mul(20)
952 .saturating_add(storages.saturating_mul(32));
953 bytes.saturating_mul(self.tx_access_list_floor_byte_multiplier())
954 }
955
956 pub fn tx_base_stipend(&self) -> u64 {
958 self.get(GasId::tx_base_stipend())
959 }
960
961 #[inline]
965 pub fn tx_account_write_cost(&self) -> u64 {
966 self.get(GasId::tx_account_write_cost())
967 }
968
969 #[inline]
973 pub fn tx_create_access_cost(&self) -> u64 {
974 self.get(GasId::tx_create_access_cost())
975 }
976
977 #[inline]
981 pub fn tx_create_cost(&self) -> u64 {
982 self.get(GasId::tx_create_cost())
983 }
984
985 #[inline]
987 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
988 self.get(GasId::tx_initcode_cost())
989 .saturating_mul(num_words(len) as u64)
990 }
991
992 #[allow(clippy::too_many_arguments)]
1013 pub fn initial_tx_gas(
1014 &self,
1015 input: &[u8],
1016 is_create: bool,
1017 access_list_accounts: u64,
1018 access_list_storages: u64,
1019 authorization_list_num: u64,
1020 eip2780: Option<Eip2780TxInfo>,
1021 ) -> InitialAndFloorGas {
1022 let tokens_in_calldata =
1024 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
1025
1026 let auth_regular_cost = authorization_list_num * self.tx_eip7702_per_empty_account_cost();
1030
1031 let base_and_to_and_value_gas = match &eip2780 {
1032 None => {
1033 let mut base = self.tx_base_stipend();
1034 if is_create {
1035 base += self.tx_create_cost();
1037 }
1038 base
1039 }
1040 Some(info) => self.eip2780_base_to_value_gas(is_create, info),
1041 };
1042
1043 let mut initial_regular_gas = tokens_in_calldata * self.tx_token_cost()
1044 + access_list_accounts * self.tx_access_list_address_cost()
1046 + access_list_storages * self.tx_access_list_storage_key_cost()
1048 + base_and_to_and_value_gas
1049 + auth_regular_cost;
1051
1052 if is_create {
1053 initial_regular_gas += self.tx_initcode_cost(input.len());
1055 }
1056
1057 let access_list_floor_tokens =
1066 self.tx_floor_tokens_in_access_list(access_list_accounts, access_list_storages);
1067 let mut floor_gas =
1068 self.tx_floor_cost(input) + access_list_floor_tokens * self.tx_floor_cost_per_token();
1069 if eip2780.is_some() {
1070 floor_gas = floor_gas - self.tx_floor_cost_base_gas() + base_and_to_and_value_gas;
1071 }
1072
1073 InitialAndFloorGas::default()
1077 .with_initial_regular_gas(initial_regular_gas)
1078 .with_floor_gas(floor_gas)
1079 }
1080
1081 fn eip2780_base_to_value_gas(&self, is_create: bool, info: &Eip2780TxInfo) -> u64 {
1090 let mut gas = eip2780::TX_BASE_COST;
1091
1092 if is_create {
1093 gas += self.tx_create_access_cost();
1096 } else if !info.is_self_transfer {
1097 gas += eip8038::COLD_ACCOUNT_ACCESS;
1099 if !info.value.is_zero() {
1100 gas += eip2780::TX_VALUE_COST;
1101 }
1102 }
1103
1104 gas
1105 }
1106
1107 pub fn initial_tx_gas_for_tx(
1112 &self,
1113 tx: impl Transaction,
1114 eip2780: Option<Eip2780TxInfo>,
1115 ) -> InitialAndFloorGas {
1116 let mut accounts = 0;
1117 let mut storages = 0;
1118 if tx.tx_type() != TransactionType::Legacy {
1120 (accounts, storages) = tx
1121 .access_list()
1122 .map(|al| {
1123 al.fold((0, 0), |(num_accounts, num_storage_slots), item| {
1124 (
1125 num_accounts + 1,
1126 num_storage_slots + item.storage_slots().count() as u64,
1127 )
1128 })
1129 })
1130 .unwrap_or_default();
1131 }
1132
1133 self.initial_tx_gas(
1134 tx.input(),
1135 tx.kind().is_create(),
1136 accounts,
1137 storages,
1138 tx.authorization_list_len() as u64,
1139 eip2780,
1140 )
1141 }
1142}
1143
1144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1152pub struct Eip2780TxInfo {
1153 pub value: U256,
1155 pub is_self_transfer: bool,
1157}
1158
1159#[inline]
1160pub(crate) const fn log2floor(value: U256) -> u64 {
1161 255u64.saturating_sub(value.leading_zeros() as u64)
1162}
1163
1164#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1166pub struct GasId(u8);
1167
1168impl GasId {
1169 #[inline]
1171 pub const fn new(id: u8) -> Self {
1172 Self(id)
1173 }
1174
1175 #[inline]
1177 pub const fn as_u8(&self) -> u8 {
1178 self.0
1179 }
1180
1181 #[inline]
1183 pub const fn as_usize(&self) -> usize {
1184 self.0 as usize
1185 }
1186
1187 pub const fn name(&self) -> &'static str {
1199 match self.0 {
1200 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
1201 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
1202 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
1203 x if x == Self::logdata().as_u8() => "logdata",
1204 x if x == Self::logtopic().as_u8() => "logtopic",
1205 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
1206 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
1207 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
1208 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
1209 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
1210 x if x == Self::create().as_u8() => "create",
1211 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
1212 x if x == Self::max_refund_quotient().as_u8() => "max_refund_quotient",
1213 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
1214 x if x == Self::cold_account_additional_cost().as_u8() => {
1215 "cold_account_additional_cost"
1216 }
1217 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
1218 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
1219 x if x == Self::sstore_static().as_u8() => "sstore_static",
1220 x if x == Self::sstore_set_without_load_cost().as_u8() => {
1221 "sstore_set_without_load_cost"
1222 }
1223 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
1224 "sstore_reset_without_cold_load_cost"
1225 }
1226 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
1227 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
1228 x if x == Self::call_stipend().as_u8() => "call_stipend",
1229 x if x == Self::cold_storage_additional_cost().as_u8() => {
1230 "cold_storage_additional_cost"
1231 }
1232 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
1233 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
1234 "new_account_cost_for_selfdestruct"
1235 }
1236 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
1237 x if x == Self::tx_eip7702_regular_gas().as_u8() => "tx_eip7702_regular_gas",
1238 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
1239 "tx_token_non_zero_byte_multiplier"
1240 }
1241 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
1242 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
1243 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
1244 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
1245 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
1246 "tx_access_list_storage_key_cost"
1247 }
1248 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
1249 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
1250 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
1251 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
1252 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
1253 x if x == Self::tx_eip7702_regular_refund().as_u8() => "tx_eip7702_regular_refund",
1254 x if x == Self::sstore_set_state_gas().as_u8() => "sstore_set_state_gas",
1255 x if x == Self::new_account_state_gas().as_u8() => "new_account_state_gas",
1256 x if x == Self::code_deposit_state_gas().as_u8() => "code_deposit_state_gas",
1257 x if x == Self::create_state_gas().as_u8() => "create_state_gas",
1258 x if x == Self::tx_eip7702_state_gas_bytecode().as_u8() => {
1259 "tx_eip7702_state_gas_bytecode"
1260 }
1261 x if x == Self::tx_floor_token_zero_byte_multiplier().as_u8() => {
1262 "tx_floor_token_zero_byte_multiplier"
1263 }
1264 x if x == Self::tx_access_list_floor_byte_multiplier().as_u8() => {
1265 "tx_access_list_floor_byte_multiplier"
1266 }
1267 x if x == Self::tx_account_write_cost().as_u8() => "tx_account_write_cost",
1268 x if x == Self::tx_create_access_cost().as_u8() => "tx_create_access_cost",
1269 _ => "unknown",
1270 }
1271 }
1272
1273 pub fn from_name(s: &str) -> Option<GasId> {
1287 match s {
1288 "exp_byte_gas" => Some(Self::exp_byte_gas()),
1289 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
1290 "copy_per_word" => Some(Self::copy_per_word()),
1291 "logdata" => Some(Self::logdata()),
1292 "logtopic" => Some(Self::logtopic()),
1293 "mcopy_per_word" => Some(Self::mcopy_per_word()),
1294 "keccak256_per_word" => Some(Self::keccak256_per_word()),
1295 "memory_linear_cost" => Some(Self::memory_linear_cost()),
1296 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
1297 "initcode_per_word" => Some(Self::initcode_per_word()),
1298 "create" => Some(Self::create()),
1299 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
1300 "max_refund_quotient" => Some(Self::max_refund_quotient()),
1301 "transfer_value_cost" => Some(Self::transfer_value_cost()),
1302 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
1303 "new_account_cost" => Some(Self::new_account_cost()),
1304 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
1305 "sstore_static" => Some(Self::sstore_static()),
1306 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
1307 "sstore_reset_without_cold_load_cost" => {
1308 Some(Self::sstore_reset_without_cold_load_cost())
1309 }
1310 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
1311 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
1312 "call_stipend" => Some(Self::call_stipend()),
1313 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
1314 "cold_storage_cost" => Some(Self::cold_storage_cost()),
1315 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
1316 "code_deposit_cost" => Some(Self::code_deposit_cost()),
1317 "tx_eip7702_regular_gas" => Some(Self::tx_eip7702_regular_gas()),
1318 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
1319 "tx_token_cost" => Some(Self::tx_token_cost()),
1320 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
1321 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
1322 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
1323 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
1324 "tx_base_stipend" => Some(Self::tx_base_stipend()),
1325 "tx_create_cost" => Some(Self::tx_create_cost()),
1326 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
1327 "sstore_set_refund" => Some(Self::sstore_set_refund()),
1328 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
1329 "tx_eip7702_regular_refund" => Some(Self::tx_eip7702_regular_refund()),
1330 "sstore_set_state_gas" => Some(Self::sstore_set_state_gas()),
1331 "new_account_state_gas" => Some(Self::new_account_state_gas()),
1332 "code_deposit_state_gas" => Some(Self::code_deposit_state_gas()),
1333 "create_state_gas" => Some(Self::create_state_gas()),
1334 "tx_eip7702_state_gas_bytecode" => Some(Self::tx_eip7702_state_gas_bytecode()),
1335 "tx_floor_token_zero_byte_multiplier" => {
1336 Some(Self::tx_floor_token_zero_byte_multiplier())
1337 }
1338 "tx_access_list_floor_byte_multiplier" => {
1339 Some(Self::tx_access_list_floor_byte_multiplier())
1340 }
1341 "tx_account_write_cost" => Some(Self::tx_account_write_cost()),
1342 "tx_create_access_cost" => Some(Self::tx_create_access_cost()),
1343 _ => None,
1344 }
1345 }
1346
1347 pub const fn exp_byte_gas() -> GasId {
1349 Self::new(1)
1350 }
1351
1352 pub const fn extcodecopy_per_word() -> GasId {
1354 Self::new(2)
1355 }
1356
1357 pub const fn copy_per_word() -> GasId {
1359 Self::new(3)
1360 }
1361
1362 pub const fn logdata() -> GasId {
1364 Self::new(4)
1365 }
1366
1367 pub const fn logtopic() -> GasId {
1369 Self::new(5)
1370 }
1371
1372 pub const fn mcopy_per_word() -> GasId {
1374 Self::new(6)
1375 }
1376
1377 pub const fn keccak256_per_word() -> GasId {
1379 Self::new(7)
1380 }
1381
1382 pub const fn memory_linear_cost() -> GasId {
1384 Self::new(8)
1385 }
1386
1387 pub const fn memory_quadratic_reduction() -> GasId {
1389 Self::new(9)
1390 }
1391
1392 pub const fn initcode_per_word() -> GasId {
1394 Self::new(10)
1395 }
1396
1397 pub const fn create() -> GasId {
1399 Self::new(11)
1400 }
1401
1402 pub const fn call_stipend_reduction() -> GasId {
1404 Self::new(12)
1405 }
1406
1407 pub const fn max_refund_quotient() -> GasId {
1409 Self::new(47)
1410 }
1411
1412 pub const fn transfer_value_cost() -> GasId {
1414 Self::new(13)
1415 }
1416
1417 pub const fn cold_account_additional_cost() -> GasId {
1419 Self::new(14)
1420 }
1421
1422 pub const fn new_account_cost() -> GasId {
1424 Self::new(15)
1425 }
1426
1427 pub const fn warm_storage_read_cost() -> GasId {
1431 Self::new(16)
1432 }
1433
1434 pub const fn sstore_static() -> GasId {
1437 Self::new(17)
1438 }
1439
1440 pub const fn sstore_set_without_load_cost() -> GasId {
1442 Self::new(18)
1443 }
1444
1445 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1447 Self::new(19)
1448 }
1449
1450 pub const fn sstore_clearing_slot_refund() -> GasId {
1452 Self::new(20)
1453 }
1454
1455 pub const fn selfdestruct_refund() -> GasId {
1457 Self::new(21)
1458 }
1459
1460 pub const fn call_stipend() -> GasId {
1462 Self::new(22)
1463 }
1464
1465 pub const fn cold_storage_additional_cost() -> GasId {
1467 Self::new(23)
1468 }
1469
1470 pub const fn cold_storage_cost() -> GasId {
1472 Self::new(24)
1473 }
1474
1475 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1477 Self::new(25)
1478 }
1479
1480 pub const fn code_deposit_cost() -> GasId {
1482 Self::new(26)
1483 }
1484
1485 pub const fn tx_eip7702_regular_gas() -> GasId {
1492 Self::new(27)
1493 }
1494
1495 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1497 Self::new(28)
1498 }
1499
1500 pub const fn tx_token_cost() -> GasId {
1502 Self::new(29)
1503 }
1504
1505 pub const fn tx_floor_cost_per_token() -> GasId {
1507 Self::new(30)
1508 }
1509
1510 pub const fn tx_floor_cost_base_gas() -> GasId {
1512 Self::new(31)
1513 }
1514
1515 pub const fn tx_access_list_address_cost() -> GasId {
1517 Self::new(32)
1518 }
1519
1520 pub const fn tx_access_list_storage_key_cost() -> GasId {
1522 Self::new(33)
1523 }
1524
1525 pub const fn tx_base_stipend() -> GasId {
1527 Self::new(34)
1528 }
1529
1530 pub const fn tx_create_cost() -> GasId {
1532 Self::new(35)
1533 }
1534
1535 pub const fn tx_initcode_cost() -> GasId {
1537 Self::new(36)
1538 }
1539
1540 pub const fn sstore_set_refund() -> GasId {
1542 Self::new(37)
1543 }
1544
1545 pub const fn sstore_reset_refund() -> GasId {
1547 Self::new(38)
1548 }
1549
1550 pub const fn tx_eip7702_regular_refund() -> GasId {
1558 Self::new(39)
1559 }
1560
1561 pub const fn sstore_set_state_gas() -> GasId {
1563 Self::new(40)
1564 }
1565
1566 pub const fn new_account_state_gas() -> GasId {
1568 Self::new(41)
1569 }
1570
1571 pub const fn code_deposit_state_gas() -> GasId {
1573 Self::new(42)
1574 }
1575
1576 pub const fn create_state_gas() -> GasId {
1578 Self::new(43)
1579 }
1580
1581 pub const fn tx_eip7702_state_gas_bytecode() -> GasId {
1585 Self::new(44)
1586 }
1587
1588 pub const fn tx_floor_token_zero_byte_multiplier() -> GasId {
1595 Self::new(45)
1596 }
1597
1598 pub const fn tx_access_list_floor_byte_multiplier() -> GasId {
1604 Self::new(46)
1605 }
1606
1607 pub const fn tx_account_write_cost() -> GasId {
1611 Self::new(48)
1612 }
1613
1614 pub const fn tx_create_access_cost() -> GasId {
1618 Self::new(49)
1619 }
1620}
1621
1622#[cfg(test)]
1623mod tests {
1624 use super::*;
1625 use std::collections::HashSet;
1626
1627 #[cfg(test)]
1628 mod log2floor_tests {
1629 use super::*;
1630
1631 #[test]
1632 fn test_log2floor_edge_cases() {
1633 assert_eq!(log2floor(U256::ZERO), 0);
1635
1636 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);
1650 assert_eq!(log2floor(U256::from(u64::MAX) + U256::from(1u64)), 64);
1651 assert_eq!(log2floor(U256::MAX), 255);
1652 }
1653 }
1654
1655 #[test]
1656 fn test_gas_id_name_and_from_str_coverage() {
1657 let mut unique_names = HashSet::new();
1658 let mut known_gas_ids = 0;
1659
1660 for i in 0..=255 {
1662 let gas_id = GasId::new(i);
1663 let name = gas_id.name();
1664
1665 if name != "unknown" {
1667 unique_names.insert(name);
1668 }
1669 }
1670
1671 for name in &unique_names {
1673 if let Some(gas_id) = GasId::from_name(name) {
1674 known_gas_ids += 1;
1675 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1677 }
1678 }
1679
1680 println!("Total unique named GasIds: {}", unique_names.len());
1681 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1682
1683 assert_eq!(
1685 unique_names.len(),
1686 known_gas_ids,
1687 "Not all unique names are resolvable via from_str"
1688 );
1689
1690 assert_eq!(
1692 unique_names.len(),
1693 49,
1694 "Expected 49 unique GasIds, found {}",
1695 unique_names.len()
1696 );
1697 }
1698
1699 #[test]
1700 fn test_max_refund_quotient_defaults_and_override() {
1701 let frontier = GasParams::new_spec(SpecId::FRONTIER);
1702 assert_eq!(frontier.max_refund_quotient(), 2);
1703 assert_eq!(frontier.get(GasId::max_refund_quotient()), 2);
1704
1705 let london = GasParams::new_spec(SpecId::LONDON);
1706 assert_eq!(london.max_refund_quotient(), 5);
1707 assert_eq!(
1708 GasId::from_name("max_refund_quotient"),
1709 Some(GasId::max_refund_quotient())
1710 );
1711 assert_eq!(GasId::max_refund_quotient().name(), "max_refund_quotient");
1712
1713 let mut custom = london;
1714 custom.override_gas([(GasId::max_refund_quotient(), 10)]);
1715 assert_eq!(custom.max_refund_quotient(), 10);
1716 }
1717
1718 #[test]
1719 fn test_tx_access_list_cost() {
1720 use crate::cfg::gas;
1721
1722 let gas_params = GasParams::new_spec(SpecId::BERLIN);
1724
1725 assert_eq!(gas_params.tx_access_list_cost(0, 0), 0);
1727
1728 assert_eq!(
1730 gas_params.tx_access_list_cost(1, 0),
1731 gas::ACCESS_LIST_ADDRESS
1732 );
1733
1734 assert_eq!(
1736 gas_params.tx_access_list_cost(0, 1),
1737 gas::ACCESS_LIST_STORAGE_KEY
1738 );
1739
1740 assert_eq!(
1742 gas_params.tx_access_list_cost(2, 5),
1743 2 * gas::ACCESS_LIST_ADDRESS + 5 * gas::ACCESS_LIST_STORAGE_KEY
1744 );
1745
1746 assert_eq!(
1748 gas_params.tx_access_list_cost(100, 200),
1749 100 * gas::ACCESS_LIST_ADDRESS + 200 * gas::ACCESS_LIST_STORAGE_KEY
1750 );
1751
1752 let gas_params_pre_berlin = GasParams::new_spec(SpecId::ISTANBUL);
1754 assert_eq!(gas_params_pre_berlin.tx_access_list_cost(10, 20), 0);
1755 }
1756
1757 #[test]
1758 fn test_initial_state_gas_for_create() {
1759 let gas_params = GasParams::new_spec(SpecId::AMSTERDAM);
1763 let create_gas = gas_params.initial_tx_gas(b"", true, 0, 0, 0, None);
1765 assert_eq!(create_gas.initial_state_gas_final(), 0);
1766
1767 let create_cost = gas_params.tx_create_cost();
1768 let initcode_cost = gas_params.tx_initcode_cost(0);
1769 assert_eq!(
1770 create_gas.initial_total_gas(),
1771 gas_params.tx_base_stipend() + create_cost + initcode_cost
1772 );
1773
1774 let call_gas = gas_params.initial_tx_gas(b"", false, 0, 0, 0, None);
1776 assert_eq!(call_gas.initial_state_gas_final(), 0);
1777 assert_eq!(call_gas.initial_total_gas(), gas_params.tx_base_stipend());
1779 }
1780
1781 #[test]
1782 fn test_initial_tx_gas_eip2780_runtime_split() {
1783 let gas_params = GasParams::new_spec(SpecId::AMSTERDAM);
1784 let info = || Eip2780TxInfo {
1785 value: U256::ZERO,
1786 is_self_transfer: false,
1787 };
1788
1789 let create_gas = gas_params.initial_tx_gas(b"", true, 0, 0, 0, Some(info()));
1793 assert_eq!(create_gas.initial_state_gas, 0);
1794 assert_eq!(
1795 create_gas.initial_regular_gas,
1796 eip2780::TX_BASE_COST + eip8038::CREATE_ACCESS
1797 );
1798
1799 assert_eq!(
1803 gas_params.tx_eip7702_per_empty_account_cost(),
1804 eip8038::EIP7702_PER_AUTH_BASE_REGULAR
1805 );
1806 let auth_gas = gas_params.initial_tx_gas(b"", false, 0, 0, 2, Some(info()));
1807 assert_eq!(auth_gas.initial_state_gas, 0);
1808 assert_eq!(
1809 auth_gas.initial_regular_gas,
1810 eip2780::TX_BASE_COST
1811 + eip8038::COLD_ACCOUNT_ACCESS
1812 + 2 * eip8038::EIP7702_PER_AUTH_BASE_REGULAR
1813 );
1814
1815 let legacy_params = GasParams::new_spec(SpecId::PRAGUE);
1818 assert_eq!(
1819 legacy_params.tx_eip7702_per_empty_account_cost(),
1820 eip7702::PER_EMPTY_ACCOUNT_COST
1821 );
1822 let legacy_auth_gas = legacy_params.initial_tx_gas(b"", false, 0, 0, 1, None);
1823 assert_eq!(legacy_auth_gas.initial_state_gas, 0);
1824 assert_eq!(
1825 legacy_auth_gas.initial_regular_gas,
1826 legacy_params.tx_base_stipend() + eip7702::PER_EMPTY_ACCOUNT_COST
1827 );
1828 let legacy_create_gas = legacy_params.initial_tx_gas(b"", true, 0, 0, 0, None);
1829 assert_eq!(legacy_create_gas.initial_state_gas, 0);
1830 }
1831
1832 #[test]
1833 fn test_eip7981_access_list_cost_amsterdam() {
1834 let params = GasParams::new_spec(SpecId::AMSTERDAM);
1840
1841 assert_eq!(params.tx_access_list_address_cost(), 2900 + 20 * 64);
1843 assert_eq!(params.tx_access_list_storage_key_cost(), 2000 + 32 * 64);
1844 assert_eq!(params.tx_access_list_cost(1, 0), 2900 + 20 * 64);
1845 assert_eq!(params.tx_access_list_cost(0, 1), 2000 + 32 * 64);
1846
1847 assert_eq!(params.tx_access_list_floor_byte_multiplier(), 4);
1849 assert_eq!(params.tx_floor_tokens_in_access_list(2, 3), (40 + 96) * 4);
1851
1852 let gas = params.initial_tx_gas(b"", false, 2, 3, 0, None);
1854 let expected_al_floor = (40 + 96) * 4 * params.tx_floor_cost_per_token();
1855 assert_eq!(
1856 gas.floor_gas(),
1857 params.tx_floor_cost_base_gas() + expected_al_floor,
1858 );
1859
1860 let prague = GasParams::new_spec(SpecId::PRAGUE);
1862 assert_eq!(prague.tx_access_list_floor_byte_multiplier(), 0);
1863 assert_eq!(prague.tx_floor_tokens_in_access_list(2, 3), 0);
1864 let prague_gas = prague.initial_tx_gas(b"", false, 2, 3, 0, None);
1865 assert_eq!(prague_gas.floor_gas(), prague.tx_floor_cost_base_gas());
1866 }
1867}