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}
21
22impl PartialEq<GasParams> for GasParams {
23 fn eq(&self, other: &GasParams) -> bool {
24 self.table == other.table
25 }
26}
27
28impl Hash for GasParams {
29 fn hash<H: Hasher>(&self, hasher: &mut H) {
30 self.table.hash(hasher);
31 }
32}
33
34impl core::fmt::Debug for GasParams {
35 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36 write!(f, "GasParams {{ table: {:?} }}", self.table)
37 }
38}
39
40#[inline]
43pub const fn num_words(len: usize) -> usize {
44 len.div_ceil(32)
45}
46
47impl Eq for GasParams {}
48#[cfg(feature = "serde")]
49mod serde {
50 use super::{Arc, GasParams};
51 use std::vec::Vec;
52
53 #[derive(serde::Serialize, serde::Deserialize)]
54 struct GasParamsSerde {
55 table: Vec<u64>,
56 }
57
58 #[cfg(feature = "serde")]
59 impl serde::Serialize for GasParams {
60 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
61 where
62 S: serde::Serializer,
63 {
64 GasParamsSerde {
65 table: self.table.to_vec(),
66 }
67 .serialize(serializer)
68 }
69 }
70
71 impl<'de> serde::Deserialize<'de> for GasParams {
72 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
73 where
74 D: serde::Deserializer<'de>,
75 {
76 let table = GasParamsSerde::deserialize(deserializer)?;
77 if table.table.len() != 256 {
78 return Err(serde::de::Error::custom("Invalid gas params length"));
79 }
80 Ok(Self::new(Arc::new(table.table.try_into().unwrap())))
81 }
82 }
83}
84
85impl Default for GasParams {
86 #[inline]
87 fn default() -> Self {
88 Self::new_spec(SpecId::default())
89 }
90}
91
92impl GasParams {
93 #[inline]
95 pub const fn new(table: Arc<[u64; 256]>) -> Self {
96 Self { table }
97 }
98
99 pub fn override_gas(&mut self, values: impl IntoIterator<Item = (GasId, u64)>) {
115 let mut table = *self.table.clone();
116 for (id, value) in values.into_iter() {
117 table[id.as_usize()] = value;
118 }
119 *self = Self::new(Arc::new(table));
120 }
121
122 #[inline]
124 pub fn table(&self) -> &[u64; 256] {
125 &self.table
126 }
127
128 #[inline(never)]
130 pub fn new_spec(spec: SpecId) -> Self {
131 use SpecId::*;
132 let gas_params = match spec {
133 FRONTIER => {
134 static TABLE: OnceLock<GasParams> = OnceLock::new();
135 TABLE.get_or_init(|| Self::new_spec_inner(spec))
136 }
137 HOMESTEAD => {
139 static TABLE: OnceLock<GasParams> = OnceLock::new();
140 TABLE.get_or_init(|| Self::new_spec_inner(spec))
141 }
142 TANGERINE => {
144 static TABLE: OnceLock<GasParams> = OnceLock::new();
145 TABLE.get_or_init(|| Self::new_spec_inner(spec))
146 }
147 SPURIOUS_DRAGON | BYZANTIUM | PETERSBURG => {
149 static TABLE: OnceLock<GasParams> = OnceLock::new();
150 TABLE.get_or_init(|| Self::new_spec_inner(spec))
151 }
152 ISTANBUL => {
154 static TABLE: OnceLock<GasParams> = OnceLock::new();
155 TABLE.get_or_init(|| Self::new_spec_inner(spec))
156 }
157 BERLIN => {
159 static TABLE: OnceLock<GasParams> = OnceLock::new();
160 TABLE.get_or_init(|| Self::new_spec_inner(spec))
161 }
162 LONDON | MERGE => {
164 static TABLE: OnceLock<GasParams> = OnceLock::new();
165 TABLE.get_or_init(|| Self::new_spec_inner(spec))
166 }
167 SHANGHAI | CANCUN => {
169 static TABLE: OnceLock<GasParams> = OnceLock::new();
170 TABLE.get_or_init(|| Self::new_spec_inner(spec))
171 }
172 PRAGUE | OSAKA => {
174 static TABLE: OnceLock<GasParams> = OnceLock::new();
175 TABLE.get_or_init(|| Self::new_spec_inner(spec))
176 }
177 SpecId::AMSTERDAM => {
179 static TABLE: OnceLock<GasParams> = OnceLock::new();
180 TABLE.get_or_init(|| Self::new_spec_inner(spec))
181 }
182 };
183 gas_params.clone()
184 }
185
186 #[inline]
188 fn new_spec_inner(spec: SpecId) -> Self {
189 let mut table = [0; 256];
190
191 table[GasId::exp_byte_gas().as_usize()] = 10;
192 table[GasId::logdata().as_usize()] = gas::LOGDATA;
193 table[GasId::logtopic().as_usize()] = gas::LOGTOPIC;
194 table[GasId::copy_per_word().as_usize()] = gas::COPY;
195 table[GasId::extcodecopy_per_word().as_usize()] = gas::COPY;
196 table[GasId::mcopy_per_word().as_usize()] = gas::COPY;
197 table[GasId::keccak256_per_word().as_usize()] = gas::KECCAK256WORD;
198 table[GasId::memory_linear_cost().as_usize()] = gas::MEMORY;
199 table[GasId::memory_quadratic_reduction().as_usize()] = 512;
200 table[GasId::initcode_per_word().as_usize()] = gas::INITCODE_WORD_COST;
201 table[GasId::create().as_usize()] = gas::CREATE;
202 table[GasId::call_stipend_reduction().as_usize()] = 64;
203 table[GasId::transfer_value_cost().as_usize()] = gas::CALLVALUE;
204 table[GasId::cold_account_additional_cost().as_usize()] = 0;
205 table[GasId::new_account_cost().as_usize()] = gas::NEWACCOUNT;
206 table[GasId::warm_storage_read_cost().as_usize()] = 0;
207 table[GasId::sstore_static().as_usize()] = gas::SSTORE_RESET;
209 table[GasId::sstore_set_without_load_cost().as_usize()] =
211 gas::SSTORE_SET - gas::SSTORE_RESET;
212 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = 0;
214 table[GasId::sstore_set_refund().as_usize()] =
216 table[GasId::sstore_set_without_load_cost().as_usize()];
217 table[GasId::sstore_reset_refund().as_usize()] =
219 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
220 table[GasId::sstore_clearing_slot_refund().as_usize()] = 15000;
222 table[GasId::selfdestruct_refund().as_usize()] = 24000;
223 table[GasId::call_stipend().as_usize()] = gas::CALL_STIPEND;
224 table[GasId::cold_storage_additional_cost().as_usize()] = 0;
225 table[GasId::cold_storage_cost().as_usize()] = 0;
226 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
227 table[GasId::code_deposit_cost().as_usize()] = gas::CODEDEPOSIT;
228 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
229 gas::NON_ZERO_BYTE_MULTIPLIER;
230 table[GasId::tx_token_cost().as_usize()] = gas::STANDARD_TOKEN_COST;
231 table[GasId::tx_base_stipend().as_usize()] = 21000;
232
233 if spec.is_enabled_in(SpecId::HOMESTEAD) {
234 table[GasId::tx_create_cost().as_usize()] = gas::CREATE;
235 }
236
237 if spec.is_enabled_in(SpecId::TANGERINE) {
238 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = gas::NEWACCOUNT;
239 }
240
241 if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
242 table[GasId::exp_byte_gas().as_usize()] = 50;
243 }
244
245 if spec.is_enabled_in(SpecId::ISTANBUL) {
246 table[GasId::sstore_static().as_usize()] = gas::ISTANBUL_SLOAD_GAS;
247 table[GasId::sstore_set_without_load_cost().as_usize()] =
248 gas::SSTORE_SET - gas::ISTANBUL_SLOAD_GAS;
249 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
250 gas::SSTORE_RESET - gas::ISTANBUL_SLOAD_GAS;
251 table[GasId::sstore_set_refund().as_usize()] =
252 table[GasId::sstore_set_without_load_cost().as_usize()];
253 table[GasId::sstore_reset_refund().as_usize()] =
254 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
255 table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] =
256 gas::NON_ZERO_BYTE_MULTIPLIER_ISTANBUL;
257 }
258
259 if spec.is_enabled_in(SpecId::BERLIN) {
260 table[GasId::sstore_static().as_usize()] = gas::WARM_STORAGE_READ_COST;
261 table[GasId::cold_account_additional_cost().as_usize()] =
262 gas::COLD_ACCOUNT_ACCESS_COST_ADDITIONAL;
263 table[GasId::cold_storage_additional_cost().as_usize()] =
264 gas::COLD_SLOAD_COST - gas::WARM_STORAGE_READ_COST;
265 table[GasId::cold_storage_cost().as_usize()] = gas::COLD_SLOAD_COST;
266 table[GasId::warm_storage_read_cost().as_usize()] = gas::WARM_STORAGE_READ_COST;
267
268 table[GasId::sstore_reset_without_cold_load_cost().as_usize()] =
269 gas::WARM_SSTORE_RESET - gas::WARM_STORAGE_READ_COST;
270 table[GasId::sstore_set_without_load_cost().as_usize()] =
271 gas::SSTORE_SET - gas::WARM_STORAGE_READ_COST;
272 table[GasId::sstore_set_refund().as_usize()] =
273 table[GasId::sstore_set_without_load_cost().as_usize()];
274 table[GasId::sstore_reset_refund().as_usize()] =
275 table[GasId::sstore_reset_without_cold_load_cost().as_usize()];
276
277 table[GasId::tx_access_list_address_cost().as_usize()] = gas::ACCESS_LIST_ADDRESS;
278 table[GasId::tx_access_list_storage_key_cost().as_usize()] =
279 gas::ACCESS_LIST_STORAGE_KEY;
280 }
281
282 if spec.is_enabled_in(SpecId::LONDON) {
283 table[GasId::sstore_clearing_slot_refund().as_usize()] =
288 gas::WARM_SSTORE_RESET + gas::ACCESS_LIST_STORAGE_KEY;
289
290 table[GasId::selfdestruct_refund().as_usize()] = 0;
291 }
292
293 if spec.is_enabled_in(SpecId::SHANGHAI) {
294 table[GasId::tx_initcode_cost().as_usize()] = gas::INITCODE_WORD_COST;
295 }
296
297 if spec.is_enabled_in(SpecId::PRAGUE) {
298 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] =
299 eip7702::PER_EMPTY_ACCOUNT_COST;
300
301 table[GasId::tx_eip7702_auth_refund().as_usize()] =
303 eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST;
304
305 table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN;
306 table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000;
307 }
308
309 if spec.is_enabled_in(SpecId::AMSTERDAM) {
311 const CPSB: u64 = 1174;
313
314 table[GasId::create().as_usize()] = 9000;
316 table[GasId::tx_create_cost().as_usize()] = 9000;
317 table[GasId::code_deposit_cost().as_usize()] = 0;
318 table[GasId::new_account_cost().as_usize()] = 0;
319 table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0;
320 table[GasId::sstore_set_without_load_cost().as_usize()] = 2800;
323
324 table[GasId::sstore_set_state_gas().as_usize()] = 32 * CPSB;
326 table[GasId::new_account_state_gas().as_usize()] = 112 * CPSB;
327 table[GasId::code_deposit_state_gas().as_usize()] = CPSB;
328 table[GasId::create_state_gas().as_usize()] = 112 * CPSB;
329
330 table[GasId::sstore_set_refund().as_usize()] = 32 * CPSB + 2800;
332
333 table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] = 7500 + (112 + 23) * CPSB;
338 table[GasId::tx_eip7702_auth_refund().as_usize()] = 112 * CPSB;
340
341 table[GasId::tx_eip7702_per_auth_state_gas().as_usize()] = (112 + 23) * CPSB;
343 }
344
345 Self::new(Arc::new(table))
346 }
347
348 #[inline]
350 pub fn get(&self, id: GasId) -> u64 {
351 self.table[id.as_usize()]
352 }
353
354 #[inline]
356 pub fn exp_cost(&self, power: U256) -> u64 {
357 if power.is_zero() {
358 return 0;
359 }
360 self.get(GasId::exp_byte_gas())
362 .saturating_mul(log2floor(power) / 8 + 1)
363 }
364
365 #[inline]
367 pub fn selfdestruct_refund(&self) -> i64 {
368 self.get(GasId::selfdestruct_refund()) as i64
369 }
370
371 #[inline]
374 pub fn selfdestruct_cold_cost(&self) -> u64 {
375 self.cold_account_additional_cost() + self.warm_storage_read_cost()
376 }
377
378 #[inline]
380 pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 {
381 let mut gas = 0;
382
383 if should_charge_topup {
385 gas += self.new_account_cost_for_selfdestruct();
386 }
387
388 if is_cold {
389 gas += self.selfdestruct_cold_cost();
395 }
396 gas
397 }
398
399 #[inline]
401 pub fn extcodecopy(&self, len: usize) -> u64 {
402 self.get(GasId::extcodecopy_per_word())
403 .saturating_mul(num_words(len) as u64)
404 }
405
406 #[inline]
408 pub fn mcopy_cost(&self, len: usize) -> u64 {
409 self.get(GasId::mcopy_per_word())
410 .saturating_mul(num_words(len) as u64)
411 }
412
413 #[inline]
415 pub fn sstore_static_gas(&self) -> u64 {
416 self.get(GasId::sstore_static())
417 }
418
419 #[inline]
421 pub fn sstore_set_without_load_cost(&self) -> u64 {
422 self.get(GasId::sstore_set_without_load_cost())
423 }
424
425 #[inline]
427 pub fn sstore_reset_without_cold_load_cost(&self) -> u64 {
428 self.get(GasId::sstore_reset_without_cold_load_cost())
429 }
430
431 #[inline]
433 pub fn sstore_clearing_slot_refund(&self) -> u64 {
434 self.get(GasId::sstore_clearing_slot_refund())
435 }
436
437 #[inline]
439 pub fn sstore_set_refund(&self) -> u64 {
440 self.get(GasId::sstore_set_refund())
441 }
442
443 #[inline]
445 pub fn sstore_reset_refund(&self) -> u64 {
446 self.get(GasId::sstore_reset_refund())
447 }
448
449 #[inline]
453 pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 {
454 if !is_istanbul {
457 if vals.is_present_zero() && !vals.is_new_zero() {
458 return self.sstore_set_without_load_cost();
459 } else {
460 return self.sstore_reset_without_cold_load_cost();
461 }
462 }
463
464 let mut gas = 0;
465
466 if is_cold {
468 gas += self.cold_storage_cost();
469 }
470
471 if vals.new_values_changes_present() && vals.is_original_eq_present() {
473 gas += if vals.is_original_zero() {
474 self.sstore_set_without_load_cost()
477 } else {
478 self.sstore_reset_without_cold_load_cost()
480 };
481 }
482 gas
483 }
484
485 #[inline]
487 pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 {
488 let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64;
490
491 if !is_istanbul {
492 if !vals.is_present_zero() && vals.is_new_zero() {
494 return sstore_clearing_slot_refund;
495 }
496 return 0;
497 }
498
499 if vals.is_new_eq_present() {
501 return 0;
502 }
503
504 if vals.is_original_eq_present() && vals.is_new_zero() {
507 return sstore_clearing_slot_refund;
508 }
509
510 let mut refund = 0;
511 if !vals.is_original_zero() {
513 if vals.is_present_zero() {
515 refund -= sstore_clearing_slot_refund;
517 } else if vals.is_new_zero() {
519 refund += sstore_clearing_slot_refund;
521 }
522 }
523
524 if vals.is_original_eq_new() {
526 if vals.is_original_zero() {
528 refund += self.sstore_set_refund() as i64;
530 } else {
532 refund += self.sstore_reset_refund() as i64;
534 }
535 }
536 refund
537 }
538
539 #[inline]
541 pub fn log_cost(&self, n: u8, len: u64) -> u64 {
542 self.get(GasId::logdata())
543 .saturating_mul(len)
544 .saturating_add(self.get(GasId::logtopic()) * n as u64)
545 }
546
547 #[inline]
549 pub fn keccak256_cost(&self, len: usize) -> u64 {
550 self.get(GasId::keccak256_per_word())
551 .saturating_mul(num_words(len) as u64)
552 }
553
554 #[inline]
556 pub fn memory_cost(&self, len: usize) -> u64 {
557 let len = len as u64;
558 self.get(GasId::memory_linear_cost())
559 .saturating_mul(len)
560 .saturating_add(
561 (len.saturating_mul(len))
562 .saturating_div(self.get(GasId::memory_quadratic_reduction())),
563 )
564 }
565
566 #[inline]
568 pub fn initcode_cost(&self, len: usize) -> u64 {
569 self.get(GasId::initcode_per_word())
570 .saturating_mul(num_words(len) as u64)
571 }
572
573 #[inline]
575 pub fn create_cost(&self) -> u64 {
576 self.get(GasId::create())
577 }
578
579 #[inline]
581 pub fn create2_cost(&self, len: usize) -> u64 {
582 self.get(GasId::create()).saturating_add(
583 self.get(GasId::keccak256_per_word())
584 .saturating_mul(num_words(len) as u64),
585 )
586 }
587
588 #[inline]
590 pub fn call_stipend(&self) -> u64 {
591 self.get(GasId::call_stipend())
592 }
593
594 #[inline]
596 pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 {
597 gas_limit - gas_limit / self.get(GasId::call_stipend_reduction())
598 }
599
600 #[inline]
602 pub fn transfer_value_cost(&self) -> u64 {
603 self.get(GasId::transfer_value_cost())
604 }
605
606 #[inline]
608 pub fn cold_account_additional_cost(&self) -> u64 {
609 self.get(GasId::cold_account_additional_cost())
610 }
611
612 #[inline]
614 pub fn cold_storage_additional_cost(&self) -> u64 {
615 self.get(GasId::cold_storage_additional_cost())
616 }
617
618 #[inline]
620 pub fn cold_storage_cost(&self) -> u64 {
621 self.get(GasId::cold_storage_cost())
622 }
623
624 #[inline]
626 pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 {
627 if !is_spurious_dragon || transfers_value {
631 return self.get(GasId::new_account_cost());
632 }
633 0
634 }
635
636 #[inline]
638 pub fn new_account_cost_for_selfdestruct(&self) -> u64 {
639 self.get(GasId::new_account_cost_for_selfdestruct())
640 }
641
642 #[inline]
644 pub fn warm_storage_read_cost(&self) -> u64 {
645 self.get(GasId::warm_storage_read_cost())
646 }
647
648 #[inline]
650 pub fn copy_cost(&self, len: usize) -> u64 {
651 self.copy_per_word_cost(num_words(len))
652 }
653
654 #[inline]
656 pub fn copy_per_word_cost(&self, word_num: usize) -> u64 {
657 self.get(GasId::copy_per_word())
658 .saturating_mul(word_num as u64)
659 }
660
661 #[inline]
663 pub fn code_deposit_cost(&self, len: usize) -> u64 {
664 self.get(GasId::code_deposit_cost())
665 .saturating_mul(len as u64)
666 }
667
668 #[inline]
670 pub fn sstore_state_gas(&self, vals: &SStoreResult) -> u64 {
671 if vals.new_values_changes_present()
672 && vals.is_original_eq_present()
673 && vals.is_original_zero()
674 {
675 self.get(GasId::sstore_set_state_gas())
676 } else {
677 0
678 }
679 }
680
681 #[inline]
683 pub fn new_account_state_gas(&self) -> u64 {
684 self.get(GasId::new_account_state_gas())
685 }
686
687 #[inline]
689 pub fn code_deposit_state_gas(&self, len: usize) -> u64 {
690 self.get(GasId::code_deposit_state_gas())
691 .saturating_mul(len as u64)
692 }
693
694 #[inline]
696 pub fn create_state_gas(&self) -> u64 {
697 self.get(GasId::create_state_gas())
698 }
699
700 #[inline]
702 pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 {
703 self.get(GasId::tx_eip7702_per_empty_account_cost())
704 }
705
706 #[inline]
712 pub fn tx_eip7702_auth_refund(&self) -> u64 {
713 self.get(GasId::tx_eip7702_auth_refund())
714 }
715
716 #[inline]
720 pub fn tx_eip7702_per_auth_state_gas(&self) -> u64 {
721 self.get(GasId::tx_eip7702_per_auth_state_gas())
722 }
723
724 #[inline]
736 pub fn split_eip7702_refund(&self, total_refund: u64) -> (u64, u64) {
737 let per_auth_refund = self.tx_eip7702_auth_refund();
738 let per_auth_state_gas = self.tx_eip7702_per_auth_state_gas();
739 if per_auth_state_gas > 0 && per_auth_refund > 0 && total_refund > 0 {
740 let state_refund_per_auth = core::cmp::min(per_auth_refund, per_auth_state_gas);
741 let num_refunded = total_refund / per_auth_refund;
742 let state_refund = num_refunded * state_refund_per_auth;
743 (state_refund, total_refund - state_refund)
744 } else {
745 (0, total_refund)
746 }
747 }
748
749 #[inline]
751 pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 {
752 self.get(GasId::tx_token_non_zero_byte_multiplier())
753 }
754
755 #[inline]
757 pub fn tx_token_cost(&self) -> u64 {
758 self.get(GasId::tx_token_cost())
759 }
760
761 pub fn tx_floor_cost_per_token(&self) -> u64 {
763 self.get(GasId::tx_floor_cost_per_token())
764 }
765
766 #[inline]
770 pub fn tx_floor_cost(&self, tokens_in_calldata: u64) -> u64 {
771 self.tx_floor_cost_per_token() * tokens_in_calldata + self.tx_floor_cost_base_gas()
772 }
773
774 pub fn tx_floor_cost_base_gas(&self) -> u64 {
776 self.get(GasId::tx_floor_cost_base_gas())
777 }
778
779 pub fn tx_access_list_address_cost(&self) -> u64 {
781 self.get(GasId::tx_access_list_address_cost())
782 }
783
784 pub fn tx_access_list_storage_key_cost(&self) -> u64 {
786 self.get(GasId::tx_access_list_storage_key_cost())
787 }
788
789 #[inline]
807 pub fn tx_access_list_cost(&self, accounts: u64, storages: u64) -> u64 {
808 accounts
809 .saturating_mul(self.tx_access_list_address_cost())
810 .saturating_add(storages.saturating_mul(self.tx_access_list_storage_key_cost()))
811 }
812
813 pub fn tx_base_stipend(&self) -> u64 {
815 self.get(GasId::tx_base_stipend())
816 }
817
818 #[inline]
822 pub fn tx_create_cost(&self) -> u64 {
823 self.get(GasId::tx_create_cost())
824 }
825
826 #[inline]
828 pub fn tx_initcode_cost(&self, len: usize) -> u64 {
829 self.get(GasId::tx_initcode_cost())
830 .saturating_mul(num_words(len) as u64)
831 }
832
833 pub fn initial_tx_gas(
848 &self,
849 input: &[u8],
850 is_create: bool,
851 access_list_accounts: u64,
852 access_list_storages: u64,
853 authorization_list_num: u64,
854 ) -> InitialAndFloorGas {
855 let mut gas = InitialAndFloorGas::default();
856
857 let tokens_in_calldata =
859 get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier());
860
861 let auth_total_cost = authorization_list_num * self.tx_eip7702_per_empty_account_cost();
865 let auth_state_gas = authorization_list_num * self.tx_eip7702_per_auth_state_gas();
866 let auth_regular_cost = auth_total_cost - auth_state_gas;
867
868 gas.initial_total_gas += tokens_in_calldata * self.tx_token_cost()
869 + access_list_accounts * self.tx_access_list_address_cost()
871 + access_list_storages * self.tx_access_list_storage_key_cost()
873 + self.tx_base_stipend()
874 + auth_regular_cost;
876
877 gas.initial_state_gas += auth_state_gas;
880
881 if is_create {
882 gas.initial_total_gas += self.tx_create_cost();
884
885 gas.initial_total_gas += self.tx_initcode_cost(input.len());
887
888 gas.initial_state_gas += self.create_state_gas();
891 }
892
893 gas.floor_gas = self.tx_floor_cost(tokens_in_calldata);
895
896 gas.initial_total_gas += gas.initial_state_gas;
899
900 gas
901 }
902}
903
904#[inline]
905pub(crate) const fn log2floor(value: U256) -> u64 {
906 255u64.saturating_sub(value.leading_zeros() as u64)
907}
908
909#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
911pub struct GasId(u8);
912
913impl GasId {
914 #[inline]
916 pub const fn new(id: u8) -> Self {
917 Self(id)
918 }
919
920 #[inline]
922 pub const fn as_u8(&self) -> u8 {
923 self.0
924 }
925
926 #[inline]
928 pub const fn as_usize(&self) -> usize {
929 self.0 as usize
930 }
931
932 pub const fn name(&self) -> &'static str {
944 match self.0 {
945 x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas",
946 x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word",
947 x if x == Self::copy_per_word().as_u8() => "copy_per_word",
948 x if x == Self::logdata().as_u8() => "logdata",
949 x if x == Self::logtopic().as_u8() => "logtopic",
950 x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word",
951 x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word",
952 x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost",
953 x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction",
954 x if x == Self::initcode_per_word().as_u8() => "initcode_per_word",
955 x if x == Self::create().as_u8() => "create",
956 x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction",
957 x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost",
958 x if x == Self::cold_account_additional_cost().as_u8() => {
959 "cold_account_additional_cost"
960 }
961 x if x == Self::new_account_cost().as_u8() => "new_account_cost",
962 x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost",
963 x if x == Self::sstore_static().as_u8() => "sstore_static",
964 x if x == Self::sstore_set_without_load_cost().as_u8() => {
965 "sstore_set_without_load_cost"
966 }
967 x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => {
968 "sstore_reset_without_cold_load_cost"
969 }
970 x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund",
971 x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund",
972 x if x == Self::call_stipend().as_u8() => "call_stipend",
973 x if x == Self::cold_storage_additional_cost().as_u8() => {
974 "cold_storage_additional_cost"
975 }
976 x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost",
977 x if x == Self::new_account_cost_for_selfdestruct().as_u8() => {
978 "new_account_cost_for_selfdestruct"
979 }
980 x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost",
981 x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => {
982 "tx_eip7702_per_empty_account_cost"
983 }
984 x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => {
985 "tx_token_non_zero_byte_multiplier"
986 }
987 x if x == Self::tx_token_cost().as_u8() => "tx_token_cost",
988 x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token",
989 x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas",
990 x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost",
991 x if x == Self::tx_access_list_storage_key_cost().as_u8() => {
992 "tx_access_list_storage_key_cost"
993 }
994 x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend",
995 x if x == Self::tx_create_cost().as_u8() => "tx_create_cost",
996 x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost",
997 x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund",
998 x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund",
999 x if x == Self::tx_eip7702_auth_refund().as_u8() => "tx_eip7702_auth_refund",
1000 x if x == Self::sstore_set_state_gas().as_u8() => "sstore_set_state_gas",
1001 x if x == Self::new_account_state_gas().as_u8() => "new_account_state_gas",
1002 x if x == Self::code_deposit_state_gas().as_u8() => "code_deposit_state_gas",
1003 x if x == Self::create_state_gas().as_u8() => "create_state_gas",
1004 x if x == Self::tx_eip7702_per_auth_state_gas().as_u8() => {
1005 "tx_eip7702_per_auth_state_gas"
1006 }
1007 _ => "unknown",
1008 }
1009 }
1010
1011 pub fn from_name(s: &str) -> Option<GasId> {
1025 match s {
1026 "exp_byte_gas" => Some(Self::exp_byte_gas()),
1027 "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()),
1028 "copy_per_word" => Some(Self::copy_per_word()),
1029 "logdata" => Some(Self::logdata()),
1030 "logtopic" => Some(Self::logtopic()),
1031 "mcopy_per_word" => Some(Self::mcopy_per_word()),
1032 "keccak256_per_word" => Some(Self::keccak256_per_word()),
1033 "memory_linear_cost" => Some(Self::memory_linear_cost()),
1034 "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()),
1035 "initcode_per_word" => Some(Self::initcode_per_word()),
1036 "create" => Some(Self::create()),
1037 "call_stipend_reduction" => Some(Self::call_stipend_reduction()),
1038 "transfer_value_cost" => Some(Self::transfer_value_cost()),
1039 "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()),
1040 "new_account_cost" => Some(Self::new_account_cost()),
1041 "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()),
1042 "sstore_static" => Some(Self::sstore_static()),
1043 "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()),
1044 "sstore_reset_without_cold_load_cost" => {
1045 Some(Self::sstore_reset_without_cold_load_cost())
1046 }
1047 "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()),
1048 "selfdestruct_refund" => Some(Self::selfdestruct_refund()),
1049 "call_stipend" => Some(Self::call_stipend()),
1050 "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()),
1051 "cold_storage_cost" => Some(Self::cold_storage_cost()),
1052 "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()),
1053 "code_deposit_cost" => Some(Self::code_deposit_cost()),
1054 "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()),
1055 "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()),
1056 "tx_token_cost" => Some(Self::tx_token_cost()),
1057 "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()),
1058 "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()),
1059 "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()),
1060 "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()),
1061 "tx_base_stipend" => Some(Self::tx_base_stipend()),
1062 "tx_create_cost" => Some(Self::tx_create_cost()),
1063 "tx_initcode_cost" => Some(Self::tx_initcode_cost()),
1064 "sstore_set_refund" => Some(Self::sstore_set_refund()),
1065 "sstore_reset_refund" => Some(Self::sstore_reset_refund()),
1066 "tx_eip7702_auth_refund" => Some(Self::tx_eip7702_auth_refund()),
1067 "sstore_set_state_gas" => Some(Self::sstore_set_state_gas()),
1068 "new_account_state_gas" => Some(Self::new_account_state_gas()),
1069 "code_deposit_state_gas" => Some(Self::code_deposit_state_gas()),
1070 "create_state_gas" => Some(Self::create_state_gas()),
1071 "tx_eip7702_per_auth_state_gas" => Some(Self::tx_eip7702_per_auth_state_gas()),
1072 _ => None,
1073 }
1074 }
1075
1076 pub const fn exp_byte_gas() -> GasId {
1078 Self::new(1)
1079 }
1080
1081 pub const fn extcodecopy_per_word() -> GasId {
1083 Self::new(2)
1084 }
1085
1086 pub const fn copy_per_word() -> GasId {
1088 Self::new(3)
1089 }
1090
1091 pub const fn logdata() -> GasId {
1093 Self::new(4)
1094 }
1095
1096 pub const fn logtopic() -> GasId {
1098 Self::new(5)
1099 }
1100
1101 pub const fn mcopy_per_word() -> GasId {
1103 Self::new(6)
1104 }
1105
1106 pub const fn keccak256_per_word() -> GasId {
1108 Self::new(7)
1109 }
1110
1111 pub const fn memory_linear_cost() -> GasId {
1113 Self::new(8)
1114 }
1115
1116 pub const fn memory_quadratic_reduction() -> GasId {
1118 Self::new(9)
1119 }
1120
1121 pub const fn initcode_per_word() -> GasId {
1123 Self::new(10)
1124 }
1125
1126 pub const fn create() -> GasId {
1128 Self::new(11)
1129 }
1130
1131 pub const fn call_stipend_reduction() -> GasId {
1133 Self::new(12)
1134 }
1135
1136 pub const fn transfer_value_cost() -> GasId {
1138 Self::new(13)
1139 }
1140
1141 pub const fn cold_account_additional_cost() -> GasId {
1143 Self::new(14)
1144 }
1145
1146 pub const fn new_account_cost() -> GasId {
1148 Self::new(15)
1149 }
1150
1151 pub const fn warm_storage_read_cost() -> GasId {
1155 Self::new(16)
1156 }
1157
1158 pub const fn sstore_static() -> GasId {
1161 Self::new(17)
1162 }
1163
1164 pub const fn sstore_set_without_load_cost() -> GasId {
1166 Self::new(18)
1167 }
1168
1169 pub const fn sstore_reset_without_cold_load_cost() -> GasId {
1171 Self::new(19)
1172 }
1173
1174 pub const fn sstore_clearing_slot_refund() -> GasId {
1176 Self::new(20)
1177 }
1178
1179 pub const fn selfdestruct_refund() -> GasId {
1181 Self::new(21)
1182 }
1183
1184 pub const fn call_stipend() -> GasId {
1186 Self::new(22)
1187 }
1188
1189 pub const fn cold_storage_additional_cost() -> GasId {
1191 Self::new(23)
1192 }
1193
1194 pub const fn cold_storage_cost() -> GasId {
1196 Self::new(24)
1197 }
1198
1199 pub const fn new_account_cost_for_selfdestruct() -> GasId {
1201 Self::new(25)
1202 }
1203
1204 pub const fn code_deposit_cost() -> GasId {
1206 Self::new(26)
1207 }
1208
1209 pub const fn tx_eip7702_per_empty_account_cost() -> GasId {
1211 Self::new(27)
1212 }
1213
1214 pub const fn tx_token_non_zero_byte_multiplier() -> GasId {
1216 Self::new(28)
1217 }
1218
1219 pub const fn tx_token_cost() -> GasId {
1221 Self::new(29)
1222 }
1223
1224 pub const fn tx_floor_cost_per_token() -> GasId {
1226 Self::new(30)
1227 }
1228
1229 pub const fn tx_floor_cost_base_gas() -> GasId {
1231 Self::new(31)
1232 }
1233
1234 pub const fn tx_access_list_address_cost() -> GasId {
1236 Self::new(32)
1237 }
1238
1239 pub const fn tx_access_list_storage_key_cost() -> GasId {
1241 Self::new(33)
1242 }
1243
1244 pub const fn tx_base_stipend() -> GasId {
1246 Self::new(34)
1247 }
1248
1249 pub const fn tx_create_cost() -> GasId {
1251 Self::new(35)
1252 }
1253
1254 pub const fn tx_initcode_cost() -> GasId {
1256 Self::new(36)
1257 }
1258
1259 pub const fn sstore_set_refund() -> GasId {
1261 Self::new(37)
1262 }
1263
1264 pub const fn sstore_reset_refund() -> GasId {
1266 Self::new(38)
1267 }
1268
1269 pub const fn tx_eip7702_auth_refund() -> GasId {
1273 Self::new(39)
1274 }
1275
1276 pub const fn sstore_set_state_gas() -> GasId {
1278 Self::new(40)
1279 }
1280
1281 pub const fn new_account_state_gas() -> GasId {
1283 Self::new(41)
1284 }
1285
1286 pub const fn code_deposit_state_gas() -> GasId {
1288 Self::new(42)
1289 }
1290
1291 pub const fn create_state_gas() -> GasId {
1293 Self::new(43)
1294 }
1295
1296 pub const fn tx_eip7702_per_auth_state_gas() -> GasId {
1300 Self::new(44)
1301 }
1302}
1303
1304#[cfg(test)]
1305mod tests {
1306 use super::*;
1307 use std::collections::HashSet;
1308
1309 #[cfg(test)]
1310 mod log2floor_tests {
1311 use super::*;
1312
1313 #[test]
1314 fn test_log2floor_edge_cases() {
1315 assert_eq!(log2floor(U256::ZERO), 0);
1317
1318 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);
1332 assert_eq!(log2floor(U256::from(u64::MAX) + U256::from(1u64)), 64);
1333 assert_eq!(log2floor(U256::MAX), 255);
1334 }
1335 }
1336
1337 #[test]
1338 fn test_gas_id_name_and_from_str_coverage() {
1339 let mut unique_names = HashSet::new();
1340 let mut known_gas_ids = 0;
1341
1342 for i in 0..=255 {
1344 let gas_id = GasId::new(i);
1345 let name = gas_id.name();
1346
1347 if name != "unknown" {
1349 unique_names.insert(name);
1350 }
1351 }
1352
1353 for name in &unique_names {
1355 if let Some(gas_id) = GasId::from_name(name) {
1356 known_gas_ids += 1;
1357 assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name);
1359 }
1360 }
1361
1362 println!("Total unique named GasIds: {}", unique_names.len());
1363 println!("GasIds resolvable via from_str: {}", known_gas_ids);
1364
1365 assert_eq!(
1367 unique_names.len(),
1368 known_gas_ids,
1369 "Not all unique names are resolvable via from_str"
1370 );
1371
1372 assert_eq!(
1374 unique_names.len(),
1375 44,
1376 "Expected 44 unique GasIds, found {}",
1377 unique_names.len()
1378 );
1379 }
1380
1381 #[test]
1382 fn test_tx_access_list_cost() {
1383 use crate::cfg::gas;
1384
1385 let gas_params = GasParams::new_spec(SpecId::BERLIN);
1387
1388 assert_eq!(gas_params.tx_access_list_cost(0, 0), 0);
1390
1391 assert_eq!(
1393 gas_params.tx_access_list_cost(1, 0),
1394 gas::ACCESS_LIST_ADDRESS
1395 );
1396
1397 assert_eq!(
1399 gas_params.tx_access_list_cost(0, 1),
1400 gas::ACCESS_LIST_STORAGE_KEY
1401 );
1402
1403 assert_eq!(
1405 gas_params.tx_access_list_cost(2, 5),
1406 2 * gas::ACCESS_LIST_ADDRESS + 5 * gas::ACCESS_LIST_STORAGE_KEY
1407 );
1408
1409 assert_eq!(
1411 gas_params.tx_access_list_cost(100, 200),
1412 100 * gas::ACCESS_LIST_ADDRESS + 200 * gas::ACCESS_LIST_STORAGE_KEY
1413 );
1414
1415 let gas_params_pre_berlin = GasParams::new_spec(SpecId::ISTANBUL);
1417 assert_eq!(gas_params_pre_berlin.tx_access_list_cost(10, 20), 0);
1418 }
1419
1420 #[test]
1421 fn test_initial_state_gas_for_create() {
1422 let gas_params = GasParams::new_spec(SpecId::AMSTERDAM);
1424
1425 let create_gas = gas_params.initial_tx_gas(b"", true, 0, 0, 0);
1427 let expected_state_gas = gas_params.create_state_gas();
1428
1429 assert_eq!(create_gas.initial_state_gas, expected_state_gas);
1430 assert_eq!(create_gas.initial_state_gas, 131488);
1431
1432 let create_cost = gas_params.tx_create_cost();
1434 let initcode_cost = gas_params.tx_initcode_cost(0);
1435 assert_eq!(
1436 create_gas.initial_total_gas,
1437 gas_params.tx_base_stipend() + create_cost + initcode_cost + expected_state_gas
1438 );
1439
1440 let call_gas = gas_params.initial_tx_gas(b"", false, 0, 0, 0);
1442 assert_eq!(call_gas.initial_state_gas, 0);
1443 assert_eq!(call_gas.initial_total_gas, gas_params.tx_base_stipend());
1445 }
1446}