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 if spec.is_enabled_in(SpecId::AMSTERDAM) {
320 const CPSB: u64 = 1174;
322
323 table[GasId::create().as_usize()] = 9000;
325 table[GasId::tx_create_cost().as_usize()] = 9000;
326 table[GasId::code_deposit_cost().as_usize()] = 0;
327 table[GasId::new_account_cost().as_usize()] = 0;
328 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
329 table[GasId::sstore_set_without_load_cost().as_usize()] = 2800;
332
333 table[GasId::sstore_set_state_gas().as_usize()] = 32 * CPSB;
335 table[GasId::new_account_state_gas().as_usize()] = 112 * CPSB;
336 table[GasId::code_deposit_state_gas().as_usize()] = CPSB;
337 table[GasId::create_state_gas().as_usize()] = 112 * CPSB;
338
339 table[GasId::sstore_set_refund().as_usize()] = 32 * CPSB + 2800;
341
342 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] = 7500 + (112 + 23) * CPSB;
347 table[GasId::tx_eip7702_auth_refund().as_usize()] = 112 * CPSB;
349
350 table[GasId::tx_eip7702_per_auth_state_gas().as_usize()] = (112 + 23) * CPSB;
352 }
353
354 Self::new(Arc::new(table))
355 }
356
357 #[inline]
359 pub const fn get(&self, id: GasId) -> u64 {
360 unsafe { *self.ptr.add(id.as_usize()) }
361 }
362
363 #[inline]
365 pub fn exp_cost(&self, power: U256) -> u64 {
366 if power.is_zero() {
367 return 0;
368 }
369 self.get(GasId::exp_byte_gas())
371 .saturating_mul(log2floor(power) / 8 + 1)
372 }
373
374 #[inline]
376 pub fn selfdestruct_refund(&self) -> i64 {
377 self.get(GasId::selfdestruct_refund()) as i64
378 }
379
380 #[inline]
383 pub fn selfdestruct_cold_cost(&self) -> u64 {
384 self.cold_account_additional_cost() + self.warm_storage_read_cost()
385 }
386
387 #[inline]
389 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
390 let mut gas = 0;
391
392 if should_charge_topup {
394 gas += self.new_account_cost_for_selfdestruct();
395 }
396
397 if is_cold {
398 gas += self.selfdestruct_cold_cost();
404 }
405 gas
406 }
407
408 #[inline]
410 pub fn extcodecopy(&self, len: usize) -> u64 {
411 self.get(GasId::extcodecopy_per_word())
412 .saturating_mul(num_words(len) as u64)
413 }
414
415 #[inline]
417 pub fn mcopy_cost(&self, len: usize) -> u64 {
418 self.get(GasId::mcopy_per_word())
419 .saturating_mul(num_words(len) as u64)
420 }
421
422 #[inline]
424 pub fn sstore_static_gas(&self) -> u64 {
425 self.get(GasId::sstore_static())
426 }
427
428 #[inline]
430 pub fn sstore_set_without_load_cost(&self) -> u64 {
431 self.get(GasId::sstore_set_without_load_cost())
432 }
433
434 #[inline]
436 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
437 self.get(GasId::sstore_reset_without_cold_load_cost())
438 }
439
440 #[inline]
442 pub fn sstore_clearing_slot_refund(&self) -> u64 {
443 self.get(GasId::sstore_clearing_slot_refund())
444 }
445
446 #[inline]
448 pub fn sstore_set_refund(&self) -> u64 {
449 self.get(GasId::sstore_set_refund())
450 }
451
452 #[inline]
454 pub fn sstore_reset_refund(&self) -> u64 {
455 self.get(GasId::sstore_reset_refund())
456 }
457
458 #[inline]
462 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
463 if !is_istanbul {
466 if vals.is_present_zero() && !vals.is_new_zero() {
467 return self.sstore_set_without_load_cost();
468 } else {
469 return self.sstore_reset_without_cold_load_cost();
470 }
471 }
472
473 let mut gas = 0;
474
475 if is_cold {
477 gas += self.cold_storage_cost();
478 }
479
480 if vals.new_values_changes_present() && vals.is_original_eq_present() {
482 gas += if vals.is_original_zero() {
483 self.sstore_set_without_load_cost()
486 } else {
487 self.sstore_reset_without_cold_load_cost()
489 };
490 }
491 gas
492 }
493
494 #[inline]
496 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
497 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
499
500 if !is_istanbul {
501 if !vals.is_present_zero() && vals.is_new_zero() {
503 return sstore_clearing_slot_refund;
504 }
505 return 0;
506 }
507
508 if vals.is_new_eq_present() {
510 return 0;
511 }
512
513 if vals.is_original_eq_present() && vals.is_new_zero() {
516 return sstore_clearing_slot_refund;
517 }
518
519 let mut refund = 0;
520 if !vals.is_original_zero() {
522 if vals.is_present_zero() {
524 refund -= sstore_clearing_slot_refund;
526 } else if vals.is_new_zero() {
528 refund += sstore_clearing_slot_refund;
530 }
531 }
532
533 if vals.is_original_eq_new() {
535 if vals.is_original_zero() {
537 refund += self.sstore_set_refund() as i64;
539 } else {
541 refund += self.sstore_reset_refund() as i64;
543 }
544 }
545 refund
546 }
547
548 #[inline]
550 pub const fn log_cost(&self, n: u8, len: u64) -> u64 {
551 self.get(GasId::logdata())
552 .saturating_mul(len)
553 .saturating_add(self.get(GasId::logtopic()) * n as u64)
554 }
555
556 #[inline]
558 pub fn keccak256_cost(&self, len: usize) -> u64 {
559 self.get(GasId::keccak256_per_word())
560 .saturating_mul(num_words(len) as u64)
561 }
562
563 #[inline]
565 pub fn memory_cost(&self, len: usize) -> u64 {
566 let len = len as u64;
567 self.get(GasId::memory_linear_cost())
568 .saturating_mul(len)
569 .saturating_add(
570 (len.saturating_mul(len))
571 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
572 )
573 }
574
575 #[inline]
577 pub fn initcode_cost(&self, len: usize) -> u64 {
578 self.get(GasId::initcode_per_word())
579 .saturating_mul(num_words(len) as u64)
580 }
581
582 #[inline]
584 pub fn create_cost(&self) -> u64 {
585 self.get(GasId::create())
586 }
587
588 #[inline]
590 pub fn create2_cost(&self, len: usize) -> u64 {
591 self.get(GasId::create()).saturating_add(
592 self.get(GasId::keccak256_per_word())
593 .saturating_mul(num_words(len) as u64),
594 )
595 }
596
597 #[inline]
599 pub fn call_stipend(&self) -> u64 {
600 self.get(GasId::call_stipend())
601 }
602
603 #[inline]
605 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
606 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
607 }
608
609 #[inline]
611 pub fn transfer_value_cost(&self) -> u64 {
612 self.get(GasId::transfer_value_cost())
613 }
614
615 #[inline]
617 pub fn cold_account_additional_cost(&self) -> u64 {
618 self.get(GasId::cold_account_additional_cost())
619 }
620
621 #[inline]
623 pub fn cold_storage_additional_cost(&self) -> u64 {
624 self.get(GasId::cold_storage_additional_cost())
625 }
626
627 #[inline]
629 pub fn cold_storage_cost(&self) -> u64 {
630 self.get(GasId::cold_storage_cost())
631 }
632
633 #[inline]
635 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
636 if !is_spurious_dragon || transfers_value {
640 return self.get(GasId::new_account_cost());
641 }
642 0
643 }
644
645 #[inline]
647 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
648 self.get(GasId::new_account_cost_for_selfdestruct())
649 }
650
651 #[inline]
653 pub fn warm_storage_read_cost(&self) -> u64 {
654 self.get(GasId::warm_storage_read_cost())
655 }
656
657 #[inline]
659 pub fn copy_cost(&self, len: usize) -> u64 {
660 self.copy_per_word_cost(num_words(len))
661 }
662
663 #[inline]
665 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
666 self.get(GasId::copy_per_word())
667 .saturating_mul(word_num as u64)
668 }
669
670 #[inline]
672 pub fn code_deposit_cost(&self, len: usize) -> u64 {
673 self.get(GasId::code_deposit_cost())
674 .saturating_mul(len as u64)
675 }
676
677 #[inline]
679 pub fn sstore_state_gas(&self, vals: &SStoreResult) -> u64 {
680 if vals.new_values_changes_present()
681 && vals.is_original_eq_present()
682 && vals.is_original_zero()
683 {
684 self.get(GasId::sstore_set_state_gas())
685 } else {
686 0
687 }
688 }
689
690 #[inline]
692 pub fn new_account_state_gas(&self) -> u64 {
693 self.get(GasId::new_account_state_gas())
694 }
695
696 #[inline]
698 pub fn code_deposit_state_gas(&self, len: usize) -> u64 {
699 self.get(GasId::code_deposit_state_gas())
700 .saturating_mul(len as u64)
701 }
702
703 #[inline]
705 pub fn create_state_gas(&self) -> u64 {
706 self.get(GasId::create_state_gas())
707 }
708
709 #[inline]
711 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
712 self.get(GasId::tx_eip7702_per_empty_account_cost())
713 }
714
715 #[inline]
721 pub fn tx_eip7702_auth_refund(&self) -> u64 {
722 self.get(GasId::tx_eip7702_auth_refund())
723 }
724
725 #[inline]
729 pub fn tx_eip7702_per_auth_state_gas(&self) -> u64 {
730 self.get(GasId::tx_eip7702_per_auth_state_gas())
731 }
732
733 #[inline]
745 pub fn split_eip7702_refund(&self, total_refund: u64) -> (u64, u64) {
746 let per_auth_refund = self.tx_eip7702_auth_refund();
747 let per_auth_state_gas = self.tx_eip7702_per_auth_state_gas();
748 if per_auth_state_gas > 0 && per_auth_refund > 0 && total_refund > 0 {
749 let state_refund_per_auth = core::cmp::min(per_auth_refund, per_auth_state_gas);
750 let num_refunded = total_refund / per_auth_refund;
751 let state_refund = num_refunded * state_refund_per_auth;
752 (state_refund, total_refund - state_refund)
753 } else {
754 (0, total_refund)
755 }
756 }
757
758 #[inline]
760 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
761 self.get(GasId::tx_token_non_zero_byte_multiplier())
762 }
763
764 #[inline]
766 pub fn tx_token_cost(&self) -> u64 {
767 self.get(GasId::tx_token_cost())
768 }
769
770 pub fn tx_floor_cost_per_token(&self) -> u64 {
772 self.get(GasId::tx_floor_cost_per_token())
773 }
774
775 #[inline]
779 pub fn tx_floor_cost(&self, tokens_in_calldata: u64) -> u64 {
780 self.tx_floor_cost_per_token() * tokens_in_calldata + self.tx_floor_cost_base_gas()
781 }
782
783 pub fn tx_floor_cost_base_gas(&self) -> u64 {
785 self.get(GasId::tx_floor_cost_base_gas())
786 }
787
788 pub fn tx_access_list_address_cost(&self) -> u64 {
790 self.get(GasId::tx_access_list_address_cost())
791 }
792
793 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
795 self.get(GasId::tx_access_list_storage_key_cost())
796 }
797
798 #[inline]
816 pub fn tx_access_list_cost(&self, accounts: u64, storages: u64) -> u64 {
817 accounts
818 .saturating_mul(self.tx_access_list_address_cost())
819 .saturating_add(storages.saturating_mul(self.tx_access_list_storage_key_cost()))
820 }
821
822 pub fn tx_base_stipend(&self) -> u64 {
824 self.get(GasId::tx_base_stipend())
825 }
826
827 #[inline]
831 pub fn tx_create_cost(&self) -> u64 {
832 self.get(GasId::tx_create_cost())
833 }
834
835 #[inline]
837 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
838 self.get(GasId::tx_initcode_cost())
839 .saturating_mul(num_words(len) as u64)
840 }
841
842 pub fn initial_tx_gas(
857 &self,
858 input: &[u8],
859 is_create: bool,
860 access_list_accounts: u64,
861 access_list_storages: u64,
862 authorization_list_num: u64,
863 ) -> InitialAndFloorGas {
864 let mut gas = InitialAndFloorGas::default();
865
866 let tokens_in_calldata =
868 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
869
870 let auth_total_cost = authorization_list_num * self.tx_eip7702_per_empty_account_cost();
874 let auth_state_gas = authorization_list_num * self.tx_eip7702_per_auth_state_gas();
875 let auth_regular_cost = auth_total_cost - auth_state_gas;
876
877 gas.initial_total_gas += tokens_in_calldata * self.tx_token_cost()
878 + access_list_accounts * self.tx_access_list_address_cost()
880 + access_list_storages * self.tx_access_list_storage_key_cost()
882 + self.tx_base_stipend()
883 + auth_regular_cost;
885
886 gas.initial_state_gas += auth_state_gas;
889
890 if is_create {
891 gas.initial_total_gas += self.tx_create_cost();
893
894 gas.initial_total_gas += self.tx_initcode_cost(input.len());
896
897 gas.initial_state_gas += self.create_state_gas();
900 }
901
902 gas.floor_gas = self.tx_floor_cost(tokens_in_calldata);
904
905 gas.initial_total_gas += gas.initial_state_gas;
908
909 gas
910 }
911}
912
913#[inline]
914pub(crate) fn log2floor(value: U256) -> u64 {
915 for i in (0..4).rev() {
916 let limb = value.as_limbs()[i];
917 if limb != 0 {
918 return i as u64 * 64 + 63 - limb.leading_zeros() as u64;
919 }
920 }
921 0
922}
923
924#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
926pub struct GasId(u8);
927
928impl GasId {
929 pub const fn new(id: u8) -> Self {
931 Self(id)
932 }
933
934 pub const fn as_u8(&self) -> u8 {
936 self.0
937 }
938
939 pub const fn as_usize(&self) -> usize {
941 self.0 as usize
942 }
943
944 pub const fn name(&self) -> &'static str {
956 match self.0 {
957 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
958 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
959 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
960 x if x == Self::logdata().as_u8() => "logdata",
961 x if x == Self::logtopic().as_u8() => "logtopic",
962 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
963 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
964 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
965 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
966 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
967 x if x == Self::create().as_u8() => "create",
968 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
969 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
970 x if x == Self::cold_account_additional_cost().as_u8() => {
971 "cold_account_additional_cost"
972 }
973 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
974 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
975 x if x == Self::sstore_static().as_u8() => "sstore_static",
976 x if x == Self::sstore_set_without_load_cost().as_u8() => {
977 "sstore_set_without_load_cost"
978 }
979 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
980 "sstore_reset_without_cold_load_cost"
981 }
982 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
983 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
984 x if x == Self::call_stipend().as_u8() => "call_stipend",
985 x if x == Self::cold_storage_additional_cost().as_u8() => {
986 "cold_storage_additional_cost"
987 }
988 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
989 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
990 "new_account_cost_for_selfdestruct"
991 }
992 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
993 x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => {
994 "tx_eip7702_per_empty_account_cost"
995 }
996 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
997 "tx_token_non_zero_byte_multiplier"
998 }
999 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
1000 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
1001 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
1002 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
1003 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
1004 "tx_access_list_storage_key_cost"
1005 }
1006 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
1007 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
1008 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
1009 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
1010 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
1011 x if x == Self::tx_eip7702_auth_refund().as_u8() => "tx_eip7702_auth_refund",
1012 x if x == Self::sstore_set_state_gas().as_u8() => "sstore_set_state_gas",
1013 x if x == Self::new_account_state_gas().as_u8() => "new_account_state_gas",
1014 x if x == Self::code_deposit_state_gas().as_u8() => "code_deposit_state_gas",
1015 x if x == Self::create_state_gas().as_u8() => "create_state_gas",
1016 x if x == Self::tx_eip7702_per_auth_state_gas().as_u8() => {
1017 "tx_eip7702_per_auth_state_gas"
1018 }
1019 _ => "unknown",
1020 }
1021 }
1022
1023 pub fn from_name(s: &str) -> Option<GasId> {
1037 match s {
1038 "exp_byte_gas" => Some(Self::exp_byte_gas()),
1039 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
1040 "copy_per_word" => Some(Self::copy_per_word()),
1041 "logdata" => Some(Self::logdata()),
1042 "logtopic" => Some(Self::logtopic()),
1043 "mcopy_per_word" => Some(Self::mcopy_per_word()),
1044 "keccak256_per_word" => Some(Self::keccak256_per_word()),
1045 "memory_linear_cost" => Some(Self::memory_linear_cost()),
1046 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
1047 "initcode_per_word" => Some(Self::initcode_per_word()),
1048 "create" => Some(Self::create()),
1049 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
1050 "transfer_value_cost" => Some(Self::transfer_value_cost()),
1051 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
1052 "new_account_cost" => Some(Self::new_account_cost()),
1053 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
1054 "sstore_static" => Some(Self::sstore_static()),
1055 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
1056 "sstore_reset_without_cold_load_cost" => {
1057 Some(Self::sstore_reset_without_cold_load_cost())
1058 }
1059 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
1060 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
1061 "call_stipend" => Some(Self::call_stipend()),
1062 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
1063 "cold_storage_cost" => Some(Self::cold_storage_cost()),
1064 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
1065 "code_deposit_cost" => Some(Self::code_deposit_cost()),
1066 "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()),
1067 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
1068 "tx_token_cost" => Some(Self::tx_token_cost()),
1069 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
1070 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
1071 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
1072 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
1073 "tx_base_stipend" => Some(Self::tx_base_stipend()),
1074 "tx_create_cost" => Some(Self::tx_create_cost()),
1075 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
1076 "sstore_set_refund" => Some(Self::sstore_set_refund()),
1077 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
1078 "tx_eip7702_auth_refund" => Some(Self::tx_eip7702_auth_refund()),
1079 "sstore_set_state_gas" => Some(Self::sstore_set_state_gas()),
1080 "new_account_state_gas" => Some(Self::new_account_state_gas()),
1081 "code_deposit_state_gas" => Some(Self::code_deposit_state_gas()),
1082 "create_state_gas" => Some(Self::create_state_gas()),
1083 "tx_eip7702_per_auth_state_gas" => Some(Self::tx_eip7702_per_auth_state_gas()),
1084 _ => None,
1085 }
1086 }
1087
1088 pub const fn exp_byte_gas() -> GasId {
1090 Self::new(1)
1091 }
1092
1093 pub const fn extcodecopy_per_word() -> GasId {
1095 Self::new(2)
1096 }
1097
1098 pub const fn copy_per_word() -> GasId {
1100 Self::new(3)
1101 }
1102
1103 pub const fn logdata() -> GasId {
1105 Self::new(4)
1106 }
1107
1108 pub const fn logtopic() -> GasId {
1110 Self::new(5)
1111 }
1112
1113 pub const fn mcopy_per_word() -> GasId {
1115 Self::new(6)
1116 }
1117
1118 pub const fn keccak256_per_word() -> GasId {
1120 Self::new(7)
1121 }
1122
1123 pub const fn memory_linear_cost() -> GasId {
1125 Self::new(8)
1126 }
1127
1128 pub const fn memory_quadratic_reduction() -> GasId {
1130 Self::new(9)
1131 }
1132
1133 pub const fn initcode_per_word() -> GasId {
1135 Self::new(10)
1136 }
1137
1138 pub const fn create() -> GasId {
1140 Self::new(11)
1141 }
1142
1143 pub const fn call_stipend_reduction() -> GasId {
1145 Self::new(12)
1146 }
1147
1148 pub const fn transfer_value_cost() -> GasId {
1150 Self::new(13)
1151 }
1152
1153 pub const fn cold_account_additional_cost() -> GasId {
1155 Self::new(14)
1156 }
1157
1158 pub const fn new_account_cost() -> GasId {
1160 Self::new(15)
1161 }
1162
1163 pub const fn warm_storage_read_cost() -> GasId {
1167 Self::new(16)
1168 }
1169
1170 pub const fn sstore_static() -> GasId {
1173 Self::new(17)
1174 }
1175
1176 pub const fn sstore_set_without_load_cost() -> GasId {
1178 Self::new(18)
1179 }
1180
1181 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1183 Self::new(19)
1184 }
1185
1186 pub const fn sstore_clearing_slot_refund() -> GasId {
1188 Self::new(20)
1189 }
1190
1191 pub const fn selfdestruct_refund() -> GasId {
1193 Self::new(21)
1194 }
1195
1196 pub const fn call_stipend() -> GasId {
1198 Self::new(22)
1199 }
1200
1201 pub const fn cold_storage_additional_cost() -> GasId {
1203 Self::new(23)
1204 }
1205
1206 pub const fn cold_storage_cost() -> GasId {
1208 Self::new(24)
1209 }
1210
1211 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1213 Self::new(25)
1214 }
1215
1216 pub const fn code_deposit_cost() -> GasId {
1218 Self::new(26)
1219 }
1220
1221 pub const fn tx_eip7702_per_empty_account_cost() -> GasId {
1223 Self::new(27)
1224 }
1225
1226 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1228 Self::new(28)
1229 }
1230
1231 pub const fn tx_token_cost() -> GasId {
1233 Self::new(29)
1234 }
1235
1236 pub const fn tx_floor_cost_per_token() -> GasId {
1238 Self::new(30)
1239 }
1240
1241 pub const fn tx_floor_cost_base_gas() -> GasId {
1243 Self::new(31)
1244 }
1245
1246 pub const fn tx_access_list_address_cost() -> GasId {
1248 Self::new(32)
1249 }
1250
1251 pub const fn tx_access_list_storage_key_cost() -> GasId {
1253 Self::new(33)
1254 }
1255
1256 pub const fn tx_base_stipend() -> GasId {
1258 Self::new(34)
1259 }
1260
1261 pub const fn tx_create_cost() -> GasId {
1263 Self::new(35)
1264 }
1265
1266 pub const fn tx_initcode_cost() -> GasId {
1268 Self::new(36)
1269 }
1270
1271 pub const fn sstore_set_refund() -> GasId {
1273 Self::new(37)
1274 }
1275
1276 pub const fn sstore_reset_refund() -> GasId {
1278 Self::new(38)
1279 }
1280
1281 pub const fn tx_eip7702_auth_refund() -> GasId {
1285 Self::new(39)
1286 }
1287
1288 pub const fn sstore_set_state_gas() -> GasId {
1290 Self::new(40)
1291 }
1292
1293 pub const fn new_account_state_gas() -> GasId {
1295 Self::new(41)
1296 }
1297
1298 pub const fn code_deposit_state_gas() -> GasId {
1300 Self::new(42)
1301 }
1302
1303 pub const fn create_state_gas() -> GasId {
1305 Self::new(43)
1306 }
1307
1308 pub const fn tx_eip7702_per_auth_state_gas() -> GasId {
1312 Self::new(44)
1313 }
1314}
1315
1316#[cfg(test)]
1317mod tests {
1318 use super::*;
1319 use std::collections::HashSet;
1320
1321 #[cfg(test)]
1322 mod log2floor_tests {
1323 use super::*;
1324
1325 #[test]
1326 fn test_log2floor_edge_cases() {
1327 assert_eq!(log2floor(U256::ZERO), 0);
1329
1330 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);
1344 assert_eq!(log2floor(U256::from(u64::MAX) + U256::from(1u64)), 64);
1345 assert_eq!(log2floor(U256::MAX), 255);
1346 }
1347 }
1348
1349 #[test]
1350 fn test_gas_id_name_and_from_str_coverage() {
1351 let mut unique_names = HashSet::new();
1352 let mut known_gas_ids = 0;
1353
1354 for i in 0..=255 {
1356 let gas_id = GasId::new(i);
1357 let name = gas_id.name();
1358
1359 if name != "unknown" {
1361 unique_names.insert(name);
1362 }
1363 }
1364
1365 for name in &unique_names {
1367 if let Some(gas_id) = GasId::from_name(name) {
1368 known_gas_ids += 1;
1369 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1371 }
1372 }
1373
1374 println!("Total unique named GasIds: {}", unique_names.len());
1375 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1376
1377 assert_eq!(
1379 unique_names.len(),
1380 known_gas_ids,
1381 "Not all unique names are resolvable via from_str"
1382 );
1383
1384 assert_eq!(
1386 unique_names.len(),
1387 44,
1388 "Expected 44 unique GasIds, found {}",
1389 unique_names.len()
1390 );
1391 }
1392
1393 #[test]
1394 fn test_tx_access_list_cost() {
1395 use crate::cfg::gas;
1396
1397 let gas_params = GasParams::new_spec(SpecId::BERLIN);
1399
1400 assert_eq!(gas_params.tx_access_list_cost(0, 0), 0);
1402
1403 assert_eq!(
1405 gas_params.tx_access_list_cost(1, 0),
1406 gas::ACCESS_LIST_ADDRESS
1407 );
1408
1409 assert_eq!(
1411 gas_params.tx_access_list_cost(0, 1),
1412 gas::ACCESS_LIST_STORAGE_KEY
1413 );
1414
1415 assert_eq!(
1417 gas_params.tx_access_list_cost(2, 5),
1418 2 * gas::ACCESS_LIST_ADDRESS + 5 * gas::ACCESS_LIST_STORAGE_KEY
1419 );
1420
1421 assert_eq!(
1423 gas_params.tx_access_list_cost(100, 200),
1424 100 * gas::ACCESS_LIST_ADDRESS + 200 * gas::ACCESS_LIST_STORAGE_KEY
1425 );
1426
1427 let gas_params_pre_berlin = GasParams::new_spec(SpecId::ISTANBUL);
1429 assert_eq!(gas_params_pre_berlin.tx_access_list_cost(10, 20), 0);
1430 }
1431
1432 #[test]
1433 fn test_initial_state_gas_for_create() {
1434 let gas_params = GasParams::new_spec(SpecId::AMSTERDAM);
1436
1437 let create_gas = gas_params.initial_tx_gas(b"", true, 0, 0, 0);
1439 let expected_state_gas = gas_params.create_state_gas();
1440
1441 assert_eq!(create_gas.initial_state_gas, expected_state_gas);
1442 assert_eq!(create_gas.initial_state_gas, 131488);
1443
1444 let create_cost = gas_params.tx_create_cost();
1446 let initcode_cost = gas_params.tx_initcode_cost(0);
1447 assert_eq!(
1448 create_gas.initial_total_gas,
1449 gas_params.tx_base_stipend() + create_cost + initcode_cost + expected_state_gas
1450 );
1451
1452 let call_gas = gas_params.initial_tx_gas(b"", false, 0, 0, 0);
1454 assert_eq!(call_gas.initial_state_gas, 0);
1455 assert_eq!(call_gas.initial_total_gas, gas_params.tx_base_stipend());
1457 }
1458}