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())
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_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
311 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
312 }
313
314 Self::new(Arc::new(table))
315 }
316
317 #[inline]
319 pub const fn get(&self, id: GasId) -> u64 {
320 unsafe { *self.ptr.add(id.as_usize()) }
321 }
322
323 #[inline]
325 pub fn exp_cost(&self, power: U256) -> u64 {
326 if power.is_zero() {
327 return 0;
328 }
329 self.get(GasId::exp_byte_gas())
331 .saturating_mul(log2floor(power) / 8 + 1)
332 }
333
334 #[inline]
336 pub fn selfdestruct_refund(&self) -> i64 {
337 self.get(GasId::selfdestruct_refund()) as i64
338 }
339
340 #[inline]
343 pub fn selfdestruct_cold_cost(&self) -> u64 {
344 self.cold_account_additional_cost() + self.warm_storage_read_cost()
345 }
346
347 #[inline]
349 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
350 let mut gas = 0;
351
352 if should_charge_topup {
354 gas += self.new_account_cost_for_selfdestruct();
355 }
356
357 if is_cold {
358 gas += self.selfdestruct_cold_cost();
364 }
365 gas
366 }
367
368 #[inline]
370 pub fn extcodecopy(&self, len: usize) -> u64 {
371 self.get(GasId::extcodecopy_per_word())
372 .saturating_mul(num_words(len) as u64)
373 }
374
375 #[inline]
377 pub fn mcopy_cost(&self, len: usize) -> u64 {
378 self.get(GasId::mcopy_per_word())
379 .saturating_mul(num_words(len) as u64)
380 }
381
382 #[inline]
384 pub fn sstore_static_gas(&self) -> u64 {
385 self.get(GasId::sstore_static())
386 }
387
388 #[inline]
390 pub fn sstore_set_without_load_cost(&self) -> u64 {
391 self.get(GasId::sstore_set_without_load_cost())
392 }
393
394 #[inline]
396 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
397 self.get(GasId::sstore_reset_without_cold_load_cost())
398 }
399
400 #[inline]
402 pub fn sstore_clearing_slot_refund(&self) -> u64 {
403 self.get(GasId::sstore_clearing_slot_refund())
404 }
405
406 #[inline]
408 pub fn sstore_set_refund(&self) -> u64 {
409 self.get(GasId::sstore_set_refund())
410 }
411
412 #[inline]
414 pub fn sstore_reset_refund(&self) -> u64 {
415 self.get(GasId::sstore_reset_refund())
416 }
417
418 #[inline]
422 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
423 if !is_istanbul {
426 if vals.is_present_zero() && !vals.is_new_zero() {
427 return self.sstore_set_without_load_cost();
428 } else {
429 return self.sstore_reset_without_cold_load_cost();
430 }
431 }
432
433 let mut gas = 0;
434
435 if is_cold {
437 gas += self.cold_storage_cost();
438 }
439
440 if vals.new_values_changes_present() && vals.is_original_eq_present() {
442 gas += if vals.is_original_zero() {
443 self.sstore_set_without_load_cost()
446 } else {
447 self.sstore_reset_without_cold_load_cost()
449 };
450 }
451 gas
452 }
453
454 #[inline]
456 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
457 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
459
460 if !is_istanbul {
461 if !vals.is_present_zero() && vals.is_new_zero() {
463 return sstore_clearing_slot_refund;
464 }
465 return 0;
466 }
467
468 if vals.is_new_eq_present() {
470 return 0;
471 }
472
473 if vals.is_original_eq_present() && vals.is_new_zero() {
476 return sstore_clearing_slot_refund;
477 }
478
479 let mut refund = 0;
480 if !vals.is_original_zero() {
482 if vals.is_present_zero() {
484 refund -= sstore_clearing_slot_refund;
486 } else if vals.is_new_zero() {
488 refund += sstore_clearing_slot_refund;
490 }
491 }
492
493 if vals.is_original_eq_new() {
495 if vals.is_original_zero() {
497 refund += self.sstore_set_refund() as i64;
499 } else {
501 refund += self.sstore_reset_refund() as i64;
503 }
504 }
505 refund
506 }
507
508 #[inline]
510 pub const fn log_cost(&self, n: u8, len: u64) -> u64 {
511 self.get(GasId::logdata())
512 .saturating_mul(len)
513 .saturating_add(self.get(GasId::logtopic()) * n as u64)
514 }
515
516 #[inline]
518 pub fn keccak256_cost(&self, len: usize) -> u64 {
519 self.get(GasId::keccak256_per_word())
520 .saturating_mul(num_words(len) as u64)
521 }
522
523 #[inline]
525 pub fn memory_cost(&self, len: usize) -> u64 {
526 let len = len as u64;
527 self.get(GasId::memory_linear_cost())
528 .saturating_mul(len)
529 .saturating_add(
530 (len.saturating_mul(len))
531 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
532 )
533 }
534
535 #[inline]
537 pub fn initcode_cost(&self, len: usize) -> u64 {
538 self.get(GasId::initcode_per_word())
539 .saturating_mul(num_words(len) as u64)
540 }
541
542 #[inline]
544 pub fn create_cost(&self) -> u64 {
545 self.get(GasId::create())
546 }
547
548 #[inline]
550 pub fn create2_cost(&self, len: usize) -> u64 {
551 self.get(GasId::create()).saturating_add(
552 self.get(GasId::keccak256_per_word())
553 .saturating_mul(num_words(len) as u64),
554 )
555 }
556
557 #[inline]
559 pub fn call_stipend(&self) -> u64 {
560 self.get(GasId::call_stipend())
561 }
562
563 #[inline]
565 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
566 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
567 }
568
569 #[inline]
571 pub fn transfer_value_cost(&self) -> u64 {
572 self.get(GasId::transfer_value_cost())
573 }
574
575 #[inline]
577 pub fn cold_account_additional_cost(&self) -> u64 {
578 self.get(GasId::cold_account_additional_cost())
579 }
580
581 #[inline]
583 pub fn cold_storage_additional_cost(&self) -> u64 {
584 self.get(GasId::cold_storage_additional_cost())
585 }
586
587 #[inline]
589 pub fn cold_storage_cost(&self) -> u64 {
590 self.get(GasId::cold_storage_cost())
591 }
592
593 #[inline]
595 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
596 if !is_spurious_dragon || transfers_value {
600 return self.get(GasId::new_account_cost());
601 }
602 0
603 }
604
605 #[inline]
607 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
608 self.get(GasId::new_account_cost_for_selfdestruct())
609 }
610
611 #[inline]
613 pub fn warm_storage_read_cost(&self) -> u64 {
614 self.get(GasId::warm_storage_read_cost())
615 }
616
617 #[inline]
619 pub fn copy_cost(&self, len: usize) -> u64 {
620 self.copy_per_word_cost(num_words(len))
621 }
622
623 #[inline]
625 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
626 self.get(GasId::copy_per_word())
627 .saturating_mul(word_num as u64)
628 }
629
630 #[inline]
632 pub fn code_deposit_cost(&self, len: usize) -> u64 {
633 self.get(GasId::code_deposit_cost())
634 .saturating_mul(len as u64)
635 }
636
637 #[inline]
639 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
640 self.get(GasId::tx_eip7702_per_empty_account_cost())
641 }
642
643 #[inline]
645 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
646 self.get(GasId::tx_token_non_zero_byte_multiplier())
647 }
648
649 #[inline]
651 pub fn tx_token_cost(&self) -> u64 {
652 self.get(GasId::tx_token_cost())
653 }
654
655 pub fn tx_floor_cost_per_token(&self) -> u64 {
657 self.get(GasId::tx_floor_cost_per_token())
658 }
659
660 #[inline]
664 pub fn tx_floor_cost(&self, tokens_in_calldata: u64) -> u64 {
665 self.tx_floor_cost_per_token() * tokens_in_calldata + self.tx_floor_cost_base_gas()
666 }
667
668 pub fn tx_floor_cost_base_gas(&self) -> u64 {
670 self.get(GasId::tx_floor_cost_base_gas())
671 }
672
673 pub fn tx_access_list_address_cost(&self) -> u64 {
675 self.get(GasId::tx_access_list_address_cost())
676 }
677
678 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
680 self.get(GasId::tx_access_list_storage_key_cost())
681 }
682
683 pub fn tx_base_stipend(&self) -> u64 {
685 self.get(GasId::tx_base_stipend())
686 }
687
688 #[inline]
692 pub fn tx_create_cost(&self) -> u64 {
693 self.get(GasId::tx_create_cost())
694 }
695
696 #[inline]
698 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
699 self.get(GasId::tx_initcode_cost())
700 .saturating_mul(num_words(len) as u64)
701 }
702
703 pub fn initial_tx_gas(
711 &self,
712 input: &[u8],
713 is_create: bool,
714 access_list_accounts: u64,
715 access_list_storages: u64,
716 authorization_list_num: u64,
717 ) -> InitialAndFloorGas {
718 let mut gas = InitialAndFloorGas::default();
719
720 let tokens_in_calldata =
722 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
723
724 gas.initial_gas += tokens_in_calldata * self.tx_token_cost()
725 + access_list_accounts * self.tx_access_list_address_cost()
727 + access_list_storages * self.tx_access_list_storage_key_cost()
729 + self.tx_base_stipend()
730 + authorization_list_num * self.tx_eip7702_per_empty_account_cost();
732
733 if is_create {
734 gas.initial_gas += self.tx_create_cost();
736
737 gas.initial_gas += self.tx_initcode_cost(input.len());
739 }
740
741 gas.floor_gas = self.tx_floor_cost(tokens_in_calldata);
743
744 gas
745 }
746}
747
748#[inline]
749pub(crate) const fn log2floor(value: U256) -> u64 {
750 let mut l: u64 = 256;
751 let mut i = 3;
752 loop {
753 if value.as_limbs()[i] == 0u64 {
754 l -= 64;
755 } else {
756 l -= value.as_limbs()[i].leading_zeros() as u64;
757 if l == 0 {
758 return l;
759 } else {
760 return l - 1;
761 }
762 }
763 if i == 0 {
764 break;
765 }
766 i -= 1;
767 }
768 l
769}
770
771#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
773pub struct GasId(u8);
774
775impl GasId {
776 pub const fn new(id: u8) -> Self {
778 Self(id)
779 }
780
781 pub const fn as_u8(&self) -> u8 {
783 self.0
784 }
785
786 pub const fn as_usize(&self) -> usize {
788 self.0 as usize
789 }
790
791 pub const fn name(&self) -> &'static str {
803 match self.0 {
804 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
805 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
806 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
807 x if x == Self::logdata().as_u8() => "logdata",
808 x if x == Self::logtopic().as_u8() => "logtopic",
809 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
810 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
811 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
812 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
813 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
814 x if x == Self::create().as_u8() => "create",
815 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
816 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
817 x if x == Self::cold_account_additional_cost().as_u8() => {
818 "cold_account_additional_cost"
819 }
820 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
821 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
822 x if x == Self::sstore_static().as_u8() => "sstore_static",
823 x if x == Self::sstore_set_without_load_cost().as_u8() => {
824 "sstore_set_without_load_cost"
825 }
826 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
827 "sstore_reset_without_cold_load_cost"
828 }
829 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
830 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
831 x if x == Self::call_stipend().as_u8() => "call_stipend",
832 x if x == Self::cold_storage_additional_cost().as_u8() => {
833 "cold_storage_additional_cost"
834 }
835 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
836 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
837 "new_account_cost_for_selfdestruct"
838 }
839 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
840 x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => {
841 "tx_eip7702_per_empty_account_cost"
842 }
843 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
844 "tx_token_non_zero_byte_multiplier"
845 }
846 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
847 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
848 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
849 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
850 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
851 "tx_access_list_storage_key_cost"
852 }
853 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
854 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
855 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
856 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
857 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
858 _ => "unknown",
859 }
860 }
861
862 pub fn from_name(s: &str) -> Option<GasId> {
876 match s {
877 "exp_byte_gas" => Some(Self::exp_byte_gas()),
878 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
879 "copy_per_word" => Some(Self::copy_per_word()),
880 "logdata" => Some(Self::logdata()),
881 "logtopic" => Some(Self::logtopic()),
882 "mcopy_per_word" => Some(Self::mcopy_per_word()),
883 "keccak256_per_word" => Some(Self::keccak256_per_word()),
884 "memory_linear_cost" => Some(Self::memory_linear_cost()),
885 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
886 "initcode_per_word" => Some(Self::initcode_per_word()),
887 "create" => Some(Self::create()),
888 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
889 "transfer_value_cost" => Some(Self::transfer_value_cost()),
890 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
891 "new_account_cost" => Some(Self::new_account_cost()),
892 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
893 "sstore_static" => Some(Self::sstore_static()),
894 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
895 "sstore_reset_without_cold_load_cost" => {
896 Some(Self::sstore_reset_without_cold_load_cost())
897 }
898 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
899 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
900 "call_stipend" => Some(Self::call_stipend()),
901 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
902 "cold_storage_cost" => Some(Self::cold_storage_cost()),
903 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
904 "code_deposit_cost" => Some(Self::code_deposit_cost()),
905 "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()),
906 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
907 "tx_token_cost" => Some(Self::tx_token_cost()),
908 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
909 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
910 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
911 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
912 "tx_base_stipend" => Some(Self::tx_base_stipend()),
913 "tx_create_cost" => Some(Self::tx_create_cost()),
914 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
915 "sstore_set_refund" => Some(Self::sstore_set_refund()),
916 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
917 _ => None,
918 }
919 }
920
921 pub const fn exp_byte_gas() -> GasId {
923 Self::new(1)
924 }
925
926 pub const fn extcodecopy_per_word() -> GasId {
928 Self::new(2)
929 }
930
931 pub const fn copy_per_word() -> GasId {
933 Self::new(3)
934 }
935
936 pub const fn logdata() -> GasId {
938 Self::new(4)
939 }
940
941 pub const fn logtopic() -> GasId {
943 Self::new(5)
944 }
945
946 pub const fn mcopy_per_word() -> GasId {
948 Self::new(6)
949 }
950
951 pub const fn keccak256_per_word() -> GasId {
953 Self::new(7)
954 }
955
956 pub const fn memory_linear_cost() -> GasId {
958 Self::new(8)
959 }
960
961 pub const fn memory_quadratic_reduction() -> GasId {
963 Self::new(9)
964 }
965
966 pub const fn initcode_per_word() -> GasId {
968 Self::new(10)
969 }
970
971 pub const fn create() -> GasId {
973 Self::new(11)
974 }
975
976 pub const fn call_stipend_reduction() -> GasId {
978 Self::new(12)
979 }
980
981 pub const fn transfer_value_cost() -> GasId {
983 Self::new(13)
984 }
985
986 pub const fn cold_account_additional_cost() -> GasId {
988 Self::new(14)
989 }
990
991 pub const fn new_account_cost() -> GasId {
993 Self::new(15)
994 }
995
996 pub const fn warm_storage_read_cost() -> GasId {
1000 Self::new(16)
1001 }
1002
1003 pub const fn sstore_static() -> GasId {
1006 Self::new(17)
1007 }
1008
1009 pub const fn sstore_set_without_load_cost() -> GasId {
1011 Self::new(18)
1012 }
1013
1014 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1016 Self::new(19)
1017 }
1018
1019 pub const fn sstore_clearing_slot_refund() -> GasId {
1021 Self::new(20)
1022 }
1023
1024 pub const fn selfdestruct_refund() -> GasId {
1026 Self::new(21)
1027 }
1028
1029 pub const fn call_stipend() -> GasId {
1031 Self::new(22)
1032 }
1033
1034 pub const fn cold_storage_additional_cost() -> GasId {
1036 Self::new(23)
1037 }
1038
1039 pub const fn cold_storage_cost() -> GasId {
1041 Self::new(24)
1042 }
1043
1044 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1046 Self::new(25)
1047 }
1048
1049 pub const fn code_deposit_cost() -> GasId {
1051 Self::new(26)
1052 }
1053
1054 pub const fn tx_eip7702_per_empty_account_cost() -> GasId {
1056 Self::new(27)
1057 }
1058
1059 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1061 Self::new(28)
1062 }
1063
1064 pub const fn tx_token_cost() -> GasId {
1066 Self::new(29)
1067 }
1068
1069 pub const fn tx_floor_cost_per_token() -> GasId {
1071 Self::new(30)
1072 }
1073
1074 pub const fn tx_floor_cost_base_gas() -> GasId {
1076 Self::new(31)
1077 }
1078
1079 pub const fn tx_access_list_address_cost() -> GasId {
1081 Self::new(32)
1082 }
1083
1084 pub const fn tx_access_list_storage_key_cost() -> GasId {
1086 Self::new(33)
1087 }
1088
1089 pub const fn tx_base_stipend() -> GasId {
1091 Self::new(34)
1092 }
1093
1094 pub const fn tx_create_cost() -> GasId {
1096 Self::new(35)
1097 }
1098
1099 pub const fn tx_initcode_cost() -> GasId {
1101 Self::new(36)
1102 }
1103
1104 pub const fn sstore_set_refund() -> GasId {
1106 Self::new(37)
1107 }
1108
1109 pub const fn sstore_reset_refund() -> GasId {
1111 Self::new(38)
1112 }
1113}
1114
1115#[cfg(test)]
1116mod tests {
1117 use super::*;
1118 use std::collections::HashSet;
1119
1120 #[test]
1121 fn test_gas_id_name_and_from_str_coverage() {
1122 let mut unique_names = HashSet::new();
1123 let mut known_gas_ids = 0;
1124
1125 for i in 0..=255 {
1127 let gas_id = GasId::new(i);
1128 let name = gas_id.name();
1129
1130 if name != "unknown" {
1132 unique_names.insert(name);
1133 }
1134 }
1135
1136 for name in &unique_names {
1138 if let Some(gas_id) = GasId::from_name(name) {
1139 known_gas_ids += 1;
1140 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1142 }
1143 }
1144
1145 println!("Total unique named GasIds: {}", unique_names.len());
1146 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1147
1148 assert_eq!(
1150 unique_names.len(),
1151 known_gas_ids,
1152 "Not all unique names are resolvable via from_str"
1153 );
1154
1155 assert_eq!(
1157 unique_names.len(),
1158 38,
1159 "Expected 38 unique GasIds, found {}",
1160 unique_names.len()
1161 );
1162 }
1163}