1use crate::{
2 call_frame::CallFrame,
3 constants::{TX_BASE_COST, TX_BASE_COST_AMSTERDAM, WORD_SIZE, WORD_SIZE_IN_BYTES_U64},
4 errors::{ExceptionalHalt, InternalError, PrecompileError, VMError},
5 memory,
6};
7use ExceptionalHalt::OutOfGas;
8use ethrex_common::{
10 Address, U256,
11 types::{Fork, TxKind, tx_fields::AccessList},
12};
13use malachite::base::num::logic::traits::*;
14use malachite::{Natural, base::num::basic::traits::Zero as _};
15
16pub const STOP: u64 = 0;
18pub const ADD: u64 = 3;
19pub const MUL: u64 = 5;
20pub const SUB: u64 = 3;
21pub const DIV: u64 = 5;
22pub const SDIV: u64 = 5;
23pub const MOD: u64 = 5;
24pub const SMOD: u64 = 5;
25pub const ADDMOD: u64 = 8;
26pub const MULMOD: u64 = 8;
27pub const EXP_STATIC: u64 = 10;
28pub const EXP_DYNAMIC_BASE: u64 = 50;
29pub const SIGNEXTEND: u64 = 5;
30pub const LT: u64 = 3;
31pub const GT: u64 = 3;
32pub const SLT: u64 = 3;
33pub const SGT: u64 = 3;
34pub const EQ: u64 = 3;
35pub const ISZERO: u64 = 3;
36pub const AND: u64 = 3;
37pub const OR: u64 = 3;
38pub const XOR: u64 = 3;
39pub const NOT: u64 = 3;
40pub const BYTE: u64 = 3;
41pub const SHL: u64 = 3;
42pub const SHR: u64 = 3;
43pub const SAR: u64 = 3;
44pub const KECCAK25_STATIC: u64 = 30;
45pub const KECCAK25_DYNAMIC_BASE: u64 = 6;
46pub const CALLDATALOAD: u64 = 3;
47pub const CALLDATASIZE: u64 = 2;
48pub const CALLDATACOPY_STATIC: u64 = 3;
49pub const CALLDATACOPY_DYNAMIC_BASE: u64 = 3;
50pub const RETURNDATASIZE: u64 = 2;
51pub const RETURNDATACOPY_STATIC: u64 = 3;
52pub const RETURNDATACOPY_DYNAMIC_BASE: u64 = 3;
53pub const ADDRESS: u64 = 2;
54pub const ORIGIN: u64 = 2;
55pub const CALLER: u64 = 2;
56pub const BLOCKHASH: u64 = 20;
57pub const COINBASE: u64 = 2;
58pub const TIMESTAMP: u64 = 2;
59pub const NUMBER: u64 = 2;
60pub const PREVRANDAO: u64 = 2;
61pub const GASLIMIT: u64 = 2;
62pub const CHAINID: u64 = 2;
63pub const SELFBALANCE: u64 = 5;
64pub const BASEFEE: u64 = 2;
65pub const BLOBHASH: u64 = 3;
66pub const BLOBBASEFEE: u64 = 2;
67pub const SLOTNUM: u64 = 2;
68pub const TXPARAM: u64 = 2;
70pub const FRAMEDATALOAD: u64 = 3;
71pub const FRAMEDATACOPY_STATIC: u64 = 3;
72pub const FRAMEDATACOPY_DYNAMIC_BASE: u64 = 3;
73pub const FRAMEPARAM: u64 = 2;
74pub const SIGPARAM: u64 = 2;
75
76pub fn framedatacopy(
77 new_memory_size: usize,
78 current_memory_size: usize,
79 size: usize,
80) -> Result<u64, VMError> {
81 copy_behavior(
82 new_memory_size,
83 current_memory_size,
84 size,
85 FRAMEDATACOPY_DYNAMIC_BASE,
86 FRAMEDATACOPY_STATIC,
87 )
88}
89pub const POP: u64 = 2;
90pub const MLOAD_STATIC: u64 = 3;
91pub const MSTORE_STATIC: u64 = 3;
92pub const MSTORE8_STATIC: u64 = 3;
93pub const JUMP: u64 = 8;
94pub const JUMPI: u64 = 10;
95pub const PC: u64 = 2;
96pub const MSIZE: u64 = 2;
97pub const GAS: u64 = 2;
98pub const JUMPDEST: u64 = 1;
99pub const TLOAD: u64 = 100;
100pub const TSTORE: u64 = 100;
101pub const MCOPY_STATIC: u64 = 3;
102pub const MCOPY_DYNAMIC_BASE: u64 = 3;
103pub const PUSH0: u64 = 2;
104pub const PUSHN: u64 = 3;
105pub const DUPN: u64 = 3;
106pub const SWAPN: u64 = 3;
107pub const EXCHANGE: u64 = 3;
108pub const LOGN_STATIC: u64 = 375;
109pub const LOGN_DYNAMIC_BASE: u64 = 375;
110pub const LOGN_DYNAMIC_BYTE_BASE: u64 = 8;
111pub const CALLVALUE: u64 = 2;
112pub const CODESIZE: u64 = 2;
113pub const CODECOPY_STATIC: u64 = 3;
114pub const CODECOPY_DYNAMIC_BASE: u64 = 3;
115pub const GASPRICE: u64 = 2;
116pub const CLZ: u64 = 5;
117
118pub const SELFDESTRUCT_STATIC: u64 = 5000;
119pub const SELFDESTRUCT_DYNAMIC: u64 = 25000;
120pub const SELFDESTRUCT_REFUND: u64 = 24000;
121
122pub const DEFAULT_STATIC: u64 = 0;
123pub const DEFAULT_COLD_DYNAMIC: u64 = 2600;
124pub const DEFAULT_WARM_DYNAMIC: u64 = 100;
125
126pub const SLOAD_STATIC: u64 = 0;
127pub const SLOAD_COLD_DYNAMIC: u64 = 2100;
128pub const SLOAD_WARM_DYNAMIC: u64 = 100;
129
130pub const SSTORE_STATIC: u64 = 0;
131pub const SSTORE_COLD_DYNAMIC: u64 = 2100;
132pub const SSTORE_DEFAULT_DYNAMIC: u64 = 100;
133pub const SSTORE_STORAGE_CREATION: u64 = 20000;
134pub const SSTORE_STORAGE_MODIFICATION: u64 = 2900;
135pub const SSTORE_STIPEND: i64 = 2300;
136
137pub const BALANCE_STATIC: u64 = DEFAULT_STATIC;
138pub const BALANCE_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
139pub const BALANCE_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
140
141pub const EXTCODESIZE_STATIC: u64 = DEFAULT_STATIC;
142pub const EXTCODESIZE_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
143pub const EXTCODESIZE_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
144
145pub const EXTCODEHASH_STATIC: u64 = DEFAULT_STATIC;
146pub const EXTCODEHASH_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
147pub const EXTCODEHASH_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
148
149pub const EXTCODECOPY_STATIC: u64 = 0;
150pub const EXTCODECOPY_DYNAMIC_BASE: u64 = 3;
151pub const EXTCODECOPY_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
152pub const EXTCODECOPY_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
153
154pub const CALL_STATIC: u64 = DEFAULT_STATIC;
155pub const CALL_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
156pub const CALL_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
157pub const CALL_POSITIVE_VALUE: u64 = 9000;
158pub const CALL_POSITIVE_VALUE_STIPEND: u64 = 2300;
159pub const CALL_TO_EMPTY_ACCOUNT: u64 = 25000;
160
161pub const CALLCODE_STATIC: u64 = DEFAULT_STATIC;
162pub const CALLCODE_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
163pub const CALLCODE_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
164pub const CALLCODE_POSITIVE_VALUE: u64 = 9000;
165pub const CALLCODE_POSITIVE_VALUE_STIPEND: u64 = 2300;
166
167pub const DELEGATECALL_STATIC: u64 = DEFAULT_STATIC;
168pub const DELEGATECALL_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
169pub const DELEGATECALL_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
170
171pub const STATICCALL_STATIC: u64 = DEFAULT_STATIC;
172pub const STATICCALL_COLD_DYNAMIC: u64 = DEFAULT_COLD_DYNAMIC;
173pub const STATICCALL_WARM_DYNAMIC: u64 = DEFAULT_WARM_DYNAMIC;
174
175pub const WARM_ADDRESS_ACCESS_COST: u64 = 100;
177pub const COLD_ADDRESS_ACCESS_COST: u64 = 2600;
178pub const NON_ZERO_VALUE_COST: u64 = 9000;
179
180pub const VALUE_TO_EMPTY_ACCOUNT_COST: u64 = 25000;
181
182pub const INIT_CODE_WORD_COST: u64 = 2;
184pub const CODE_DEPOSIT_COST: u64 = 200;
185pub const CREATE_BASE_COST: u64 = 32000;
186
187pub const STATE_BYTES_PER_NEW_ACCOUNT: u64 = 120;
189pub const STATE_BYTES_PER_STORAGE_SET: u64 = 64;
190pub const STATE_BYTES_PER_AUTH_BASE: u64 = 23; pub fn cost_per_state_byte(_block_gas_limit: u64) -> u64 {
196 1530
197}
198
199pub const CODE_DEPOSIT_REGULAR_COST_PER_WORD: u64 = 6; pub const CALLDATA_COST_ZERO_BYTE: u64 = 4;
203pub const CALLDATA_COST_NON_ZERO_BYTE: u64 = 16;
204pub const STANDARD_TOKEN_COST: u64 = 4;
205
206pub const BLOB_GAS_PER_BLOB: u64 = 131072;
208
209pub const ACCESS_LIST_STORAGE_KEY_COST: u64 = 1900;
211pub const ACCESS_LIST_ADDRESS_COST: u64 = 2400;
212
213pub const COLD_ACCOUNT_ACCESS_AMSTERDAM: u64 = 3000;
215pub const COLD_STORAGE_ACCESS_AMSTERDAM: u64 = 2100;
216pub const ACCESS_LIST_ADDRESS_COST_AMSTERDAM: u64 =
220 COLD_ACCOUNT_ACCESS_AMSTERDAM - WARM_ADDRESS_ACCESS_COST;
221pub const ACCESS_LIST_STORAGE_KEY_COST_AMSTERDAM: u64 =
222 COLD_STORAGE_ACCESS_AMSTERDAM - WARM_ADDRESS_ACCESS_COST;
223pub const STORAGE_WRITE_AMSTERDAM: u64 = 10000;
224pub const ACCOUNT_WRITE_AMSTERDAM: u64 = 9000;
225pub const CALL_VALUE_AMSTERDAM: u64 = ACCOUNT_WRITE_AMSTERDAM + CALL_POSITIVE_VALUE_STIPEND;
226#[expect(
227 clippy::as_conversions,
228 reason = "compile-time constant well below i64::MAX"
229)]
230pub const STORAGE_CLEAR_REFUND_AMSTERDAM: i64 =
231 ((STORAGE_WRITE_AMSTERDAM + COLD_STORAGE_ACCESS_AMSTERDAM) * 4800 / 5000) as i64;
232pub const CREATE_ACCESS_AMSTERDAM: u64 = ACCOUNT_WRITE_AMSTERDAM + COLD_ACCOUNT_ACCESS_AMSTERDAM;
233
234pub const TX_VALUE_COST_AMSTERDAM: u64 = 6000;
240
241pub const AUTH_TUPLE_BYTES_AMSTERDAM: u64 = 101;
244
245pub const PER_AUTH_BASE_COST_AMSTERDAM: u64 = 7816;
252
253pub fn tx_base_cost(fork: Fork) -> u64 {
257 if fork >= Fork::Amsterdam {
258 TX_BASE_COST_AMSTERDAM
259 } else {
260 TX_BASE_COST
261 }
262}
263
264pub fn cold_account_access_cost(fork: Fork) -> u64 {
266 if fork >= Fork::Amsterdam {
267 COLD_ACCOUNT_ACCESS_AMSTERDAM
268 } else {
269 COLD_ADDRESS_ACCESS_COST
270 }
271}
272
273pub fn cold_storage_access_cost(fork: Fork) -> u64 {
275 if fork >= Fork::Amsterdam {
276 COLD_STORAGE_ACCESS_AMSTERDAM
277 } else {
278 SLOAD_COLD_DYNAMIC
279 }
280}
281
282pub fn access_list_address_cost(fork: Fork) -> u64 {
284 if fork >= Fork::Amsterdam {
285 ACCESS_LIST_ADDRESS_COST_AMSTERDAM
286 } else {
287 ACCESS_LIST_ADDRESS_COST
288 }
289}
290
291pub fn access_list_storage_key_cost(fork: Fork) -> u64 {
293 if fork >= Fork::Amsterdam {
294 ACCESS_LIST_STORAGE_KEY_COST_AMSTERDAM
295 } else {
296 ACCESS_LIST_STORAGE_KEY_COST
297 }
298}
299
300pub fn call_positive_value_cost(fork: Fork) -> u64 {
305 if fork >= Fork::Amsterdam {
306 CALL_VALUE_AMSTERDAM
307 } else {
308 CALL_POSITIVE_VALUE
309 }
310}
311
312pub fn recipient_regular_gas(to: &TxKind, value: U256, sender: Address, fork: Fork) -> u64 {
319 if fork < Fork::Amsterdam {
320 return 0;
321 }
322
323 let is_self_transfer = matches!(to, TxKind::Call(addr) if *addr == sender);
324 if is_self_transfer {
325 return 0;
326 }
327
328 if matches!(to, TxKind::Create) {
331 return CREATE_ACCESS_AMSTERDAM;
332 }
333
334 #[expect(
335 clippy::arithmetic_side_effects,
336 reason = "sum of small constant gas costs (<= ~9000), cannot overflow u64"
337 )]
338 if value.is_zero() {
339 cold_account_access_cost(fork)
340 } else {
341 cold_account_access_cost(fork) + TX_VALUE_COST_AMSTERDAM
342 }
343}
344
345pub const ECRECOVER_COST: u64 = 3000;
347pub const BLS12_381_G1ADD_COST: u64 = 375;
348pub const BLS12_381_G2ADD_COST: u64 = 600;
349pub const BLS12_381_MAP_FP_TO_G1_COST: u64 = 5500;
350pub const BLS12_PAIRING_CHECK_MUL_COST: u64 = 32600;
351pub const BLS12_PAIRING_CHECK_FIXED_COST: u64 = 37700;
352pub const BLS12_381_MAP_FP2_TO_G2_COST: u64 = 23800;
353pub const P256_VERIFY_COST: u64 = 6900;
354
355pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10;
357pub const TOTAL_COST_FLOOR_PER_TOKEN_AMSTERDAM: u64 = 16;
359
360pub fn total_cost_floor_per_token(fork: Fork) -> u64 {
363 if fork >= Fork::Amsterdam {
364 TOTAL_COST_FLOOR_PER_TOKEN_AMSTERDAM
365 } else {
366 TOTAL_COST_FLOOR_PER_TOKEN
367 }
368}
369
370pub const SHA2_256_STATIC_COST: u64 = 60;
371pub const SHA2_256_DYNAMIC_BASE: u64 = 12;
372
373pub const RIPEMD_160_STATIC_COST: u64 = 600;
374pub const RIPEMD_160_DYNAMIC_BASE: u64 = 120;
375
376pub const IDENTITY_STATIC_COST: u64 = 15;
377pub const IDENTITY_DYNAMIC_BASE: u64 = 3;
378
379pub const MODEXP_STATIC_COST: u64 = 200;
380pub const MODEXP_DYNAMIC_QUOTIENT: u64 = 3;
381pub const MODEXP_EXPONENT_FACTOR: u64 = 8;
382
383pub const MODEXP_STATIC_COST_OSAKA: u64 = 500;
384pub const MODEXP_DYNAMIC_QUOTIENT_OSAKA: u64 = 1;
385pub const MODEXP_EXPONENT_FACTOR_OSAKA: u64 = 16;
386
387pub const ECADD_COST: u64 = 150;
388pub const ECMUL_COST: u64 = 6000;
389
390pub const ECPAIRING_BASE_COST: u64 = 45000;
391pub const ECPAIRING_GROUP_COST: u64 = 34000;
392
393pub const POINT_EVALUATION_COST: u64 = 50000;
394
395pub const BLAKE2F_ROUND_COST: u64 = 1;
396
397pub const BLS12_381_MSM_MULTIPLIER: u64 = 1000;
398pub const BLS12_381_G1_K_DISCOUNT: [u64; 128] = [
399 1000, 949, 848, 797, 764, 750, 738, 728, 719, 712, 705, 698, 692, 687, 682, 677, 673, 669, 665,
400 661, 658, 654, 651, 648, 645, 642, 640, 637, 635, 632, 630, 627, 625, 623, 621, 619, 617, 615,
401 613, 611, 609, 608, 606, 604, 603, 601, 599, 598, 596, 595, 593, 592, 591, 589, 588, 586, 585,
402 584, 582, 581, 580, 579, 577, 576, 575, 574, 573, 572, 570, 569, 568, 567, 566, 565, 564, 563,
403 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 551, 550, 549, 548, 547, 547, 546, 545,
404 544, 543, 542, 541, 540, 540, 539, 538, 537, 536, 536, 535, 534, 533, 532, 532, 531, 530, 529,
405 528, 528, 527, 526, 525, 525, 524, 523, 522, 522, 521, 520, 520, 519,
406];
407pub const G1_MUL_COST: u64 = 12000;
408pub const BLS12_381_G2_K_DISCOUNT: [u64; 128] = [
409 1000, 1000, 923, 884, 855, 832, 812, 796, 782, 770, 759, 749, 740, 732, 724, 717, 711, 704,
410 699, 693, 688, 683, 679, 674, 670, 666, 663, 659, 655, 652, 649, 646, 643, 640, 637, 634, 632,
411 629, 627, 624, 622, 620, 618, 615, 613, 611, 609, 607, 606, 604, 602, 600, 598, 597, 595, 593,
412 592, 590, 589, 587, 586, 584, 583, 582, 580, 579, 578, 576, 575, 574, 573, 571, 570, 569, 568,
413 567, 566, 565, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 552, 551, 550, 549,
414 548, 547, 546, 545, 545, 544, 543, 542, 541, 541, 540, 539, 538, 537, 537, 536, 535, 535, 534,
415 533, 532, 532, 531, 530, 530, 529, 528, 528, 527, 526, 526, 525, 524, 524,
416];
417pub const G2_MUL_COST: u64 = 22500;
418
419pub fn exp(exponent: U256) -> Result<u64, VMError> {
420 let exponent_byte_size = (exponent.bits().checked_add(7).ok_or(OutOfGas)?) / 8;
421
422 let exponent_byte_size: u64 = exponent_byte_size
423 .try_into()
424 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
425
426 let exponent_byte_size_cost = EXP_DYNAMIC_BASE
427 .checked_mul(exponent_byte_size)
428 .ok_or(OutOfGas)?;
429
430 EXP_STATIC
431 .checked_add(exponent_byte_size_cost)
432 .ok_or(OutOfGas.into())
433}
434
435pub fn calldatacopy(
436 new_memory_size: usize,
437 current_memory_size: usize,
438 size: usize,
439) -> Result<u64, VMError> {
440 copy_behavior(
441 new_memory_size,
442 current_memory_size,
443 size,
444 CALLDATACOPY_DYNAMIC_BASE,
445 CALLDATACOPY_STATIC,
446 )
447}
448
449pub fn codecopy(
450 new_memory_size: usize,
451 current_memory_size: usize,
452 size: usize,
453) -> Result<u64, VMError> {
454 copy_behavior(
455 new_memory_size,
456 current_memory_size,
457 size,
458 CODECOPY_DYNAMIC_BASE,
459 CODECOPY_STATIC,
460 )
461}
462
463pub fn exit_opcode(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
465 memory::expansion_cost(new_memory_size, current_memory_size)
466}
467
468pub fn returndatacopy(
469 new_memory_size: usize,
470 current_memory_size: usize,
471 size: usize,
472) -> Result<u64, VMError> {
473 copy_behavior(
474 new_memory_size,
475 current_memory_size,
476 size,
477 RETURNDATACOPY_DYNAMIC_BASE,
478 RETURNDATACOPY_STATIC,
479 )
480}
481
482fn copy_behavior(
483 new_memory_size: usize,
484 current_memory_size: usize,
485 size: usize,
486 dynamic_base: u64,
487 static_cost: u64,
488) -> Result<u64, VMError> {
489 let minimum_word_size = (size
490 .checked_add(WORD_SIZE)
491 .ok_or(OutOfGas)?
492 .saturating_sub(1))
493 / WORD_SIZE;
494
495 let minimum_word_size: u64 = minimum_word_size
496 .try_into()
497 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
498
499 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
500
501 let minimum_word_size_cost = dynamic_base
502 .checked_mul(minimum_word_size)
503 .ok_or(OutOfGas)?;
504 static_cost
505 .checked_add(minimum_word_size_cost)
506 .ok_or(OutOfGas)?
507 .checked_add(memory_expansion_cost)
508 .ok_or(OutOfGas.into())
509}
510
511pub fn keccak256(
512 new_memory_size: usize,
513 current_memory_size: usize,
514 size: usize,
515) -> Result<u64, VMError> {
516 copy_behavior(
517 new_memory_size,
518 current_memory_size,
519 size,
520 KECCAK25_DYNAMIC_BASE,
521 KECCAK25_STATIC,
522 )
523}
524
525pub fn log(
526 new_memory_size: usize,
527 current_memory_size: usize,
528 size: usize,
529 number_of_topics: usize,
530) -> Result<u64, VMError> {
531 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
532
533 #[expect(clippy::as_conversions)]
536 let topics_cost = LOGN_DYNAMIC_BASE
537 .checked_mul(number_of_topics as u64)
538 .ok_or(OutOfGas)?;
539
540 let size: u64 = size
541 .try_into()
542 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
543 let bytes_cost = LOGN_DYNAMIC_BYTE_BASE.checked_mul(size).ok_or(OutOfGas)?;
544
545 topics_cost
546 .checked_add(LOGN_STATIC)
547 .ok_or(OutOfGas)?
548 .checked_add(bytes_cost)
549 .ok_or(OutOfGas)?
550 .checked_add(memory_expansion_cost)
551 .ok_or(OutOfGas.into())
552}
553
554pub fn mload(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
555 mem_expansion_behavior(new_memory_size, current_memory_size, MLOAD_STATIC)
556}
557
558pub fn mstore(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
559 mem_expansion_behavior(new_memory_size, current_memory_size, MSTORE_STATIC)
560}
561
562pub fn mstore8(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
563 mem_expansion_behavior(new_memory_size, current_memory_size, MSTORE8_STATIC)
564}
565
566fn mem_expansion_behavior(
567 new_memory_size: usize,
568 current_memory_size: usize,
569 static_cost: u64,
570) -> Result<u64, VMError> {
571 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
572
573 static_cost
574 .checked_add(memory_expansion_cost)
575 .ok_or(OutOfGas.into())
576}
577
578pub fn sload(storage_slot_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
579 let static_gas = SLOAD_STATIC;
580 let dynamic_cost = if storage_slot_was_cold {
581 cold_storage_access_cost(fork)
582 } else {
583 SLOAD_WARM_DYNAMIC
584 };
585 static_gas.checked_add(dynamic_cost).ok_or(OutOfGas.into())
586}
587
588pub fn sstore(
589 original_value: U256,
590 current_value: U256,
591 new_value: U256,
592 storage_slot_was_cold: bool,
593 fork: Fork,
594) -> Result<u64, VMError> {
595 let static_gas = SSTORE_STATIC;
596
597 if fork >= Fork::Amsterdam {
601 let access = if storage_slot_was_cold {
602 cold_storage_access_cost(fork)
603 } else {
604 SSTORE_DEFAULT_DYNAMIC
605 };
606 let write = if current_value == original_value && new_value != current_value {
607 STORAGE_WRITE_AMSTERDAM
608 } else {
609 0
610 };
611 return static_gas
612 .checked_add(access)
613 .ok_or(OutOfGas)?
614 .checked_add(write)
615 .ok_or(OutOfGas.into());
616 }
617
618 let mut base_dynamic_gas = if new_value == current_value {
619 SSTORE_DEFAULT_DYNAMIC
620 } else if current_value == original_value {
621 if original_value.is_zero() {
622 SSTORE_STORAGE_CREATION
623 } else {
624 SSTORE_STORAGE_MODIFICATION
625 }
626 } else {
627 SSTORE_DEFAULT_DYNAMIC
628 };
629 if storage_slot_was_cold {
631 base_dynamic_gas = base_dynamic_gas
632 .checked_add(cold_storage_access_cost(fork))
633 .ok_or(OutOfGas)?;
634 }
635 static_gas
636 .checked_add(base_dynamic_gas)
637 .ok_or(OutOfGas.into())
638}
639
640pub fn mcopy(
641 new_memory_size: usize,
642 current_memory_size: usize,
643 size: usize,
644) -> Result<u64, VMError> {
645 let words_copied = (size
646 .checked_add(WORD_SIZE)
647 .ok_or(OutOfGas)?
648 .saturating_sub(1))
649 / WORD_SIZE;
650
651 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
652
653 let words_copied: u64 = words_copied
654 .try_into()
655 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
656
657 let copied_words_cost = MCOPY_DYNAMIC_BASE
658 .checked_mul(words_copied)
659 .ok_or(OutOfGas)?;
660
661 MCOPY_STATIC
662 .checked_add(copied_words_cost)
663 .ok_or(OutOfGas)?
664 .checked_add(memory_expansion_cost)
665 .ok_or(OutOfGas.into())
666}
667
668pub fn create(
669 new_memory_size: usize,
670 current_memory_size: usize,
671 code_size_in_memory: usize,
672 fork: Fork,
673) -> Result<u64, VMError> {
674 compute_gas_create(
675 new_memory_size,
676 current_memory_size,
677 code_size_in_memory,
678 false,
679 fork,
680 )
681}
682
683pub fn create_2(
684 new_memory_size: usize,
685 current_memory_size: usize,
686 code_size_in_memory: usize,
687 fork: Fork,
688) -> Result<u64, VMError> {
689 compute_gas_create(
690 new_memory_size,
691 current_memory_size,
692 code_size_in_memory,
693 true,
694 fork,
695 )
696}
697
698fn compute_gas_create(
699 new_memory_size: usize,
700 current_memory_size: usize,
701 code_size_in_memory: usize,
702 is_create_2: bool,
703 fork: Fork,
704) -> Result<u64, VMError> {
705 let minimum_word_size = (code_size_in_memory.checked_add(31).ok_or(OutOfGas)?) / 32;
706
707 let minimum_word_size: u64 = minimum_word_size
708 .try_into()
709 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
710
711 let init_code_cost = if fork >= Fork::Shanghai {
713 minimum_word_size
714 .checked_mul(INIT_CODE_WORD_COST)
715 .ok_or(OutOfGas)? } else {
717 0
718 };
719
720 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
721
722 let hash_cost = if is_create_2 {
723 minimum_word_size
724 .checked_mul(KECCAK25_DYNAMIC_BASE)
725 .ok_or(OutOfGas)? } else {
727 0
728 };
729
730 let create_base_cost = if fork >= Fork::Amsterdam {
733 CREATE_ACCESS_AMSTERDAM
734 } else {
735 CREATE_BASE_COST
736 };
737
738 let gas_create_cost = memory_expansion_cost
739 .checked_add(init_code_cost)
740 .ok_or(OutOfGas)?
741 .checked_add(create_base_cost)
742 .ok_or(OutOfGas)?
743 .checked_add(hash_cost)
744 .ok_or(OutOfGas)?;
745
746 Ok(gas_create_cost)
747}
748
749pub fn selfdestruct_base(address_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
753 let cold_cost = if address_was_cold {
754 cold_account_access_cost(fork)
755 } else {
756 0
757 };
758 SELFDESTRUCT_STATIC
759 .checked_add(cold_cost)
760 .ok_or(OutOfGas.into())
761}
762
763pub fn selfdestruct(
764 address_was_cold: bool,
765 account_is_empty: bool,
766 balance_to_transfer: U256,
767 fork: Fork,
768) -> Result<u64, VMError> {
769 let mut dynamic_cost = if address_was_cold {
770 cold_account_access_cost(fork)
771 } else {
772 0
773 };
774
775 if account_is_empty && balance_to_transfer > U256::zero() {
779 if fork >= Fork::Amsterdam {
780 dynamic_cost = dynamic_cost
785 .checked_add(ACCOUNT_WRITE_AMSTERDAM)
786 .ok_or(OutOfGas)?;
787 } else {
788 dynamic_cost = dynamic_cost
789 .checked_add(SELFDESTRUCT_DYNAMIC)
790 .ok_or(OutOfGas)?;
791 }
792 }
793
794 SELFDESTRUCT_STATIC
795 .checked_add(dynamic_cost)
796 .ok_or(OutOfGas.into())
797}
798
799pub fn tx_calldata(calldata: &[u8]) -> Result<u64, VMError> {
800 let len = u64::try_from(calldata.len()).map_err(|_| InternalError::TypeConversion)?;
807 let non_zero_bytes = u64::try_from(calldata.iter().filter(|&&byte| byte != 0).count())
808 .map_err(|_| InternalError::TypeConversion)?;
809 let zero_bytes = len.saturating_sub(non_zero_bytes);
811
812 let non_zero_cost = non_zero_bytes
813 .checked_mul(CALLDATA_COST_NON_ZERO_BYTE)
814 .ok_or(OutOfGas)?;
815 let zero_cost = zero_bytes
816 .checked_mul(CALLDATA_COST_ZERO_BYTE)
817 .ok_or(OutOfGas)?;
818
819 non_zero_cost.checked_add(zero_cost).ok_or(OutOfGas.into())
820}
821
822pub fn access_list_bytes(access_list: &AccessList) -> u64 {
825 let mut bytes: u64 = 0;
826 for (_addr, keys) in access_list {
827 bytes = bytes.saturating_add(20);
828 let keys_len = u64::try_from(keys.len()).unwrap_or(u64::MAX);
829 bytes = bytes.saturating_add(32_u64.saturating_mul(keys_len));
830 }
831 bytes
832}
833
834pub fn floor_tokens_in_access_list(access_list: &AccessList) -> u64 {
837 access_list_bytes(access_list).saturating_mul(STANDARD_TOKEN_COST)
838}
839
840fn address_access_cost(
841 address_was_cold: bool,
842 static_cost: u64,
843 _cold_dynamic_cost: u64,
844 warm_dynamic_cost: u64,
845 fork: Fork,
846) -> Result<u64, VMError> {
847 let dynamic_cost: u64 = if address_was_cold {
850 cold_account_access_cost(fork)
851 } else {
852 warm_dynamic_cost
853 };
854
855 static_cost.checked_add(dynamic_cost).ok_or(OutOfGas.into())
856}
857
858pub fn balance(address_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
859 address_access_cost(
860 address_was_cold,
861 BALANCE_STATIC,
862 BALANCE_COLD_DYNAMIC,
863 BALANCE_WARM_DYNAMIC,
864 fork,
865 )
866}
867
868pub fn extcodesize(address_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
869 let access_cost = address_access_cost(
870 address_was_cold,
871 EXTCODESIZE_STATIC,
872 EXTCODESIZE_COLD_DYNAMIC,
873 EXTCODESIZE_WARM_DYNAMIC,
874 fork,
875 )?;
876 let extra = if fork >= Fork::Amsterdam {
880 WARM_ADDRESS_ACCESS_COST
881 } else {
882 0
883 };
884 access_cost.checked_add(extra).ok_or(OutOfGas.into())
885}
886
887pub fn extcodecopy(
888 size: usize,
889 new_memory_size: usize,
890 current_memory_size: usize,
891 address_was_cold: bool,
892 fork: Fork,
893) -> Result<u64, VMError> {
894 let base_access_cost = copy_behavior(
895 new_memory_size,
896 current_memory_size,
897 size,
898 EXTCODECOPY_DYNAMIC_BASE,
899 EXTCODECOPY_STATIC,
900 )?;
901 let expansion_access_cost = address_access_cost(
902 address_was_cold,
903 EXTCODECOPY_STATIC,
904 EXTCODECOPY_COLD_DYNAMIC,
905 EXTCODECOPY_WARM_DYNAMIC,
906 fork,
907 )?;
908
909 let extra = if fork >= Fork::Amsterdam {
913 WARM_ADDRESS_ACCESS_COST
914 } else {
915 0
916 };
917
918 base_access_cost
919 .checked_add(expansion_access_cost)
920 .ok_or(OutOfGas)?
921 .checked_add(extra)
922 .ok_or(OutOfGas.into())
923}
924
925pub fn extcodehash(address_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
926 address_access_cost(
927 address_was_cold,
928 EXTCODEHASH_STATIC,
929 EXTCODEHASH_COLD_DYNAMIC,
930 EXTCODEHASH_WARM_DYNAMIC,
931 fork,
932 )
933}
934
935#[allow(clippy::too_many_arguments)]
936pub fn call(
937 new_memory_size: usize,
938 current_memory_size: usize,
939 address_was_cold: bool,
940 address_is_empty: bool,
941 value_to_transfer: U256,
942 gas_from_stack: U256,
943 gas_left: u64,
944 fork: Fork,
945) -> Result<(u64, u64), VMError> {
946 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
947
948 let address_access_cost = address_access_cost(
949 address_was_cold,
950 CALL_STATIC,
951 CALL_COLD_DYNAMIC,
952 CALL_WARM_DYNAMIC,
953 fork,
954 )?;
955 let positive_value_cost = if !value_to_transfer.is_zero() {
956 call_positive_value_cost(fork)
957 } else {
958 0
959 };
960
961 let value_to_empty_account =
963 if address_is_empty && !value_to_transfer.is_zero() && fork < Fork::Amsterdam {
964 CALL_TO_EMPTY_ACCOUNT
965 } else {
966 0
967 };
968
969 let call_gas_costs = memory_expansion_cost
970 .checked_add(address_access_cost)
971 .ok_or(OutOfGas)?
972 .checked_add(positive_value_cost)
973 .ok_or(OutOfGas)?
974 .checked_add(value_to_empty_account)
975 .ok_or(OutOfGas)?;
976
977 calculate_cost_and_gas_limit_call(
978 value_to_transfer.is_zero(),
979 gas_from_stack,
980 gas_left,
981 call_gas_costs,
982 CALL_POSITIVE_VALUE_STIPEND,
983 )
984}
985
986pub fn callcode(
987 new_memory_size: usize,
988 current_memory_size: usize,
989 address_was_cold: bool,
990 value_to_transfer: U256,
991 gas_from_stack: U256,
992 gas_left: u64,
993 fork: Fork,
994) -> Result<(u64, u64), VMError> {
995 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
996 let address_access_cost = address_access_cost(
997 address_was_cold,
998 DELEGATECALL_STATIC,
999 DELEGATECALL_COLD_DYNAMIC,
1000 DELEGATECALL_WARM_DYNAMIC,
1001 fork,
1002 )?;
1003
1004 let positive_value_cost = if !value_to_transfer.is_zero() {
1005 call_positive_value_cost(fork)
1006 } else {
1007 0
1008 };
1009 let call_gas_costs = memory_expansion_cost
1010 .checked_add(address_access_cost)
1011 .ok_or(OutOfGas)?
1012 .checked_add(positive_value_cost)
1013 .ok_or(OutOfGas)?;
1014
1015 calculate_cost_and_gas_limit_call(
1016 value_to_transfer.is_zero(),
1017 gas_from_stack,
1018 gas_left,
1019 call_gas_costs,
1020 CALLCODE_POSITIVE_VALUE_STIPEND,
1021 )
1022}
1023
1024pub fn delegatecall(
1025 new_memory_size: usize,
1026 current_memory_size: usize,
1027 address_was_cold: bool,
1028 gas_from_stack: U256,
1029 gas_left: u64,
1030 fork: Fork,
1031) -> Result<(u64, u64), VMError> {
1032 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
1033
1034 let address_access_cost = address_access_cost(
1035 address_was_cold,
1036 DELEGATECALL_STATIC,
1037 DELEGATECALL_COLD_DYNAMIC,
1038 DELEGATECALL_WARM_DYNAMIC,
1039 fork,
1040 )?;
1041
1042 let call_gas_costs = memory_expansion_cost
1043 .checked_add(address_access_cost)
1044 .ok_or(OutOfGas)?;
1045
1046 calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
1047}
1048
1049pub fn staticcall(
1050 new_memory_size: usize,
1051 current_memory_size: usize,
1052 address_was_cold: bool,
1053 gas_from_stack: U256,
1054 gas_left: u64,
1055 fork: Fork,
1056) -> Result<(u64, u64), VMError> {
1057 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
1058
1059 let address_access_cost = address_access_cost(
1060 address_was_cold,
1061 STATICCALL_STATIC,
1062 STATICCALL_COLD_DYNAMIC,
1063 STATICCALL_WARM_DYNAMIC,
1064 fork,
1065 )?;
1066
1067 let call_gas_costs = memory_expansion_cost
1068 .checked_add(address_access_cost)
1069 .ok_or(OutOfGas)?;
1070
1071 calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
1072}
1073
1074pub fn sha2_256(data_size: usize) -> Result<u64, VMError> {
1075 precompile(data_size, SHA2_256_STATIC_COST, SHA2_256_DYNAMIC_BASE)
1076}
1077
1078pub fn ripemd_160(data_size: usize) -> Result<u64, VMError> {
1079 precompile(data_size, RIPEMD_160_STATIC_COST, RIPEMD_160_DYNAMIC_BASE)
1080}
1081
1082pub fn identity(data_size: usize) -> Result<u64, VMError> {
1083 precompile(data_size, IDENTITY_STATIC_COST, IDENTITY_DYNAMIC_BASE)
1084}
1085
1086pub fn modexp(
1087 exponent_first_32_bytes: &Natural,
1088 base_size: usize,
1089 exponent_size: usize,
1090 modulus_size: usize,
1091 fork: Fork,
1092) -> Result<u64, VMError> {
1093 let base_size: u64 = base_size
1094 .try_into()
1095 .map_err(|_| PrecompileError::ParsingInputError)?;
1096 let exponent_size: u64 = exponent_size
1097 .try_into()
1098 .map_err(|_| PrecompileError::ParsingInputError)?;
1099 let modulus_size: u64 = modulus_size
1100 .try_into()
1101 .map_err(|_| PrecompileError::ParsingInputError)?;
1102
1103 let max_length = base_size.max(modulus_size);
1104
1105 let words = (max_length.checked_add(7).ok_or(OutOfGas)?) / 8;
1108
1109 let multiplication_complexity = if fork >= Fork::Osaka {
1110 if max_length > 32 {
1111 2_u64
1112 .checked_mul(words.checked_pow(2).ok_or(OutOfGas)?)
1113 .ok_or(OutOfGas)?
1114 } else {
1115 16
1116 }
1117 } else {
1118 words.checked_pow(2).ok_or(OutOfGas)?
1119 };
1120
1121 let modexp_exponent_factor = if fork >= Fork::Osaka {
1122 MODEXP_EXPONENT_FACTOR_OSAKA
1123 } else {
1124 MODEXP_EXPONENT_FACTOR
1125 };
1126
1127 let calculate_iteration_count =
1128 if exponent_size <= 32 && *exponent_first_32_bytes != Natural::ZERO {
1129 exponent_first_32_bytes
1130 .significant_bits()
1131 .checked_sub(1)
1132 .ok_or(InternalError::Underflow)?
1133 } else if exponent_size > 32 {
1134 let extra_size = (exponent_size
1135 .checked_sub(32)
1136 .ok_or(InternalError::Underflow)?)
1137 .checked_mul(modexp_exponent_factor)
1138 .ok_or(OutOfGas)?;
1139 extra_size
1140 .checked_add(exponent_first_32_bytes.significant_bits().max(1))
1141 .ok_or(OutOfGas)?
1142 .checked_sub(1)
1143 .ok_or(InternalError::Underflow)?
1144 } else {
1145 0
1146 }
1147 .max(1);
1148
1149 let modexp_static_cost = if fork >= Fork::Osaka {
1150 MODEXP_STATIC_COST_OSAKA
1151 } else {
1152 MODEXP_STATIC_COST
1153 };
1154
1155 let modexp_dynamic_quotient = if fork >= Fork::Osaka {
1156 MODEXP_DYNAMIC_QUOTIENT_OSAKA
1157 } else {
1158 MODEXP_DYNAMIC_QUOTIENT
1159 };
1160
1161 let cost = modexp_static_cost.max(
1162 multiplication_complexity
1163 .checked_mul(calculate_iteration_count)
1164 .ok_or(OutOfGas)?
1165 .checked_div(modexp_dynamic_quotient)
1166 .ok_or(OutOfGas)?,
1167 );
1168 Ok(cost)
1169}
1170
1171fn precompile(data_size: usize, static_cost: u64, dynamic_base: u64) -> Result<u64, VMError> {
1172 let data_size: u64 = data_size
1173 .try_into()
1174 .map_err(|_| PrecompileError::ParsingInputError)?;
1175
1176 let data_word_cost = data_size
1177 .checked_add(WORD_SIZE_IN_BYTES_U64 - 1)
1178 .ok_or(OutOfGas)?
1179 / WORD_SIZE_IN_BYTES_U64;
1180
1181 let static_gas = static_cost;
1182 let dynamic_gas = dynamic_base.checked_mul(data_word_cost).ok_or(OutOfGas)?;
1183
1184 static_gas.checked_add(dynamic_gas).ok_or(OutOfGas.into())
1185}
1186
1187pub fn ecpairing(groups_number: usize) -> Result<u64, VMError> {
1188 let groups_number = u64::try_from(groups_number).map_err(|_| InternalError::TypeConversion)?;
1189
1190 let groups_cost = groups_number
1191 .checked_mul(ECPAIRING_GROUP_COST)
1192 .ok_or(OutOfGas)?;
1193 groups_cost
1194 .checked_add(ECPAIRING_BASE_COST)
1195 .ok_or(OutOfGas.into())
1196}
1197
1198#[expect(clippy::arithmetic_side_effects, reason = "can't overflow")]
1201#[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1202pub fn max_message_call_gas(current_call_frame: &CallFrame) -> Result<u64, VMError> {
1203 let mut remaining_gas = current_call_frame.gas_remaining;
1204
1205 remaining_gas -= remaining_gas / 64;
1206
1207 Ok(remaining_gas as u64)
1208}
1209
1210fn calculate_cost_and_gas_limit_call(
1211 value_is_zero: bool,
1212 gas_from_stack: U256,
1213 gas_left: u64,
1214 call_gas_costs: u64,
1215 stipend: u64,
1216) -> Result<(u64, u64), VMError> {
1217 let gas_stipend = if value_is_zero { 0 } else { stipend };
1218 let gas_left = gas_left.checked_sub(call_gas_costs).ok_or(OutOfGas)?;
1219
1220 let max_gas_for_call = gas_left.checked_sub(gas_left / 64).ok_or(OutOfGas)?;
1222
1223 let gas: u64 = gas_from_stack
1224 .min(max_gas_for_call.into())
1225 .try_into()
1226 .map_err(|_err| ExceptionalHalt::OutOfGas)?;
1227
1228 Ok((
1229 gas.checked_add(call_gas_costs)
1230 .ok_or(ExceptionalHalt::OutOfGas)?,
1231 gas.checked_add(gas_stipend)
1232 .ok_or(ExceptionalHalt::OutOfGas)?,
1233 ))
1234}
1235
1236pub fn bls12_msm(k: usize, discount_table: &[u64; 128], mul_cost: u64) -> Result<u64, VMError> {
1237 if k == 0 {
1238 return Ok(0);
1239 }
1240
1241 let discount = if k < discount_table.len() {
1242 discount_table
1243 .get(k.checked_sub(1).ok_or(InternalError::Underflow)?)
1244 .copied()
1245 .ok_or(InternalError::Slicing)?
1246 } else {
1247 discount_table
1248 .last()
1249 .copied()
1250 .ok_or(InternalError::Slicing)?
1251 };
1252
1253 let gas_cost = u64::try_from(k)
1254 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?
1255 .checked_mul(mul_cost)
1256 .ok_or(ExceptionalHalt::VeryLargeNumber)?
1257 .checked_mul(discount)
1258 .ok_or(ExceptionalHalt::VeryLargeNumber)?
1259 / BLS12_381_MSM_MULTIPLIER;
1260 Ok(gas_cost)
1261}
1262
1263pub fn bls12_pairing_check(k: usize) -> Result<u64, VMError> {
1264 let gas_cost = u64::try_from(k)
1265 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?
1266 .checked_mul(BLS12_PAIRING_CHECK_MUL_COST)
1267 .ok_or(InternalError::Overflow)?
1268 .checked_add(BLS12_PAIRING_CHECK_FIXED_COST)
1269 .ok_or(InternalError::Overflow)?;
1270 Ok(gas_cost)
1271}