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 = 3000;
216pub const ACCESS_LIST_ADDRESS_COST_AMSTERDAM: u64 = 3000;
217pub const ACCESS_LIST_STORAGE_KEY_COST_AMSTERDAM: u64 = 3000;
218pub const STORAGE_WRITE_AMSTERDAM: u64 = 10000;
219pub const ACCOUNT_WRITE_AMSTERDAM: u64 = 8000;
220pub const CALL_VALUE_AMSTERDAM: u64 = 10300;
221pub const STORAGE_CLEAR_REFUND_AMSTERDAM: i64 = 12480;
222pub const CREATE_ACCESS_AMSTERDAM: u64 = 11000;
223
224pub const TX_VALUE_COST_AMSTERDAM: u64 = 4244;
229pub const TRANSFER_LOG_COST_AMSTERDAM: u64 = 1756;
230
231pub const AUTH_TUPLE_BYTES_AMSTERDAM: u64 = 101;
234
235pub const PER_AUTH_BASE_COST_AMSTERDAM: u64 = 7816;
242
243pub fn tx_base_cost(fork: Fork) -> u64 {
247 if fork >= Fork::Amsterdam {
248 TX_BASE_COST_AMSTERDAM
249 } else {
250 TX_BASE_COST
251 }
252}
253
254pub fn cold_account_access_cost(fork: Fork) -> u64 {
256 if fork >= Fork::Amsterdam {
257 COLD_ACCOUNT_ACCESS_AMSTERDAM
258 } else {
259 COLD_ADDRESS_ACCESS_COST
260 }
261}
262
263pub fn cold_storage_access_cost(fork: Fork) -> u64 {
265 if fork >= Fork::Amsterdam {
266 COLD_STORAGE_ACCESS_AMSTERDAM
267 } else {
268 SLOAD_COLD_DYNAMIC
269 }
270}
271
272pub fn access_list_address_cost(fork: Fork) -> u64 {
274 if fork >= Fork::Amsterdam {
275 ACCESS_LIST_ADDRESS_COST_AMSTERDAM
276 } else {
277 ACCESS_LIST_ADDRESS_COST
278 }
279}
280
281pub fn access_list_storage_key_cost(fork: Fork) -> u64 {
283 if fork >= Fork::Amsterdam {
284 ACCESS_LIST_STORAGE_KEY_COST_AMSTERDAM
285 } else {
286 ACCESS_LIST_STORAGE_KEY_COST
287 }
288}
289
290pub fn call_positive_value_cost(fork: Fork) -> u64 {
295 if fork >= Fork::Amsterdam {
296 CALL_VALUE_AMSTERDAM
297 } else {
298 CALL_POSITIVE_VALUE
299 }
300}
301
302pub fn recipient_regular_gas(to: &TxKind, value: U256, sender: Address, fork: Fork) -> u64 {
309 if fork < Fork::Amsterdam {
310 return 0;
311 }
312
313 let is_self_transfer = matches!(to, TxKind::Call(addr) if *addr == sender);
314 if is_self_transfer {
315 return 0;
316 }
317
318 let is_create = matches!(to, TxKind::Create);
319 let regular_gas = if is_create {
320 CREATE_ACCESS_AMSTERDAM
321 } else {
322 cold_account_access_cost(fork)
323 };
324
325 #[expect(
326 clippy::arithmetic_side_effects,
327 reason = "sum of small constant gas costs (<= ~17000), cannot overflow u64"
328 )]
329 if !value.is_zero() {
330 if is_create {
331 regular_gas + TRANSFER_LOG_COST_AMSTERDAM
332 } else {
333 regular_gas + TRANSFER_LOG_COST_AMSTERDAM + TX_VALUE_COST_AMSTERDAM
334 }
335 } else {
336 regular_gas
337 }
338}
339
340pub const ECRECOVER_COST: u64 = 3000;
342pub const BLS12_381_G1ADD_COST: u64 = 375;
343pub const BLS12_381_G2ADD_COST: u64 = 600;
344pub const BLS12_381_MAP_FP_TO_G1_COST: u64 = 5500;
345pub const BLS12_PAIRING_CHECK_MUL_COST: u64 = 32600;
346pub const BLS12_PAIRING_CHECK_FIXED_COST: u64 = 37700;
347pub const BLS12_381_MAP_FP2_TO_G2_COST: u64 = 23800;
348pub const P256_VERIFY_COST: u64 = 6900;
349
350pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10;
352pub const TOTAL_COST_FLOOR_PER_TOKEN_AMSTERDAM: u64 = 16;
354
355pub fn total_cost_floor_per_token(fork: Fork) -> u64 {
358 if fork >= Fork::Amsterdam {
359 TOTAL_COST_FLOOR_PER_TOKEN_AMSTERDAM
360 } else {
361 TOTAL_COST_FLOOR_PER_TOKEN
362 }
363}
364
365pub const SHA2_256_STATIC_COST: u64 = 60;
366pub const SHA2_256_DYNAMIC_BASE: u64 = 12;
367
368pub const RIPEMD_160_STATIC_COST: u64 = 600;
369pub const RIPEMD_160_DYNAMIC_BASE: u64 = 120;
370
371pub const IDENTITY_STATIC_COST: u64 = 15;
372pub const IDENTITY_DYNAMIC_BASE: u64 = 3;
373
374pub const MODEXP_STATIC_COST: u64 = 200;
375pub const MODEXP_DYNAMIC_QUOTIENT: u64 = 3;
376pub const MODEXP_EXPONENT_FACTOR: u64 = 8;
377
378pub const MODEXP_STATIC_COST_OSAKA: u64 = 500;
379pub const MODEXP_DYNAMIC_QUOTIENT_OSAKA: u64 = 1;
380pub const MODEXP_EXPONENT_FACTOR_OSAKA: u64 = 16;
381
382pub const ECADD_COST: u64 = 150;
383pub const ECMUL_COST: u64 = 6000;
384
385pub const ECPAIRING_BASE_COST: u64 = 45000;
386pub const ECPAIRING_GROUP_COST: u64 = 34000;
387
388pub const POINT_EVALUATION_COST: u64 = 50000;
389
390pub const BLAKE2F_ROUND_COST: u64 = 1;
391
392pub const BLS12_381_MSM_MULTIPLIER: u64 = 1000;
393pub const BLS12_381_G1_K_DISCOUNT: [u64; 128] = [
394 1000, 949, 848, 797, 764, 750, 738, 728, 719, 712, 705, 698, 692, 687, 682, 677, 673, 669, 665,
395 661, 658, 654, 651, 648, 645, 642, 640, 637, 635, 632, 630, 627, 625, 623, 621, 619, 617, 615,
396 613, 611, 609, 608, 606, 604, 603, 601, 599, 598, 596, 595, 593, 592, 591, 589, 588, 586, 585,
397 584, 582, 581, 580, 579, 577, 576, 575, 574, 573, 572, 570, 569, 568, 567, 566, 565, 564, 563,
398 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 551, 550, 549, 548, 547, 547, 546, 545,
399 544, 543, 542, 541, 540, 540, 539, 538, 537, 536, 536, 535, 534, 533, 532, 532, 531, 530, 529,
400 528, 528, 527, 526, 525, 525, 524, 523, 522, 522, 521, 520, 520, 519,
401];
402pub const G1_MUL_COST: u64 = 12000;
403pub const BLS12_381_G2_K_DISCOUNT: [u64; 128] = [
404 1000, 1000, 923, 884, 855, 832, 812, 796, 782, 770, 759, 749, 740, 732, 724, 717, 711, 704,
405 699, 693, 688, 683, 679, 674, 670, 666, 663, 659, 655, 652, 649, 646, 643, 640, 637, 634, 632,
406 629, 627, 624, 622, 620, 618, 615, 613, 611, 609, 607, 606, 604, 602, 600, 598, 597, 595, 593,
407 592, 590, 589, 587, 586, 584, 583, 582, 580, 579, 578, 576, 575, 574, 573, 571, 570, 569, 568,
408 567, 566, 565, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 552, 551, 550, 549,
409 548, 547, 546, 545, 545, 544, 543, 542, 541, 541, 540, 539, 538, 537, 537, 536, 535, 535, 534,
410 533, 532, 532, 531, 530, 530, 529, 528, 528, 527, 526, 526, 525, 524, 524,
411];
412pub const G2_MUL_COST: u64 = 22500;
413
414pub fn exp(exponent: U256) -> Result<u64, VMError> {
415 let exponent_byte_size = (exponent.bits().checked_add(7).ok_or(OutOfGas)?) / 8;
416
417 let exponent_byte_size: u64 = exponent_byte_size
418 .try_into()
419 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
420
421 let exponent_byte_size_cost = EXP_DYNAMIC_BASE
422 .checked_mul(exponent_byte_size)
423 .ok_or(OutOfGas)?;
424
425 EXP_STATIC
426 .checked_add(exponent_byte_size_cost)
427 .ok_or(OutOfGas.into())
428}
429
430pub fn calldatacopy(
431 new_memory_size: usize,
432 current_memory_size: usize,
433 size: usize,
434) -> Result<u64, VMError> {
435 copy_behavior(
436 new_memory_size,
437 current_memory_size,
438 size,
439 CALLDATACOPY_DYNAMIC_BASE,
440 CALLDATACOPY_STATIC,
441 )
442}
443
444pub fn codecopy(
445 new_memory_size: usize,
446 current_memory_size: usize,
447 size: usize,
448) -> Result<u64, VMError> {
449 copy_behavior(
450 new_memory_size,
451 current_memory_size,
452 size,
453 CODECOPY_DYNAMIC_BASE,
454 CODECOPY_STATIC,
455 )
456}
457
458pub fn exit_opcode(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
460 memory::expansion_cost(new_memory_size, current_memory_size)
461}
462
463pub fn returndatacopy(
464 new_memory_size: usize,
465 current_memory_size: usize,
466 size: usize,
467) -> Result<u64, VMError> {
468 copy_behavior(
469 new_memory_size,
470 current_memory_size,
471 size,
472 RETURNDATACOPY_DYNAMIC_BASE,
473 RETURNDATACOPY_STATIC,
474 )
475}
476
477fn copy_behavior(
478 new_memory_size: usize,
479 current_memory_size: usize,
480 size: usize,
481 dynamic_base: u64,
482 static_cost: u64,
483) -> Result<u64, VMError> {
484 let minimum_word_size = (size
485 .checked_add(WORD_SIZE)
486 .ok_or(OutOfGas)?
487 .saturating_sub(1))
488 / WORD_SIZE;
489
490 let minimum_word_size: u64 = minimum_word_size
491 .try_into()
492 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
493
494 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
495
496 let minimum_word_size_cost = dynamic_base
497 .checked_mul(minimum_word_size)
498 .ok_or(OutOfGas)?;
499 static_cost
500 .checked_add(minimum_word_size_cost)
501 .ok_or(OutOfGas)?
502 .checked_add(memory_expansion_cost)
503 .ok_or(OutOfGas.into())
504}
505
506pub fn keccak256(
507 new_memory_size: usize,
508 current_memory_size: usize,
509 size: usize,
510) -> Result<u64, VMError> {
511 copy_behavior(
512 new_memory_size,
513 current_memory_size,
514 size,
515 KECCAK25_DYNAMIC_BASE,
516 KECCAK25_STATIC,
517 )
518}
519
520pub fn log(
521 new_memory_size: usize,
522 current_memory_size: usize,
523 size: usize,
524 number_of_topics: usize,
525) -> Result<u64, VMError> {
526 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
527
528 #[expect(clippy::as_conversions)]
531 let topics_cost = LOGN_DYNAMIC_BASE
532 .checked_mul(number_of_topics as u64)
533 .ok_or(OutOfGas)?;
534
535 let size: u64 = size
536 .try_into()
537 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
538 let bytes_cost = LOGN_DYNAMIC_BYTE_BASE.checked_mul(size).ok_or(OutOfGas)?;
539
540 topics_cost
541 .checked_add(LOGN_STATIC)
542 .ok_or(OutOfGas)?
543 .checked_add(bytes_cost)
544 .ok_or(OutOfGas)?
545 .checked_add(memory_expansion_cost)
546 .ok_or(OutOfGas.into())
547}
548
549pub fn mload(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
550 mem_expansion_behavior(new_memory_size, current_memory_size, MLOAD_STATIC)
551}
552
553pub fn mstore(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
554 mem_expansion_behavior(new_memory_size, current_memory_size, MSTORE_STATIC)
555}
556
557pub fn mstore8(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
558 mem_expansion_behavior(new_memory_size, current_memory_size, MSTORE8_STATIC)
559}
560
561fn mem_expansion_behavior(
562 new_memory_size: usize,
563 current_memory_size: usize,
564 static_cost: u64,
565) -> Result<u64, VMError> {
566 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
567
568 static_cost
569 .checked_add(memory_expansion_cost)
570 .ok_or(OutOfGas.into())
571}
572
573pub fn sload(storage_slot_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
574 let static_gas = SLOAD_STATIC;
575 let dynamic_cost = if storage_slot_was_cold {
576 cold_storage_access_cost(fork)
577 } else {
578 SLOAD_WARM_DYNAMIC
579 };
580 static_gas.checked_add(dynamic_cost).ok_or(OutOfGas.into())
581}
582
583pub fn sstore(
584 original_value: U256,
585 current_value: U256,
586 new_value: U256,
587 storage_slot_was_cold: bool,
588 fork: Fork,
589) -> Result<u64, VMError> {
590 let static_gas = SSTORE_STATIC;
591
592 if fork >= Fork::Amsterdam {
596 let access = if storage_slot_was_cold {
597 cold_storage_access_cost(fork)
598 } else {
599 SSTORE_DEFAULT_DYNAMIC
600 };
601 let write = if current_value == original_value && new_value != current_value {
602 STORAGE_WRITE_AMSTERDAM
603 } else {
604 0
605 };
606 return static_gas
607 .checked_add(access)
608 .ok_or(OutOfGas)?
609 .checked_add(write)
610 .ok_or(OutOfGas.into());
611 }
612
613 let mut base_dynamic_gas = if new_value == current_value {
614 SSTORE_DEFAULT_DYNAMIC
615 } else if current_value == original_value {
616 if original_value.is_zero() {
617 SSTORE_STORAGE_CREATION
618 } else {
619 SSTORE_STORAGE_MODIFICATION
620 }
621 } else {
622 SSTORE_DEFAULT_DYNAMIC
623 };
624 if storage_slot_was_cold {
626 base_dynamic_gas = base_dynamic_gas
627 .checked_add(cold_storage_access_cost(fork))
628 .ok_or(OutOfGas)?;
629 }
630 static_gas
631 .checked_add(base_dynamic_gas)
632 .ok_or(OutOfGas.into())
633}
634
635pub fn mcopy(
636 new_memory_size: usize,
637 current_memory_size: usize,
638 size: usize,
639) -> Result<u64, VMError> {
640 let words_copied = (size
641 .checked_add(WORD_SIZE)
642 .ok_or(OutOfGas)?
643 .saturating_sub(1))
644 / WORD_SIZE;
645
646 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
647
648 let words_copied: u64 = words_copied
649 .try_into()
650 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
651
652 let copied_words_cost = MCOPY_DYNAMIC_BASE
653 .checked_mul(words_copied)
654 .ok_or(OutOfGas)?;
655
656 MCOPY_STATIC
657 .checked_add(copied_words_cost)
658 .ok_or(OutOfGas)?
659 .checked_add(memory_expansion_cost)
660 .ok_or(OutOfGas.into())
661}
662
663pub fn create(
664 new_memory_size: usize,
665 current_memory_size: usize,
666 code_size_in_memory: usize,
667 fork: Fork,
668) -> Result<u64, VMError> {
669 compute_gas_create(
670 new_memory_size,
671 current_memory_size,
672 code_size_in_memory,
673 false,
674 fork,
675 )
676}
677
678pub fn create_2(
679 new_memory_size: usize,
680 current_memory_size: usize,
681 code_size_in_memory: usize,
682 fork: Fork,
683) -> Result<u64, VMError> {
684 compute_gas_create(
685 new_memory_size,
686 current_memory_size,
687 code_size_in_memory,
688 true,
689 fork,
690 )
691}
692
693fn compute_gas_create(
694 new_memory_size: usize,
695 current_memory_size: usize,
696 code_size_in_memory: usize,
697 is_create_2: bool,
698 fork: Fork,
699) -> Result<u64, VMError> {
700 let minimum_word_size = (code_size_in_memory.checked_add(31).ok_or(OutOfGas)?) / 32;
701
702 let minimum_word_size: u64 = minimum_word_size
703 .try_into()
704 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
705
706 let init_code_cost = if fork >= Fork::Shanghai {
708 minimum_word_size
709 .checked_mul(INIT_CODE_WORD_COST)
710 .ok_or(OutOfGas)? } else {
712 0
713 };
714
715 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
716
717 let hash_cost = if is_create_2 {
718 minimum_word_size
719 .checked_mul(KECCAK25_DYNAMIC_BASE)
720 .ok_or(OutOfGas)? } else {
722 0
723 };
724
725 let create_base_cost = if fork >= Fork::Amsterdam {
728 CREATE_ACCESS_AMSTERDAM
729 } else {
730 CREATE_BASE_COST
731 };
732
733 let gas_create_cost = memory_expansion_cost
734 .checked_add(init_code_cost)
735 .ok_or(OutOfGas)?
736 .checked_add(create_base_cost)
737 .ok_or(OutOfGas)?
738 .checked_add(hash_cost)
739 .ok_or(OutOfGas)?;
740
741 Ok(gas_create_cost)
742}
743
744pub fn selfdestruct_base(address_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
748 let cold_cost = if address_was_cold {
749 cold_account_access_cost(fork)
750 } else {
751 0
752 };
753 SELFDESTRUCT_STATIC
754 .checked_add(cold_cost)
755 .ok_or(OutOfGas.into())
756}
757
758pub fn selfdestruct(
759 address_was_cold: bool,
760 account_is_empty: bool,
761 balance_to_transfer: U256,
762 fork: Fork,
763) -> Result<u64, VMError> {
764 let mut dynamic_cost = if address_was_cold {
765 cold_account_access_cost(fork)
766 } else {
767 0
768 };
769
770 if account_is_empty && balance_to_transfer > U256::zero() {
774 if fork >= Fork::Amsterdam {
775 dynamic_cost = dynamic_cost
780 .checked_add(ACCOUNT_WRITE_AMSTERDAM)
781 .ok_or(OutOfGas)?;
782 } else {
783 dynamic_cost = dynamic_cost
784 .checked_add(SELFDESTRUCT_DYNAMIC)
785 .ok_or(OutOfGas)?;
786 }
787 }
788
789 SELFDESTRUCT_STATIC
790 .checked_add(dynamic_cost)
791 .ok_or(OutOfGas.into())
792}
793
794pub fn tx_calldata(calldata: &[u8]) -> Result<u64, VMError> {
795 let len = u64::try_from(calldata.len()).map_err(|_| InternalError::TypeConversion)?;
802 let non_zero_bytes = u64::try_from(calldata.iter().filter(|&&byte| byte != 0).count())
803 .map_err(|_| InternalError::TypeConversion)?;
804 let zero_bytes = len.saturating_sub(non_zero_bytes);
806
807 let non_zero_cost = non_zero_bytes
808 .checked_mul(CALLDATA_COST_NON_ZERO_BYTE)
809 .ok_or(OutOfGas)?;
810 let zero_cost = zero_bytes
811 .checked_mul(CALLDATA_COST_ZERO_BYTE)
812 .ok_or(OutOfGas)?;
813
814 non_zero_cost.checked_add(zero_cost).ok_or(OutOfGas.into())
815}
816
817pub fn access_list_bytes(access_list: &AccessList) -> u64 {
820 let mut bytes: u64 = 0;
821 for (_addr, keys) in access_list {
822 bytes = bytes.saturating_add(20);
823 let keys_len = u64::try_from(keys.len()).unwrap_or(u64::MAX);
824 bytes = bytes.saturating_add(32_u64.saturating_mul(keys_len));
825 }
826 bytes
827}
828
829pub fn floor_tokens_in_access_list(access_list: &AccessList) -> u64 {
832 access_list_bytes(access_list).saturating_mul(STANDARD_TOKEN_COST)
833}
834
835fn address_access_cost(
836 address_was_cold: bool,
837 static_cost: u64,
838 _cold_dynamic_cost: u64,
839 warm_dynamic_cost: u64,
840 fork: Fork,
841) -> Result<u64, VMError> {
842 let dynamic_cost: u64 = if address_was_cold {
845 cold_account_access_cost(fork)
846 } else {
847 warm_dynamic_cost
848 };
849
850 static_cost.checked_add(dynamic_cost).ok_or(OutOfGas.into())
851}
852
853pub fn balance(address_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
854 address_access_cost(
855 address_was_cold,
856 BALANCE_STATIC,
857 BALANCE_COLD_DYNAMIC,
858 BALANCE_WARM_DYNAMIC,
859 fork,
860 )
861}
862
863pub fn extcodesize(address_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
864 let access_cost = address_access_cost(
865 address_was_cold,
866 EXTCODESIZE_STATIC,
867 EXTCODESIZE_COLD_DYNAMIC,
868 EXTCODESIZE_WARM_DYNAMIC,
869 fork,
870 )?;
871 let extra = if fork >= Fork::Amsterdam {
875 WARM_ADDRESS_ACCESS_COST
876 } else {
877 0
878 };
879 access_cost.checked_add(extra).ok_or(OutOfGas.into())
880}
881
882pub fn extcodecopy(
883 size: usize,
884 new_memory_size: usize,
885 current_memory_size: usize,
886 address_was_cold: bool,
887 fork: Fork,
888) -> Result<u64, VMError> {
889 let base_access_cost = copy_behavior(
890 new_memory_size,
891 current_memory_size,
892 size,
893 EXTCODECOPY_DYNAMIC_BASE,
894 EXTCODECOPY_STATIC,
895 )?;
896 let expansion_access_cost = address_access_cost(
897 address_was_cold,
898 EXTCODECOPY_STATIC,
899 EXTCODECOPY_COLD_DYNAMIC,
900 EXTCODECOPY_WARM_DYNAMIC,
901 fork,
902 )?;
903
904 let extra = if fork >= Fork::Amsterdam {
908 WARM_ADDRESS_ACCESS_COST
909 } else {
910 0
911 };
912
913 base_access_cost
914 .checked_add(expansion_access_cost)
915 .ok_or(OutOfGas)?
916 .checked_add(extra)
917 .ok_or(OutOfGas.into())
918}
919
920pub fn extcodehash(address_was_cold: bool, fork: Fork) -> Result<u64, VMError> {
921 address_access_cost(
922 address_was_cold,
923 EXTCODEHASH_STATIC,
924 EXTCODEHASH_COLD_DYNAMIC,
925 EXTCODEHASH_WARM_DYNAMIC,
926 fork,
927 )
928}
929
930#[allow(clippy::too_many_arguments)]
931pub fn call(
932 new_memory_size: usize,
933 current_memory_size: usize,
934 address_was_cold: bool,
935 address_is_empty: bool,
936 value_to_transfer: U256,
937 gas_from_stack: U256,
938 gas_left: u64,
939 fork: Fork,
940) -> Result<(u64, u64), VMError> {
941 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
942
943 let address_access_cost = address_access_cost(
944 address_was_cold,
945 CALL_STATIC,
946 CALL_COLD_DYNAMIC,
947 CALL_WARM_DYNAMIC,
948 fork,
949 )?;
950 let positive_value_cost = if !value_to_transfer.is_zero() {
951 call_positive_value_cost(fork)
952 } else {
953 0
954 };
955
956 let value_to_empty_account =
958 if address_is_empty && !value_to_transfer.is_zero() && fork < Fork::Amsterdam {
959 CALL_TO_EMPTY_ACCOUNT
960 } else {
961 0
962 };
963
964 let call_gas_costs = memory_expansion_cost
965 .checked_add(address_access_cost)
966 .ok_or(OutOfGas)?
967 .checked_add(positive_value_cost)
968 .ok_or(OutOfGas)?
969 .checked_add(value_to_empty_account)
970 .ok_or(OutOfGas)?;
971
972 calculate_cost_and_gas_limit_call(
973 value_to_transfer.is_zero(),
974 gas_from_stack,
975 gas_left,
976 call_gas_costs,
977 CALL_POSITIVE_VALUE_STIPEND,
978 )
979}
980
981pub fn callcode(
982 new_memory_size: usize,
983 current_memory_size: usize,
984 address_was_cold: bool,
985 value_to_transfer: U256,
986 gas_from_stack: U256,
987 gas_left: u64,
988 fork: Fork,
989) -> Result<(u64, u64), VMError> {
990 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
991 let address_access_cost = address_access_cost(
992 address_was_cold,
993 DELEGATECALL_STATIC,
994 DELEGATECALL_COLD_DYNAMIC,
995 DELEGATECALL_WARM_DYNAMIC,
996 fork,
997 )?;
998
999 let positive_value_cost = if !value_to_transfer.is_zero() {
1000 call_positive_value_cost(fork)
1001 } else {
1002 0
1003 };
1004 let call_gas_costs = memory_expansion_cost
1005 .checked_add(address_access_cost)
1006 .ok_or(OutOfGas)?
1007 .checked_add(positive_value_cost)
1008 .ok_or(OutOfGas)?;
1009
1010 calculate_cost_and_gas_limit_call(
1011 value_to_transfer.is_zero(),
1012 gas_from_stack,
1013 gas_left,
1014 call_gas_costs,
1015 CALLCODE_POSITIVE_VALUE_STIPEND,
1016 )
1017}
1018
1019pub fn delegatecall(
1020 new_memory_size: usize,
1021 current_memory_size: usize,
1022 address_was_cold: bool,
1023 gas_from_stack: U256,
1024 gas_left: u64,
1025 fork: Fork,
1026) -> Result<(u64, u64), VMError> {
1027 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
1028
1029 let address_access_cost = address_access_cost(
1030 address_was_cold,
1031 DELEGATECALL_STATIC,
1032 DELEGATECALL_COLD_DYNAMIC,
1033 DELEGATECALL_WARM_DYNAMIC,
1034 fork,
1035 )?;
1036
1037 let call_gas_costs = memory_expansion_cost
1038 .checked_add(address_access_cost)
1039 .ok_or(OutOfGas)?;
1040
1041 calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
1042}
1043
1044pub fn staticcall(
1045 new_memory_size: usize,
1046 current_memory_size: usize,
1047 address_was_cold: bool,
1048 gas_from_stack: U256,
1049 gas_left: u64,
1050 fork: Fork,
1051) -> Result<(u64, u64), VMError> {
1052 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
1053
1054 let address_access_cost = address_access_cost(
1055 address_was_cold,
1056 STATICCALL_STATIC,
1057 STATICCALL_COLD_DYNAMIC,
1058 STATICCALL_WARM_DYNAMIC,
1059 fork,
1060 )?;
1061
1062 let call_gas_costs = memory_expansion_cost
1063 .checked_add(address_access_cost)
1064 .ok_or(OutOfGas)?;
1065
1066 calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
1067}
1068
1069pub fn sha2_256(data_size: usize) -> Result<u64, VMError> {
1070 precompile(data_size, SHA2_256_STATIC_COST, SHA2_256_DYNAMIC_BASE)
1071}
1072
1073pub fn ripemd_160(data_size: usize) -> Result<u64, VMError> {
1074 precompile(data_size, RIPEMD_160_STATIC_COST, RIPEMD_160_DYNAMIC_BASE)
1075}
1076
1077pub fn identity(data_size: usize) -> Result<u64, VMError> {
1078 precompile(data_size, IDENTITY_STATIC_COST, IDENTITY_DYNAMIC_BASE)
1079}
1080
1081pub fn modexp(
1082 exponent_first_32_bytes: &Natural,
1083 base_size: usize,
1084 exponent_size: usize,
1085 modulus_size: usize,
1086 fork: Fork,
1087) -> Result<u64, VMError> {
1088 let base_size: u64 = base_size
1089 .try_into()
1090 .map_err(|_| PrecompileError::ParsingInputError)?;
1091 let exponent_size: u64 = exponent_size
1092 .try_into()
1093 .map_err(|_| PrecompileError::ParsingInputError)?;
1094 let modulus_size: u64 = modulus_size
1095 .try_into()
1096 .map_err(|_| PrecompileError::ParsingInputError)?;
1097
1098 let max_length = base_size.max(modulus_size);
1099
1100 let words = (max_length.checked_add(7).ok_or(OutOfGas)?) / 8;
1103
1104 let multiplication_complexity = if fork >= Fork::Osaka {
1105 if max_length > 32 {
1106 2_u64
1107 .checked_mul(words.checked_pow(2).ok_or(OutOfGas)?)
1108 .ok_or(OutOfGas)?
1109 } else {
1110 16
1111 }
1112 } else {
1113 words.checked_pow(2).ok_or(OutOfGas)?
1114 };
1115
1116 let modexp_exponent_factor = if fork >= Fork::Osaka {
1117 MODEXP_EXPONENT_FACTOR_OSAKA
1118 } else {
1119 MODEXP_EXPONENT_FACTOR
1120 };
1121
1122 let calculate_iteration_count =
1123 if exponent_size <= 32 && *exponent_first_32_bytes != Natural::ZERO {
1124 exponent_first_32_bytes
1125 .significant_bits()
1126 .checked_sub(1)
1127 .ok_or(InternalError::Underflow)?
1128 } else if exponent_size > 32 {
1129 let extra_size = (exponent_size
1130 .checked_sub(32)
1131 .ok_or(InternalError::Underflow)?)
1132 .checked_mul(modexp_exponent_factor)
1133 .ok_or(OutOfGas)?;
1134 extra_size
1135 .checked_add(exponent_first_32_bytes.significant_bits().max(1))
1136 .ok_or(OutOfGas)?
1137 .checked_sub(1)
1138 .ok_or(InternalError::Underflow)?
1139 } else {
1140 0
1141 }
1142 .max(1);
1143
1144 let modexp_static_cost = if fork >= Fork::Osaka {
1145 MODEXP_STATIC_COST_OSAKA
1146 } else {
1147 MODEXP_STATIC_COST
1148 };
1149
1150 let modexp_dynamic_quotient = if fork >= Fork::Osaka {
1151 MODEXP_DYNAMIC_QUOTIENT_OSAKA
1152 } else {
1153 MODEXP_DYNAMIC_QUOTIENT
1154 };
1155
1156 let cost = modexp_static_cost.max(
1157 multiplication_complexity
1158 .checked_mul(calculate_iteration_count)
1159 .ok_or(OutOfGas)?
1160 .checked_div(modexp_dynamic_quotient)
1161 .ok_or(OutOfGas)?,
1162 );
1163 Ok(cost)
1164}
1165
1166fn precompile(data_size: usize, static_cost: u64, dynamic_base: u64) -> Result<u64, VMError> {
1167 let data_size: u64 = data_size
1168 .try_into()
1169 .map_err(|_| PrecompileError::ParsingInputError)?;
1170
1171 let data_word_cost = data_size
1172 .checked_add(WORD_SIZE_IN_BYTES_U64 - 1)
1173 .ok_or(OutOfGas)?
1174 / WORD_SIZE_IN_BYTES_U64;
1175
1176 let static_gas = static_cost;
1177 let dynamic_gas = dynamic_base.checked_mul(data_word_cost).ok_or(OutOfGas)?;
1178
1179 static_gas.checked_add(dynamic_gas).ok_or(OutOfGas.into())
1180}
1181
1182pub fn ecpairing(groups_number: usize) -> Result<u64, VMError> {
1183 let groups_number = u64::try_from(groups_number).map_err(|_| InternalError::TypeConversion)?;
1184
1185 let groups_cost = groups_number
1186 .checked_mul(ECPAIRING_GROUP_COST)
1187 .ok_or(OutOfGas)?;
1188 groups_cost
1189 .checked_add(ECPAIRING_BASE_COST)
1190 .ok_or(OutOfGas.into())
1191}
1192
1193#[expect(clippy::arithmetic_side_effects, reason = "can't overflow")]
1196#[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1197pub fn max_message_call_gas(current_call_frame: &CallFrame) -> Result<u64, VMError> {
1198 let mut remaining_gas = current_call_frame.gas_remaining;
1199
1200 remaining_gas -= remaining_gas / 64;
1201
1202 Ok(remaining_gas as u64)
1203}
1204
1205fn calculate_cost_and_gas_limit_call(
1206 value_is_zero: bool,
1207 gas_from_stack: U256,
1208 gas_left: u64,
1209 call_gas_costs: u64,
1210 stipend: u64,
1211) -> Result<(u64, u64), VMError> {
1212 let gas_stipend = if value_is_zero { 0 } else { stipend };
1213 let gas_left = gas_left.checked_sub(call_gas_costs).ok_or(OutOfGas)?;
1214
1215 let max_gas_for_call = gas_left.checked_sub(gas_left / 64).ok_or(OutOfGas)?;
1217
1218 let gas: u64 = gas_from_stack
1219 .min(max_gas_for_call.into())
1220 .try_into()
1221 .map_err(|_err| ExceptionalHalt::OutOfGas)?;
1222
1223 Ok((
1224 gas.checked_add(call_gas_costs)
1225 .ok_or(ExceptionalHalt::OutOfGas)?,
1226 gas.checked_add(gas_stipend)
1227 .ok_or(ExceptionalHalt::OutOfGas)?,
1228 ))
1229}
1230
1231pub fn bls12_msm(k: usize, discount_table: &[u64; 128], mul_cost: u64) -> Result<u64, VMError> {
1232 if k == 0 {
1233 return Ok(0);
1234 }
1235
1236 let discount = if k < discount_table.len() {
1237 discount_table
1238 .get(k.checked_sub(1).ok_or(InternalError::Underflow)?)
1239 .copied()
1240 .ok_or(InternalError::Slicing)?
1241 } else {
1242 discount_table
1243 .last()
1244 .copied()
1245 .ok_or(InternalError::Slicing)?
1246 };
1247
1248 let gas_cost = u64::try_from(k)
1249 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?
1250 .checked_mul(mul_cost)
1251 .ok_or(ExceptionalHalt::VeryLargeNumber)?
1252 .checked_mul(discount)
1253 .ok_or(ExceptionalHalt::VeryLargeNumber)?
1254 / BLS12_381_MSM_MULTIPLIER;
1255 Ok(gas_cost)
1256}
1257
1258pub fn bls12_pairing_check(k: usize) -> Result<u64, VMError> {
1259 let gas_cost = u64::try_from(k)
1260 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?
1261 .checked_mul(BLS12_PAIRING_CHECK_MUL_COST)
1262 .ok_or(InternalError::Overflow)?
1263 .checked_add(BLS12_PAIRING_CHECK_FIXED_COST)
1264 .ok_or(InternalError::Overflow)?;
1265 Ok(gas_cost)
1266}