use crate::FrameResult;
use context::journaled_state::account::JournaledAccountTr;
use context_interface::{
journaled_state::JournalTr,
result::{ExecutionResult, HaltReason, HaltReasonTr, ResultGas},
Block, Cfg, ContextTr, Database, LocalContextTr, Transaction,
};
use interpreter::{Gas, InitialAndFloorGas, SuccessOrHalt};
use primitives::{hardfork::SpecId, U256};
pub fn build_result_gas(gas: &Gas, init_and_floor_gas: InitialAndFloorGas) -> ResultGas {
let state_gas = gas
.state_gas_spent()
.saturating_add(init_and_floor_gas.initial_state_gas)
.saturating_sub(init_and_floor_gas.eip7702_reservoir_refund);
ResultGas::default()
.with_total_gas_spent(
gas.limit()
.saturating_sub(gas.remaining())
.saturating_sub(gas.reservoir()),
)
.with_refunded(gas.refunded() as u64)
.with_floor_gas(init_and_floor_gas.floor_gas)
.with_state_gas_spent(state_gas)
}
pub fn eip7623_check_gas_floor(gas: &mut Gas, init_and_floor_gas: InitialAndFloorGas) {
let gas_used_before_refund = gas.total_gas_spent().saturating_sub(gas.reservoir());
let gas_used_after_refund = gas_used_before_refund.saturating_sub(gas.refunded() as u64);
if gas_used_after_refund < init_and_floor_gas.floor_gas {
gas.set_spent(init_and_floor_gas.floor_gas + gas.reservoir());
gas.set_refund(0);
}
}
pub fn refund(spec: SpecId, gas: &mut Gas, eip7702_refund: i64) {
gas.record_refund(eip7702_refund);
gas.set_final_refund(spec.is_enabled_in(SpecId::LONDON));
}
#[inline]
pub fn reimburse_caller<CTX: ContextTr>(
context: &mut CTX,
gas: &Gas,
additional_refund: U256,
) -> Result<(), <CTX::Db as Database>::Error> {
let basefee = context.block().basefee() as u128;
let caller = context.tx().caller();
let effective_gas_price = context.tx().effective_gas_price(basefee);
let reimbursable = gas.remaining() + gas.reservoir() + gas.refunded() as u64;
context
.journal_mut()
.load_account_mut(caller)?
.incr_balance(
U256::from(effective_gas_price.saturating_mul(reimbursable as u128))
+ additional_refund,
);
Ok(())
}
#[inline]
pub fn reward_beneficiary<CTX: ContextTr>(
context: &mut CTX,
gas: &Gas,
) -> Result<(), <CTX::Db as Database>::Error> {
let (block, tx, cfg, journal, _, _) = context.all_mut();
let basefee = block.basefee() as u128;
let effective_gas_price = tx.effective_gas_price(basefee);
let coinbase_gas_price = if cfg.spec().into().is_enabled_in(SpecId::LONDON) {
effective_gas_price.saturating_sub(basefee)
} else {
effective_gas_price
};
let effective_used = gas.used().saturating_sub(gas.reservoir());
journal
.load_account_mut(block.beneficiary())?
.incr_balance(U256::from(coinbase_gas_price * effective_used as u128));
Ok(())
}
pub fn output<CTX: ContextTr<Journal: JournalTr>, HALTREASON: HaltReasonTr>(
context: &mut CTX,
result: FrameResult,
result_gas: ResultGas,
) -> ExecutionResult<HALTREASON> {
let output = result.output();
let instruction_result = result.into_interpreter_result();
let logs = context.journal_mut().take_logs();
match SuccessOrHalt::<HALTREASON>::from(instruction_result.result) {
SuccessOrHalt::Success(reason) => ExecutionResult::Success {
reason,
gas: result_gas,
logs,
output,
},
SuccessOrHalt::Revert => ExecutionResult::Revert {
gas: result_gas,
logs,
output: output.into_data(),
},
SuccessOrHalt::Halt(reason) => {
if matches!(
instruction_result.result,
interpreter::InstructionResult::PrecompileError
) {
if let Some(message) = context.local_mut().take_precompile_error_context() {
return ExecutionResult::Halt {
reason: HALTREASON::from(HaltReason::PrecompileErrorWithContext(message)),
gas: result_gas,
logs,
};
}
}
ExecutionResult::Halt {
reason,
gas: result_gas,
logs,
}
}
flag @ (SuccessOrHalt::FatalExternalError | SuccessOrHalt::Internal(_)) => {
panic!(
"Encountered unexpected internal return flag: {flag:?} with instruction result: {instruction_result:?}"
)
}
}
}