1use crate::{
4 cfg::gas::{self, get_tokens_in_calldata, InitialAndFloorGas},
5 context::SStoreResult,
6};
7use core::hash::{Hash, Hasher};
8use primitives::{
9 eip7702,
10 hardfork::SpecId::{self},
11 OnceLock, U256,
12};
13use std::sync::Arc;
14
15#[derive(Clone)]
17pub struct GasParams {
18 table: Arc<[u64; 256]>,
20 ptr: *const u64,
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
36unsafe impl Send for GasParams {}
38unsafe impl Sync for GasParams {}
40
41impl core::fmt::Debug for GasParams {
42 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43 write!(f, "GasParams {{ table: {:?} }}", self.table)
44 }
45}
46
47#[inline]
50pub const fn num_words(len: usize) -> usize {
51 len.div_ceil(32)
52}
53
54impl Eq for GasParams {}
55#[cfg(feature = "serde")]
56mod serde {
57 use super::{Arc, GasParams};
58 use std::vec::Vec;
59
60 #[derive(serde::Serialize, serde::Deserialize)]
61 struct GasParamsSerde {
62 table: Vec<u64>,
63 }
64
65 #[cfg(feature = "serde")]
66 impl serde::Serialize for GasParams {
67 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
68 where
69 S: serde::Serializer,
70 {
71 GasParamsSerde {
72 table: self.table.to_vec(),
73 }
74 .serialize(serializer)
75 }
76 }
77
78 impl<'de> serde::Deserialize<'de> for GasParams {
79 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80 where
81 D: serde::Deserializer<'de>,
82 {
83 let table = GasParamsSerde::deserialize(deserializer)?;
84 if table.table.len() != 256 {
85 return Err(serde::de::Error::custom("Invalid gas params length"));
86 }
87 Ok(Self::new(Arc::new(table.table.try_into().unwrap())))
88 }
89 }
90}
91
92impl Default for GasParams {
93 fn default() -> Self {
94 Self::new_spec(SpecId::default())
95 }
96}
97
98impl GasParams {
99 #[inline]
101 pub fn new(table: Arc<[u64; 256]>) -> Self {
102 Self {
103 ptr: table.as_ptr(),
104 table,
105 }
106 }
107
108 pub fn override_gas(&mut self, values: impl IntoIterator<Item = (GasId, u64)>) {
124 let mut table = *self.table.clone();
125 for (id, value) in values.into_iter() {
126 table[id.as_usize()] = value;
127 }
128 *self = Self::new(Arc::new(table));
129 }
130
131 #[inline]
133 pub fn table(&self) -> &[u64; 256] {
134 &self.table
135 }
136
137 #[inline(never)]
139 pub fn new_spec(spec: SpecId) -> Self {
140 use SpecId::*;
141 let gas_params = match spec {
142 FRONTIER | FRONTIER_THAWING => {
143 static TABLE: OnceLock<GasParams> = OnceLock::new();
144 TABLE.get_or_init(|| Self::new_spec_inner(spec))
145 }
146 HOMESTEAD | DAO_FORK => {
148 static TABLE: OnceLock<GasParams> = OnceLock::new();
149 TABLE.get_or_init(|| Self::new_spec_inner(spec))
150 }
151 TANGERINE => {
153 static TABLE: OnceLock<GasParams> = OnceLock::new();
154 TABLE.get_or_init(|| Self::new_spec_inner(spec))
155 }
156 SPURIOUS_DRAGON | BYZANTIUM | CONSTANTINOPLE | PETERSBURG => {
158 static TABLE: OnceLock<GasParams> = OnceLock::new();
159 TABLE.get_or_init(|| Self::new_spec_inner(spec))
160 }
161 ISTANBUL | MUIR_GLACIER => {
163 static TABLE: OnceLock<GasParams> = OnceLock::new();
164 TABLE.get_or_init(|| Self::new_spec_inner(spec))
165 }
166 BERLIN => {
168 static TABLE: OnceLock<GasParams> = OnceLock::new();
169 TABLE.get_or_init(|| Self::new_spec_inner(spec))
170 }
171 LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE => {
173 static TABLE: OnceLock<GasParams> = OnceLock::new();
174 TABLE.get_or_init(|| Self::new_spec_inner(spec))
175 }
176 SHANGHAI | CANCUN => {
178 static TABLE: OnceLock<GasParams> = OnceLock::new();
179 TABLE.get_or_init(|| Self::new_spec_inner(spec))
180 }
181 PRAGUE | OSAKA => {
183 static TABLE: OnceLock<GasParams> = OnceLock::new();
184 TABLE.get_or_init(|| Self::new_spec_inner(spec))
185 }
186 SpecId::AMSTERDAM => {
188 static TABLE: OnceLock<GasParams> = OnceLock::new();
189 TABLE.get_or_init(|| Self::new_spec_inner(spec))
190 }
191 };
192 gas_params.clone()
193 }
194
195 #[inline]
197 fn new_spec_inner(spec: SpecId) -> Self {
198 let mut table = [0; 256];
199
200 table[GasId::exp_byte_gas().as_usize()] = 10;
201 table[GasId::logdata().as_usize()] = gas::LOGDATA;
202 table[GasId::logtopic().as_usize()] = gas::LOGTOPIC;
203 table[GasId::copy_per_word().as_usize()] = gas::COPY;
204 table[GasId::extcodecopy_per_word().as_usize()] = gas::COPY;
205 table[GasId::mcopy_per_word().as_usize()] = gas::COPY;
206 table[GasId::keccak256_per_word().as_usize()] = gas::KECCAK256WORD;
207 table[GasId::memory_linear_cost().as_usize()] = gas::MEMORY;
208 table[GasId::memory_quadratic_reduction().as_usize()] = 512;
209 table[GasId::initcode_per_word().as_usize()] = gas::INITCODE_WORD_COST;
210 table[GasId::create().as_usize()] = gas::CREATE;
211 table[GasId::call_stipend_reduction().as_usize()] = 64;
212 table[GasId::transfer_value_cost().as_usize()] = gas::CALLVALUE;
213 table[GasId::cold_account_additional_cost().as_usize()] = 0;
214 table[GasId::new_account_cost().as_usize()] = gas::NEWACCOUNT;
215 table[GasId::warm_storage_read_cost().as_usize()] = 0;
216 table[GasId::sstore_static().as_usize()] = gas::SSTORE_RESET;
218 table[GasId::sstore_set_without_load_cost().as_usize()] =
220 gas::SSTORE_SET - gas::SSTORE_RESET;
221 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = 0;
223 table[GasId::sstore_set_refund().as_usize()] =
225 table[GasId::sstore_set_without_load_cost().as_usize()];
226 table[GasId::sstore_reset_refund().as_usize()] =
228 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
229 table[GasId::sstore_clearing_slot_refund().as_usize()] = 15000;
231 table[GasId::selfdestruct_refund().as_usize()] = 24000;
232 table[GasId::call_stipend().as_usize()] = gas::CALL_STIPEND;
233 table[GasId::cold_storage_additional_cost().as_usize()] = 0;
234 table[GasId::cold_storage_cost().as_usize()] = 0;
235 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
236 table[GasId::code_deposit_cost().as_usize()] = gas::CODEDEPOSIT;
237 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
238 gas::NON_ZERO_BYTE_MULTIPLIER;
239 table[GasId::tx_token_cost().as_usize()] = gas::STANDARD_TOKEN_COST;
240 table[GasId::tx_base_stipend().as_usize()] = 21000;
241
242 if spec.is_enabled_in(SpecId::HOMESTEAD) {
243 table[GasId::tx_create_cost().as_usize()] = gas::CREATE;
244 }
245
246 if spec.is_enabled_in(SpecId::TANGERINE) {
247 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = gas::NEWACCOUNT;
248 }
249
250 if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
251 table[GasId::exp_byte_gas().as_usize()] = 50;
252 }
253
254 if spec.is_enabled_in(SpecId::ISTANBUL) {
255 table[GasId::sstore_static().as_usize()] = gas::ISTANBUL_SLOAD_GAS;
256 table[GasId::sstore_set_without_load_cost().as_usize()] =
257 gas::SSTORE_SET - gas::ISTANBUL_SLOAD_GAS;
258 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
259 gas::SSTORE_RESET - gas::ISTANBUL_SLOAD_GAS;
260 table[GasId::sstore_set_refund().as_usize()] =
261 table[GasId::sstore_set_without_load_cost().as_usize()];
262 table[GasId::sstore_reset_refund().as_usize()] =
263 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
264 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
265 gas::NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
266 }
267
268 if spec.is_enabled_in(SpecId::BERLIN) {
269 table[GasId::sstore_static().as_usize()] = gas::WARM_STORAGE_READ_COST;
270 table[GasId::cold_account_additional_cost().as_usize()] =
271 gas::COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
272 table[GasId::cold_storage_additional_cost().as_usize()] =
273 gas::COLD_SLOAD_COST - gas::WARM_STORAGE_READ_COST;
274 table[GasId::cold_storage_cost().as_usize()] = gas::COLD_SLOAD_COST;
275 table[GasId::warm_storage_read_cost().as_usize()] = gas::WARM_STORAGE_READ_COST;
276
277 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
278 gas::WARM_SSTORE_RESET - gas::WARM_STORAGE_READ_COST;
279 table[GasId::sstore_set_without_load_cost().as_usize()] =
280 gas::SSTORE_SET - gas::WARM_STORAGE_READ_COST;
281 table[GasId::sstore_set_refund().as_usize()] =
282 table[GasId::sstore_set_without_load_cost().as_usize()];
283 table[GasId::sstore_reset_refund().as_usize()] =
284 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
285
286 table[GasId::tx_access_list_address_cost().as_usize()] = gas::ACCESS_LIST_ADDRESS;
287 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
288 gas::ACCESS_LIST_STORAGE_KEY;
289 }
290
291 if spec.is_enabled_in(SpecId::LONDON) {
292 table[GasId::sstore_clearing_slot_refund().as_usize()] =
297 gas::WARM_SSTORE_RESET + gas::ACCESS_LIST_STORAGE_KEY;
298
299 table[GasId::selfdestruct_refund().as_usize()] = 0;
300 }
301
302 if spec.is_enabled_in(SpecId::SHANGHAI) {
303 table[GasId::tx_initcode_cost().as_usize()] = gas::INITCODE_WORD_COST;
304 }
305
306 if spec.is_enabled_in(SpecId::PRAGUE) {
307 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] =
308 eip7702::PER_EMPTY_ACCOUNT_COST;
309
310 table[GasId::tx_eip7702_auth_refund().as_usize()] =
312 eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST;
313
314 table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
315 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
316 }
317
318 Self::new(Arc::new(table))
319 }
320
321 #[inline]
323 pub const fn get(&self, id: GasId) -> u64 {
324 unsafe { *self.ptr.add(id.as_usize()) }
325 }
326
327 #[inline]
329 pub fn exp_cost(&self, power: U256) -> u64 {
330 if power.is_zero() {
331 return 0;
332 }
333 self.get(GasId::exp_byte_gas())
335 .saturating_mul(log2floor(power) / 8 + 1)
336 }
337
338 #[inline]
340 pub fn selfdestruct_refund(&self) -> i64 {
341 self.get(GasId::selfdestruct_refund()) as i64
342 }
343
344 #[inline]
347 pub fn selfdestruct_cold_cost(&self) -> u64 {
348 self.cold_account_additional_cost() + self.warm_storage_read_cost()
349 }
350
351 #[inline]
353 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
354 let mut gas = 0;
355
356 if should_charge_topup {
358 gas += self.new_account_cost_for_selfdestruct();
359 }
360
361 if is_cold {
362 gas += self.selfdestruct_cold_cost();
368 }
369 gas
370 }
371
372 #[inline]
374 pub fn extcodecopy(&self, len: usize) -> u64 {
375 self.get(GasId::extcodecopy_per_word())
376 .saturating_mul(num_words(len) as u64)
377 }
378
379 #[inline]
381 pub fn mcopy_cost(&self, len: usize) -> u64 {
382 self.get(GasId::mcopy_per_word())
383 .saturating_mul(num_words(len) as u64)
384 }
385
386 #[inline]
388 pub fn sstore_static_gas(&self) -> u64 {
389 self.get(GasId::sstore_static())
390 }
391
392 #[inline]
394 pub fn sstore_set_without_load_cost(&self) -> u64 {
395 self.get(GasId::sstore_set_without_load_cost())
396 }
397
398 #[inline]
400 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
401 self.get(GasId::sstore_reset_without_cold_load_cost())
402 }
403
404 #[inline]
406 pub fn sstore_clearing_slot_refund(&self) -> u64 {
407 self.get(GasId::sstore_clearing_slot_refund())
408 }
409
410 #[inline]
412 pub fn sstore_set_refund(&self) -> u64 {
413 self.get(GasId::sstore_set_refund())
414 }
415
416 #[inline]
418 pub fn sstore_reset_refund(&self) -> u64 {
419 self.get(GasId::sstore_reset_refund())
420 }
421
422 #[inline]
426 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
427 if !is_istanbul {
430 if vals.is_present_zero() && !vals.is_new_zero() {
431 return self.sstore_set_without_load_cost();
432 } else {
433 return self.sstore_reset_without_cold_load_cost();
434 }
435 }
436
437 let mut gas = 0;
438
439 if is_cold {
441 gas += self.cold_storage_cost();
442 }
443
444 if vals.new_values_changes_present() && vals.is_original_eq_present() {
446 gas += if vals.is_original_zero() {
447 self.sstore_set_without_load_cost()
450 } else {
451 self.sstore_reset_without_cold_load_cost()
453 };
454 }
455 gas
456 }
457
458 #[inline]
460 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
461 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
463
464 if !is_istanbul {
465 if !vals.is_present_zero() && vals.is_new_zero() {
467 return sstore_clearing_slot_refund;
468 }
469 return 0;
470 }
471
472 if vals.is_new_eq_present() {
474 return 0;
475 }
476
477 if vals.is_original_eq_present() && vals.is_new_zero() {
480 return sstore_clearing_slot_refund;
481 }
482
483 let mut refund = 0;
484 if !vals.is_original_zero() {
486 if vals.is_present_zero() {
488 refund -= sstore_clearing_slot_refund;
490 } else if vals.is_new_zero() {
492 refund += sstore_clearing_slot_refund;
494 }
495 }
496
497 if vals.is_original_eq_new() {
499 if vals.is_original_zero() {
501 refund += self.sstore_set_refund() as i64;
503 } else {
505 refund += self.sstore_reset_refund() as i64;
507 }
508 }
509 refund
510 }
511
512 #[inline]
514 pub const fn log_cost(&self, n: u8, len: u64) -> u64 {
515 self.get(GasId::logdata())
516 .saturating_mul(len)
517 .saturating_add(self.get(GasId::logtopic()) * n as u64)
518 }
519
520 #[inline]
522 pub fn keccak256_cost(&self, len: usize) -> u64 {
523 self.get(GasId::keccak256_per_word())
524 .saturating_mul(num_words(len) as u64)
525 }
526
527 #[inline]
529 pub fn memory_cost(&self, len: usize) -> u64 {
530 let len = len as u64;
531 self.get(GasId::memory_linear_cost())
532 .saturating_mul(len)
533 .saturating_add(
534 (len.saturating_mul(len))
535 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
536 )
537 }
538
539 #[inline]
541 pub fn initcode_cost(&self, len: usize) -> u64 {
542 self.get(GasId::initcode_per_word())
543 .saturating_mul(num_words(len) as u64)
544 }
545
546 #[inline]
548 pub fn create_cost(&self) -> u64 {
549 self.get(GasId::create())
550 }
551
552 #[inline]
554 pub fn create2_cost(&self, len: usize) -> u64 {
555 self.get(GasId::create()).saturating_add(
556 self.get(GasId::keccak256_per_word())
557 .saturating_mul(num_words(len) as u64),
558 )
559 }
560
561 #[inline]
563 pub fn call_stipend(&self) -> u64 {
564 self.get(GasId::call_stipend())
565 }
566
567 #[inline]
569 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
570 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
571 }
572
573 #[inline]
575 pub fn transfer_value_cost(&self) -> u64 {
576 self.get(GasId::transfer_value_cost())
577 }
578
579 #[inline]
581 pub fn cold_account_additional_cost(&self) -> u64 {
582 self.get(GasId::cold_account_additional_cost())
583 }
584
585 #[inline]
587 pub fn cold_storage_additional_cost(&self) -> u64 {
588 self.get(GasId::cold_storage_additional_cost())
589 }
590
591 #[inline]
593 pub fn cold_storage_cost(&self) -> u64 {
594 self.get(GasId::cold_storage_cost())
595 }
596
597 #[inline]
599 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
600 if !is_spurious_dragon || transfers_value {
604 return self.get(GasId::new_account_cost());
605 }
606 0
607 }
608
609 #[inline]
611 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
612 self.get(GasId::new_account_cost_for_selfdestruct())
613 }
614
615 #[inline]
617 pub fn warm_storage_read_cost(&self) -> u64 {
618 self.get(GasId::warm_storage_read_cost())
619 }
620
621 #[inline]
623 pub fn copy_cost(&self, len: usize) -> u64 {
624 self.copy_per_word_cost(num_words(len))
625 }
626
627 #[inline]
629 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
630 self.get(GasId::copy_per_word())
631 .saturating_mul(word_num as u64)
632 }
633
634 #[inline]
636 pub fn code_deposit_cost(&self, len: usize) -> u64 {
637 self.get(GasId::code_deposit_cost())
638 .saturating_mul(len as u64)
639 }
640
641 #[inline]
643 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
644 self.get(GasId::tx_eip7702_per_empty_account_cost())
645 }
646
647 #[inline]
653 pub fn tx_eip7702_auth_refund(&self) -> u64 {
654 self.get(GasId::tx_eip7702_auth_refund())
655 }
656
657 #[inline]
659 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
660 self.get(GasId::tx_token_non_zero_byte_multiplier())
661 }
662
663 #[inline]
665 pub fn tx_token_cost(&self) -> u64 {
666 self.get(GasId::tx_token_cost())
667 }
668
669 pub fn tx_floor_cost_per_token(&self) -> u64 {
671 self.get(GasId::tx_floor_cost_per_token())
672 }
673
674 #[inline]
678 pub fn tx_floor_cost(&self, tokens_in_calldata: u64) -> u64 {
679 self.tx_floor_cost_per_token() * tokens_in_calldata + self.tx_floor_cost_base_gas()
680 }
681
682 pub fn tx_floor_cost_base_gas(&self) -> u64 {
684 self.get(GasId::tx_floor_cost_base_gas())
685 }
686
687 pub fn tx_access_list_address_cost(&self) -> u64 {
689 self.get(GasId::tx_access_list_address_cost())
690 }
691
692 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
694 self.get(GasId::tx_access_list_storage_key_cost())
695 }
696
697 #[inline]
715 pub fn tx_access_list_cost(&self, accounts: u64, storages: u64) -> u64 {
716 accounts
717 .saturating_mul(self.tx_access_list_address_cost())
718 .saturating_add(storages.saturating_mul(self.tx_access_list_storage_key_cost()))
719 }
720
721 pub fn tx_base_stipend(&self) -> u64 {
723 self.get(GasId::tx_base_stipend())
724 }
725
726 #[inline]
730 pub fn tx_create_cost(&self) -> u64 {
731 self.get(GasId::tx_create_cost())
732 }
733
734 #[inline]
736 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
737 self.get(GasId::tx_initcode_cost())
738 .saturating_mul(num_words(len) as u64)
739 }
740
741 pub fn initial_tx_gas(
749 &self,
750 input: &[u8],
751 is_create: bool,
752 access_list_accounts: u64,
753 access_list_storages: u64,
754 authorization_list_num: u64,
755 ) -> InitialAndFloorGas {
756 let mut gas = InitialAndFloorGas::default();
757
758 let tokens_in_calldata =
760 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
761
762 gas.initial_gas += tokens_in_calldata * self.tx_token_cost()
763 + access_list_accounts * self.tx_access_list_address_cost()
765 + access_list_storages * self.tx_access_list_storage_key_cost()
767 + self.tx_base_stipend()
768 + authorization_list_num * self.tx_eip7702_per_empty_account_cost();
770
771 if is_create {
772 gas.initial_gas += self.tx_create_cost();
774
775 gas.initial_gas += self.tx_initcode_cost(input.len());
777 }
778
779 gas.floor_gas = self.tx_floor_cost(tokens_in_calldata);
781
782 gas
783 }
784}
785
786#[inline]
787pub(crate) const fn log2floor(value: U256) -> u64 {
788 let mut l: u64 = 256;
789 let mut i = 3;
790 loop {
791 if value.as_limbs()[i] == 0u64 {
792 l -= 64;
793 } else {
794 l -= value.as_limbs()[i].leading_zeros() as u64;
795 if l == 0 {
796 return l;
797 } else {
798 return l - 1;
799 }
800 }
801 if i == 0 {
802 break;
803 }
804 i -= 1;
805 }
806 l
807}
808
809#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
811pub struct GasId(u8);
812
813impl GasId {
814 pub const fn new(id: u8) -> Self {
816 Self(id)
817 }
818
819 pub const fn as_u8(&self) -> u8 {
821 self.0
822 }
823
824 pub const fn as_usize(&self) -> usize {
826 self.0 as usize
827 }
828
829 pub const fn name(&self) -> &'static str {
841 match self.0 {
842 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
843 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
844 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
845 x if x == Self::logdata().as_u8() => "logdata",
846 x if x == Self::logtopic().as_u8() => "logtopic",
847 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
848 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
849 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
850 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
851 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
852 x if x == Self::create().as_u8() => "create",
853 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
854 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
855 x if x == Self::cold_account_additional_cost().as_u8() => {
856 "cold_account_additional_cost"
857 }
858 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
859 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
860 x if x == Self::sstore_static().as_u8() => "sstore_static",
861 x if x == Self::sstore_set_without_load_cost().as_u8() => {
862 "sstore_set_without_load_cost"
863 }
864 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
865 "sstore_reset_without_cold_load_cost"
866 }
867 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
868 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
869 x if x == Self::call_stipend().as_u8() => "call_stipend",
870 x if x == Self::cold_storage_additional_cost().as_u8() => {
871 "cold_storage_additional_cost"
872 }
873 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
874 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
875 "new_account_cost_for_selfdestruct"
876 }
877 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
878 x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => {
879 "tx_eip7702_per_empty_account_cost"
880 }
881 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
882 "tx_token_non_zero_byte_multiplier"
883 }
884 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
885 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
886 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
887 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
888 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
889 "tx_access_list_storage_key_cost"
890 }
891 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
892 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
893 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
894 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
895 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
896 x if x == Self::tx_eip7702_auth_refund().as_u8() => "tx_eip7702_auth_refund",
897 _ => "unknown",
898 }
899 }
900
901 pub fn from_name(s: &str) -> Option<GasId> {
915 match s {
916 "exp_byte_gas" => Some(Self::exp_byte_gas()),
917 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
918 "copy_per_word" => Some(Self::copy_per_word()),
919 "logdata" => Some(Self::logdata()),
920 "logtopic" => Some(Self::logtopic()),
921 "mcopy_per_word" => Some(Self::mcopy_per_word()),
922 "keccak256_per_word" => Some(Self::keccak256_per_word()),
923 "memory_linear_cost" => Some(Self::memory_linear_cost()),
924 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
925 "initcode_per_word" => Some(Self::initcode_per_word()),
926 "create" => Some(Self::create()),
927 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
928 "transfer_value_cost" => Some(Self::transfer_value_cost()),
929 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
930 "new_account_cost" => Some(Self::new_account_cost()),
931 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
932 "sstore_static" => Some(Self::sstore_static()),
933 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
934 "sstore_reset_without_cold_load_cost" => {
935 Some(Self::sstore_reset_without_cold_load_cost())
936 }
937 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
938 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
939 "call_stipend" => Some(Self::call_stipend()),
940 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
941 "cold_storage_cost" => Some(Self::cold_storage_cost()),
942 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
943 "code_deposit_cost" => Some(Self::code_deposit_cost()),
944 "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()),
945 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
946 "tx_token_cost" => Some(Self::tx_token_cost()),
947 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
948 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
949 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
950 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
951 "tx_base_stipend" => Some(Self::tx_base_stipend()),
952 "tx_create_cost" => Some(Self::tx_create_cost()),
953 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
954 "sstore_set_refund" => Some(Self::sstore_set_refund()),
955 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
956 "tx_eip7702_auth_refund" => Some(Self::tx_eip7702_auth_refund()),
957 _ => None,
958 }
959 }
960
961 pub const fn exp_byte_gas() -> GasId {
963 Self::new(1)
964 }
965
966 pub const fn extcodecopy_per_word() -> GasId {
968 Self::new(2)
969 }
970
971 pub const fn copy_per_word() -> GasId {
973 Self::new(3)
974 }
975
976 pub const fn logdata() -> GasId {
978 Self::new(4)
979 }
980
981 pub const fn logtopic() -> GasId {
983 Self::new(5)
984 }
985
986 pub const fn mcopy_per_word() -> GasId {
988 Self::new(6)
989 }
990
991 pub const fn keccak256_per_word() -> GasId {
993 Self::new(7)
994 }
995
996 pub const fn memory_linear_cost() -> GasId {
998 Self::new(8)
999 }
1000
1001 pub const fn memory_quadratic_reduction() -> GasId {
1003 Self::new(9)
1004 }
1005
1006 pub const fn initcode_per_word() -> GasId {
1008 Self::new(10)
1009 }
1010
1011 pub const fn create() -> GasId {
1013 Self::new(11)
1014 }
1015
1016 pub const fn call_stipend_reduction() -> GasId {
1018 Self::new(12)
1019 }
1020
1021 pub const fn transfer_value_cost() -> GasId {
1023 Self::new(13)
1024 }
1025
1026 pub const fn cold_account_additional_cost() -> GasId {
1028 Self::new(14)
1029 }
1030
1031 pub const fn new_account_cost() -> GasId {
1033 Self::new(15)
1034 }
1035
1036 pub const fn warm_storage_read_cost() -> GasId {
1040 Self::new(16)
1041 }
1042
1043 pub const fn sstore_static() -> GasId {
1046 Self::new(17)
1047 }
1048
1049 pub const fn sstore_set_without_load_cost() -> GasId {
1051 Self::new(18)
1052 }
1053
1054 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1056 Self::new(19)
1057 }
1058
1059 pub const fn sstore_clearing_slot_refund() -> GasId {
1061 Self::new(20)
1062 }
1063
1064 pub const fn selfdestruct_refund() -> GasId {
1066 Self::new(21)
1067 }
1068
1069 pub const fn call_stipend() -> GasId {
1071 Self::new(22)
1072 }
1073
1074 pub const fn cold_storage_additional_cost() -> GasId {
1076 Self::new(23)
1077 }
1078
1079 pub const fn cold_storage_cost() -> GasId {
1081 Self::new(24)
1082 }
1083
1084 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1086 Self::new(25)
1087 }
1088
1089 pub const fn code_deposit_cost() -> GasId {
1091 Self::new(26)
1092 }
1093
1094 pub const fn tx_eip7702_per_empty_account_cost() -> GasId {
1096 Self::new(27)
1097 }
1098
1099 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1101 Self::new(28)
1102 }
1103
1104 pub const fn tx_token_cost() -> GasId {
1106 Self::new(29)
1107 }
1108
1109 pub const fn tx_floor_cost_per_token() -> GasId {
1111 Self::new(30)
1112 }
1113
1114 pub const fn tx_floor_cost_base_gas() -> GasId {
1116 Self::new(31)
1117 }
1118
1119 pub const fn tx_access_list_address_cost() -> GasId {
1121 Self::new(32)
1122 }
1123
1124 pub const fn tx_access_list_storage_key_cost() -> GasId {
1126 Self::new(33)
1127 }
1128
1129 pub const fn tx_base_stipend() -> GasId {
1131 Self::new(34)
1132 }
1133
1134 pub const fn tx_create_cost() -> GasId {
1136 Self::new(35)
1137 }
1138
1139 pub const fn tx_initcode_cost() -> GasId {
1141 Self::new(36)
1142 }
1143
1144 pub const fn sstore_set_refund() -> GasId {
1146 Self::new(37)
1147 }
1148
1149 pub const fn sstore_reset_refund() -> GasId {
1151 Self::new(38)
1152 }
1153
1154 pub const fn tx_eip7702_auth_refund() -> GasId {
1158 Self::new(39)
1159 }
1160}
1161
1162#[cfg(test)]
1163mod tests {
1164 use super::*;
1165 use std::collections::HashSet;
1166
1167 #[test]
1168 fn test_gas_id_name_and_from_str_coverage() {
1169 let mut unique_names = HashSet::new();
1170 let mut known_gas_ids = 0;
1171
1172 for i in 0..=255 {
1174 let gas_id = GasId::new(i);
1175 let name = gas_id.name();
1176
1177 if name != "unknown" {
1179 unique_names.insert(name);
1180 }
1181 }
1182
1183 for name in &unique_names {
1185 if let Some(gas_id) = GasId::from_name(name) {
1186 known_gas_ids += 1;
1187 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1189 }
1190 }
1191
1192 println!("Total unique named GasIds: {}", unique_names.len());
1193 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1194
1195 assert_eq!(
1197 unique_names.len(),
1198 known_gas_ids,
1199 "Not all unique names are resolvable via from_str"
1200 );
1201
1202 assert_eq!(
1204 unique_names.len(),
1205 39,
1206 "Expected 39 unique GasIds, found {}",
1207 unique_names.len()
1208 );
1209 }
1210
1211 #[test]
1212 fn test_tx_access_list_cost() {
1213 use crate::cfg::gas;
1214
1215 let gas_params = GasParams::new_spec(SpecId::BERLIN);
1217
1218 assert_eq!(gas_params.tx_access_list_cost(0, 0), 0);
1220
1221 assert_eq!(
1223 gas_params.tx_access_list_cost(1, 0),
1224 gas::ACCESS_LIST_ADDRESS
1225 );
1226
1227 assert_eq!(
1229 gas_params.tx_access_list_cost(0, 1),
1230 gas::ACCESS_LIST_STORAGE_KEY
1231 );
1232
1233 assert_eq!(
1235 gas_params.tx_access_list_cost(2, 5),
1236 2 * gas::ACCESS_LIST_ADDRESS + 5 * gas::ACCESS_LIST_STORAGE_KEY
1237 );
1238
1239 assert_eq!(
1241 gas_params.tx_access_list_cost(100, 200),
1242 100 * gas::ACCESS_LIST_ADDRESS + 200 * gas::ACCESS_LIST_STORAGE_KEY
1243 );
1244
1245 let gas_params_pre_berlin = GasParams::new_spec(SpecId::ISTANBUL);
1247 assert_eq!(gas_params_pre_berlin.tx_access_list_cost(10, 20), 0);
1248 }
1249}