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