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_burn_log, 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
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) = eip7702_peek_delegation(vm.db, &vm.substate, callee)?;
78 let is_delegation_7702 = delegation.is_some();
79 let (eip7702_gas_consumed, code_address) = match delegation {
80 Some((auth_address, access_cost)) => (access_cost, auth_address),
81 None => (0, callee),
82 };
83 let create_cost = if address_is_empty {
84 gas_cost::CALL_TO_EMPTY_ACCOUNT
85 } else {
86 0
87 };
88
89 vm.record_bal_call_touch(
92 callee,
93 code_address,
94 is_delegation_7702,
95 eip7702_gas_consumed,
96 new_memory_size,
97 vm.current_call_frame.memory.len(),
98 address_was_cold,
99 value_cost,
100 create_cost,
101 );
102
103 let bytecode = if let Some((auth_address, access_cost)) = delegation {
106 vm.current_call_frame.check_gas(
107 static_cost
108 .checked_add(access_cost)
109 .ok_or(ExceptionalHalt::OutOfGas)?,
110 )?;
111 vm.substate.add_accessed_address(auth_address);
112 vm.db.get_account_code(auth_address)?.clone()
113 } else {
114 callee_code
115 };
116
117 let fork = vm.env.config.fork;
118
119 #[expect(clippy::as_conversions, reason = "safe")]
121 let gas_left = (vm.current_call_frame.gas_remaining as u64)
122 .checked_sub(eip7702_gas_consumed)
123 .ok_or(ExceptionalHalt::OutOfGas)?;
124
125 let needs_state_gas = fork >= Fork::Amsterdam && address_is_empty;
130 let gas_left = if needs_state_gas {
131 let state_gas_new_account = vm.state_gas_new_account;
132 let from_reservoir = vm.state_gas_reservoir.min(state_gas_new_account);
133 #[expect(
135 clippy::arithmetic_side_effects,
136 reason = "from_reservoir <= state_gas_new_account"
137 )]
138 let spill = state_gas_new_account - from_reservoir;
139 gas_left
140 .checked_sub(spill)
141 .ok_or(ExceptionalHalt::OutOfGas)?
142 } else {
143 gas_left
144 };
145
146 let (gas_cost, gas_limit) = gas_cost::call(
147 new_memory_size,
148 vm.current_call_frame.memory.len(),
149 address_was_cold,
150 address_is_empty,
151 value,
152 gas,
153 gas_left,
154 fork,
155 )?;
156
157 vm.current_call_frame.increase_consumed_gas(
159 gas_cost
160 .checked_add(eip7702_gas_consumed)
161 .ok_or(ExceptionalHalt::OutOfGas)?,
162 )?;
163
164 if needs_state_gas {
166 vm.increase_state_gas(vm.state_gas_new_account)?;
167 }
168
169 if vm.opcode_tracer.active {
175 let geth_cost = gas_cost.saturating_add(eip7702_gas_consumed);
176 vm.opcode_tracer.last_opcode_gas_cost = Some(geth_cost);
177 }
178
179 vm.current_call_frame.memory.resize(new_memory_size)?;
184
185 let data = vm.get_calldata(args_offset, args_len)?;
187 vm.tracer.enter(
188 CallType::CALL,
189 vm.current_call_frame.to,
190 callee,
191 value,
192 gas_limit,
193 &data,
194 );
195
196 vm.generic_call(
198 gas_limit,
199 value,
200 vm.current_call_frame.to,
201 callee,
202 code_address,
203 true,
204 vm.current_call_frame.is_static,
205 data,
206 return_offset,
207 return_len,
208 bytecode,
209 is_delegation_7702,
210 )
211 }
212}
213
214pub struct OpCallCodeHandler;
215impl OpcodeHandler for OpCallCodeHandler {
216 #[inline(always)]
217 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
218 let [
219 gas,
220 address,
221 value,
222 args_offset,
223 args_len,
224 return_offset,
225 return_len,
226 ] = *vm.current_call_frame.stack.pop()?;
227 let address = word_to_address(address);
228 let (args_len, args_offset) = size_offset_to_usize(args_len, args_offset)?;
229 let (return_len, return_offset) = size_offset_to_usize(return_len, return_offset)?;
230
231 let value_cost = if !value.is_zero() {
232 gas_cost::CALLCODE_POSITIVE_VALUE
233 } else {
234 0
235 };
236 let (new_memory_size, address_was_cold, static_cost) = vm.check_call_static_gas(
237 args_offset,
238 args_len,
239 return_offset,
240 return_len,
241 address,
242 value_cost,
243 )?;
244
245 vm.substate.add_accessed_address(address);
246 let (target_code, delegation) = eip7702_peek_delegation(vm.db, &vm.substate, address)?;
250 let is_delegation_7702 = delegation.is_some();
251 let (eip7702_gas_consumed, code_address) = match delegation {
252 Some((auth_address, access_cost)) => (access_cost, auth_address),
253 None => (0, address),
254 };
255
256 vm.record_bal_call_touch(
258 address,
259 code_address,
260 is_delegation_7702,
261 eip7702_gas_consumed,
262 new_memory_size,
263 vm.current_call_frame.memory.len(),
264 address_was_cold,
265 value_cost,
266 0,
267 );
268
269 let bytecode = if let Some((auth_address, access_cost)) = delegation {
270 vm.current_call_frame.check_gas(
271 static_cost
272 .checked_add(access_cost)
273 .ok_or(ExceptionalHalt::OutOfGas)?,
274 )?;
275 vm.substate.add_accessed_address(auth_address);
276 vm.db.get_account_code(auth_address)?.clone()
277 } else {
278 target_code
279 };
280
281 #[expect(clippy::as_conversions, reason = "safe")]
282 let gas_left = (vm.current_call_frame.gas_remaining as u64)
283 .checked_sub(eip7702_gas_consumed)
284 .ok_or(ExceptionalHalt::OutOfGas)?;
285 let (gas_cost, gas_limit) = gas_cost::callcode(
286 new_memory_size,
287 vm.current_call_frame.memory.len(),
288 address_was_cold,
289 value,
290 gas,
291 gas_left,
292 )?;
293 vm.current_call_frame.increase_consumed_gas(
294 gas_cost
295 .checked_add(eip7702_gas_consumed)
296 .ok_or(ExceptionalHalt::OutOfGas)?,
297 )?;
298
299 if vm.opcode_tracer.active {
301 let geth_cost = gas_cost.saturating_add(eip7702_gas_consumed);
302 vm.opcode_tracer.last_opcode_gas_cost = Some(geth_cost);
303 }
304
305 vm.current_call_frame.memory.resize(new_memory_size)?;
310
311 let data = vm.get_calldata(args_offset, args_len)?;
313 vm.tracer.enter(
314 CallType::CALLCODE,
315 vm.current_call_frame.to,
316 code_address,
317 value,
318 gas_limit,
319 &data,
320 );
321
322 vm.generic_call(
324 gas_limit,
325 value,
326 vm.current_call_frame.to,
327 vm.current_call_frame.to,
328 code_address,
329 true,
330 vm.current_call_frame.is_static,
331 data,
332 return_offset,
333 return_len,
334 bytecode,
335 is_delegation_7702,
336 )
337 }
338}
339
340pub struct OpDelegateCallHandler;
341impl OpcodeHandler for OpDelegateCallHandler {
342 #[inline(always)]
343 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
344 let [
345 gas,
346 address,
347 args_offset,
348 args_len,
349 return_offset,
350 return_len,
351 ] = *vm.current_call_frame.stack.pop()?;
352 let address = word_to_address(address);
353 let (args_len, args_offset) = size_offset_to_usize(args_len, args_offset)?;
354 let (return_len, return_offset) = size_offset_to_usize(return_len, return_offset)?;
355
356 let (new_memory_size, address_was_cold, static_cost) =
357 vm.check_call_static_gas(args_offset, args_len, return_offset, return_len, address, 0)?;
358
359 vm.substate.add_accessed_address(address);
360 let (target_code, delegation) = eip7702_peek_delegation(vm.db, &vm.substate, address)?;
364 let is_delegation_7702 = delegation.is_some();
365 let (eip7702_gas_consumed, code_address) = match delegation {
366 Some((auth_address, access_cost)) => (access_cost, auth_address),
367 None => (0, address),
368 };
369
370 vm.record_bal_call_touch(
372 address,
373 code_address,
374 is_delegation_7702,
375 eip7702_gas_consumed,
376 new_memory_size,
377 vm.current_call_frame.memory.len(),
378 address_was_cold,
379 0,
380 0,
381 );
382
383 let bytecode = if let Some((auth_address, access_cost)) = delegation {
384 vm.current_call_frame.check_gas(
385 static_cost
386 .checked_add(access_cost)
387 .ok_or(ExceptionalHalt::OutOfGas)?,
388 )?;
389 vm.substate.add_accessed_address(auth_address);
390 vm.db.get_account_code(auth_address)?.clone()
391 } else {
392 target_code
393 };
394
395 #[expect(clippy::as_conversions, reason = "safe")]
396 let gas_left = (vm.current_call_frame.gas_remaining as u64)
397 .checked_sub(eip7702_gas_consumed)
398 .ok_or(ExceptionalHalt::OutOfGas)?;
399 let (gas_cost, gas_limit) = gas_cost::delegatecall(
400 new_memory_size,
401 vm.current_call_frame.memory.len(),
402 address_was_cold,
403 gas,
404 gas_left,
405 )?;
406 vm.current_call_frame.increase_consumed_gas(
407 gas_cost
408 .checked_add(eip7702_gas_consumed)
409 .ok_or(ExceptionalHalt::OutOfGas)?,
410 )?;
411
412 if vm.opcode_tracer.active {
414 let geth_cost = gas_cost.saturating_add(eip7702_gas_consumed);
415 vm.opcode_tracer.last_opcode_gas_cost = Some(geth_cost);
416 }
417
418 vm.current_call_frame.memory.resize(new_memory_size)?;
423
424 let data = vm.get_calldata(args_offset, args_len)?;
426 vm.tracer.enter(
429 CallType::DELEGATECALL,
430 vm.current_call_frame.to,
431 code_address,
432 vm.current_call_frame.msg_value,
433 gas_limit,
434 &data,
435 );
436
437 vm.generic_call(
439 gas_limit,
440 vm.current_call_frame.msg_value,
441 vm.current_call_frame.msg_sender,
442 vm.current_call_frame.to,
443 code_address,
444 false,
445 vm.current_call_frame.is_static,
446 data,
447 return_offset,
448 return_len,
449 bytecode,
450 is_delegation_7702,
451 )
452 }
453}
454
455pub struct OpStaticCallHandler;
456impl OpcodeHandler for OpStaticCallHandler {
457 #[inline(always)]
458 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
459 let [
460 gas,
461 address,
462 args_offset,
463 args_len,
464 return_offset,
465 return_len,
466 ] = *vm.current_call_frame.stack.pop()?;
467 let address = word_to_address(address);
468 let (args_len, args_offset) = size_offset_to_usize(args_len, args_offset)?;
469 let (return_len, return_offset) = size_offset_to_usize(return_len, return_offset)?;
470
471 let (new_memory_size, address_was_cold, static_cost) =
472 vm.check_call_static_gas(args_offset, args_len, return_offset, return_len, address, 0)?;
473
474 vm.substate.add_accessed_address(address);
475 let (target_code, delegation) = eip7702_peek_delegation(vm.db, &vm.substate, address)?;
479 let is_delegation_7702 = delegation.is_some();
480 let (eip7702_gas_consumed, code_address) = match delegation {
481 Some((auth_address, access_cost)) => (access_cost, auth_address),
482 None => (0, address),
483 };
484
485 vm.record_bal_call_touch(
487 address,
488 code_address,
489 is_delegation_7702,
490 eip7702_gas_consumed,
491 new_memory_size,
492 vm.current_call_frame.memory.len(),
493 address_was_cold,
494 0,
495 0,
496 );
497
498 let bytecode = if let Some((auth_address, access_cost)) = delegation {
499 vm.current_call_frame.check_gas(
500 static_cost
501 .checked_add(access_cost)
502 .ok_or(ExceptionalHalt::OutOfGas)?,
503 )?;
504 vm.substate.add_accessed_address(auth_address);
505 vm.db.get_account_code(auth_address)?.clone()
506 } else {
507 target_code
508 };
509
510 #[expect(clippy::as_conversions, reason = "safe")]
511 let gas_left = (vm.current_call_frame.gas_remaining as u64)
512 .checked_sub(eip7702_gas_consumed)
513 .ok_or(ExceptionalHalt::OutOfGas)?;
514 let (gas_cost, gas_limit) = gas_cost::staticcall(
515 new_memory_size,
516 vm.current_call_frame.memory.len(),
517 address_was_cold,
518 gas,
519 gas_left,
520 )?;
521 vm.current_call_frame.increase_consumed_gas(
522 gas_cost
523 .checked_add(eip7702_gas_consumed)
524 .ok_or(ExceptionalHalt::OutOfGas)?,
525 )?;
526
527 if vm.opcode_tracer.active {
529 let geth_cost = gas_cost.saturating_add(eip7702_gas_consumed);
530 vm.opcode_tracer.last_opcode_gas_cost = Some(geth_cost);
531 }
532
533 vm.current_call_frame.memory.resize(new_memory_size)?;
538
539 let data = vm.get_calldata(args_offset, args_len)?;
541 vm.tracer.enter(
542 CallType::STATICCALL,
543 vm.current_call_frame.to,
544 address,
545 U256::zero(),
546 gas_limit,
547 &data,
548 );
549
550 vm.generic_call(
552 gas_limit,
553 U256::zero(),
554 vm.current_call_frame.to,
555 address,
556 address,
557 true,
558 true,
559 data,
560 return_offset,
561 return_len,
562 bytecode,
563 is_delegation_7702,
564 )
565 }
566}
567
568pub struct OpReturnHandler;
569impl OpcodeHandler for OpReturnHandler {
570 #[inline(always)]
571 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
572 let [offset, len] = *vm.current_call_frame.stack.pop()?;
573 let (len, offset) = size_offset_to_usize(len, offset)?;
574
575 vm.current_call_frame
576 .increase_consumed_gas(gas_cost::exit_opcode(
577 calculate_memory_size(offset, len)?,
578 vm.current_call_frame.memory.len(),
579 )?)?;
580
581 if len != 0 {
582 vm.current_call_frame.output = vm.current_call_frame.memory.load_range(offset, len)?;
583 }
584
585 Ok(OpcodeResult::Halt)
586 }
587}
588
589pub struct OpCreateHandler;
590impl OpcodeHandler for OpCreateHandler {
591 #[inline(always)]
592 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
593 if vm.env.config.fork >= Fork::Amsterdam && vm.current_call_frame.is_static {
596 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
597 }
598
599 let [value_in_wei, code_offset, code_len] = *vm.current_call_frame.stack.pop()?;
600 let (code_len, code_offset) = size_offset_to_usize(code_len, code_offset)?;
601
602 let create_gas = gas_cost::create(
603 calculate_memory_size(code_offset, code_len)?,
604 vm.current_call_frame.memory.len(),
605 code_len,
606 vm.env.config.fork,
607 )?;
608 vm.current_call_frame.increase_consumed_gas(create_gas)?;
609
610 if vm.opcode_tracer.active {
612 vm.opcode_tracer.last_opcode_gas_cost = Some(create_gas);
613 }
614
615 vm.generic_create(value_in_wei, code_offset, code_len, None)
616 }
617}
618
619pub struct OpCreate2Handler;
620impl OpcodeHandler for OpCreate2Handler {
621 #[inline(always)]
622 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
623 if vm.env.config.fork >= Fork::Amsterdam && vm.current_call_frame.is_static {
626 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
627 }
628
629 let [value_in_wei, code_offset, code_len, salt] = *vm.current_call_frame.stack.pop()?;
630 let (code_len, code_offset) = size_offset_to_usize(code_len, code_offset)?;
631
632 let create2_gas = gas_cost::create_2(
633 calculate_memory_size(code_offset, code_len)?,
634 vm.current_call_frame.memory.len(),
635 code_len,
636 vm.env.config.fork,
637 )?;
638 vm.current_call_frame.increase_consumed_gas(create2_gas)?;
639
640 if vm.opcode_tracer.active {
642 vm.opcode_tracer.last_opcode_gas_cost = Some(create2_gas);
643 }
644
645 vm.generic_create(value_in_wei, code_offset, code_len, Some(salt))
646 }
647}
648
649pub struct OpSelfDestructHandler;
650impl OpcodeHandler for OpSelfDestructHandler {
651 #[inline(always)]
652 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
653 if vm.current_call_frame.is_static {
654 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
655 }
656
657 let beneficiary = word_to_address(vm.current_call_frame.stack.pop1()?);
658 let to = vm.current_call_frame.to;
659
660 let target_account_is_cold = vm.substate.add_accessed_address(beneficiary);
661
662 if vm.env.config.fork >= Fork::Amsterdam {
666 let base_cost = gas_cost::selfdestruct_base(target_account_is_cold)?;
667 #[expect(clippy::as_conversions, reason = "base_cost fits in i64")]
669 if vm.current_call_frame.gas_remaining < (base_cost as i64) {
670 return Err(ExceptionalHalt::OutOfGas.into());
671 }
672 }
673
674 let target_account_is_empty = vm.db.get_account(beneficiary)?.is_empty();
675 let balance = vm.db.get_account(to)?.info.balance;
676
677 if vm.env.config.fork >= Fork::Amsterdam {
683 let accessed_slots = vm.substate.get_accessed_storage_slots(&to);
685 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
686 recorder.record_touched_address(beneficiary);
687 recorder.record_touched_address(to);
688 if balance > U256::zero() {
689 recorder.set_initial_balance(to, balance);
690 }
691 for key in &accessed_slots {
692 let slot = U256::from_big_endian(key.as_bytes());
693 recorder.record_storage_read(to, slot);
694 }
695 }
696
697 vm.current_call_frame
699 .increase_consumed_gas(gas_cost::selfdestruct(
700 target_account_is_cold,
701 target_account_is_empty,
702 balance,
703 vm.env.config.fork,
704 )?)?;
705
706 if target_account_is_empty && balance > U256::zero() {
708 vm.increase_state_gas(vm.state_gas_new_account)?;
709 }
710 } else {
711 vm.current_call_frame
712 .increase_consumed_gas(gas_cost::selfdestruct(
713 target_account_is_cold,
714 target_account_is_empty,
715 balance,
716 vm.env.config.fork,
717 )?)?;
718
719 let accessed_slots = vm.substate.get_accessed_storage_slots(&to);
721 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
722 recorder.record_touched_address(beneficiary);
723 recorder.record_touched_address(to);
724 if balance > U256::zero() {
725 recorder.set_initial_balance(to, balance);
726 }
727 for key in &accessed_slots {
728 let slot = U256::from_big_endian(key.as_bytes());
729 recorder.record_storage_read(to, slot);
730 }
731 }
732 }
733
734 if vm.env.config.fork >= Fork::Cancun {
736 vm.transfer(to, beneficiary, balance)?;
737
738 if vm.substate.is_account_created(&to) {
740 vm.get_account_mut(to)?.info.balance = U256::zero();
742
743 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
745 recorder.record_balance_change(to, U256::zero());
746 }
747
748 vm.substate.add_selfdestruct(to);
749 }
750
751 if vm.env.config.fork >= Fork::Amsterdam && !balance.is_zero() {
753 if to != beneficiary {
754 let log = create_eth_transfer_log(to, beneficiary, balance);
755 vm.substate.add_log(log);
756 } else if vm.substate.is_account_created(&to) {
757 let log = create_burn_log(to, balance);
760 vm.substate.add_log(log);
761 }
762 }
763 } else {
764 vm.increase_account_balance(beneficiary, balance)?;
765 vm.get_account_mut(to)?.info.balance = U256::zero();
766
767 if let Some(recorder) = vm.db.bal_recorder.as_mut() {
769 recorder.record_balance_change(to, U256::zero());
770 }
771
772 vm.substate.add_selfdestruct(to);
773 }
774
775 vm.tracer.enter(
776 CallType::SELFDESTRUCT,
777 vm.current_call_frame.to,
778 beneficiary,
779 balance,
780 0,
781 &Default::default(),
782 );
783 vm.tracer.exit_early(0, None)?;
784
785 Ok(OpcodeResult::Halt)
786 }
787}
788
789pub struct OpRevertHandler;
790impl OpcodeHandler for OpRevertHandler {
791 #[inline(always)]
792 fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
793 let [offset, len] = *vm.current_call_frame.stack.pop()?;
794 let (len, offset) = size_offset_to_usize(len, offset)?;
795
796 vm.current_call_frame
797 .increase_consumed_gas(gas_cost::exit_opcode(
798 calculate_memory_size(offset, len)?,
799 vm.current_call_frame.memory.len(),
800 )?)?;
801
802 if len != 0 {
803 vm.current_call_frame.output = vm.current_call_frame.memory.load_range(offset, len)?;
804 }
805
806 Err(VMError::RevertOpcode)
807 }
808}
809
810impl<'a> VM<'a> {
811 pub fn generic_create(
813 &mut self,
814 value: U256,
815 code_offset_in_memory: usize,
816 code_size_in_memory: usize,
817 salt: Option<U256>,
818 ) -> Result<OpcodeResult, VMError> {
819 let init_code_max = if self.env.config.fork >= Fork::Amsterdam {
821 AMSTERDAM_INIT_CODE_MAX_SIZE
822 } else {
823 INIT_CODE_MAX_SIZE
824 };
825 if code_size_in_memory > init_code_max && self.env.config.fork >= Fork::Shanghai {
826 return Err(ExceptionalHalt::OutOfGas.into());
827 }
828
829 if self.env.config.fork >= Fork::Amsterdam {
832 self.increase_state_gas(self.state_gas_new_account)?;
833 }
834
835 let current_call_frame = &mut self.current_call_frame;
836
837 if self.env.config.fork < Fork::Amsterdam && current_call_frame.is_static {
839 return Err(ExceptionalHalt::OpcodeNotAllowedInStaticContext.into());
840 }
841
842 current_call_frame.sub_return_data = Bytes::new();
844
845 let gas_limit = gas_cost::max_message_call_gas(current_call_frame)?;
847 current_call_frame.increase_consumed_gas(gas_limit)?;
848
849 let code = self
851 .current_call_frame
852 .memory
853 .load_range(code_offset_in_memory, code_size_in_memory)?;
854
855 let deployer = self.current_call_frame.to;
857 let (deployer_balance, deployer_nonce) = {
858 let deployer_account = self.db.get_account(deployer)?;
859 (deployer_account.info.balance, deployer_account.info.nonce)
860 };
861
862 let new_address = match salt {
864 Some(salt) => calculate_create2_address(deployer, &code, salt)?,
865 None => calculate_create_address(deployer, deployer_nonce),
866 };
867
868 let call_type = match salt {
870 Some(_) => CallType::CREATE2,
871 None => CallType::CREATE,
872 };
873 self.tracer
874 .enter(call_type, deployer, new_address, value, gas_limit, &code);
875
876 let new_depth = self
877 .current_call_frame
878 .depth
879 .checked_add(1)
880 .ok_or(InternalError::Overflow)?;
881
882 let checks = [
888 (deployer_balance < value, "OutOfFund"),
889 (new_depth > 1024, "MaxDepth"),
890 (deployer_nonce == u64::MAX, "MaxNonce"),
891 ];
892 for (condition, reason) in checks {
893 if condition {
894 if self.env.config.fork >= Fork::Amsterdam {
898 self.credit_state_gas_refund(self.state_gas_new_account)?;
899 }
900 self.early_revert_message_call(gas_limit, reason.to_string())?;
901 return Ok(OpcodeResult::Continue);
902 }
903 }
904
905 self.substate.add_accessed_address(new_address);
907
908 if let Some(recorder) = self.db.bal_recorder.as_mut() {
910 recorder.record_touched_address(new_address);
911 }
912
913 self.increment_account_nonce(deployer)?;
915
916 let new_account = self.get_account_mut(new_address)?;
918 if new_account.create_would_collide() {
919 if self.env.config.fork >= Fork::Amsterdam {
922 self.credit_state_gas_refund(self.state_gas_new_account)?;
923 }
924 self.current_call_frame.stack.push(FAIL)?;
925 self.tracer
926 .exit_early(gas_limit, Some("CreateAccExists".to_string()))?;
927 return Ok(OpcodeResult::Continue);
928 }
929
930 let bal_checkpoint = self.db.bal_recorder.as_ref().map(|r| r.checkpoint());
932
933 let mut stack = self.stack_pool.pop().unwrap_or_default();
934 stack.clear();
935
936 let next_memory = self.current_call_frame.memory.next_memory();
937
938 let mut new_call_frame = CallFrame::new(
939 deployer,
940 new_address,
941 new_address,
942 Code::from_bytecode_unchecked(code, H256::zero()),
944 value,
945 Bytes::new(),
946 false,
947 gas_limit,
948 new_depth,
949 true,
950 true,
951 0,
952 0,
953 stack,
954 next_memory,
955 );
956 new_call_frame.call_frame_backup.bal_checkpoint = bal_checkpoint;
958 new_call_frame.state_gas_used_at_entry = self.state_gas_used;
962
963 self.add_callframe(new_call_frame);
964
965 self.increment_account_nonce(new_address)?; self.transfer(deployer, new_address, value)?;
968
969 self.substate.push_backup();
970 self.substate.add_created_account(new_address); if self.env.config.fork >= Fork::Amsterdam && !value.is_zero() {
975 let log = create_eth_transfer_log(deployer, new_address, value);
976 self.substate.add_log(log);
977 }
978
979 Ok(OpcodeResult::Continue)
980 }
981
982 fn check_call_static_gas(
988 &mut self,
989 args_offset: usize,
990 args_len: usize,
991 return_offset: usize,
992 return_len: usize,
993 address: Address,
994 value_cost: u64,
995 ) -> Result<(usize, bool, u64), VMError> {
996 let new_memory_size = calculate_memory_size(args_offset, args_len)?
997 .max(calculate_memory_size(return_offset, return_len)?);
998 let address_was_cold = !self.substate.is_address_accessed(&address);
999 let memory_expansion_cost =
1000 memory::expansion_cost(new_memory_size, self.current_call_frame.memory.len())?;
1001 let access_gas_cost = if address_was_cold {
1002 gas_cost::COLD_ADDRESS_ACCESS_COST
1003 } else {
1004 gas_cost::WARM_ADDRESS_ACCESS_COST
1005 };
1006 let static_cost = memory_expansion_cost
1007 .checked_add(access_gas_cost)
1008 .ok_or(ExceptionalHalt::OutOfGas)?
1009 .checked_add(value_cost)
1010 .ok_or(ExceptionalHalt::OutOfGas)?;
1011 self.current_call_frame.check_gas(static_cost)?;
1012 Ok((new_memory_size, address_was_cold, static_cost))
1013 }
1014
1015 #[expect(
1018 clippy::too_many_arguments,
1019 reason = "matches EIP-7928 EELS reference parameters"
1020 )]
1021 fn record_bal_call_touch(
1022 &mut self,
1023 target: Address,
1024 code_address: Address,
1025 is_delegation_7702: bool,
1026 eip7702_gas_consumed: u64,
1027 new_memory_size: usize,
1028 current_memory_size: usize,
1029 address_was_cold: bool,
1030 value_cost: u64,
1031 create_cost: u64,
1032 ) {
1033 let Some(recorder) = self.db.bal_recorder.as_mut() else {
1034 return;
1035 };
1036 let mem_cost =
1040 memory::expansion_cost(new_memory_size, current_memory_size).unwrap_or(u64::MAX);
1041 let access_cost = if address_was_cold {
1042 gas_cost::COLD_ADDRESS_ACCESS_COST
1043 } else {
1044 gas_cost::WARM_ADDRESS_ACCESS_COST
1045 };
1046 let basic_cost = mem_cost
1047 .saturating_add(access_cost)
1048 .saturating_add(value_cost);
1049 let gas_remaining = self.current_call_frame.gas_remaining;
1050
1051 if gas_remaining >= i64::try_from(basic_cost).unwrap_or(i64::MAX) {
1052 recorder.record_touched_address(target);
1053
1054 if is_delegation_7702 {
1055 let delegation_check = basic_cost
1056 .saturating_add(create_cost)
1057 .saturating_add(eip7702_gas_consumed);
1058 if gas_remaining >= i64::try_from(delegation_check).unwrap_or(i64::MAX) {
1059 recorder.record_touched_address(code_address);
1060 }
1061 }
1062 }
1063 }
1064
1065 #[expect(
1072 clippy::too_many_arguments,
1073 reason = "inlined for performance, many args needed"
1074 )]
1075 #[inline(always)]
1076 pub fn generic_call(
1077 &mut self,
1078 gas_limit: u64,
1079 value: U256,
1080 msg_sender: Address,
1081 to: Address,
1082 code_address: Address,
1083 should_transfer_value: bool,
1084 is_static: bool,
1085 calldata: Bytes,
1086 ret_offset: usize,
1087 ret_size: usize,
1088 bytecode: Code,
1089 is_delegation_7702: bool,
1090 ) -> Result<OpcodeResult, VMError> {
1091 self.current_call_frame.sub_return_data.clear();
1093
1094 if should_transfer_value && !value.is_zero() {
1096 let sender_balance = self.db.get_account(msg_sender)?.info.balance;
1097 if sender_balance < value {
1098 self.early_revert_message_call(gas_limit, "OutOfFund".to_string())?;
1099 return Ok(OpcodeResult::Continue);
1100 }
1101 }
1102
1103 let new_depth = self
1105 .current_call_frame
1106 .depth
1107 .checked_add(1)
1108 .ok_or(InternalError::Overflow)?;
1109 if new_depth > 1024 {
1110 self.early_revert_message_call(gas_limit, "MaxDepth".to_string())?;
1111 return Ok(OpcodeResult::Continue);
1112 }
1113
1114 if precompiles::is_precompile(&code_address, self.env.config.fork, self.vm_type)
1115 && !is_delegation_7702
1116 {
1117 if let Some(recorder) = self.db.bal_recorder.as_mut() {
1119 recorder.record_touched_address(code_address);
1120 }
1121
1122 let mut gas_remaining = gas_limit;
1123 let ctx_result = Self::execute_precompile(
1124 code_address,
1125 &calldata,
1126 gas_limit,
1127 &mut gas_remaining,
1128 self.env.config.fork,
1129 self.db.store.precompile_cache(),
1130 self.crypto,
1131 )?;
1132
1133 let call_frame = &mut self.current_call_frame;
1134
1135 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1137 if ctx_result.is_success() {
1138 call_frame.gas_remaining = (call_frame.gas_remaining as u64)
1139 .checked_add(
1140 gas_limit
1141 .checked_sub(ctx_result.gas_used)
1142 .ok_or(InternalError::Underflow)?,
1143 )
1144 .ok_or(InternalError::Overflow)?
1145 as i64;
1146 }
1147
1148 call_frame.memory.store_data(
1150 ret_offset,
1151 if ctx_result.output.len() >= ret_size {
1152 ctx_result
1153 .output
1154 .get(..ret_size)
1155 .ok_or(ExceptionalHalt::OutOfBounds)?
1156 } else {
1157 &ctx_result.output
1158 },
1159 )?;
1160 call_frame.sub_return_data = ctx_result.output.clone();
1161
1162 call_frame.stack.push(match &ctx_result.result {
1164 TxResult::Success => SUCCESS,
1165 TxResult::Revert(_) => FAIL,
1166 })?;
1167
1168 if should_transfer_value && ctx_result.is_success() {
1170 self.transfer(msg_sender, to, value)?;
1171
1172 if self.env.config.fork >= Fork::Amsterdam && !value.is_zero() && msg_sender != to {
1175 let log = create_eth_transfer_log(msg_sender, to, value);
1176 self.substate.add_log(log);
1177 }
1178 }
1179
1180 self.tracer.exit_context(&ctx_result, false)?;
1181 } else {
1182 let bal_checkpoint = self.db.bal_recorder.as_ref().map(|r| r.checkpoint());
1184
1185 let mut stack = self.stack_pool.pop().unwrap_or_default();
1186 stack.clear();
1187
1188 let next_memory = self.current_call_frame.memory.next_memory();
1189
1190 let mut new_call_frame = CallFrame::new(
1191 msg_sender,
1192 to,
1193 code_address,
1194 bytecode,
1195 value,
1196 calldata,
1197 is_static,
1198 gas_limit,
1199 new_depth,
1200 should_transfer_value,
1201 false,
1202 ret_offset,
1203 ret_size,
1204 stack,
1205 next_memory,
1206 );
1207 new_call_frame.call_frame_backup.bal_checkpoint = bal_checkpoint;
1209 new_call_frame.state_gas_used_at_entry = self.state_gas_used;
1210
1211 self.add_callframe(new_call_frame);
1212
1213 if should_transfer_value {
1215 self.transfer(msg_sender, to, value)?;
1216 }
1217
1218 self.substate.push_backup();
1219
1220 if should_transfer_value
1224 && self.env.config.fork >= Fork::Amsterdam
1225 && !value.is_zero()
1226 && msg_sender != to
1227 {
1228 let log = create_eth_transfer_log(msg_sender, to, value);
1229 self.substate.add_log(log);
1230 }
1231 }
1232
1233 Ok(OpcodeResult::Continue)
1234 }
1235
1236 pub fn handle_state_backup(
1244 &mut self,
1245 ctx_result: &ContextResult,
1246 consume_backup: bool,
1247 ) -> Result<(), VMError> {
1248 if ctx_result.is_success() {
1249 self.substate.commit_backup();
1250 } else {
1251 self.substate.revert_backup();
1252 if consume_backup {
1253 self.restore_cache_state_consuming()?;
1254 } else {
1255 self.restore_cache_state()?;
1256 }
1257 }
1258
1259 Ok(())
1260 }
1261
1262 pub fn handle_return(&mut self, ctx_result: &ContextResult) -> Result<(), VMError> {
1266 self.handle_state_backup(ctx_result, true)?;
1269 let executed_call_frame = self.pop_call_frame()?;
1270
1271 if executed_call_frame.is_create {
1273 self.handle_return_create(executed_call_frame, ctx_result)?;
1274 } else {
1275 self.handle_return_call(executed_call_frame, ctx_result)?;
1276 }
1277
1278 Ok(())
1279 }
1280
1281 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1282 pub fn handle_return_call(
1283 &mut self,
1284 executed_call_frame: CallFrame,
1285 ctx_result: &ContextResult,
1286 ) -> Result<(), VMError> {
1287 let CallFrame {
1288 gas_limit,
1289 ret_offset,
1290 ret_size,
1291 memory: old_callframe_memory,
1292 state_gas_used_at_entry,
1293 call_frame_backup,
1294 stack,
1295 ..
1296 } = executed_call_frame;
1297
1298 #[cfg(not(target_arch = "riscv64"))]
1299 old_callframe_memory.clean_from_base();
1300
1301 #[cfg(target_arch = "riscv64")]
1302 old_callframe_memory.truncate_to_base();
1303
1304 let parent_call_frame = &mut self.current_call_frame;
1305
1306 let child_unused_gas = gas_limit
1308 .checked_sub(ctx_result.gas_used)
1309 .ok_or(InternalError::Underflow)?;
1310 parent_call_frame.gas_remaining = parent_call_frame
1311 .gas_remaining
1312 .checked_add(child_unused_gas as i64)
1313 .ok_or(InternalError::Overflow)?;
1314
1315 parent_call_frame.memory.store_data(
1317 ret_offset,
1318 if ctx_result.output.len() >= ret_size {
1319 ctx_result
1320 .output
1321 .get(..ret_size)
1322 .ok_or(ExceptionalHalt::OutOfBounds)?
1323 } else {
1324 &ctx_result.output
1325 },
1326 )?;
1327
1328 parent_call_frame.sub_return_data = ctx_result.output.clone();
1329
1330 match &ctx_result.result {
1332 TxResult::Success => {
1333 self.current_call_frame.stack.push(SUCCESS)?;
1334 self.merge_call_frame_backup_with_parent(&call_frame_backup)?;
1335 }
1339 TxResult::Revert(_) => {
1340 self.incorporate_child_state_gas_on_revert(state_gas_used_at_entry)?;
1341 self.current_call_frame.stack.push(FAIL)?;
1342 }
1343 };
1344
1345 self.tracer.exit_context(ctx_result, false)?;
1346
1347 let mut stack = stack;
1348 stack.clear();
1349 self.stack_pool.push(stack);
1350
1351 Ok(())
1352 }
1353
1354 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1355 pub fn handle_return_create(
1356 &mut self,
1357 executed_call_frame: CallFrame,
1358 ctx_result: &ContextResult,
1359 ) -> Result<(), VMError> {
1360 let CallFrame {
1361 gas_limit,
1362 to,
1363 call_frame_backup,
1364 memory: old_callframe_memory,
1365 state_gas_used_at_entry,
1366 stack,
1367 ..
1368 } = executed_call_frame;
1369
1370 #[cfg(not(target_arch = "riscv64"))]
1371 old_callframe_memory.clean_from_base();
1372
1373 #[cfg(target_arch = "riscv64")]
1374 old_callframe_memory.truncate_to_base();
1375
1376 let unused_gas = gas_limit
1378 .checked_sub(ctx_result.gas_used)
1379 .ok_or(InternalError::Underflow)?;
1380 self.current_call_frame.gas_remaining = self
1381 .current_call_frame
1382 .gas_remaining
1383 .checked_add(unused_gas as i64)
1384 .ok_or(InternalError::Overflow)?;
1385
1386 match ctx_result.result.clone() {
1388 TxResult::Success => {
1389 self.current_call_frame.stack.push(address_to_word(to))?;
1390 self.merge_call_frame_backup_with_parent(&call_frame_backup)?;
1391 }
1395 TxResult::Revert(err) => {
1396 self.incorporate_child_state_gas_on_revert(state_gas_used_at_entry)?;
1397
1398 if self.env.config.fork >= Fork::Amsterdam {
1402 self.credit_state_gas_refund(self.state_gas_new_account)?;
1403 }
1404
1405 if err.is_revert_opcode() {
1407 self.current_call_frame.sub_return_data = ctx_result.output.clone();
1408 }
1409
1410 self.current_call_frame.stack.push(FAIL)?;
1411 }
1412 };
1413
1414 self.tracer.exit_context(ctx_result, false)?;
1415
1416 let mut stack = stack;
1417 stack.clear();
1418 self.stack_pool.push(stack);
1419
1420 Ok(())
1421 }
1422
1423 fn get_calldata(&mut self, offset: usize, size: usize) -> Result<Bytes, VMError> {
1424 self.current_call_frame.memory.load_range(offset, size)
1425 }
1426
1427 #[expect(clippy::as_conversions, reason = "remaining gas conversion")]
1428 fn early_revert_message_call(&mut self, gas_limit: u64, reason: String) -> Result<(), VMError> {
1429 let callframe = &mut self.current_call_frame;
1430
1431 callframe.gas_remaining = callframe
1433 .gas_remaining
1434 .checked_add(gas_limit as i64)
1435 .ok_or(InternalError::Overflow)?;
1436 callframe.stack.push(FAIL)?; self.tracer.exit_early(0, Some(reason))?;
1439 Ok(())
1440 }
1441}