1use crate::{
2 call_frame::CallFrame,
3 constants::{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 REGULAR_GAS_CREATE: u64 = 9000; pub const CODE_DEPOSIT_REGULAR_COST_PER_WORD: u64 = 6; pub const CALLDATA_COST_ZERO_BYTE: u64 = 4;
181pub const CALLDATA_COST_NON_ZERO_BYTE: u64 = 16;
182pub const STANDARD_TOKEN_COST: u64 = 4;
183
184pub const BLOB_GAS_PER_BLOB: u64 = 131072;
186
187pub const ACCESS_LIST_STORAGE_KEY_COST: u64 = 1900;
189pub const ACCESS_LIST_ADDRESS_COST: u64 = 2400;
190
191pub const ECRECOVER_COST: u64 = 3000;
193pub const BLS12_381_G1ADD_COST: u64 = 375;
194pub const BLS12_381_G2ADD_COST: u64 = 600;
195pub const BLS12_381_MAP_FP_TO_G1_COST: u64 = 5500;
196pub const BLS12_PAIRING_CHECK_MUL_COST: u64 = 32600;
197pub const BLS12_PAIRING_CHECK_FIXED_COST: u64 = 37700;
198pub const BLS12_381_MAP_FP2_TO_G2_COST: u64 = 23800;
199pub const P256_VERIFY_COST: u64 = 6900;
200
201pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10;
203pub const TOTAL_COST_FLOOR_PER_TOKEN_AMSTERDAM: u64 = 16;
205
206pub fn total_cost_floor_per_token(fork: Fork) -> u64 {
209 if fork >= Fork::Amsterdam {
210 TOTAL_COST_FLOOR_PER_TOKEN_AMSTERDAM
211 } else {
212 TOTAL_COST_FLOOR_PER_TOKEN
213 }
214}
215
216pub const SHA2_256_STATIC_COST: u64 = 60;
217pub const SHA2_256_DYNAMIC_BASE: u64 = 12;
218
219pub const RIPEMD_160_STATIC_COST: u64 = 600;
220pub const RIPEMD_160_DYNAMIC_BASE: u64 = 120;
221
222pub const IDENTITY_STATIC_COST: u64 = 15;
223pub const IDENTITY_DYNAMIC_BASE: u64 = 3;
224
225pub const MODEXP_STATIC_COST: u64 = 200;
226pub const MODEXP_DYNAMIC_QUOTIENT: u64 = 3;
227pub const MODEXP_EXPONENT_FACTOR: u64 = 8;
228
229pub const MODEXP_STATIC_COST_OSAKA: u64 = 500;
230pub const MODEXP_DYNAMIC_QUOTIENT_OSAKA: u64 = 1;
231pub const MODEXP_EXPONENT_FACTOR_OSAKA: u64 = 16;
232
233pub const ECADD_COST: u64 = 150;
234pub const ECMUL_COST: u64 = 6000;
235
236pub const ECPAIRING_BASE_COST: u64 = 45000;
237pub const ECPAIRING_GROUP_COST: u64 = 34000;
238
239pub const POINT_EVALUATION_COST: u64 = 50000;
240
241pub const BLAKE2F_ROUND_COST: u64 = 1;
242
243pub const BLS12_381_MSM_MULTIPLIER: u64 = 1000;
244pub const BLS12_381_G1_K_DISCOUNT: [u64; 128] = [
245 1000, 949, 848, 797, 764, 750, 738, 728, 719, 712, 705, 698, 692, 687, 682, 677, 673, 669, 665,
246 661, 658, 654, 651, 648, 645, 642, 640, 637, 635, 632, 630, 627, 625, 623, 621, 619, 617, 615,
247 613, 611, 609, 608, 606, 604, 603, 601, 599, 598, 596, 595, 593, 592, 591, 589, 588, 586, 585,
248 584, 582, 581, 580, 579, 577, 576, 575, 574, 573, 572, 570, 569, 568, 567, 566, 565, 564, 563,
249 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 551, 550, 549, 548, 547, 547, 546, 545,
250 544, 543, 542, 541, 540, 540, 539, 538, 537, 536, 536, 535, 534, 533, 532, 532, 531, 530, 529,
251 528, 528, 527, 526, 525, 525, 524, 523, 522, 522, 521, 520, 520, 519,
252];
253pub const G1_MUL_COST: u64 = 12000;
254pub const BLS12_381_G2_K_DISCOUNT: [u64; 128] = [
255 1000, 1000, 923, 884, 855, 832, 812, 796, 782, 770, 759, 749, 740, 732, 724, 717, 711, 704,
256 699, 693, 688, 683, 679, 674, 670, 666, 663, 659, 655, 652, 649, 646, 643, 640, 637, 634, 632,
257 629, 627, 624, 622, 620, 618, 615, 613, 611, 609, 607, 606, 604, 602, 600, 598, 597, 595, 593,
258 592, 590, 589, 587, 586, 584, 583, 582, 580, 579, 578, 576, 575, 574, 573, 571, 570, 569, 568,
259 567, 566, 565, 563, 562, 561, 560, 559, 558, 557, 556, 555, 554, 553, 552, 552, 551, 550, 549,
260 548, 547, 546, 545, 545, 544, 543, 542, 541, 541, 540, 539, 538, 537, 537, 536, 535, 535, 534,
261 533, 532, 532, 531, 530, 530, 529, 528, 528, 527, 526, 526, 525, 524, 524,
262];
263pub const G2_MUL_COST: u64 = 22500;
264
265pub fn exp(exponent: U256) -> Result<u64, VMError> {
266 let exponent_byte_size = (exponent.bits().checked_add(7).ok_or(OutOfGas)?) / 8;
267
268 let exponent_byte_size: u64 = exponent_byte_size
269 .try_into()
270 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
271
272 let exponent_byte_size_cost = EXP_DYNAMIC_BASE
273 .checked_mul(exponent_byte_size)
274 .ok_or(OutOfGas)?;
275
276 EXP_STATIC
277 .checked_add(exponent_byte_size_cost)
278 .ok_or(OutOfGas.into())
279}
280
281pub fn calldatacopy(
282 new_memory_size: usize,
283 current_memory_size: usize,
284 size: usize,
285) -> Result<u64, VMError> {
286 copy_behavior(
287 new_memory_size,
288 current_memory_size,
289 size,
290 CALLDATACOPY_DYNAMIC_BASE,
291 CALLDATACOPY_STATIC,
292 )
293}
294
295pub fn codecopy(
296 new_memory_size: usize,
297 current_memory_size: usize,
298 size: usize,
299) -> Result<u64, VMError> {
300 copy_behavior(
301 new_memory_size,
302 current_memory_size,
303 size,
304 CODECOPY_DYNAMIC_BASE,
305 CODECOPY_STATIC,
306 )
307}
308
309pub fn exit_opcode(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
311 memory::expansion_cost(new_memory_size, current_memory_size)
312}
313
314pub fn returndatacopy(
315 new_memory_size: usize,
316 current_memory_size: usize,
317 size: usize,
318) -> Result<u64, VMError> {
319 copy_behavior(
320 new_memory_size,
321 current_memory_size,
322 size,
323 RETURNDATACOPY_DYNAMIC_BASE,
324 RETURNDATACOPY_STATIC,
325 )
326}
327
328fn copy_behavior(
329 new_memory_size: usize,
330 current_memory_size: usize,
331 size: usize,
332 dynamic_base: u64,
333 static_cost: u64,
334) -> Result<u64, VMError> {
335 let minimum_word_size = (size
336 .checked_add(WORD_SIZE)
337 .ok_or(OutOfGas)?
338 .saturating_sub(1))
339 / WORD_SIZE;
340
341 let minimum_word_size: u64 = minimum_word_size
342 .try_into()
343 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
344
345 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
346
347 let minimum_word_size_cost = dynamic_base
348 .checked_mul(minimum_word_size)
349 .ok_or(OutOfGas)?;
350 static_cost
351 .checked_add(minimum_word_size_cost)
352 .ok_or(OutOfGas)?
353 .checked_add(memory_expansion_cost)
354 .ok_or(OutOfGas.into())
355}
356
357pub fn keccak256(
358 new_memory_size: usize,
359 current_memory_size: usize,
360 size: usize,
361) -> Result<u64, VMError> {
362 copy_behavior(
363 new_memory_size,
364 current_memory_size,
365 size,
366 KECCAK25_DYNAMIC_BASE,
367 KECCAK25_STATIC,
368 )
369}
370
371pub fn log(
372 new_memory_size: usize,
373 current_memory_size: usize,
374 size: usize,
375 number_of_topics: usize,
376) -> Result<u64, VMError> {
377 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
378
379 #[expect(clippy::as_conversions)]
382 let topics_cost = LOGN_DYNAMIC_BASE
383 .checked_mul(number_of_topics as u64)
384 .ok_or(OutOfGas)?;
385
386 let size: u64 = size
387 .try_into()
388 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
389 let bytes_cost = LOGN_DYNAMIC_BYTE_BASE.checked_mul(size).ok_or(OutOfGas)?;
390
391 topics_cost
392 .checked_add(LOGN_STATIC)
393 .ok_or(OutOfGas)?
394 .checked_add(bytes_cost)
395 .ok_or(OutOfGas)?
396 .checked_add(memory_expansion_cost)
397 .ok_or(OutOfGas.into())
398}
399
400pub fn mload(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
401 mem_expansion_behavior(new_memory_size, current_memory_size, MLOAD_STATIC)
402}
403
404pub fn mstore(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
405 mem_expansion_behavior(new_memory_size, current_memory_size, MSTORE_STATIC)
406}
407
408pub fn mstore8(new_memory_size: usize, current_memory_size: usize) -> Result<u64, VMError> {
409 mem_expansion_behavior(new_memory_size, current_memory_size, MSTORE8_STATIC)
410}
411
412fn mem_expansion_behavior(
413 new_memory_size: usize,
414 current_memory_size: usize,
415 static_cost: u64,
416) -> Result<u64, VMError> {
417 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
418
419 static_cost
420 .checked_add(memory_expansion_cost)
421 .ok_or(OutOfGas.into())
422}
423
424pub fn sload(storage_slot_was_cold: bool) -> Result<u64, VMError> {
425 let static_gas = SLOAD_STATIC;
426 let dynamic_cost = if storage_slot_was_cold {
427 SLOAD_COLD_DYNAMIC
428 } else {
429 SLOAD_WARM_DYNAMIC
430 };
431 static_gas.checked_add(dynamic_cost).ok_or(OutOfGas.into())
432}
433
434pub fn sstore(
435 original_value: U256,
436 current_value: U256,
437 new_value: U256,
438 storage_slot_was_cold: bool,
439 fork: Fork,
440) -> Result<u64, VMError> {
441 let static_gas = SSTORE_STATIC;
442
443 let mut base_dynamic_gas = if new_value == current_value {
444 SSTORE_DEFAULT_DYNAMIC
445 } else if current_value == original_value {
446 if original_value.is_zero() {
447 if fork >= Fork::Amsterdam {
450 SSTORE_STORAGE_MODIFICATION
451 } else {
452 SSTORE_STORAGE_CREATION
453 }
454 } else {
455 SSTORE_STORAGE_MODIFICATION
456 }
457 } else {
458 SSTORE_DEFAULT_DYNAMIC
459 };
460 if storage_slot_was_cold {
462 base_dynamic_gas = base_dynamic_gas
463 .checked_add(SSTORE_COLD_DYNAMIC)
464 .ok_or(OutOfGas)?;
465 }
466 static_gas
467 .checked_add(base_dynamic_gas)
468 .ok_or(OutOfGas.into())
469}
470
471pub fn mcopy(
472 new_memory_size: usize,
473 current_memory_size: usize,
474 size: usize,
475) -> Result<u64, VMError> {
476 let words_copied = (size
477 .checked_add(WORD_SIZE)
478 .ok_or(OutOfGas)?
479 .saturating_sub(1))
480 / WORD_SIZE;
481
482 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
483
484 let words_copied: u64 = words_copied
485 .try_into()
486 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
487
488 let copied_words_cost = MCOPY_DYNAMIC_BASE
489 .checked_mul(words_copied)
490 .ok_or(OutOfGas)?;
491
492 MCOPY_STATIC
493 .checked_add(copied_words_cost)
494 .ok_or(OutOfGas)?
495 .checked_add(memory_expansion_cost)
496 .ok_or(OutOfGas.into())
497}
498
499pub fn create(
500 new_memory_size: usize,
501 current_memory_size: usize,
502 code_size_in_memory: usize,
503 fork: Fork,
504) -> Result<u64, VMError> {
505 compute_gas_create(
506 new_memory_size,
507 current_memory_size,
508 code_size_in_memory,
509 false,
510 fork,
511 )
512}
513
514pub fn create_2(
515 new_memory_size: usize,
516 current_memory_size: usize,
517 code_size_in_memory: usize,
518 fork: Fork,
519) -> Result<u64, VMError> {
520 compute_gas_create(
521 new_memory_size,
522 current_memory_size,
523 code_size_in_memory,
524 true,
525 fork,
526 )
527}
528
529fn compute_gas_create(
530 new_memory_size: usize,
531 current_memory_size: usize,
532 code_size_in_memory: usize,
533 is_create_2: bool,
534 fork: Fork,
535) -> Result<u64, VMError> {
536 let minimum_word_size = (code_size_in_memory.checked_add(31).ok_or(OutOfGas)?) / 32;
537
538 let minimum_word_size: u64 = minimum_word_size
539 .try_into()
540 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?;
541
542 let init_code_cost = if fork >= Fork::Shanghai {
544 minimum_word_size
545 .checked_mul(INIT_CODE_WORD_COST)
546 .ok_or(OutOfGas)? } else {
548 0
549 };
550
551 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
552
553 let hash_cost = if is_create_2 {
554 minimum_word_size
555 .checked_mul(KECCAK25_DYNAMIC_BASE)
556 .ok_or(OutOfGas)? } else {
558 0
559 };
560
561 let create_base_cost = if fork >= Fork::Amsterdam {
562 REGULAR_GAS_CREATE
563 } else {
564 CREATE_BASE_COST
565 };
566
567 let gas_create_cost = memory_expansion_cost
568 .checked_add(init_code_cost)
569 .ok_or(OutOfGas)?
570 .checked_add(create_base_cost)
571 .ok_or(OutOfGas)?
572 .checked_add(hash_cost)
573 .ok_or(OutOfGas)?;
574
575 Ok(gas_create_cost)
576}
577
578pub fn selfdestruct_base(address_was_cold: bool) -> Result<u64, VMError> {
582 let cold_cost = if address_was_cold {
583 COLD_ADDRESS_ACCESS_COST
584 } else {
585 0
586 };
587 SELFDESTRUCT_STATIC
588 .checked_add(cold_cost)
589 .ok_or(OutOfGas.into())
590}
591
592pub fn selfdestruct(
593 address_was_cold: bool,
594 account_is_empty: bool,
595 balance_to_transfer: U256,
596 fork: Fork,
597) -> Result<u64, VMError> {
598 let mut dynamic_cost = if address_was_cold {
599 COLD_ADDRESS_ACCESS_COST
600 } else {
601 0
602 };
603
604 if account_is_empty && balance_to_transfer > U256::zero() && fork < Fork::Amsterdam {
607 dynamic_cost = dynamic_cost
608 .checked_add(SELFDESTRUCT_DYNAMIC)
609 .ok_or(OutOfGas)?;
610 }
611
612 SELFDESTRUCT_STATIC
613 .checked_add(dynamic_cost)
614 .ok_or(OutOfGas.into())
615}
616
617pub fn tx_calldata(calldata: &[u8]) -> Result<u64, VMError> {
618 let len = u64::try_from(calldata.len()).map_err(|_| InternalError::TypeConversion)?;
625 let non_zero_bytes = u64::try_from(calldata.iter().filter(|&&byte| byte != 0).count())
626 .map_err(|_| InternalError::TypeConversion)?;
627 let zero_bytes = len.saturating_sub(non_zero_bytes);
629
630 let non_zero_cost = non_zero_bytes
631 .checked_mul(CALLDATA_COST_NON_ZERO_BYTE)
632 .ok_or(OutOfGas)?;
633 let zero_cost = zero_bytes
634 .checked_mul(CALLDATA_COST_ZERO_BYTE)
635 .ok_or(OutOfGas)?;
636
637 non_zero_cost.checked_add(zero_cost).ok_or(OutOfGas.into())
638}
639
640pub fn access_list_bytes(access_list: &AccessList) -> u64 {
643 let mut bytes: u64 = 0;
644 for (_addr, keys) in access_list {
645 bytes = bytes.saturating_add(20);
646 let keys_len = u64::try_from(keys.len()).unwrap_or(u64::MAX);
647 bytes = bytes.saturating_add(32_u64.saturating_mul(keys_len));
648 }
649 bytes
650}
651
652pub fn floor_tokens_in_access_list(access_list: &AccessList) -> u64 {
655 access_list_bytes(access_list).saturating_mul(STANDARD_TOKEN_COST)
656}
657
658fn address_access_cost(
659 address_was_cold: bool,
660 static_cost: u64,
661 cold_dynamic_cost: u64,
662 warm_dynamic_cost: u64,
663) -> Result<u64, VMError> {
664 let dynamic_cost: u64 = if address_was_cold {
665 cold_dynamic_cost
666 } else {
667 warm_dynamic_cost
668 };
669
670 static_cost.checked_add(dynamic_cost).ok_or(OutOfGas.into())
671}
672
673pub fn balance(address_was_cold: bool) -> Result<u64, VMError> {
674 address_access_cost(
675 address_was_cold,
676 BALANCE_STATIC,
677 BALANCE_COLD_DYNAMIC,
678 BALANCE_WARM_DYNAMIC,
679 )
680}
681
682pub fn extcodesize(address_was_cold: bool) -> Result<u64, VMError> {
683 address_access_cost(
684 address_was_cold,
685 EXTCODESIZE_STATIC,
686 EXTCODESIZE_COLD_DYNAMIC,
687 EXTCODESIZE_WARM_DYNAMIC,
688 )
689}
690
691pub fn extcodecopy(
692 size: usize,
693 new_memory_size: usize,
694 current_memory_size: usize,
695 address_was_cold: bool,
696) -> Result<u64, VMError> {
697 let base_access_cost = copy_behavior(
698 new_memory_size,
699 current_memory_size,
700 size,
701 EXTCODECOPY_DYNAMIC_BASE,
702 EXTCODECOPY_STATIC,
703 )?;
704 let expansion_access_cost = address_access_cost(
705 address_was_cold,
706 EXTCODECOPY_STATIC,
707 EXTCODECOPY_COLD_DYNAMIC,
708 EXTCODECOPY_WARM_DYNAMIC,
709 )?;
710
711 base_access_cost
712 .checked_add(expansion_access_cost)
713 .ok_or(OutOfGas.into())
714}
715
716pub fn extcodehash(address_was_cold: bool) -> Result<u64, VMError> {
717 address_access_cost(
718 address_was_cold,
719 EXTCODEHASH_STATIC,
720 EXTCODEHASH_COLD_DYNAMIC,
721 EXTCODEHASH_WARM_DYNAMIC,
722 )
723}
724
725#[allow(clippy::too_many_arguments)]
726pub fn call(
727 new_memory_size: usize,
728 current_memory_size: usize,
729 address_was_cold: bool,
730 address_is_empty: bool,
731 value_to_transfer: U256,
732 gas_from_stack: U256,
733 gas_left: u64,
734 fork: Fork,
735) -> Result<(u64, u64), VMError> {
736 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
737
738 let address_access_cost = address_access_cost(
739 address_was_cold,
740 CALL_STATIC,
741 CALL_COLD_DYNAMIC,
742 CALL_WARM_DYNAMIC,
743 )?;
744 let positive_value_cost = if !value_to_transfer.is_zero() {
745 CALL_POSITIVE_VALUE
746 } else {
747 0
748 };
749
750 let value_to_empty_account =
752 if address_is_empty && !value_to_transfer.is_zero() && fork < Fork::Amsterdam {
753 CALL_TO_EMPTY_ACCOUNT
754 } else {
755 0
756 };
757
758 let call_gas_costs = memory_expansion_cost
759 .checked_add(address_access_cost)
760 .ok_or(OutOfGas)?
761 .checked_add(positive_value_cost)
762 .ok_or(OutOfGas)?
763 .checked_add(value_to_empty_account)
764 .ok_or(OutOfGas)?;
765
766 calculate_cost_and_gas_limit_call(
767 value_to_transfer.is_zero(),
768 gas_from_stack,
769 gas_left,
770 call_gas_costs,
771 CALL_POSITIVE_VALUE_STIPEND,
772 )
773}
774
775pub fn callcode(
776 new_memory_size: usize,
777 current_memory_size: usize,
778 address_was_cold: bool,
779 value_to_transfer: U256,
780 gas_from_stack: U256,
781 gas_left: u64,
782) -> Result<(u64, u64), VMError> {
783 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
784 let address_access_cost = address_access_cost(
785 address_was_cold,
786 DELEGATECALL_STATIC,
787 DELEGATECALL_COLD_DYNAMIC,
788 DELEGATECALL_WARM_DYNAMIC,
789 )?;
790
791 let positive_value_cost = if !value_to_transfer.is_zero() {
792 CALLCODE_POSITIVE_VALUE
793 } else {
794 0
795 };
796 let call_gas_costs = memory_expansion_cost
797 .checked_add(address_access_cost)
798 .ok_or(OutOfGas)?
799 .checked_add(positive_value_cost)
800 .ok_or(OutOfGas)?;
801
802 calculate_cost_and_gas_limit_call(
803 value_to_transfer.is_zero(),
804 gas_from_stack,
805 gas_left,
806 call_gas_costs,
807 CALLCODE_POSITIVE_VALUE_STIPEND,
808 )
809}
810
811pub fn delegatecall(
812 new_memory_size: usize,
813 current_memory_size: usize,
814 address_was_cold: bool,
815 gas_from_stack: U256,
816 gas_left: u64,
817) -> Result<(u64, u64), VMError> {
818 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
819
820 let address_access_cost = address_access_cost(
821 address_was_cold,
822 DELEGATECALL_STATIC,
823 DELEGATECALL_COLD_DYNAMIC,
824 DELEGATECALL_WARM_DYNAMIC,
825 )?;
826
827 let call_gas_costs = memory_expansion_cost
828 .checked_add(address_access_cost)
829 .ok_or(OutOfGas)?;
830
831 calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
832}
833
834pub fn staticcall(
835 new_memory_size: usize,
836 current_memory_size: usize,
837 address_was_cold: bool,
838 gas_from_stack: U256,
839 gas_left: u64,
840) -> Result<(u64, u64), VMError> {
841 let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?;
842
843 let address_access_cost = address_access_cost(
844 address_was_cold,
845 STATICCALL_STATIC,
846 STATICCALL_COLD_DYNAMIC,
847 STATICCALL_WARM_DYNAMIC,
848 )?;
849
850 let call_gas_costs = memory_expansion_cost
851 .checked_add(address_access_cost)
852 .ok_or(OutOfGas)?;
853
854 calculate_cost_and_gas_limit_call(true, gas_from_stack, gas_left, call_gas_costs, 0)
855}
856
857pub fn sha2_256(data_size: usize) -> Result<u64, VMError> {
858 precompile(data_size, SHA2_256_STATIC_COST, SHA2_256_DYNAMIC_BASE)
859}
860
861pub fn ripemd_160(data_size: usize) -> Result<u64, VMError> {
862 precompile(data_size, RIPEMD_160_STATIC_COST, RIPEMD_160_DYNAMIC_BASE)
863}
864
865pub fn identity(data_size: usize) -> Result<u64, VMError> {
866 precompile(data_size, IDENTITY_STATIC_COST, IDENTITY_DYNAMIC_BASE)
867}
868
869pub fn modexp(
870 exponent_first_32_bytes: &Natural,
871 base_size: usize,
872 exponent_size: usize,
873 modulus_size: usize,
874 fork: Fork,
875) -> Result<u64, VMError> {
876 let base_size: u64 = base_size
877 .try_into()
878 .map_err(|_| PrecompileError::ParsingInputError)?;
879 let exponent_size: u64 = exponent_size
880 .try_into()
881 .map_err(|_| PrecompileError::ParsingInputError)?;
882 let modulus_size: u64 = modulus_size
883 .try_into()
884 .map_err(|_| PrecompileError::ParsingInputError)?;
885
886 let max_length = base_size.max(modulus_size);
887
888 let words = (max_length.checked_add(7).ok_or(OutOfGas)?) / 8;
891
892 let multiplication_complexity = if fork >= Fork::Osaka {
893 if max_length > 32 {
894 2_u64
895 .checked_mul(words.checked_pow(2).ok_or(OutOfGas)?)
896 .ok_or(OutOfGas)?
897 } else {
898 16
899 }
900 } else {
901 words.checked_pow(2).ok_or(OutOfGas)?
902 };
903
904 let modexp_exponent_factor = if fork >= Fork::Osaka {
905 MODEXP_EXPONENT_FACTOR_OSAKA
906 } else {
907 MODEXP_EXPONENT_FACTOR
908 };
909
910 let calculate_iteration_count =
911 if exponent_size <= 32 && *exponent_first_32_bytes != Natural::ZERO {
912 exponent_first_32_bytes
913 .significant_bits()
914 .checked_sub(1)
915 .ok_or(InternalError::Underflow)?
916 } else if exponent_size > 32 {
917 let extra_size = (exponent_size
918 .checked_sub(32)
919 .ok_or(InternalError::Underflow)?)
920 .checked_mul(modexp_exponent_factor)
921 .ok_or(OutOfGas)?;
922 extra_size
923 .checked_add(exponent_first_32_bytes.significant_bits().max(1))
924 .ok_or(OutOfGas)?
925 .checked_sub(1)
926 .ok_or(InternalError::Underflow)?
927 } else {
928 0
929 }
930 .max(1);
931
932 let modexp_static_cost = if fork >= Fork::Osaka {
933 MODEXP_STATIC_COST_OSAKA
934 } else {
935 MODEXP_STATIC_COST
936 };
937
938 let modexp_dynamic_quotient = if fork >= Fork::Osaka {
939 MODEXP_DYNAMIC_QUOTIENT_OSAKA
940 } else {
941 MODEXP_DYNAMIC_QUOTIENT
942 };
943
944 let cost = modexp_static_cost.max(
945 multiplication_complexity
946 .checked_mul(calculate_iteration_count)
947 .ok_or(OutOfGas)?
948 .checked_div(modexp_dynamic_quotient)
949 .ok_or(OutOfGas)?,
950 );
951 Ok(cost)
952}
953
954fn precompile(data_size: usize, static_cost: u64, dynamic_base: u64) -> Result<u64, VMError> {
955 let data_size: u64 = data_size
956 .try_into()
957 .map_err(|_| PrecompileError::ParsingInputError)?;
958
959 let data_word_cost = data_size
960 .checked_add(WORD_SIZE_IN_BYTES_U64 - 1)
961 .ok_or(OutOfGas)?
962 / WORD_SIZE_IN_BYTES_U64;
963
964 let static_gas = static_cost;
965 let dynamic_gas = dynamic_base.checked_mul(data_word_cost).ok_or(OutOfGas)?;
966
967 static_gas.checked_add(dynamic_gas).ok_or(OutOfGas.into())
968}
969
970pub fn ecpairing(groups_number: usize) -> Result<u64, VMError> {
971 let groups_number = u64::try_from(groups_number).map_err(|_| InternalError::TypeConversion)?;
972
973 let groups_cost = groups_number
974 .checked_mul(ECPAIRING_GROUP_COST)
975 .ok_or(OutOfGas)?;
976 groups_cost
977 .checked_add(ECPAIRING_BASE_COST)
978 .ok_or(OutOfGas.into())
979}
980
981#[expect(clippy::arithmetic_side_effects, reason = "can't overflow")]
984#[expect(clippy::as_conversions, reason = "remaining gas conversion")]
985pub fn max_message_call_gas(current_call_frame: &CallFrame) -> Result<u64, VMError> {
986 let mut remaining_gas = current_call_frame.gas_remaining;
987
988 remaining_gas -= remaining_gas / 64;
989
990 Ok(remaining_gas as u64)
991}
992
993fn calculate_cost_and_gas_limit_call(
994 value_is_zero: bool,
995 gas_from_stack: U256,
996 gas_left: u64,
997 call_gas_costs: u64,
998 stipend: u64,
999) -> Result<(u64, u64), VMError> {
1000 let gas_stipend = if value_is_zero { 0 } else { stipend };
1001 let gas_left = gas_left.checked_sub(call_gas_costs).ok_or(OutOfGas)?;
1002
1003 let max_gas_for_call = gas_left.checked_sub(gas_left / 64).ok_or(OutOfGas)?;
1005
1006 let gas: u64 = gas_from_stack
1007 .min(max_gas_for_call.into())
1008 .try_into()
1009 .map_err(|_err| ExceptionalHalt::OutOfGas)?;
1010
1011 Ok((
1012 gas.checked_add(call_gas_costs)
1013 .ok_or(ExceptionalHalt::OutOfGas)?,
1014 gas.checked_add(gas_stipend)
1015 .ok_or(ExceptionalHalt::OutOfGas)?,
1016 ))
1017}
1018
1019pub fn bls12_msm(k: usize, discount_table: &[u64; 128], mul_cost: u64) -> Result<u64, VMError> {
1020 if k == 0 {
1021 return Ok(0);
1022 }
1023
1024 let discount = if k < discount_table.len() {
1025 discount_table
1026 .get(k.checked_sub(1).ok_or(InternalError::Underflow)?)
1027 .copied()
1028 .ok_or(InternalError::Slicing)?
1029 } else {
1030 discount_table
1031 .last()
1032 .copied()
1033 .ok_or(InternalError::Slicing)?
1034 };
1035
1036 let gas_cost = u64::try_from(k)
1037 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?
1038 .checked_mul(mul_cost)
1039 .ok_or(ExceptionalHalt::VeryLargeNumber)?
1040 .checked_mul(discount)
1041 .ok_or(ExceptionalHalt::VeryLargeNumber)?
1042 / BLS12_381_MSM_MULTIPLIER;
1043 Ok(gas_cost)
1044}
1045
1046pub fn bls12_pairing_check(k: usize) -> Result<u64, VMError> {
1047 let gas_cost = u64::try_from(k)
1048 .map_err(|_| ExceptionalHalt::VeryLargeNumber)?
1049 .checked_mul(BLS12_PAIRING_CHECK_MUL_COST)
1050 .ok_or(InternalError::Overflow)?
1051 .checked_add(BLS12_PAIRING_CHECK_FIXED_COST)
1052 .ok_or(InternalError::Overflow)?;
1053 Ok(gas_cost)
1054}