1use crate::{
15 call_frame::CallFrame,
16 constants::{AMSTERDAM_INIT_CODE_MAX_SIZE, FAIL, INIT_CODE_MAX_SIZE, SUCCESS},
17 errors::{ContextResult, ExceptionalHalt, InternalError, OpcodeResult, TxResult, VMError},
18 gas_cost,
19 memory::{self, calculate_memory_size},
20 opcode_handlers::OpcodeHandler,
21 precompiles,
22 utils::{address_to_word, create_eth_transfer_log, word_to_address, *},
23 vm::VM,
24};
25use bytes::Bytes;
26use ethrex_common::{Address, H256, U256, evm::calculate_create_address, types::Fork};
27use ethrex_common::{tracing::CallType, types::Code};
28
29pub struct OpCallHandler;
30impl OpcodeHandler for OpCallHandler {
31 #[inline(always)]
32 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
33 let [
34 gas,
35 callee,
36 value,
37 args_offset,
38 args_len,
39 return_offset,
40 return_len,
41 ] = *vm.current_call_frame.stack.pop()?;
42 let callee = word_to_address(callee);
43 let (args_len, args_offset) = size_offset_to_usize(args_len, args_offset)?;
44 let (return_len, return_offset) = size_offset_to_usize(return_len, return_offset)?;
45
46 if vm.current_call_frame.is_static && !value.is_zero() {
48 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
49 }
50
51 let value_cost = if !value.is_zero() {
52 gas_cost::call_positive_value_cost(vm.env.config.fork)
53 } else {
54 0
55 };
56 let (new_memory_size, address_was_cold, static_cost) = vm.check_call_static_gas(
57 args_offset,
58 args_len,
59 return_offset,
60 return_len,
61 callee,
62 value_cost,
63 )?;
64
65 vm.substate.add_accessed_address(callee);
66 let address_is_empty = if value.is_zero() {
70 false
71 } else {
72 vm.db.get_account(callee)?.is_empty()
73 };
74 let (callee_code, delegation) =
78 eip7702_peek_delegation(vm.db, &vm.substate, callee, vm.env.config.fork)?;
79 let is_delegation_7702 = delegation.is_some();
80 let (eip7702_gas_consumed, code_address) = match delegation {
81 Some((auth_address, access_cost)) => (access_cost, auth_address),
82 None => (0, callee),
83 };
84 let create_cost = if address_is_empty {
85 gas_cost::CALL_TO_EMPTY_ACCOUNT
86 } else {
87 0
88 };
89
90 vm.record_bal_call_touch(
93 callee,
94 code_address,
95 is_delegation_7702,
96 eip7702_gas_consumed,
97 new_memory_size,
98 vm.current_call_frame.memory.len(),
99 address_was_cold,
100 value_cost,
101 create_cost,
102 );
103
104 let bytecode = if let Some((auth_address, access_cost)) = delegation {
107 vm.current_call_frame.check_gas(
108 static_cost
109 .checked_add(access_cost)
110 .ok_or(ExceptionalHalt::OutOfGas)?,
111 )?;
112 vm.substate.add_accessed_address(auth_address);
113 vm.db.get_account_code(auth_address)?.clone()
114 } else {
115 callee_code
116 };
117
118 let fork = vm.env.config.fork;
119
120 #[expect(clippy::as_conversions, reason = "safe")]
122 let gas_left = (vm.current_call_frame.gas_remaining as u64)
123 .checked_sub(eip7702_gas_consumed)
124 .ok_or(ExceptionalHalt::OutOfGas)?;
125
126 let needs_state_gas = fork >= Fork::Amsterdam && address_is_empty;
131 let gas_left = if needs_state_gas {
132 let state_gas_new_account = vm.state_gas_new_account;
133 let from_reservoir = vm.state_gas_reservoir.min(state_gas_new_account);
134 #[expect(
136 clippy::arithmetic_side_effects,
137 reason = "from_reservoir <= state_gas_new_account"
138 )]
139 let spill = state_gas_new_account - from_reservoir;
140 gas_left
141 .checked_sub(spill)
142 .ok_or(ExceptionalHalt::OutOfGas)?
143 } else {
144 gas_left
145 };
146
147 let (gas_cost, gas_limit) = gas_cost::call(
148 new_memory_size,
149 vm.current_call_frame.memory.len(),
150 address_was_cold,
151 address_is_empty,
152 value,
153 gas,
154 gas_left,
155 fork,
156 )?;
157
158 vm.current_call_frame.increase_consumed_gas(
160 gas_cost
161 .checked_add(eip7702_gas_consumed)
162 .ok_or(ExceptionalHalt::OutOfGas)?,
163 )?;
164
165 if needs_state_gas {
167 vm.increase_state_gas(vm.state_gas_new_account)?;
168 }
169
170 if vm.opcode_tracer.active {
176 let geth_cost = gas_cost.saturating_add(eip7702_gas_consumed);
177 vm.opcode_tracer.last_opcode_gas_cost = Some(geth_cost);
178 }
179
180 vm.current_call_frame.memory.resize(new_memory_size)?;
185
186 let data = vm.get_calldata(args_offset, args_len)?;
188 vm.tracer.enter(
189 CallType::CALL,
190 vm.current_call_frame.to,
191 callee,
192 value,
193 gas_limit,
194 &data,
195 );
196
197 vm.generic_call(
199 gas_limit,
200 value,
201 vm.current_call_frame.to,
202 callee,
203 code_address,
204 true,
205 vm.current_call_frame.is_static,
206 data,
207 return_offset,
208 return_len,
209 bytecode,
210 is_delegation_7702,
211 needs_state_gas,
212 )
213 }
214}
215
216pub struct OpCallCodeHandler;
217impl OpcodeHandler for OpCallCodeHandler {
218 #[inline(always)]
219 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
220 let [
221 gas,
222 address,
223 value,
224 args_offset,
225 args_len,
226 return_offset,
227 return_len,
228 ] = *vm.current_call_frame.stack.pop()?;
229 let address = word_to_address(address);
230 let (args_len, args_offset) = size_offset_to_usize(args_len, args_offset)?;
231 let (return_len, return_offset) = size_offset_to_usize(return_len, return_offset)?;
232
233 let value_cost = if !value.is_zero() {
234 gas_cost::call_positive_value_cost(vm.env.config.fork)
235 } else {
236 0
237 };
238 let (new_memory_size, address_was_cold, static_cost) = vm.check_call_static_gas(
239 args_offset,
240 args_len,
241 return_offset,
242 return_len,
243 address,
244 value_cost,
245 )?;
246
247 vm.substate.add_accessed_address(address);
248 let (target_code, delegation) =
252 eip7702_peek_delegation(vm.db, &vm.substate, address, vm.env.config.fork)?;
253 let is_delegation_7702 = delegation.is_some();
254 let (eip7702_gas_consumed, code_address) = match delegation {
255 Some((auth_address, access_cost)) => (access_cost, auth_address),
256 None => (0, address),
257 };
258
259 vm.record_bal_call_touch(
261 address,
262 code_address,
263 is_delegation_7702,
264 eip7702_gas_consumed,
265 new_memory_size,
266 vm.current_call_frame.memory.len(),
267 address_was_cold,
268 value_cost,
269 0,
270 );
271
272 let bytecode = if let Some((auth_address, access_cost)) = delegation {
273 vm.current_call_frame.check_gas(
274 static_cost
275 .checked_add(access_cost)
276 .ok_or(ExceptionalHalt::OutOfGas)?,
277 )?;
278 vm.substate.add_accessed_address(auth_address);
279 vm.db.get_account_code(auth_address)?.clone()
280 } else {
281 target_code
282 };
283
284 #[expect(clippy::as_conversions, reason = "safe")]
285 let gas_left = (vm.current_call_frame.gas_remaining as u64)
286 .checked_sub(eip7702_gas_consumed)
287 .ok_or(ExceptionalHalt::OutOfGas)?;
288 let (gas_cost, gas_limit) = gas_cost::callcode(
289 new_memory_size,
290 vm.current_call_frame.memory.len(),
291 address_was_cold,
292 value,
293 gas,
294 gas_left,
295 vm.env.config.fork,
296 )?;
297 vm.current_call_frame.increase_consumed_gas(
298 gas_cost
299 .checked_add(eip7702_gas_consumed)
300 .ok_or(ExceptionalHalt::OutOfGas)?,
301 )?;
302
303 if vm.opcode_tracer.active {
305 let geth_cost = gas_cost.saturating_add(eip7702_gas_consumed);
306 vm.opcode_tracer.last_opcode_gas_cost = Some(geth_cost);
307 }
308
309 vm.current_call_frame.memory.resize(new_memory_size)?;
314
315 let data = vm.get_calldata(args_offset, args_len)?;
317 vm.tracer.enter(
318 CallType::CALLCODE,
319 vm.current_call_frame.to,
320 code_address,
321 value,
322 gas_limit,
323 &data,
324 );
325
326 vm.generic_call(
328 gas_limit,
329 value,
330 vm.current_call_frame.to,
331 vm.current_call_frame.to,
332 code_address,
333 true,
334 vm.current_call_frame.is_static,
335 data,
336 return_offset,
337 return_len,
338 bytecode,
339 is_delegation_7702,
340 false,
341 )
342 }
343}
344
345pub struct OpDelegateCallHandler;
346impl OpcodeHandler for OpDelegateCallHandler {
347 #[inline(always)]
348 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
349 let [
350 gas,
351 address,
352 args_offset,
353 args_len,
354 return_offset,
355 return_len,
356 ] = *vm.current_call_frame.stack.pop()?;
357 let address = word_to_address(address);
358 let (args_len, args_offset) = size_offset_to_usize(args_len, args_offset)?;
359 let (return_len, return_offset) = size_offset_to_usize(return_len, return_offset)?;
360
361 let (new_memory_size, address_was_cold, static_cost) =
362 vm.check_call_static_gas(args_offset, args_len, return_offset, return_len, address, 0)?;
363
364 vm.substate.add_accessed_address(address);
365 let (target_code, delegation) =
369 eip7702_peek_delegation(vm.db, &vm.substate, address, vm.env.config.fork)?;
370 let is_delegation_7702 = delegation.is_some();
371 let (eip7702_gas_consumed, code_address) = match delegation {
372 Some((auth_address, access_cost)) => (access_cost, auth_address),
373 None => (0, address),
374 };
375
376 vm.record_bal_call_touch(
378 address,
379 code_address,
380 is_delegation_7702,
381 eip7702_gas_consumed,
382 new_memory_size,
383 vm.current_call_frame.memory.len(),
384 address_was_cold,
385 0,
386 0,
387 );
388
389 let bytecode = if let Some((auth_address, access_cost)) = delegation {
390 vm.current_call_frame.check_gas(
391 static_cost
392 .checked_add(access_cost)
393 .ok_or(ExceptionalHalt::OutOfGas)?,
394 )?;
395 vm.substate.add_accessed_address(auth_address);
396 vm.db.get_account_code(auth_address)?.clone()
397 } else {
398 target_code
399 };
400
401 #[expect(clippy::as_conversions, reason = "safe")]
402 let gas_left = (vm.current_call_frame.gas_remaining as u64)
403 .checked_sub(eip7702_gas_consumed)
404 .ok_or(ExceptionalHalt::OutOfGas)?;
405 let (gas_cost, gas_limit) = gas_cost::delegatecall(
406 new_memory_size,
407 vm.current_call_frame.memory.len(),
408 address_was_cold,
409 gas,
410 gas_left,
411 vm.env.config.fork,
412 )?;
413 vm.current_call_frame.increase_consumed_gas(
414 gas_cost
415 .checked_add(eip7702_gas_consumed)
416 .ok_or(ExceptionalHalt::OutOfGas)?,
417 )?;
418
419 if vm.opcode_tracer.active {
421 let geth_cost = gas_cost.saturating_add(eip7702_gas_consumed);
422 vm.opcode_tracer.last_opcode_gas_cost = Some(geth_cost);
423 }
424
425 vm.current_call_frame.memory.resize(new_memory_size)?;
430
431 let data = vm.get_calldata(args_offset, args_len)?;
433 vm.tracer.enter(
436 CallType::DELEGATECALL,
437 vm.current_call_frame.to,
438 code_address,
439 vm.current_call_frame.msg_value,
440 gas_limit,
441 &data,
442 );
443
444 vm.generic_call(
446 gas_limit,
447 vm.current_call_frame.msg_value,
448 vm.current_call_frame.msg_sender,
449 vm.current_call_frame.to,
450 code_address,
451 false,
452 vm.current_call_frame.is_static,
453 data,
454 return_offset,
455 return_len,
456 bytecode,
457 is_delegation_7702,
458 false,
459 )
460 }
461}
462
463pub struct OpStaticCallHandler;
464impl OpcodeHandler for OpStaticCallHandler {
465 #[inline(always)]
466 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
467 let [
468 gas,
469 address,
470 args_offset,
471 args_len,
472 return_offset,
473 return_len,
474 ] = *vm.current_call_frame.stack.pop()?;
475 let address = word_to_address(address);
476 let (args_len, args_offset) = size_offset_to_usize(args_len, args_offset)?;
477 let (return_len, return_offset) = size_offset_to_usize(return_len, return_offset)?;
478
479 let (new_memory_size, address_was_cold, static_cost) =
480 vm.check_call_static_gas(args_offset, args_len, return_offset, return_len, address, 0)?;
481
482 vm.substate.add_accessed_address(address);
483 let (target_code, delegation) =
487 eip7702_peek_delegation(vm.db, &vm.substate, address, vm.env.config.fork)?;
488 let is_delegation_7702 = delegation.is_some();
489 let (eip7702_gas_consumed, code_address) = match delegation {
490 Some((auth_address, access_cost)) => (access_cost, auth_address),
491 None => (0, address),
492 };
493
494 vm.record_bal_call_touch(
496 address,
497 code_address,
498 is_delegation_7702,
499 eip7702_gas_consumed,
500 new_memory_size,
501 vm.current_call_frame.memory.len(),
502 address_was_cold,
503 0,
504 0,
505 );
506
507 let bytecode = if let Some((auth_address, access_cost)) = delegation {
508 vm.current_call_frame.check_gas(
509 static_cost
510 .checked_add(access_cost)
511 .ok_or(ExceptionalHalt::OutOfGas)?,
512 )?;
513 vm.substate.add_accessed_address(auth_address);
514 vm.db.get_account_code(auth_address)?.clone()
515 } else {
516 target_code
517 };
518
519 #[expect(clippy::as_conversions, reason = "safe")]
520 let gas_left = (vm.current_call_frame.gas_remaining as u64)
521 .checked_sub(eip7702_gas_consumed)
522 .ok_or(ExceptionalHalt::OutOfGas)?;
523 let (gas_cost, gas_limit) = gas_cost::staticcall(
524 new_memory_size,
525 vm.current_call_frame.memory.len(),
526 address_was_cold,
527 gas,
528 gas_left,
529 vm.env.config.fork,
530 )?;
531 vm.current_call_frame.increase_consumed_gas(
532 gas_cost
533 .checked_add(eip7702_gas_consumed)
534 .ok_or(ExceptionalHalt::OutOfGas)?,
535 )?;
536
537 if vm.opcode_tracer.active {
539 let geth_cost = gas_cost.saturating_add(eip7702_gas_consumed);
540 vm.opcode_tracer.last_opcode_gas_cost = Some(geth_cost);
541 }
542
543 vm.current_call_frame.memory.resize(new_memory_size)?;
548
549 let data = vm.get_calldata(args_offset, args_len)?;
551 vm.tracer.enter(
552 CallType::STATICCALL,
553 vm.current_call_frame.to,
554 address,
555 U256::zero(),
556 gas_limit,
557 &data,
558 );
559
560 vm.generic_call(
562 gas_limit,
563 U256::zero(),
564 vm.current_call_frame.to,
565 address,
566 address,
567 true,
568 true,
569 data,
570 return_offset,
571 return_len,
572 bytecode,
573 is_delegation_7702,
574 false,
575 )
576 }
577}
578
579pub struct OpReturnHandler;
580impl OpcodeHandler for OpReturnHandler {
581 #[inline(always)]
582 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
583 let [offset, len] = *vm.current_call_frame.stack.pop()?;
584 let (len, offset) = size_offset_to_usize(len, offset)?;
585
586 vm.current_call_frame
587 .increase_consumed_gas(gas_cost::exit_opcode(
588 calculate_memory_size(offset, len)?,
589 vm.current_call_frame.memory.len(),
590 )?)?;
591
592 if len != 0 {
593 vm.current_call_frame.output = vm.current_call_frame.memory.load_range(offset, len)?;
594 }
595
596 Ok(OpcodeResult::Halt)
597 }
598}
599
600pub struct OpCreateHandler;
601impl OpcodeHandler for OpCreateHandler {
602 #[inline(always)]
603 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
604 if vm.env.config.fork >= Fork::Amsterdam && vm.current_call_frame.is_static {
607 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
608 }
609
610 let [value_in_wei, code_offset, code_len] = *vm.current_call_frame.stack.pop()?;
611 let (code_len, code_offset) = size_offset_to_usize(code_len, code_offset)?;
612
613 let create_gas = gas_cost::create(
614 calculate_memory_size(code_offset, code_len)?,
615 vm.current_call_frame.memory.len(),
616 code_len,
617 vm.env.config.fork,
618 )?;
619 vm.current_call_frame.increase_consumed_gas(create_gas)?;
620
621 if vm.opcode_tracer.active {
623 vm.opcode_tracer.last_opcode_gas_cost = Some(create_gas);
624 }
625
626 vm.generic_create(value_in_wei, code_offset, code_len, None)
627 }
628}
629
630pub struct OpCreate2Handler;
631impl OpcodeHandler for OpCreate2Handler {
632 #[inline(always)]
633 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
634 if vm.env.config.fork >= Fork::Amsterdam && vm.current_call_frame.is_static {
637 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
638 }
639
640 let [value_in_wei, code_offset, code_len, salt] = *vm.current_call_frame.stack.pop()?;
641 let (code_len, code_offset) = size_offset_to_usize(code_len, code_offset)?;
642
643 let create2_gas = gas_cost::create_2(
644 calculate_memory_size(code_offset, code_len)?,
645 vm.current_call_frame.memory.len(),
646 code_len,
647 vm.env.config.fork,
648 )?;
649 vm.current_call_frame.increase_consumed_gas(create2_gas)?;
650
651 if vm.opcode_tracer.active {
653 vm.opcode_tracer.last_opcode_gas_cost = Some(create2_gas);
654 }
655
656 vm.generic_create(value_in_wei, code_offset, code_len, Some(salt))
657 }
658}
659
660pub struct OpSelfDestructHandler;
661impl OpcodeHandler for OpSelfDestructHandler {
662 #[inline(always)]
663 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
664 if vm.current_call_frame.is_static {
665 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
666 }
667
668 let beneficiary = word_to_address(vm.current_call_frame.stack.pop1()?);
669 let to = vm.current_call_frame.to;
670
671 let target_account_is_cold = vm.substate.add_accessed_address(beneficiary);
672
673 if vm.env.config.fork >= Fork::Amsterdam {
677 let base_cost =
678 gas_cost::selfdestruct_base(target_account_is_cold, vm.env.config.fork)?;
679 #[expect(clippy::as_conversions, reason = "base_cost fits in i64")]
681 if vm.current_call_frame.gas_remaining < (base_cost as i64) {
682 return Err(ExceptionalHalt::OutOfGas.into());
683 }
684 }
685
686 let target_account_is_empty = vm.db.get_account(beneficiary)?.is_empty();
687 let balance = vm.db.get_account(to)?.info.balance;
688
689 if vm.env.config.fork >= Fork::Amsterdam {
695 let accessed_slots = vm.substate.get_accessed_storage_slots(&to);
697 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
698 recorder.record_touched_address(beneficiary);
699 recorder.record_touched_address(to);
700 if balance > U256::zero() {
701 recorder.set_initial_balance(to, balance);
702 }
703 for key in &accessed_slots {
704 let slot = U256::from_big_endian(key.as_bytes());
705 recorder.record_storage_read(to, slot);
706 }
707 }
708
709 vm.current_call_frame
711 .increase_consumed_gas(gas_cost::selfdestruct(
712 target_account_is_cold,
713 target_account_is_empty,
714 balance,
715 vm.env.config.fork,
716 )?)?;
717
718 if target_account_is_empty && balance > U256::zero() {
720 vm.increase_state_gas(vm.state_gas_new_account)?;
721 }
722 } else {
723 vm.current_call_frame
724 .increase_consumed_gas(gas_cost::selfdestruct(
725 target_account_is_cold,
726 target_account_is_empty,
727 balance,
728 vm.env.config.fork,
729 )?)?;
730
731 let accessed_slots = vm.substate.get_accessed_storage_slots(&to);
733 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
734 recorder.record_touched_address(beneficiary);
735 recorder.record_touched_address(to);
736 if balance > U256::zero() {
737 recorder.set_initial_balance(to, balance);
738 }
739 for key in &accessed_slots {
740 let slot = U256::from_big_endian(key.as_bytes());
741 recorder.record_storage_read(to, slot);
742 }
743 }
744 }
745
746 if vm.env.config.fork >= Fork::Cancun {
748 if !(vm.env.config.fork >= Fork::Amsterdam && to == beneficiary) {
753 vm.transfer(to, beneficiary, balance)?;
754 }
755
756 if vm.substate.is_account_created(&to) {
758 if vm.env.config.fork < Fork::Amsterdam {
762 vm.get_account_mut(to)?.info.balance = U256::zero();
763
764 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
766 recorder.record_balance_change(to, U256::zero());
767 }
768 }
769
770 vm.substate.add_selfdestruct(to);
771 }
772
773 if vm.env.config.fork >= Fork::Amsterdam && !balance.is_zero() && to != beneficiary {
777 let log = create_eth_transfer_log(to, beneficiary, balance);
778 vm.substate.add_log(log);
779 }
781 } else {
782 vm.increase_account_balance(beneficiary, balance)?;
783 vm.get_account_mut(to)?.info.balance = U256::zero();
784
785 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
787 recorder.record_balance_change(to, U256::zero());
788 }
789
790 vm.substate.add_selfdestruct(to);
791 }
792
793 vm.tracer.enter(
794 CallType::SELFDESTRUCT,
795 vm.current_call_frame.to,
796 beneficiary,
797 balance,
798 0,
799 &Default::default(),
800 );
801 vm.tracer.exit_early(0, None)?;
802
803 Ok(OpcodeResult::Halt)
804 }
805}
806
807pub struct OpRevertHandler;
808impl OpcodeHandler for OpRevertHandler {
809 #[inline(always)]
810 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
811 let [offset, len] = *vm.current_call_frame.stack.pop()?;
812 let (len, offset) = size_offset_to_usize(len, offset)?;
813
814 vm.current_call_frame
815 .increase_consumed_gas(gas_cost::exit_opcode(
816 calculate_memory_size(offset, len)?,
817 vm.current_call_frame.memory.len(),
818 )?)?;
819
820 if len != 0 {
821 vm.current_call_frame.output = vm.current_call_frame.memory.load_range(offset, len)?;
822 }
823
824 Err(VMError::RevertOpcode)
825 }
826}
827
828impl<'a> VM<'a> {
829 pub fn generic_create(
831 &mut self,
832 value: U256,
833 code_offset_in_memory: usize,
834 code_size_in_memory: usize,
835 salt: Option<U256>,
836 ) -> Result<OpcodeResult, VMError> {
837 let init_code_max = if self.env.config.fork >= Fork::Amsterdam {
839 AMSTERDAM_INIT_CODE_MAX_SIZE
840 } else {
841 INIT_CODE_MAX_SIZE
842 };
843 if code_size_in_memory > init_code_max && self.env.config.fork >= Fork::Shanghai {
844 return Err(ExceptionalHalt::OutOfGas.into());
845 }
846
847 if self.env.config.fork >= Fork::Amsterdam {
850 self.increase_state_gas(self.state_gas_new_account)?;
851 }
852
853 let current_call_frame = &mut self.current_call_frame;
854
855 if self.env.config.fork < Fork::Amsterdam && current_call_frame.is_static {
857 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
858 }
859
860 current_call_frame.sub_return_data = Bytes::new();
862
863 let gas_limit = gas_cost::max_message_call_gas(current_call_frame)?;
865 current_call_frame.increase_consumed_gas(gas_limit)?;
866
867 let code = self
869 .current_call_frame
870 .memory
871 .load_range(code_offset_in_memory, code_size_in_memory)?;
872
873 let deployer = self.current_call_frame.to;
875 let (deployer_balance, deployer_nonce) = {
876 let deployer_account = self.db.get_account(deployer)?;
877 (deployer_account.info.balance, deployer_account.info.nonce)
878 };
879
880 let new_address = match salt {
882 Some(salt) => calculate_create2_address(deployer, &code, salt)?,
883 None => calculate_create_address(deployer, deployer_nonce),
884 };
885
886 let call_type = match salt {
888 Some(_) => CallType::CREATE2,
889 None => CallType::CREATE,
890 };
891 self.tracer
892 .enter(call_type, deployer, new_address, value, gas_limit, &code);
893
894 let new_depth = self
895 .current_call_frame
896 .depth
897 .checked_add(1)
898 .ok_or(InternalError::Overflow)?;
899
900 let checks = [
906 (deployer_balance < value, "OutOfFund"),
907 (new_depth > 1024, "MaxDepth"),
908 (deployer_nonce == u64::MAX, "MaxNonce"),
909 ];
910 for (condition, reason) in checks {
911 if condition {
912 if self.env.config.fork >= Fork::Amsterdam {
916 self.credit_state_gas_refund(self.state_gas_new_account)?;
917 }
918 self.early_revert_message_call(gas_limit, reason.to_string())?;
919 return Ok(OpcodeResult::Continue);
920 }
921 }
922
923 self.substate.add_accessed_address(new_address);
925
926 if let Some(recorder) = self.db.bal_recorder.as_mut() {
928 recorder.record_touched_address(new_address);
929 }
930
931 self.increment_account_nonce(deployer)?;
933
934 let new_account = self.get_account_mut(new_address)?;
936 if new_account.create_would_collide() {
937 if self.env.config.fork >= Fork::Amsterdam {
940 self.credit_state_gas_refund(self.state_gas_new_account)?;
941 }
942 self.current_call_frame.stack.push(FAIL)?;
943 self.tracer
944 .exit_early(gas_limit, Some("CreateAccExists".to_string()))?;
945 return Ok(OpcodeResult::Continue);
946 }
947 let target_alive = !self.get_account_mut(new_address)?.is_empty();
957
958 let bal_checkpoint = self.db.bal_recorder.as_ref().map(|r| r.checkpoint());
960
961 let mut stack = self.stack_pool.pop().unwrap_or_default();
962 stack.clear();
963
964 let next_memory = self.current_call_frame.memory.next_memory();
965
966 let mut new_call_frame = CallFrame::new(
967 deployer,
968 new_address,
969 new_address,
970 Code::from_bytecode_unchecked(code, H256::zero()),
972 value,
973 Bytes::new(),
974 false,
975 gas_limit,
976 new_depth,
977 true,
978 true,
979 0,
980 0,
981 stack,
982 next_memory,
983 );
984 new_call_frame.call_frame_backup.bal_checkpoint = bal_checkpoint;
986 new_call_frame.state_gas_used_at_entry = self.state_gas_used;
990 new_call_frame.target_alive = target_alive;
993
994 self.add_callframe(new_call_frame);
995
996 self.increment_account_nonce(new_address)?; self.transfer(deployer, new_address, value)?;
999
1000 self.substate.push_backup();
1001 self.substate.add_created_account(new_address); if self.env.config.fork >= Fork::Amsterdam && !value.is_zero() {
1006 let log = create_eth_transfer_log(deployer, new_address, value);
1007 self.substate.add_log(log);
1008 }
1009
1010 Ok(OpcodeResult::Continue)
1011 }
1012
1013 fn check_call_static_gas(
1019 &mut self,
1020 args_offset: usize,
1021 args_len: usize,
1022 return_offset: usize,
1023 return_len: usize,
1024 address: Address,
1025 value_cost: u64,
1026 ) -> Result<(usize, bool, u64), VMError> {
1027 let new_memory_size = calculate_memory_size(args_offset, args_len)?
1028 .max(calculate_memory_size(return_offset, return_len)?);
1029 let address_was_cold = !self.substate.is_address_accessed(&address);
1030 let memory_expansion_cost =
1031 memory::expansion_cost(new_memory_size, self.current_call_frame.memory.len())?;
1032 let access_gas_cost = if address_was_cold {
1033 gas_cost::cold_account_access_cost(self.env.config.fork)
1034 } else {
1035 gas_cost::WARM_ADDRESS_ACCESS_COST
1036 };
1037 let static_cost = memory_expansion_cost
1038 .checked_add(access_gas_cost)
1039 .ok_or(ExceptionalHalt::OutOfGas)?
1040 .checked_add(value_cost)
1041 .ok_or(ExceptionalHalt::OutOfGas)?;
1042 self.current_call_frame.check_gas(static_cost)?;
1043 Ok((new_memory_size, address_was_cold, static_cost))
1044 }
1045
1046 #[expect(
1049 clippy::too_many_arguments,
1050 reason = "matches EIP-7928 EELS reference parameters"
1051 )]
1052 fn record_bal_call_touch(
1053 &mut self,
1054 target: Address,
1055 code_address: Address,
1056 is_delegation_7702: bool,
1057 eip7702_gas_consumed: u64,
1058 new_memory_size: usize,
1059 current_memory_size: usize,
1060 address_was_cold: bool,
1061 value_cost: u64,
1062 create_cost: u64,
1063 ) {
1064 let Some(recorder) = self.db.bal_recorder.as_mut() else {
1065 return;
1066 };
1067 let mem_cost =
1071 memory::expansion_cost(new_memory_size, current_memory_size).unwrap_or(u64::MAX);
1072 let access_cost = if address_was_cold {
1073 gas_cost::cold_account_access_cost(self.env.config.fork)
1074 } else {
1075 gas_cost::WARM_ADDRESS_ACCESS_COST
1076 };
1077 let basic_cost = mem_cost
1078 .saturating_add(access_cost)
1079 .saturating_add(value_cost);
1080 let gas_remaining = self.current_call_frame.gas_remaining;
1081
1082 if gas_remaining >= i64::try_from(basic_cost).unwrap_or(i64::MAX) {
1083 recorder.record_touched_address(target);
1084
1085 if is_delegation_7702 {
1086 let delegation_check = basic_cost
1087 .saturating_add(create_cost)
1088 .saturating_add(eip7702_gas_consumed);
1089 if gas_remaining >= i64::try_from(delegation_check).unwrap_or(i64::MAX) {
1090 recorder.record_touched_address(code_address);
1091 }
1092 }
1093 }
1094 }
1095
1096 #[expect(
1103 clippy::too_many_arguments,
1104 reason = "inlined for performance, many args needed"
1105 )]
1106 #[inline(always)]
1107 pub fn generic_call(
1108 &mut self,
1109 gas_limit: u64,
1110 value: U256,
1111 msg_sender: Address,
1112 to: Address,
1113 code_address: Address,
1114 should_transfer_value: bool,
1115 is_static: bool,
1116 calldata: Bytes,
1117 ret_offset: usize,
1118 ret_size: usize,
1119 bytecode: Code,
1120 is_delegation_7702: bool,
1121 new_account_charged: bool,
1122 ) -> Result<OpcodeResult, VMError> {
1123 self.current_call_frame.sub_return_data.clear();
1125
1126 if should_transfer_value && !value.is_zero() {
1128 let sender_balance = self.db.get_account(msg_sender)?.info.balance;
1129 if sender_balance < value {
1130 self.refund_new_account_state_gas(new_account_charged)?;
1132 self.early_revert_message_call(gas_limit, "OutOfFund".to_string())?;
1133 return Ok(OpcodeResult::Continue);
1134 }
1135 }
1136
1137 let new_depth = self
1139 .current_call_frame
1140 .depth
1141 .checked_add(1)
1142 .ok_or(InternalError::Overflow)?;
1143 if new_depth > 1024 {
1144 self.refund_new_account_state_gas(new_account_charged)?;
1145 self.early_revert_message_call(gas_limit, "MaxDepth".to_string())?;
1146 return Ok(OpcodeResult::Continue);
1147 }
1148
1149 if precompiles::is_precompile(&code_address, self.env.config.fork, self.vm_type)
1150 && !is_delegation_7702
1151 {
1152 if let Some(recorder) = self.db.bal_recorder.as_mut() {
1154 recorder.record_touched_address(code_address);
1155 }
1156
1157 let mut gas_remaining = gas_limit;
1158 let ctx_result = Self::execute_precompile(
1159 code_address,
1160 &calldata,
1161 gas_limit,
1162 &mut gas_remaining,
1163 self.env.config.fork,
1164 self.db.store.precompile_cache(),
1165 self.crypto,
1166 )?;
1167
1168 let call_frame = &mut self.current_call_frame;
1169
1170 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1172 if ctx_result.is_success() {
1173 call_frame.gas_remaining = (call_frame.gas_remaining as u64)
1174 .checked_add(
1175 gas_limit
1176 .checked_sub(ctx_result.gas_used)
1177 .ok_or(InternalError::Underflow)?,
1178 )
1179 .ok_or(InternalError::Overflow)?
1180 as i64;
1181 }
1182
1183 call_frame.memory.store_data(
1185 ret_offset,
1186 if ctx_result.output.len() >= ret_size {
1187 ctx_result
1188 .output
1189 .get(..ret_size)
1190 .ok_or(ExceptionalHalt::OutOfBounds)?
1191 } else {
1192 &ctx_result.output
1193 },
1194 )?;
1195 call_frame.sub_return_data = ctx_result.output.clone();
1196
1197 call_frame.stack.push(match &ctx_result.result {
1199 TxResult::Success => SUCCESS,
1200 TxResult::Revert(_) => FAIL,
1201 })?;
1202
1203 self.refund_new_account_state_gas(new_account_charged && !ctx_result.is_success())?;
1207
1208 if should_transfer_value && ctx_result.is_success() {
1210 self.transfer(msg_sender, to, value)?;
1211
1212 if self.env.config.fork >= Fork::Amsterdam && !value.is_zero() && msg_sender != to {
1215 let log = create_eth_transfer_log(msg_sender, to, value);
1216 self.substate.add_log(log);
1217 }
1218 }
1219
1220 self.tracer.exit_context(&ctx_result, false)?;
1221 } else {
1222 let bal_checkpoint = self.db.bal_recorder.as_ref().map(|r| r.checkpoint());
1224
1225 let mut stack = self.stack_pool.pop().unwrap_or_default();
1226 stack.clear();
1227
1228 let next_memory = self.current_call_frame.memory.next_memory();
1229
1230 let mut new_call_frame = CallFrame::new(
1231 msg_sender,
1232 to,
1233 code_address,
1234 bytecode,
1235 value,
1236 calldata,
1237 is_static,
1238 gas_limit,
1239 new_depth,
1240 should_transfer_value,
1241 false,
1242 ret_offset,
1243 ret_size,
1244 stack,
1245 next_memory,
1246 );
1247 new_call_frame.call_frame_backup.bal_checkpoint = bal_checkpoint;
1249 new_call_frame.state_gas_used_at_entry = self.state_gas_used;
1250 new_call_frame.new_account_state_gas_charged = new_account_charged;
1251
1252 self.add_callframe(new_call_frame);
1253
1254 if should_transfer_value {
1256 self.transfer(msg_sender, to, value)?;
1257 }
1258
1259 self.substate.push_backup();
1260
1261 if should_transfer_value
1265 && self.env.config.fork >= Fork::Amsterdam
1266 && !value.is_zero()
1267 && msg_sender != to
1268 {
1269 let log = create_eth_transfer_log(msg_sender, to, value);
1270 self.substate.add_log(log);
1271 }
1272 }
1273
1274 Ok(OpcodeResult::Continue)
1275 }
1276
1277 pub fn handle_state_backup(
1285 &mut self,
1286 ctx_result: &ContextResult,
1287 consume_backup: bool,
1288 ) -> Result<(), VMError> {
1289 if ctx_result.is_success() {
1290 self.substate.commit_backup();
1291 } else {
1292 self.substate.revert_backup();
1293 if consume_backup {
1294 self.restore_cache_state_consuming()?;
1295 } else {
1296 self.restore_cache_state()?;
1297 }
1298 }
1299
1300 Ok(())
1301 }
1302
1303 pub fn handle_return(&mut self, ctx_result: &ContextResult) -> Result<(), VMError> {
1307 self.handle_state_backup(ctx_result, true)?;
1310 let executed_call_frame = self.pop_call_frame()?;
1311
1312 if executed_call_frame.is_create {
1314 self.handle_return_create(executed_call_frame, ctx_result)?;
1315 } else {
1316 self.handle_return_call(executed_call_frame, ctx_result)?;
1317 }
1318
1319 Ok(())
1320 }
1321
1322 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1323 pub fn handle_return_call(
1324 &mut self,
1325 executed_call_frame: CallFrame,
1326 ctx_result: &ContextResult,
1327 ) -> Result<(), VMError> {
1328 let CallFrame {
1329 gas_limit,
1330 ret_offset,
1331 ret_size,
1332 memory: old_callframe_memory,
1333 frame_state_gas_spilled: child_frame_state_gas_spilled,
1334 call_frame_backup,
1335 stack,
1336 new_account_state_gas_charged,
1337 ..
1338 } = executed_call_frame;
1339
1340 #[cfg(not(target_arch = "riscv64"))]
1341 old_callframe_memory.clean_from_base();
1342
1343 #[cfg(target_arch = "riscv64")]
1344 old_callframe_memory.truncate_to_base();
1345
1346 let parent_call_frame = &mut self.current_call_frame;
1347
1348 let child_unused_gas = gas_limit
1350 .checked_sub(ctx_result.gas_used)
1351 .ok_or(InternalError::Underflow)?;
1352 parent_call_frame.gas_remaining = parent_call_frame
1353 .gas_remaining
1354 .checked_add(child_unused_gas as i64)
1355 .ok_or(InternalError::Overflow)?;
1356
1357 parent_call_frame.memory.store_data(
1359 ret_offset,
1360 if ctx_result.output.len() >= ret_size {
1361 ctx_result
1362 .output
1363 .get(..ret_size)
1364 .ok_or(ExceptionalHalt::OutOfBounds)?
1365 } else {
1366 &ctx_result.output
1367 },
1368 )?;
1369
1370 parent_call_frame.sub_return_data = ctx_result.output.clone();
1371
1372 match &ctx_result.result {
1374 TxResult::Success => {
1375 self.current_call_frame.stack.push(SUCCESS)?;
1376 self.merge_call_frame_backup_with_parent(&call_frame_backup)?;
1377 self.current_call_frame.frame_state_gas_spilled = self
1383 .current_call_frame
1384 .frame_state_gas_spilled
1385 .checked_add(child_frame_state_gas_spilled)
1386 .ok_or(InternalError::Overflow)?;
1387 }
1388 TxResult::Revert(_) => {
1389 self.refund_new_account_state_gas(new_account_state_gas_charged)?;
1395 self.current_call_frame.stack.push(FAIL)?;
1396 }
1397 };
1398
1399 self.tracer.exit_context(ctx_result, false)?;
1400
1401 let mut stack = stack;
1402 stack.clear();
1403 self.stack_pool.push(stack);
1404
1405 Ok(())
1406 }
1407
1408 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1409 pub fn handle_return_create(
1410 &mut self,
1411 executed_call_frame: CallFrame,
1412 ctx_result: &ContextResult,
1413 ) -> Result<(), VMError> {
1414 let CallFrame {
1415 gas_limit,
1416 to,
1417 call_frame_backup,
1418 memory: old_callframe_memory,
1419 frame_state_gas_spilled: child_frame_state_gas_spilled,
1420 target_alive,
1421 stack,
1422 ..
1423 } = executed_call_frame;
1424
1425 #[cfg(not(target_arch = "riscv64"))]
1426 old_callframe_memory.clean_from_base();
1427
1428 #[cfg(target_arch = "riscv64")]
1429 old_callframe_memory.truncate_to_base();
1430
1431 let unused_gas = gas_limit
1433 .checked_sub(ctx_result.gas_used)
1434 .ok_or(InternalError::Underflow)?;
1435 self.current_call_frame.gas_remaining = self
1436 .current_call_frame
1437 .gas_remaining
1438 .checked_add(unused_gas as i64)
1439 .ok_or(InternalError::Overflow)?;
1440
1441 match ctx_result.result.clone() {
1443 TxResult::Success => {
1444 self.current_call_frame.stack.push(address_to_word(to))?;
1445 self.merge_call_frame_backup_with_parent(&call_frame_backup)?;
1446 self.current_call_frame.frame_state_gas_spilled = self
1452 .current_call_frame
1453 .frame_state_gas_spilled
1454 .checked_add(child_frame_state_gas_spilled)
1455 .ok_or(InternalError::Overflow)?;
1456 if self.env.config.fork >= Fork::Amsterdam && target_alive {
1465 self.credit_state_gas_refund(self.state_gas_new_account)?;
1466 }
1467 }
1468 TxResult::Revert(err) => {
1469 if self.env.config.fork >= Fork::Amsterdam {
1477 self.credit_state_gas_refund(self.state_gas_new_account)?;
1478 }
1479
1480 if err.is_revert_opcode() {
1482 self.current_call_frame.sub_return_data = ctx_result.output.clone();
1483 }
1484
1485 self.current_call_frame.stack.push(FAIL)?;
1486 }
1487 };
1488
1489 self.tracer.exit_context(ctx_result, false)?;
1490
1491 let mut stack = stack;
1492 stack.clear();
1493 self.stack_pool.push(stack);
1494
1495 Ok(())
1496 }
1497
1498 fn get_calldata(&mut self, offset: usize, size: usize) -> Result<Bytes, VMError> {
1499 self.current_call_frame.memory.load_range(offset, size)
1500 }
1501
1502 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1503 fn early_revert_message_call(&mut self, gas_limit: u64, reason: String) -> Result<(), VMError> {
1504 let callframe = &mut self.current_call_frame;
1505
1506 callframe.gas_remaining = callframe
1508 .gas_remaining
1509 .checked_add(gas_limit as i64)
1510 .ok_or(InternalError::Overflow)?;
1511 callframe.stack.push(FAIL)?; self.tracer.exit_early(0, Some(reason))?;
1514 Ok(())
1515 }
1516}