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