revm_handler/
post_execution.rs

1use crate::FrameResult;
2use context_interface::{
3    journaled_state::JournalTr,
4    result::{ExecutionResult, HaltReasonTr},
5    Block, Cfg, ContextTr, Database, Transaction,
6};
7use interpreter::{Gas, InitialAndFloorGas, SuccessOrHalt};
8use primitives::{hardfork::SpecId, U256};
9use state::EvmState;
10
11/// Ensures minimum gas floor is spent according to EIP-7623.
12pub fn eip7623_check_gas_floor(gas: &mut Gas, init_and_floor_gas: InitialAndFloorGas) {
13    // EIP-7623: Increase calldata cost
14    // spend at least a gas_floor amount of gas.
15    if gas.spent_sub_refunded() < init_and_floor_gas.floor_gas {
16        gas.set_spent(init_and_floor_gas.floor_gas);
17        // clear refund
18        gas.set_refund(0);
19    }
20}
21
22/// Calculates and applies gas refunds based on the specification.
23pub fn refund(spec: SpecId, gas: &mut Gas, eip7702_refund: i64) {
24    gas.record_refund(eip7702_refund);
25    // Calculate gas refund for transaction.
26    // If spec is set to london, it will decrease the maximum refund amount to 5th part of
27    // gas spend. (Before london it was 2th part of gas spend)
28    gas.set_final_refund(spec.is_enabled_in(SpecId::LONDON));
29}
30
31/// Reimburses the caller for unused gas.
32#[inline]
33pub fn reimburse_caller<CTX: ContextTr>(
34    context: &mut CTX,
35    gas: &mut Gas,
36    additional_refund: U256,
37) -> Result<(), <CTX::Db as Database>::Error> {
38    let basefee = context.block().basefee() as u128;
39    let caller = context.tx().caller();
40    let effective_gas_price = context.tx().effective_gas_price(basefee);
41
42    // Return balance of not spend gas.
43    context.journal_mut().balance_incr(
44        caller,
45        U256::from(
46            effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128),
47        ) + additional_refund,
48    )?;
49
50    Ok(())
51}
52
53/// Rewards the beneficiary with transaction fees.
54#[inline]
55pub fn reward_beneficiary<CTX: ContextTr>(
56    context: &mut CTX,
57    gas: &mut Gas,
58) -> Result<(), <CTX::Db as Database>::Error> {
59    let beneficiary = context.block().beneficiary();
60    let basefee = context.block().basefee() as u128;
61    let effective_gas_price = context.tx().effective_gas_price(basefee);
62
63    // Transfer fee to coinbase/beneficiary.
64    // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded.
65    let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) {
66        effective_gas_price.saturating_sub(basefee)
67    } else {
68        effective_gas_price
69    };
70
71    // reward beneficiary
72    context.journal_mut().balance_incr(
73        beneficiary,
74        U256::from(coinbase_gas_price * (gas.spent() - gas.refunded() as u64) as u128),
75    )?;
76
77    Ok(())
78}
79
80/// Calculate last gas spent and transform internal reason to external.
81///
82/// TODO make Journal FinalOutput more generic.
83pub fn output<CTX: ContextTr<Journal: JournalTr<State = EvmState>>, HALTREASON: HaltReasonTr>(
84    context: &mut CTX,
85    // TODO, make this more generic and nice.
86    // FrameResult should be a generic that returns gas and interpreter result.
87    result: FrameResult,
88) -> ExecutionResult<HALTREASON> {
89    // Used gas with refund calculated.
90    let gas_refunded = result.gas().refunded() as u64;
91    let final_gas_used = result.gas().spent() - gas_refunded;
92    let output = result.output();
93    let instruction_result = result.into_interpreter_result();
94
95    // take logs from journal.
96    let logs = context.journal_mut().take_logs();
97
98    match SuccessOrHalt::<HALTREASON>::from(instruction_result.result) {
99        SuccessOrHalt::Success(reason) => ExecutionResult::Success {
100            reason,
101            gas_used: final_gas_used,
102            gas_refunded,
103            logs,
104            output,
105        },
106        SuccessOrHalt::Revert => ExecutionResult::Revert {
107            gas_used: final_gas_used,
108            output: output.into_data(),
109        },
110        SuccessOrHalt::Halt(reason) => ExecutionResult::Halt {
111            reason,
112            gas_used: final_gas_used,
113        },
114        // Only two internal return flags.
115        flag @ (SuccessOrHalt::FatalExternalError | SuccessOrHalt::Internal(_)) => {
116            panic!(
117                "Encountered unexpected internal return flag: {flag:?} with instruction result: {instruction_result:?}"
118            )
119        }
120    }
121}