Skip to main content

revm_handler/
handler.rs

1use crate::{
2    evm::FrameTr,
3    execution,
4    frame::handle_reservoir_remaining_gas,
5    post_execution::{self, build_result_gas},
6    pre_execution::{self, apply_eip7702_auth_list, PreExecutionOutput},
7    validation, EvmTr, FrameResult, ItemOrResult,
8};
9use context::{
10    result::{ExecutionResult, FromStringError},
11    LocalContextTr,
12};
13use context_interface::{
14    cfg::gas_params,
15    context::{take_error, ContextError},
16    journaled_state::JournalCheckpoint,
17    result::{HaltReasonTr, InvalidHeader, InvalidTransaction, ResultGas},
18    Cfg, ContextTr, Database, JournalTr, Transaction,
19};
20use interpreter::{interpreter_action::FrameInit, GasTracker, InitialAndFloorGas, SharedMemory};
21use primitives::{TxKind, U256};
22
23/// Trait for errors that can occur during EVM execution.
24///
25/// This trait represents the minimal error requirements for EVM execution,
26/// ensuring that all necessary error types can be converted into the handler's error type.
27pub trait EvmTrError<EVM: EvmTr>:
28    From<InvalidTransaction>
29    + From<InvalidHeader>
30    + From<<<EVM::Context as ContextTr>::Db as Database>::Error>
31    + From<ContextError<<<EVM::Context as ContextTr>::Db as Database>::Error>>
32    + FromStringError
33{
34}
35
36impl<
37        EVM: EvmTr,
38        T: From<InvalidTransaction>
39            + From<InvalidHeader>
40            + From<<<EVM::Context as ContextTr>::Db as Database>::Error>
41            + From<ContextError<<<EVM::Context as ContextTr>::Db as Database>::Error>>
42            + FromStringError,
43    > EvmTrError<EVM> for T
44{
45}
46
47/// The main implementation of Ethereum Mainnet transaction execution.
48///
49/// The [`Handler::run`] method serves as the entry point for execution and provides
50/// out-of-the-box support for executing Ethereum mainnet transactions.
51///
52/// This trait allows EVM variants to customize execution logic by implementing
53/// their own method implementations.
54///
55/// The handler logic consists of four phases:
56///   * Validation - Validates tx/block/config fields and loads caller account and validates initial gas requirements and
57///     balance checks.
58///   * Pre-execution - Loads and warms accounts, deducts initial gas
59///   * Execution - Executes the main frame loop, delegating to [`EvmTr`] for creating and running call frames.
60///   * Post-execution - Calculates final refunds, validates gas floor, reimburses caller,
61///     and rewards beneficiary
62///
63///
64/// The [`Handler::catch_error`] method handles cleanup of intermediate state if an error
65/// occurs during execution.
66///
67/// # Returns
68///
69/// Returns execution status, error, gas spend and logs. State change is not returned and it is
70/// contained inside Context Journal. This setup allows multiple transactions to be chain executed.
71///
72/// To finalize the execution and obtain changed state, call [`JournalTr::finalize`] function.
73pub trait Handler {
74    /// The EVM type containing Context, Instruction, and Precompiles implementations.
75    type Evm: EvmTr<
76        Context: ContextTr<Journal: JournalTr, Local: LocalContextTr>,
77        Frame: FrameTr<FrameInit = FrameInit, FrameResult = FrameResult>,
78    >;
79    /// The error type returned by this handler.
80    type Error: EvmTrError<Self::Evm>;
81    /// The halt reason type included in the output
82    type HaltReason: HaltReasonTr;
83
84    /// The main entry point for transaction execution.
85    ///
86    /// This method calls [`Handler::run_without_catch_error`] and if it returns an error,
87    /// calls [`Handler::catch_error`] to handle the error and cleanup.
88    ///
89    /// The [`Handler::catch_error`] method ensures intermediate state is properly cleared.
90    ///
91    /// # Error handling
92    ///
93    /// In case of error, the journal can be in an inconsistent state and should be cleared by calling
94    /// [`JournalTr::discard_tx`] method or dropped.
95    ///
96    /// # Returns
97    ///
98    /// Returns execution result, error, gas spend and logs.
99    #[inline]
100    fn run(
101        &mut self,
102        evm: &mut Self::Evm,
103    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
104        // Run inner handler and catch all errors to handle cleanup.
105        match self.run_without_catch_error(evm) {
106            Ok(output) => Ok(output),
107            Err(e) => self.catch_error(evm, e),
108        }
109    }
110
111    /// Runs the system call.
112    ///
113    /// System call is a special transaction where caller is a [`crate::SYSTEM_ADDRESS`]
114    ///
115    /// It is used to call a system contracts and it skips all the `validation` and `pre-execution` and most of `post-execution` phases.
116    /// For example it will not deduct the caller or reward the beneficiary.
117    ///
118    /// State changs can be obtained by calling [`JournalTr::finalize`] method from the [`EvmTr::Context`].
119    ///
120    /// # Error handling
121    ///
122    /// By design system call should not fail and should always succeed.
123    /// In case of an error (If fetching account/storage on rpc fails), the journal can be in an inconsistent
124    /// state and should be cleared by calling [`JournalTr::discard_tx`] method or dropped.
125    #[inline]
126    fn run_system_call(
127        &mut self,
128        evm: &mut Self::Evm,
129    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
130        // dummy values that are not used.
131        let init_and_floor_gas = InitialAndFloorGas::new(0, 0);
132        let mut gas = self.tx_gas(evm, &init_and_floor_gas);
133        // System calls skip pre-execution, so the checkpoint that
134        // [`Handler::execution`] settles is opened here.
135        let checkpoint = evm.ctx().journal_mut().checkpoint();
136        // call execution and than output.
137        match self
138            .execution(evm, checkpoint, &mut gas)
139            .and_then(|exec_result| {
140                let exec_result = match exec_result {
141                    Some(exec_result) => exec_result,
142                    // Unreachable in practice: system calls carry no value and
143                    // target non-delegated system contracts, so no runtime
144                    // charges apply.
145                    None => self.runtime_oog_result(evm, &init_and_floor_gas, &mut gas)?,
146                };
147                // System calls have no intrinsic gas; build ResultGas from frame result.
148                let gas = exec_result.gas();
149                let result_gas = build_result_gas(false, gas, init_and_floor_gas);
150                self.execution_result(evm, exec_result, result_gas)
151            }) {
152            out @ Ok(_) => out,
153            Err(e) => self.catch_error(evm, e),
154        }
155    }
156
157    /// Called by [`Handler::run`] to execute the core handler logic.
158    ///
159    /// Executes the four phases in sequence: [Handler::validate],
160    /// [Handler::pre_execution], [Handler::execution], [Handler::post_execution].
161    ///
162    /// Returns any errors without catching them or calling [`Handler::catch_error`].
163    #[inline]
164    fn run_without_catch_error(
165        &mut self,
166        evm: &mut Self::Evm,
167    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
168        let init_and_floor_gas = self.validate(evm)?;
169        // Create the transaction-level gas tracker from the validated
170        // intrinsic gas, mirroring how frames create their gas at frame init.
171        // All later phases charge and settle against it.
172        let mut gas = self.tx_gas(evm, &init_and_floor_gas);
173        // Pre-execution returns the EIP-7702 refund and the EIP-2780 runtime
174        // gas phase checkpoint. `None` — from pre-execution or execution —
175        // means the runtime gas phase ran out of gas: the transaction is
176        // included as an out-of-gas halt without entering execution.
177        let pre_execution = self.pre_execution(evm, &mut gas)?;
178
179        let refund = pre_execution.map(|pe| pe.eip7702_refund).unwrap_or(0) as i64;
180
181        let mut exec_result = None;
182        if let Some(pre_execution) = pre_execution {
183            exec_result = self.execution(evm, pre_execution.checkpoint, &mut gas)?;
184        }
185        let mut exec_result = match exec_result {
186            Some(exec_result) => exec_result,
187            None => self.runtime_oog_result(evm, &init_and_floor_gas, &mut gas)?,
188        };
189
190        let result_gas = self.post_execution(evm, &mut exec_result, init_and_floor_gas, refund)?;
191
192        // Prepare the output
193        self.execution_result(evm, exec_result, result_gas)
194    }
195
196    /// Validates the execution environment and transaction parameters.
197    ///
198    /// Calculates initial and floor gas requirements, verifies they are covered by the gas limit,
199    /// validates the transaction against state, and deducts the caller.
200    #[inline]
201    fn validate(&self, evm: &mut Self::Evm) -> Result<InitialAndFloorGas, Self::Error> {
202        self.validate_env(evm)?;
203        let mut init_and_floor_gas = self.validate_initial_tx_gas(evm)?;
204        self.validate_against_state_and_deduct_caller(evm, &mut init_and_floor_gas)?;
205        Ok(init_and_floor_gas)
206    }
207
208    /// Creates the transaction-level [`GasTracker`] from the validated initial gas.
209    ///
210    /// The gas limit is the transaction gas limit, `remaining` is the regular
211    /// gas budget left after the intrinsic gas (constrained by the EIP-8037
212    /// `TX_MAX_GAS_LIMIT` cap) and `reservoir` is the state gas pool, so the
213    /// intrinsic gas is accounted as already spent.
214    #[inline]
215    fn tx_gas(&self, evm: &mut Self::Evm, init_and_floor_gas: &InitialAndFloorGas) -> GasTracker {
216        let ctx = evm.ctx_ref();
217        let tx_gas_limit = ctx.tx().gas_limit();
218        let (remaining, reservoir) = init_and_floor_gas
219            .initial_gas_and_reservoir(tx_gas_limit, ctx.cfg().tx_gas_limit_cap());
220        GasTracker::new(tx_gas_limit, remaining, reservoir)
221    }
222
223    /// Prepares the EVM state for execution.
224    ///
225    /// Loads the beneficiary account (EIP-3651: Warm COINBASE) and all accounts/storage from the access list (EIP-2929).
226    ///
227    /// For EIP-7702 transactions, applies the authorization list and delegates successful authorizations.
228    /// Authorizations are applied before execution begins.
229    ///
230    /// Returns the pre-execution gas decisions ([`PreExecutionOutput`]): the
231    /// EIP-7702 gas refund and the still-open EIP-2780 runtime gas phase
232    /// checkpoint, which [`Handler::execution`] settles. Returns `None` when
233    /// the EIP-2780 authorization charges ran out of gas: the transaction
234    /// stays valid but must skip execution and be included as an out-of-gas
235    /// halt ([`Handler::runtime_oog_result`]).
236    #[inline]
237    fn pre_execution(
238        &self,
239        evm: &mut Self::Evm,
240        gas: &mut GasTracker,
241    ) -> Result<Option<PreExecutionOutput>, Self::Error> {
242        self.load_accounts(evm)?;
243
244        // EIP-2780: the checkpoint spans the whole runtime gas phase.
245        let checkpoint = evm.ctx().journal_mut().checkpoint();
246
247        let Some(eip7702_refund) = self.apply_eip7702_auth_list(evm, gas)? else {
248            // Out-of-gas while processing the authorizations: revert the
249            // applied delegations; the transaction is included as an
250            // out-of-gas halt. (An EIP-7702 transaction is always a call, so
251            // no create nonce bump is needed here.)
252            evm.ctx().journal_mut().checkpoint_revert(checkpoint);
253            return Ok(None);
254        };
255
256        Ok(Some(PreExecutionOutput {
257            eip7702_refund,
258            checkpoint,
259        }))
260    }
261
262    /// Creates and executes the initial frame, then processes the execution loop.
263    ///
264    /// First-frame creation completes the EIP-2780 runtime gas phase: it
265    /// charges the recipient/create-target costs on the transaction-level
266    /// gas, and `checkpoint` (opened at pre-execution around the applied
267    /// authorizations) is committed here — or reverted when those charges run
268    /// out of gas, in which case `None` is returned and the caller includes
269    /// the transaction as an out-of-gas halt
270    /// ([`Handler::runtime_oog_result`]).
271    ///
272    /// Always calls [Handler::last_frame_result] to handle returned gas from the call.
273    #[inline]
274    fn execution(
275        &mut self,
276        evm: &mut Self::Evm,
277        checkpoint: JournalCheckpoint,
278        gas: &mut GasTracker,
279    ) -> Result<Option<FrameResult>, Self::Error> {
280        // Create the first frame action from the transaction-level gas. Like a
281        // frame forwarding gas to a child, the first frame receives all
282        // remaining regular gas and the reservoir. The EIP-2780 refundable
283        // first-frame charges travel on the frame inputs like the `charged_*`
284        // flags of the CALL/CREATE opcodes.
285        let Some(first_frame_input) = self.first_frame_input(evm, gas)? else {
286            execution::runtime_oog_unwind(evm.ctx(), checkpoint)?;
287            return Ok(None);
288        };
289        // The runtime gas phase is complete: commit its state changes.
290        evm.ctx().journal_mut().checkpoint_commit();
291
292        // Run execution loop
293        let mut frame_result = self.run_exec_loop(evm, first_frame_input)?;
294
295        // Handle last frame result
296        self.last_frame_result(evm, &mut frame_result, gas)?;
297        Ok(Some(frame_result))
298    }
299
300    /// Builds the result for a transaction whose EIP-2780 runtime gas phase
301    /// ran out of gas ([`Handler::pre_execution`] or [`Handler::execution`]
302    /// returned `None`).
303    ///
304    /// The transaction is valid but its gas cannot cover the state-dependent
305    /// runtime charges. It is included as an out-of-gas halt: execution is
306    /// skipped, all regular gas is consumed (the reservoir is returned), and
307    /// the runtime state changes were already reverted when the phase bailed
308    /// out.
309    #[inline]
310    fn runtime_oog_result(
311        &mut self,
312        evm: &mut Self::Evm,
313        init_and_floor_gas: &InitialAndFloorGas,
314        gas: &mut GasTracker,
315    ) -> Result<FrameResult, Self::Error> {
316        // The runtime gas phase's partial charges are dropped: rebuild the
317        // pristine transaction-level gas. Execution is skipped and the
318        // untouched reservoir is carried by the synthetic out-of-gas frame
319        // result; the settle below consumes all regular gas and returns the
320        // reservoir.
321        *gas = self.tx_gas(evm, init_and_floor_gas);
322        let tx_gas_limit = evm.ctx().tx().gas_limit();
323        let reservoir = gas.reservoir();
324        let mut frame_result = match evm.ctx().tx().kind() {
325            TxKind::Call(_) => FrameResult::new_call_oog(tx_gas_limit, 0..0, reservoir),
326            TxKind::Create => FrameResult::new_create_oog(tx_gas_limit, reservoir),
327        };
328        self.last_frame_result(evm, &mut frame_result, gas)?;
329        Ok(frame_result)
330    }
331
332    /// Handles the final steps of transaction execution.
333    ///
334    /// Calculates final refunds and validates the gas floor (EIP-7623) to ensure minimum gas is spent.
335    /// After EIP-7623, at least floor gas must be consumed.
336    ///
337    /// Reimburses unused gas to the caller and rewards the beneficiary with transaction fees.
338    /// The effective gas price determines rewards, with the base fee being burned.
339    ///
340    /// Finally, finalizes output by returning the journal state and clearing internal state
341    /// for the next execution.
342    #[inline]
343    fn post_execution(
344        &self,
345        evm: &mut Self::Evm,
346        exec_result: &mut FrameResult,
347        init_and_floor_gas: InitialAndFloorGas,
348        eip7702_gas_refund: i64,
349    ) -> Result<ResultGas, Self::Error> {
350        // Calculate final refund and add EIP-7702 refund to gas.
351        self.refund(evm, exec_result, eip7702_gas_refund)?;
352
353        // Build ResultGas from the final gas state
354        // This includes all necessary fields and gas values.
355        let result_gas = post_execution::build_result_gas(
356            exec_result.instruction_result().is_halt(),
357            exec_result.gas(),
358            init_and_floor_gas,
359        );
360
361        // Ensure gas floor is met and minimum floor gas is spent.
362        // if `cfg.is_eip7623_disabled` is true, floor gas will be set to zero
363        self.eip7623_check_gas_floor(evm, exec_result, init_and_floor_gas);
364        // Return unused gas to caller
365        self.reimburse_caller(evm, exec_result)?;
366        // Pay transaction fees to beneficiary
367        self.reward_beneficiary(evm, exec_result)?;
368        // Build ResultGas from the final gas state
369        Ok(result_gas)
370    }
371
372    /* VALIDATION */
373
374    /// Validates block, transaction and configuration fields.
375    ///
376    /// Performs all validation checks that can be done without loading state.
377    /// For example, verifies transaction gas limit is below block gas limit.
378    #[inline]
379    fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
380        validation::validate_env(evm.ctx())
381    }
382
383    /// Calculates initial gas costs based on transaction type and input data.
384    ///
385    /// Includes additional costs for access list and authorization list.
386    ///
387    /// Verifies the initial cost does not exceed the transaction gas limit.
388    #[inline]
389    fn validate_initial_tx_gas(
390        &self,
391        evm: &mut Self::Evm,
392    ) -> Result<InitialAndFloorGas, Self::Error> {
393        let ctx = evm.ctx_ref();
394        let is_amsterdam_eip2780_enabled = ctx.cfg().is_amsterdam_eip2780_enabled();
395        let tx = ctx.tx();
396        let eip2780 = is_amsterdam_eip2780_enabled.then(|| {
397            // Self-transfer: a `Call` whose recipient is the sender itself.
398            let is_self_transfer = tx.kind().to() == Some(&tx.caller());
399            gas_params::Eip2780TxInfo {
400                value: tx.value(),
401                is_self_transfer,
402            }
403        });
404        let gas = validation::validate_initial_tx_gas_with_gas_params(
405            tx,
406            ctx.cfg().spec().into(),
407            ctx.cfg().gas_params(),
408            ctx.cfg().is_eip7623_disabled(),
409            ctx.cfg().is_amsterdam_eip8037_enabled(),
410            ctx.cfg().tx_gas_limit_cap(),
411            eip2780,
412        )?;
413
414        Ok(gas)
415    }
416
417    /* PRE EXECUTION */
418
419    /// Loads access list and beneficiary account, marking them as warm in the [`context::Journal`].
420    #[inline]
421    fn load_accounts(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
422        pre_execution::load_accounts(evm)
423    }
424
425    /// Processes the authorization list, validating authority signatures, nonces and chain IDs.
426    /// Applies valid authorizations to accounts.
427    ///
428    /// Returns the EIP-7702 gas refund, or `None` when the EIP-2780
429    /// authorization charges ran out of gas — [`Handler::pre_execution`] owns
430    /// the runtime gas phase checkpoint and reverts it in that case.
431    #[inline]
432    fn apply_eip7702_auth_list(
433        &self,
434        evm: &mut Self::Evm,
435        gas: &mut GasTracker,
436    ) -> Result<Option<u64>, Self::Error> {
437        apply_eip7702_auth_list(evm.ctx_mut(), gas)
438    }
439
440    /// Deducts the maximum possible fee from caller's balance.
441    ///
442    /// If cfg.is_balance_check_disabled, this method will add back enough funds to ensure that
443    /// the caller's balance is at least tx.value() before returning. Note that the amount of funds
444    /// added back in this case may exceed the maximum fee.
445    ///
446    /// Unused fees are returned to caller after execution completes.
447    #[inline]
448    fn validate_against_state_and_deduct_caller(
449        &self,
450        evm: &mut Self::Evm,
451        _init_and_floor_gas: &mut InitialAndFloorGas,
452    ) -> Result<(), Self::Error> {
453        pre_execution::validate_against_state_and_deduct_caller(evm.ctx())
454    }
455
456    /* EXECUTION */
457
458    /// Creates initial frame input from the transaction parameters and the
459    /// transaction-level gas, forwarding all remaining regular gas and the
460    /// reservoir to the frame.
461    ///
462    /// Under EIP-2780 the target loading also records the runtime
463    /// recipient/create-target charges on `gas` and marks the refundable ones
464    /// on the frame inputs' `charged_*` flags (see
465    /// [`execution::create_init_frame`]), so [`Handler::last_frame_result`]
466    /// can refund them from the outcome.
467    ///
468    /// Returns `None` when those charges run out of gas.
469    #[inline]
470    fn first_frame_input(
471        &mut self,
472        evm: &mut Self::Evm,
473        gas: &mut GasTracker,
474    ) -> Result<Option<FrameInit>, Self::Error> {
475        let ctx = evm.ctx_mut();
476        let mut memory = SharedMemory::new_with_buffer(ctx.local().shared_memory_buffer().clone());
477        memory.set_memory_limit(ctx.cfg().memory_limit());
478
479        let Some(frame_input) = execution::create_init_frame(ctx, gas)? else {
480            return Ok(None);
481        };
482
483        Ok(Some(FrameInit {
484            depth: 0,
485            memory,
486            frame_input,
487        }))
488    }
489
490    /// Processes the result of the initial call and settles it into the
491    /// transaction-level gas.
492    ///
493    /// Emulates how a parent frame settles a returning child
494    /// ([`handle_reservoir_remaining_gas`]): a failing frame rolls its
495    /// state-gas charges back in LIFO order and drops its refund counter, an
496    /// exceptional halt additionally consumes its regular gas; unused regular
497    /// gas then returns to the transaction-level gas and the reservoir is
498    /// adopted from the frame. The EIP-2780 refundable first-frame charge is
499    /// refunded from the outcome's `charged_*` flags, mirroring
500    /// `EthFrame::return_result`.
501    ///
502    /// The settled transaction-level gas is written back to the frame result,
503    /// which carries it into the post-execution phase.
504    #[inline]
505    fn last_frame_result(
506        &mut self,
507        evm: &mut Self::Evm,
508        frame_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
509        parent_gas: &mut GasTracker,
510    ) -> Result<(), Self::Error> {
511        let instruction_result = frame_result.instruction_result();
512
513        // All regular gas was forwarded to the first frame: consume it on the
514        // transaction-level gas; the settle below returns the frame's unused
515        // part.
516        parent_gas.spend_all();
517
518        // Settle the frame into the transaction-level gas like a parent frame.
519        handle_reservoir_remaining_gas(
520            instruction_result,
521            parent_gas,
522            frame_result.gas_mut().tracker_mut(),
523        );
524
525        // Refund the EIP-2780 refundable first-frame charge when no account
526        // leaf was created, exactly like `EthFrame::return_result` refunds
527        // the upfront CALL/CREATE state charges of inner frames.
528        if let Some(charge) = frame_result.refundable_state_gas(evm.ctx().cfg().gas_params()) {
529            parent_gas.refill_reservoir(charge);
530            // Unlike an inner frame's caller, the transaction ends here: an
531            // exceptional halt consumes all regular gas, including the
532            // spilled portion the refill just credited back to `remaining`.
533            if instruction_result.is_halt() {
534                parent_gas.spend_all();
535            }
536        }
537
538        // The frame result carries the transaction-level gas onward to the
539        // post-execution phase.
540        *frame_result.gas_mut().tracker_mut() = *parent_gas;
541
542        Ok(())
543    }
544
545    /* FRAMES */
546
547    /// Executes the main frame processing loop.
548    ///
549    /// This loop manages the frame stack, processing each frame until execution completes.
550    /// For each iteration:
551    /// 1. Calls the current frame
552    /// 2. Handles the returned frame input or result
553    /// 3. Creates new frames or propagates results as needed
554    #[inline]
555    fn run_exec_loop(
556        &mut self,
557        evm: &mut Self::Evm,
558        first_frame_input: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameInit,
559    ) -> Result<FrameResult, Self::Error> {
560        let res = evm.frame_init(first_frame_input)?;
561
562        if let ItemOrResult::Result(frame_result) = res {
563            return Ok(frame_result);
564        }
565
566        loop {
567            let call_or_result = evm.frame_run()?;
568
569            let result = match call_or_result {
570                ItemOrResult::Item(init) => {
571                    match evm.frame_init(init)? {
572                        ItemOrResult::Item(_) => {
573                            continue;
574                        }
575                        // Do not pop the frame since no new frame was created
576                        ItemOrResult::Result(result) => result,
577                    }
578                }
579                ItemOrResult::Result(result) => result,
580            };
581
582            if let Some(result) = evm.frame_return_result(result)? {
583                return Ok(result);
584            }
585        }
586    }
587
588    /* POST EXECUTION */
589
590    /// Validates that the minimum gas floor requirements are satisfied.
591    ///
592    /// Ensures that at least the floor gas amount has been consumed during execution.
593    #[inline]
594    fn eip7623_check_gas_floor(
595        &self,
596        _evm: &mut Self::Evm,
597        exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
598        init_and_floor_gas: InitialAndFloorGas,
599    ) {
600        post_execution::eip7623_check_gas_floor(exec_result.gas_mut(), init_and_floor_gas)
601    }
602
603    /// Calculates the final gas refund amount, including any EIP-7702 refunds.
604    #[inline]
605    fn refund(
606        &self,
607        evm: &mut Self::Evm,
608        exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
609        eip7702_refund: i64,
610    ) -> Result<(), Self::Error> {
611        post_execution::refund(
612            evm.ctx().cfg().gas_params(),
613            exec_result.gas_mut(),
614            eip7702_refund,
615        );
616
617        Ok(())
618    }
619
620    /// Returns unused gas costs to the transaction sender's account.
621    #[inline]
622    fn reimburse_caller(
623        &self,
624        evm: &mut Self::Evm,
625        exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
626    ) -> Result<(), Self::Error> {
627        post_execution::reimburse_caller(evm.ctx(), exec_result.gas(), U256::ZERO)
628            .map_err(From::from)
629    }
630
631    /// Transfers transaction fees to the block beneficiary's account.
632    #[inline]
633    fn reward_beneficiary(
634        &self,
635        evm: &mut Self::Evm,
636        exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
637    ) -> Result<(), Self::Error> {
638        post_execution::reward_beneficiary(evm.ctx(), exec_result.gas()).map_err(From::from)
639    }
640
641    /// Processes the final execution output.
642    ///
643    /// This method, retrieves the final state from the journal, converts internal results to the external output format.
644    /// Internal state is cleared and EVM is prepared for the next transaction.
645    #[inline]
646    fn execution_result(
647        &mut self,
648        evm: &mut Self::Evm,
649        result: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
650        result_gas: ResultGas,
651    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
652        take_error::<Self::Error, _>(evm.ctx().error())?;
653
654        let exec_result = post_execution::output(evm.ctx(), result, result_gas);
655
656        // commit transaction
657        evm.ctx().journal_mut().commit_tx();
658        evm.ctx().local_mut().clear();
659        evm.frame_stack().clear();
660
661        Ok(exec_result)
662    }
663
664    /// Handles cleanup when an error occurs during execution.
665    ///
666    /// Ensures the journal state is properly cleared before propagating the error.
667    /// On happy path journal is cleared in [`Handler::execution_result`] method.
668    #[inline]
669    fn catch_error(
670        &self,
671        evm: &mut Self::Evm,
672        error: Self::Error,
673    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
674        // clean up local context. Initcode cache needs to be discarded.
675        evm.ctx().local_mut().clear();
676        evm.ctx().journal_mut().discard_tx();
677        evm.frame_stack().clear();
678        Err(error)
679    }
680}