revm_context_interface/cfg/
gas_params.rs1use 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()).clone()
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_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::tx_token_non_zero_byte_multiplier().as_usize()] =
255 gas::NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
256 }
257
258 if spec.is_enabled_in(SpecId::BERLIN) {
259 table[GasId::sstore_static().as_usize()] = gas::WARM_STORAGE_READ_COST;
260 table[GasId::cold_account_additional_cost().as_usize()] =
261 gas::COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
262 table[GasId::cold_storage_additional_cost().as_usize()] =
263 gas::COLD_SLOAD_COST - gas::WARM_STORAGE_READ_COST;
264 table[GasId::cold_storage_cost().as_usize()] = gas::COLD_SLOAD_COST;
265 table[GasId::warm_storage_read_cost().as_usize()] = gas::WARM_STORAGE_READ_COST;
266
267 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
268 gas::WARM_SSTORE_RESET - gas::WARM_STORAGE_READ_COST;
269 table[GasId::sstore_set_without_load_cost().as_usize()] =
270 gas::SSTORE_SET - gas::WARM_STORAGE_READ_COST;
271
272 table[GasId::tx_access_list_address_cost().as_usize()] = gas::ACCESS_LIST_ADDRESS;
273 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
274 gas::ACCESS_LIST_STORAGE_KEY;
275 }
276
277 if spec.is_enabled_in(SpecId::LONDON) {
278 table[GasId::sstore_clearing_slot_refund().as_usize()] =
283 gas::WARM_SSTORE_RESET + gas::ACCESS_LIST_STORAGE_KEY;
284
285 table[GasId::selfdestruct_refund().as_usize()] = 0;
286 }
287
288 if spec.is_enabled_in(SpecId::SHANGHAI) {
289 table[GasId::tx_initcode_cost().as_usize()] = gas::INITCODE_WORD_COST;
290 }
291
292 if spec.is_enabled_in(SpecId::PRAGUE) {
293 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] =
294 eip7702::PER_EMPTY_ACCOUNT_COST;
295
296 table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
297 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
298 }
299
300 Self::new(Arc::new(table))
301 }
302
303 #[inline]
305 pub const fn get(&self, id: GasId) -> u64 {
306 unsafe { *self.ptr.add(id.as_usize()) }
307 }
308
309 #[inline]
311 pub fn exp_cost(&self, power: U256) -> u64 {
312 if power.is_zero() {
313 return 0;
314 }
315 self.get(GasId::exp_byte_gas())
317 .saturating_mul(log2floor(power) / 8 + 1)
318 }
319
320 #[inline]
322 pub fn selfdestruct_refund(&self) -> i64 {
323 self.get(GasId::selfdestruct_refund()) as i64
324 }
325
326 #[inline]
329 pub fn selfdestruct_cold_cost(&self) -> u64 {
330 self.cold_account_additional_cost() + self.warm_storage_read_cost()
331 }
332
333 #[inline]
335 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
336 let mut gas = 0;
337
338 if should_charge_topup {
340 gas += self.new_account_cost_for_selfdestruct();
341 }
342
343 if is_cold {
344 gas += self.selfdestruct_cold_cost();
350 }
351 gas
352 }
353
354 #[inline]
356 pub fn extcodecopy(&self, len: usize) -> u64 {
357 self.get(GasId::extcodecopy_per_word())
358 .saturating_mul(num_words(len) as u64)
359 }
360
361 #[inline]
363 pub fn mcopy_cost(&self, len: usize) -> u64 {
364 self.get(GasId::mcopy_per_word())
365 .saturating_mul(num_words(len) as u64)
366 }
367
368 #[inline]
370 pub fn sstore_static_gas(&self) -> u64 {
371 self.get(GasId::sstore_static())
372 }
373
374 #[inline]
376 pub fn sstore_set_without_load_cost(&self) -> u64 {
377 self.get(GasId::sstore_set_without_load_cost())
378 }
379
380 #[inline]
382 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
383 self.get(GasId::sstore_reset_without_cold_load_cost())
384 }
385
386 #[inline]
388 pub fn sstore_clearing_slot_refund(&self) -> u64 {
389 self.get(GasId::sstore_clearing_slot_refund())
390 }
391
392 #[inline]
396 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
397 if !is_istanbul {
400 if vals.is_present_zero() && !vals.is_new_zero() {
401 return self.sstore_set_without_load_cost();
402 } else {
403 return self.sstore_reset_without_cold_load_cost();
404 }
405 }
406
407 let mut gas = 0;
408
409 if is_cold {
411 gas += self.cold_storage_cost();
412 }
413
414 if vals.new_values_changes_present() && vals.is_original_eq_present() {
416 gas += if vals.is_original_zero() {
417 self.sstore_set_without_load_cost()
420 } else {
421 self.sstore_reset_without_cold_load_cost()
423 };
424 }
425 gas
426 }
427
428 #[inline]
430 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
431 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
433
434 if !is_istanbul {
435 if !vals.is_present_zero() && vals.is_new_zero() {
437 return sstore_clearing_slot_refund;
438 }
439 return 0;
440 }
441
442 if vals.is_new_eq_present() {
444 return 0;
445 }
446
447 if vals.is_original_eq_present() && vals.is_new_zero() {
450 return sstore_clearing_slot_refund;
451 }
452
453 let mut refund = 0;
454 if !vals.is_original_zero() {
456 if vals.is_present_zero() {
458 refund -= sstore_clearing_slot_refund;
460 } else if vals.is_new_zero() {
462 refund += sstore_clearing_slot_refund;
464 }
465 }
466
467 if vals.is_original_eq_new() {
469 if vals.is_original_zero() {
471 refund += self.sstore_set_without_load_cost() as i64;
473 } else {
475 refund += self.sstore_reset_without_cold_load_cost() as i64;
477 }
478 }
479 refund
480 }
481
482 #[inline]
484 pub const fn log_cost(&self, n: u8, len: u64) -> u64 {
485 self.get(GasId::logdata())
486 .saturating_mul(len)
487 .saturating_add(self.get(GasId::logtopic()) * n as u64)
488 }
489
490 #[inline]
492 pub fn keccak256_cost(&self, len: usize) -> u64 {
493 self.get(GasId::keccak256_per_word())
494 .saturating_mul(num_words(len) as u64)
495 }
496
497 #[inline]
499 pub fn memory_cost(&self, len: usize) -> u64 {
500 let len = len as u64;
501 self.get(GasId::memory_linear_cost())
502 .saturating_mul(len)
503 .saturating_add(
504 (len.saturating_mul(len))
505 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
506 )
507 }
508
509 #[inline]
511 pub fn initcode_cost(&self, len: usize) -> u64 {
512 self.get(GasId::initcode_per_word())
513 .saturating_mul(num_words(len) as u64)
514 }
515
516 #[inline]
518 pub fn create_cost(&self) -> u64 {
519 self.get(GasId::create())
520 }
521
522 #[inline]
524 pub fn create2_cost(&self, len: usize) -> u64 {
525 self.get(GasId::create()).saturating_add(
526 self.get(GasId::keccak256_per_word())
527 .saturating_mul(num_words(len) as u64),
528 )
529 }
530
531 #[inline]
533 pub fn call_stipend(&self) -> u64 {
534 self.get(GasId::call_stipend())
535 }
536
537 #[inline]
539 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
540 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
541 }
542
543 #[inline]
545 pub fn transfer_value_cost(&self) -> u64 {
546 self.get(GasId::transfer_value_cost())
547 }
548
549 #[inline]
551 pub fn cold_account_additional_cost(&self) -> u64 {
552 self.get(GasId::cold_account_additional_cost())
553 }
554
555 #[inline]
557 pub fn cold_storage_additional_cost(&self) -> u64 {
558 self.get(GasId::cold_storage_additional_cost())
559 }
560
561 #[inline]
563 pub fn cold_storage_cost(&self) -> u64 {
564 self.get(GasId::cold_storage_cost())
565 }
566
567 #[inline]
569 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
570 if !is_spurious_dragon || transfers_value {
574 return self.get(GasId::new_account_cost());
575 }
576 0
577 }
578
579 #[inline]
581 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
582 self.get(GasId::new_account_cost_for_selfdestruct())
583 }
584
585 #[inline]
587 pub fn warm_storage_read_cost(&self) -> u64 {
588 self.get(GasId::warm_storage_read_cost())
589 }
590
591 #[inline]
593 pub fn copy_cost(&self, len: usize) -> u64 {
594 self.copy_per_word_cost(num_words(len))
595 }
596
597 #[inline]
599 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
600 self.get(GasId::copy_per_word())
601 .saturating_mul(word_num as u64)
602 }
603
604 #[inline]
606 pub fn code_deposit_cost(&self, len: usize) -> u64 {
607 self.get(GasId::code_deposit_cost())
608 .saturating_mul(len as u64)
609 }
610
611 #[inline]
613 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
614 self.get(GasId::tx_eip7702_per_empty_account_cost())
615 }
616
617 #[inline]
619 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
620 self.get(GasId::tx_token_non_zero_byte_multiplier())
621 }
622
623 #[inline]
625 pub fn tx_token_cost(&self) -> u64 {
626 self.get(GasId::tx_token_cost())
627 }
628
629 pub fn tx_floor_cost_per_token(&self) -> u64 {
631 self.get(GasId::tx_floor_cost_per_token())
632 }
633
634 #[inline]
638 pub fn tx_floor_cost(&self, tokens_in_calldata: u64) -> u64 {
639 self.tx_floor_cost_per_token() * tokens_in_calldata + self.tx_floor_cost_base_gas()
640 }
641
642 pub fn tx_floor_cost_base_gas(&self) -> u64 {
644 self.get(GasId::tx_floor_cost_base_gas())
645 }
646
647 pub fn tx_access_list_address_cost(&self) -> u64 {
649 self.get(GasId::tx_access_list_address_cost())
650 }
651
652 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
654 self.get(GasId::tx_access_list_storage_key_cost())
655 }
656
657 pub fn tx_base_stipend(&self) -> u64 {
659 self.get(GasId::tx_base_stipend())
660 }
661
662 #[inline]
666 pub fn tx_create_cost(&self) -> u64 {
667 self.get(GasId::tx_create_cost())
668 }
669
670 #[inline]
672 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
673 self.get(GasId::tx_initcode_cost())
674 .saturating_mul(num_words(len) as u64)
675 }
676
677 pub fn initial_tx_gas(
685 &self,
686 input: &[u8],
687 is_create: bool,
688 access_list_accounts: u64,
689 access_list_storages: u64,
690 authorization_list_num: u64,
691 ) -> InitialAndFloorGas {
692 let mut gas = InitialAndFloorGas::default();
693
694 let tokens_in_calldata =
696 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
697
698 gas.initial_gas += tokens_in_calldata * self.tx_token_cost()
699 + access_list_accounts * self.tx_access_list_address_cost()
701 + access_list_storages * self.tx_access_list_storage_key_cost()
703 + self.tx_base_stipend()
704 + authorization_list_num * self.tx_eip7702_per_empty_account_cost();
706
707 if is_create {
708 gas.initial_gas += self.tx_create_cost();
710
711 gas.initial_gas += self.tx_initcode_cost(input.len());
713 }
714
715 gas.floor_gas = self.tx_floor_cost(tokens_in_calldata);
717
718 gas
719 }
720}
721
722#[inline]
723pub(crate) const fn log2floor(value: U256) -> u64 {
724 let mut l: u64 = 256;
725 let mut i = 3;
726 loop {
727 if value.as_limbs()[i] == 0u64 {
728 l -= 64;
729 } else {
730 l -= value.as_limbs()[i].leading_zeros() as u64;
731 if l == 0 {
732 return l;
733 } else {
734 return l - 1;
735 }
736 }
737 if i == 0 {
738 break;
739 }
740 i -= 1;
741 }
742 l
743}
744
745#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
747pub struct GasId(u8);
748
749impl GasId {
750 pub const fn new(id: u8) -> Self {
752 Self(id)
753 }
754
755 pub const fn as_u8(&self) -> u8 {
757 self.0
758 }
759
760 pub const fn as_usize(&self) -> usize {
762 self.0 as usize
763 }
764
765 pub const fn name(&self) -> &'static str {
777 match self.0 {
778 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
779 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
780 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
781 x if x == Self::logdata().as_u8() => "logdata",
782 x if x == Self::logtopic().as_u8() => "logtopic",
783 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
784 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
785 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
786 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
787 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
788 x if x == Self::create().as_u8() => "create",
789 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
790 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
791 x if x == Self::cold_account_additional_cost().as_u8() => {
792 "cold_account_additional_cost"
793 }
794 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
795 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
796 x if x == Self::sstore_static().as_u8() => "sstore_static",
797 x if x == Self::sstore_set_without_load_cost().as_u8() => {
798 "sstore_set_without_load_cost"
799 }
800 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
801 "sstore_reset_without_cold_load_cost"
802 }
803 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
804 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
805 x if x == Self::call_stipend().as_u8() => "call_stipend",
806 x if x == Self::cold_storage_additional_cost().as_u8() => {
807 "cold_storage_additional_cost"
808 }
809 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
810 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
811 "new_account_cost_for_selfdestruct"
812 }
813 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
814 x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => {
815 "tx_eip7702_per_empty_account_cost"
816 }
817 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
818 "tx_token_non_zero_byte_multiplier"
819 }
820 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
821 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
822 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
823 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
824 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
825 "tx_access_list_storage_key_cost"
826 }
827 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
828 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
829 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
830 _ => "unknown",
831 }
832 }
833
834 pub fn from_name(s: &str) -> Option<GasId> {
848 match s {
849 "exp_byte_gas" => Some(Self::exp_byte_gas()),
850 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
851 "copy_per_word" => Some(Self::copy_per_word()),
852 "logdata" => Some(Self::logdata()),
853 "logtopic" => Some(Self::logtopic()),
854 "mcopy_per_word" => Some(Self::mcopy_per_word()),
855 "keccak256_per_word" => Some(Self::keccak256_per_word()),
856 "memory_linear_cost" => Some(Self::memory_linear_cost()),
857 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
858 "initcode_per_word" => Some(Self::initcode_per_word()),
859 "create" => Some(Self::create()),
860 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
861 "transfer_value_cost" => Some(Self::transfer_value_cost()),
862 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
863 "new_account_cost" => Some(Self::new_account_cost()),
864 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
865 "sstore_static" => Some(Self::sstore_static()),
866 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
867 "sstore_reset_without_cold_load_cost" => {
868 Some(Self::sstore_reset_without_cold_load_cost())
869 }
870 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
871 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
872 "call_stipend" => Some(Self::call_stipend()),
873 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
874 "cold_storage_cost" => Some(Self::cold_storage_cost()),
875 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
876 "code_deposit_cost" => Some(Self::code_deposit_cost()),
877 "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()),
878 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
879 "tx_token_cost" => Some(Self::tx_token_cost()),
880 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
881 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
882 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
883 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
884 "tx_base_stipend" => Some(Self::tx_base_stipend()),
885 "tx_create_cost" => Some(Self::tx_create_cost()),
886 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
887 _ => None,
888 }
889 }
890
891 pub const fn exp_byte_gas() -> GasId {
893 Self::new(1)
894 }
895
896 pub const fn extcodecopy_per_word() -> GasId {
898 Self::new(2)
899 }
900
901 pub const fn copy_per_word() -> GasId {
903 Self::new(3)
904 }
905
906 pub const fn logdata() -> GasId {
908 Self::new(4)
909 }
910
911 pub const fn logtopic() -> GasId {
913 Self::new(5)
914 }
915
916 pub const fn mcopy_per_word() -> GasId {
918 Self::new(6)
919 }
920
921 pub const fn keccak256_per_word() -> GasId {
923 Self::new(7)
924 }
925
926 pub const fn memory_linear_cost() -> GasId {
928 Self::new(8)
929 }
930
931 pub const fn memory_quadratic_reduction() -> GasId {
933 Self::new(9)
934 }
935
936 pub const fn initcode_per_word() -> GasId {
938 Self::new(10)
939 }
940
941 pub const fn create() -> GasId {
943 Self::new(11)
944 }
945
946 pub const fn call_stipend_reduction() -> GasId {
948 Self::new(12)
949 }
950
951 pub const fn transfer_value_cost() -> GasId {
953 Self::new(13)
954 }
955
956 pub const fn cold_account_additional_cost() -> GasId {
958 Self::new(14)
959 }
960
961 pub const fn new_account_cost() -> GasId {
963 Self::new(15)
964 }
965
966 pub const fn warm_storage_read_cost() -> GasId {
970 Self::new(16)
971 }
972
973 pub const fn sstore_static() -> GasId {
976 Self::new(17)
977 }
978
979 pub const fn sstore_set_without_load_cost() -> GasId {
981 Self::new(18)
982 }
983
984 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
986 Self::new(19)
987 }
988
989 pub const fn sstore_clearing_slot_refund() -> GasId {
991 Self::new(20)
992 }
993
994 pub const fn selfdestruct_refund() -> GasId {
996 Self::new(21)
997 }
998
999 pub const fn call_stipend() -> GasId {
1001 Self::new(22)
1002 }
1003
1004 pub const fn cold_storage_additional_cost() -> GasId {
1006 Self::new(23)
1007 }
1008
1009 pub const fn cold_storage_cost() -> GasId {
1011 Self::new(24)
1012 }
1013
1014 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1016 Self::new(25)
1017 }
1018
1019 pub const fn code_deposit_cost() -> GasId {
1021 Self::new(26)
1022 }
1023
1024 pub const fn tx_eip7702_per_empty_account_cost() -> GasId {
1026 Self::new(27)
1027 }
1028
1029 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1031 Self::new(28)
1032 }
1033
1034 pub const fn tx_token_cost() -> GasId {
1036 Self::new(29)
1037 }
1038
1039 pub const fn tx_floor_cost_per_token() -> GasId {
1041 Self::new(30)
1042 }
1043
1044 pub const fn tx_floor_cost_base_gas() -> GasId {
1046 Self::new(31)
1047 }
1048
1049 pub const fn tx_access_list_address_cost() -> GasId {
1051 Self::new(32)
1052 }
1053
1054 pub const fn tx_access_list_storage_key_cost() -> GasId {
1056 Self::new(33)
1057 }
1058
1059 pub const fn tx_base_stipend() -> GasId {
1061 Self::new(34)
1062 }
1063
1064 pub const fn tx_create_cost() -> GasId {
1066 Self::new(35)
1067 }
1068
1069 pub const fn tx_initcode_cost() -> GasId {
1071 Self::new(36)
1072 }
1073}
1074
1075#[cfg(test)]
1076mod tests {
1077 use super::*;
1078 use std::collections::HashSet;
1079
1080 #[test]
1081 fn test_gas_id_name_and_from_str_coverage() {
1082 let mut unique_names = HashSet::new();
1083 let mut known_gas_ids = 0;
1084
1085 for i in 0..=255 {
1087 let gas_id = GasId::new(i);
1088 let name = gas_id.name();
1089
1090 if name != "unknown" {
1092 unique_names.insert(name);
1093 }
1094 }
1095
1096 for name in &unique_names {
1098 if let Some(gas_id) = GasId::from_name(name) {
1099 known_gas_ids += 1;
1100 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1102 }
1103 }
1104
1105 println!("Total unique named GasIds: {}", unique_names.len());
1106 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1107
1108 assert_eq!(
1110 unique_names.len(),
1111 known_gas_ids,
1112 "Not all unique names are resolvable via from_str"
1113 );
1114
1115 assert_eq!(
1117 unique_names.len(),
1118 36,
1119 "Expected 36 unique GasIds, found {}",
1120 unique_names.len()
1121 );
1122 }
1123}