use crate::{
    evm::FrameTr, item_or_result::FrameInitOrResult, precompile_provider::PrecompileProvider,
    CallFrame, CreateFrame, FrameData, FrameResult, ItemOrResult,
};
use context::{result::FromStringError, LocalContextTr};
use context_interface::{
    context::{take_error, ContextError},
    journaled_state::{account::JournaledAccountTr, JournalCheckpoint, JournalTr},
    local::{FrameToken, OutFrame},
    Cfg, ContextTr, Database,
};
use core::cmp::min;
use derive_where::derive_where;
use interpreter::{
    interpreter::{EthInterpreter, ExtBytecode},
    interpreter_action::FrameInit,
    interpreter_types::ReturnData,
    CallInput, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme,
    FrameInput, Gas, InputsImpl, InstructionResult, Interpreter, InterpreterAction,
    InterpreterResult, InterpreterTypes, SharedMemory,
};
use primitives::{
    constants::CALL_STACK_LIMIT,
    hardfork::SpecId::{self, HOMESTEAD, LONDON, SPURIOUS_DRAGON},
    Address, Bytes, U256,
};
use state::Bytecode;
use std::{borrow::ToOwned, boxed::Box, vec::Vec};

/// Frame implementation for Ethereum.
#[derive_where(Clone, Debug; IW,
    <IW as InterpreterTypes>::Stack,
    <IW as InterpreterTypes>::Memory,
    <IW as InterpreterTypes>::Bytecode,
    <IW as InterpreterTypes>::ReturnData,
    <IW as InterpreterTypes>::Input,
    <IW as InterpreterTypes>::RuntimeFlag,
    <IW as InterpreterTypes>::Extend,
)]
pub struct EthFrame<IW: InterpreterTypes = EthInterpreter> {
    /// Frame-specific data (Call, Create, or EOFCreate).
    pub data: FrameData,
    /// Input data for the frame.
    pub input: FrameInput,
    /// Current call depth in the execution stack.
    pub depth: usize,
    /// Journal checkpoint for state reversion.
    pub checkpoint: JournalCheckpoint,
    /// Interpreter instance for executing bytecode.
    pub interpreter: Interpreter<IW>,
    /// Whether the frame has been finished its execution.
    /// Frame is considered finished if it has been called and returned a result.
    pub is_finished: bool,
}

impl<IT: InterpreterTypes> FrameTr for EthFrame<IT> {
    type FrameResult = FrameResult;
    type FrameInit = FrameInit;
}

impl Default for EthFrame<EthInterpreter> {
    fn default() -> Self {
        Self::do_default(Interpreter::default())
    }
}

impl EthFrame<EthInterpreter> {
    /// Creates an new invalid [`EthFrame`].
    pub fn invalid() -> Self {
        Self::do_default(Interpreter::invalid())
    }

    fn do_default(interpreter: Interpreter<EthInterpreter>) -> Self {
        Self {
            data: FrameData::Call(CallFrame {
                return_memory_range: 0..0,
            }),
            input: FrameInput::Empty,
            depth: 0,
            checkpoint: JournalCheckpoint::default(),
            interpreter,
            is_finished: false,
        }
    }

    /// Returns true if the frame has finished execution.
    pub const fn is_finished(&self) -> bool {
        self.is_finished
    }

    /// Sets the finished state of the frame.
    pub const fn set_finished(&mut self, finished: bool) {
        self.is_finished = finished;
    }
}

/// Type alias for database errors from a context.
pub type ContextTrDbError<CTX> = <<CTX as ContextTr>::Db as Database>::Error;

impl EthFrame<EthInterpreter> {
    /// Clear and initialize a frame.
    #[expect(clippy::too_many_arguments)]
    #[inline(always)]
    pub fn clear(
        &mut self,
        data: FrameData,
        input: FrameInput,
        depth: usize,
        memory: SharedMemory,
        bytecode: ExtBytecode,
        inputs: InputsImpl,
        is_static: bool,
        spec_id: SpecId,
        gas_limit: u64,
        reservoir_remaining_gas: u64,
        checkpoint: JournalCheckpoint,
    ) {
        let Self {
            data: data_ref,
            input: input_ref,
            depth: depth_ref,
            interpreter,
            checkpoint: checkpoint_ref,
            is_finished: is_finished_ref,
        } = self;
        *data_ref = data;
        *input_ref = input;
        *depth_ref = depth;
        *is_finished_ref = false;
        interpreter.clear(
            memory,
            bytecode,
            inputs,
            is_static,
            spec_id,
            gas_limit,
            reservoir_remaining_gas,
        );
        *checkpoint_ref = checkpoint;
    }

    /// Make call frame
    #[inline]
    pub fn make_call_frame<
        CTX: ContextTr,
        PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
        ERROR: From<ContextTrDbError<CTX>> + FromStringError,
    >(
        mut this: OutFrame<'_, Self>,
        ctx: &mut CTX,
        precompiles: &mut PRECOMPILES,
        depth: usize,
        memory: SharedMemory,
        inputs: Box<CallInputs>,
    ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
        let reservoir_remaining_gas = inputs.reservoir;
        let charged_new_account_state_gas = inputs.charged_new_account_state_gas;
        let gas =
            Gas::new_with_regular_gas_and_reservoir(inputs.gas_limit, reservoir_remaining_gas);
        let return_result = |instruction_result: InstructionResult| {
            Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
                result: InterpreterResult {
                    result: instruction_result,
                    gas,
                    output: Bytes::new(),
                },
                memory_offset: inputs.return_memory_offset.clone(),
                was_precompile_called: false,
                precompile_call_logs: Vec::new(),
                charged_new_account_state_gas,
            })))
        };

        // Check depth
        if depth > CALL_STACK_LIMIT as usize {
            return return_result(InstructionResult::CallTooDeep);
        }

        // Create subroutine checkpoint
        let checkpoint = ctx.journal_mut().checkpoint();

        // Touch address. For "EIP-158 State Clear", this will erase empty accounts.
        if let CallValue::Transfer(value) = inputs.value {
            // Transfer value from caller to called account
            // Target will get touched even if balance transferred is zero.
            if let Some(i) =
                ctx.journal_mut()
                    .transfer_loaded(inputs.caller, inputs.target_address, value)
            {
                ctx.journal_mut().checkpoint_revert(checkpoint);
                return return_result(i.into());
            }
        }

        let interpreter_input = InputsImpl {
            target_address: inputs.target_address,
            caller_address: inputs.caller,
            bytecode_address: Some(inputs.bytecode_address),
            input: inputs.input.clone(),
            call_value: inputs.value.get(),
        };
        let is_static = inputs.is_static;
        let gas_limit = inputs.gas_limit;

        if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? {
            let mut logs = Vec::new();
            if result.result.is_ok() {
                // Preserve the reservoir on the result gas so it can be reimbursed.
                // Precompiles don't use reservoir gas, but the first frame carries it.
                ctx.journal_mut().checkpoint_commit();
            } else {
                // clone logs that precompile created, only possible with custom precompiles.
                // checkpoint.log_i will be always correct.
                logs = ctx.journal_mut().logs()[checkpoint.log_i..].to_vec();
                ctx.journal_mut().checkpoint_revert(checkpoint);
            }
            return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
                result,
                memory_offset: inputs.return_memory_offset.clone(),
                was_precompile_called: true,
                precompile_call_logs: logs,
                charged_new_account_state_gas,
            })));
        }

        // Get bytecode and hash - either from known_bytecode or load from account
        let (bytecode_hash, bytecode) = inputs.known_bytecode.clone();

        // Returns success if bytecode is empty.
        if bytecode.is_empty() {
            ctx.journal_mut().checkpoint_commit();
            return return_result(InstructionResult::Stop);
        }

        // Create interpreter and executes call and push new CallStackFrame.
        this.get(EthFrame::invalid).clear(
            FrameData::Call(CallFrame {
                return_memory_range: inputs.return_memory_offset.clone(),
            }),
            FrameInput::Call(inputs),
            depth,
            memory,
            ExtBytecode::new_with_hash(bytecode, bytecode_hash),
            interpreter_input,
            is_static,
            ctx.cfg().spec().into(),
            gas_limit,
            reservoir_remaining_gas,
            checkpoint,
        );
        Ok(ItemOrResult::Item(this.consume()))
    }

    /// Make create frame.
    #[inline]
    pub fn make_create_frame<
        CTX: ContextTr,
        ERROR: From<ContextTrDbError<CTX>> + FromStringError,
    >(
        mut this: OutFrame<'_, Self>,
        context: &mut CTX,
        depth: usize,
        memory: SharedMemory,
        inputs: Box<CreateInputs>,
    ) -> Result<ItemOrResult<FrameToken, FrameResult>, ERROR> {
        let reservoir_remaining_gas = inputs.reservoir();
        let spec = context.cfg().spec().into();
        // EIP-8037 refund for the CREATE opcode's upfront `create_state_gas` is
        // applied uniformly in `return_result` when the create fails (revert,
        // halt, or early-fail with `address == None`), so early-fail results
        // only carry the reservoir they inherited from the parent.
        let return_error = |e| {
            Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome {
                result: InterpreterResult {
                    result: e,
                    gas: Gas::new_with_regular_gas_and_reservoir(
                        inputs.gas_limit(),
                        reservoir_remaining_gas,
                    ),
                    output: Bytes::new(),
                },
                address: None,
            })))
        };

        // Check depth
        if depth > CALL_STACK_LIMIT as usize {
            return return_error(InstructionResult::CallTooDeep);
        }

        // Fetch balance of caller.
        let journal = context.journal_mut();
        let mut caller_info = journal.load_account_mut(inputs.caller())?;

        // Check if caller has enough balance to send to the created contract.
        // decrement of balance is done in the create_account_checkpoint.
        if *caller_info.balance() < inputs.value() {
            return return_error(InstructionResult::OutOfFunds);
        }

        // Increase nonce of caller and check if it overflows
        let old_nonce = caller_info.nonce();
        if !caller_info.bump_nonce() {
            return return_error(InstructionResult::Return);
        };

        // Create address — uses OnceCell cache so that if an inspector already called
        // `created_address`, the expensive keccak256 is not recomputed.
        let created_address = inputs.created_address(old_nonce);
        let init_code_hash = matches!(inputs.scheme(), CreateScheme::Create2 { .. })
            .then(|| inputs.init_code_hash());

        drop(caller_info); // Drop caller info to avoid borrow checker issues.

        // warm load account.
        journal.load_account(created_address)?;

        // Create account, transfer funds and make the journal checkpoint.
        let checkpoint = match context.journal_mut().create_account_checkpoint(
            inputs.caller(),
            created_address,
            inputs.value(),
            spec,
        ) {
            Ok(checkpoint) => checkpoint,
            Err(e) => return return_error(e.into()),
        };

        let bytecode = ExtBytecode::new_with_optional_hash(
            Bytecode::new_legacy(inputs.init_code().clone()),
            init_code_hash,
        );

        let interpreter_input = InputsImpl {
            target_address: created_address,
            caller_address: inputs.caller(),
            bytecode_address: None,
            input: CallInput::Bytes(Bytes::new()),
            call_value: inputs.value(),
        };
        let gas_limit = inputs.gas_limit();

        this.get(EthFrame::invalid).clear(
            FrameData::Create(CreateFrame { created_address }),
            FrameInput::Create(inputs),
            depth,
            memory,
            bytecode,
            interpreter_input,
            false,
            spec,
            gas_limit,
            reservoir_remaining_gas,
            checkpoint,
        );

        Ok(ItemOrResult::Item(this.consume()))
    }

    /// Initializes a frame with the given context and precompiles.
    pub fn init_with_context<
        CTX: ContextTr,
        PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
    >(
        this: OutFrame<'_, Self>,
        ctx: &mut CTX,
        precompiles: &mut PRECOMPILES,
        frame_init: FrameInit,
    ) -> Result<
        ItemOrResult<FrameToken, FrameResult>,
        ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
    > {
        // TODO cleanup inner make functions
        let FrameInit {
            depth,
            memory,
            frame_input,
        } = frame_init;

        match frame_input {
            FrameInput::Call(inputs) => {
                Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs)
            }
            FrameInput::Create(inputs) => Self::make_create_frame(this, ctx, depth, memory, inputs),
            FrameInput::Empty => unreachable!(),
        }
    }
}

impl EthFrame<EthInterpreter> {
    /// Processes the next interpreter action, either creating a new frame or returning a result.
    pub fn process_next_action<
        CTX: ContextTr,
        ERROR: From<ContextTrDbError<CTX>> + FromStringError,
    >(
        &mut self,
        context: &mut CTX,
        next_action: InterpreterAction,
    ) -> Result<FrameInitOrResult<Self>, ERROR> {
        // Run interpreter

        let mut interpreter_result = match next_action {
            InterpreterAction::NewFrame(frame_input) => {
                let depth = self.depth + 1;
                return Ok(ItemOrResult::Item(FrameInit {
                    frame_input,
                    depth,
                    memory: self.interpreter.memory.new_child_context(),
                }));
            }
            InterpreterAction::Return(result) => result,
        };

        // Handle return from frame
        let result = match &self.data {
            FrameData::Call(frame) => {
                // return_call
                // Revert changes or not.
                if interpreter_result.result.is_ok() {
                    context.journal_mut().checkpoint_commit();
                } else {
                    context.journal_mut().checkpoint_revert(self.checkpoint);
                }
                // Propagate EIP-8037 new-account state-gas flag from the frame
                // input so the parent can refund the upfront charge if the call
                // ends in revert/halt.
                let charged_new_account_state_gas = match &self.input {
                    FrameInput::Call(inputs) => inputs.charged_new_account_state_gas,
                    _ => false,
                };
                let mut outcome =
                    CallOutcome::new(interpreter_result, frame.return_memory_range.clone());
                outcome.charged_new_account_state_gas = charged_new_account_state_gas;
                ItemOrResult::Result(FrameResult::Call(outcome))
            }
            FrameData::Create(frame) => {
                return_create(
                    context,
                    self.checkpoint,
                    &mut interpreter_result,
                    frame.created_address,
                );

                ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
                    interpreter_result,
                    Some(frame.created_address),
                )))
            }
        };

        Ok(result)
    }

    /// Processes a frame result and updates the interpreter state accordingly.
    pub fn return_result<CTX: ContextTr, ERROR: From<ContextTrDbError<CTX>> + FromStringError>(
        &mut self,
        ctx: &mut CTX,
        result: FrameResult,
    ) -> Result<(), ERROR> {
        self.interpreter.memory.free_child_context();
        take_error::<ERROR, _>(ctx.error())?;

        // Insert result to the top frame.
        match result {
            FrameResult::Call(outcome) => {
                let out_gas = outcome.gas();
                let ins_result = *outcome.instruction_result();
                let returned_len = outcome.result.output.len();

                let interpreter = &mut self.interpreter;
                let mem_length = outcome.memory_length();
                let mem_start = outcome.memory_start();
                interpreter.return_data.set_buffer(outcome.result.output);

                let target_len = min(mem_length, returned_len);

                if ins_result == InstructionResult::FatalExternalError {
                    panic!("Fatal external error in insert_call_outcome");
                }

                let item = if ins_result.is_ok() {
                    U256::from(1)
                } else {
                    U256::ZERO
                };
                // Safe to push without stack limit check
                let _ = interpreter.stack.push(item);

                // Return unspend gas.
                if ins_result.is_ok_or_revert() {
                    interpreter.gas.erase_cost(out_gas.remaining());
                    interpreter
                        .memory
                        .set(mem_start, &interpreter.return_data.buffer()[..target_len]);
                }

                // handle reservoir remaining gas
                handle_reservoir_remaining_gas(ins_result, &mut interpreter.gas, &out_gas);

                if ins_result.is_ok() {
                    interpreter.gas.record_refund(out_gas.refunded());
                }
            }
            FrameResult::Create(outcome) => {
                let instruction_result = *outcome.instruction_result();
                let interpreter = &mut self.interpreter;

                if instruction_result == InstructionResult::Revert {
                    // Save data to return data buffer if the create reverted
                    interpreter
                        .return_data
                        .set_buffer(outcome.output().to_owned());
                } else {
                    // Otherwise clear it. Note that RETURN opcode should abort.
                    interpreter.return_data.clear();
                };

                assert_ne!(
                    instruction_result,
                    InstructionResult::FatalExternalError,
                    "Fatal external error in insert_eofcreate_outcome"
                );

                let this_gas = &mut interpreter.gas;
                // Refund unused gas for success and revert cases.
                if instruction_result.is_ok_or_revert() {
                    this_gas.erase_cost(outcome.gas().remaining());
                }

                // handle reservoir remaining gas
                handle_reservoir_remaining_gas(instruction_result, this_gas, outcome.gas());

                // EIP-8037: The CREATE opcode charged `create_state_gas` upfront on
                // this frame's tracker. When the child fails to deploy a contract
                // (revert, halt, or early-fail paths that return `address == None`
                // such as nonce overflow, depth, OutOfFunds), refund the upfront
                // charge to the reservoir and undo it on `state_gas_spent`.
                // The nonce-overflow path reports `InstructionResult::Return` (ok)
                // with `address == None`, so gate on address rather than the result.
                //
                // Use `refill_reservoir` so that this refund is counted in
                // `refill_amount` and gets unwound if the parent itself
                // reverts/halts (matching 0→x→0 storage restoration).
                let create_failed = outcome.address.is_none() || !instruction_result.is_ok();

                if create_failed && ctx.cfg().is_amsterdam_eip8037_enabled() {
                    let state_gas_charged =
                        ctx.cfg().gas_params().create_state_gas(ctx.local().cpsb());
                    this_gas.refill_reservoir(state_gas_charged);
                }

                let stack_item = if instruction_result.is_ok() {
                    this_gas.record_refund(outcome.gas().refunded());
                    outcome.address.unwrap_or_default().into_word().into()
                } else {
                    U256::ZERO
                };

                // Safe to push without stack limit check
                let _ = interpreter.stack.push(stack_item);
            }
        }

        Ok(())
    }
}

/// Handles the remaining gas of the parent frame.
#[inline]
pub const fn handle_reservoir_remaining_gas(
    instruction_result: InstructionResult,
    parent_gas: &mut Gas,
    child_gas: &Gas,
) {
    if instruction_result.is_ok() {
        // On success: parent takes the child's final reservoir.
        parent_gas.set_reservoir(child_gas.reservoir());
        // Accumulate child's state gas into parent's total.
        // Parent may have already charged state gas (e.g., new_account + create) before
        // creating the child frame. Child starts with state_gas_spent=0, so we must add
        // rather than overwrite to preserve the parent's prior charges.
        //
        // `child.state_gas_spent()` can be negative (EIP-8037 issue #2) when the
        // child did more 0→x→0 restorations than 0→x creations; the negative
        // contribution is the parent's matching charge flowing back out.
        parent_gas.set_state_gas_spent(
            parent_gas
                .state_gas_spent()
                .saturating_add(child_gas.state_gas_spent()),
        );
        // Propagate child's 0→x→0 refill total so that if the parent
        // later reverts/halts, the refill contribution can be unwound.
        parent_gas.set_refill_amount(
            parent_gas
                .refill_amount()
                .saturating_add(child_gas.refill_amount()),
        );
    } else {
        // On revert/halt: the child's state changes are rolled back, so any
        // 0→x→0 refills the child (or its descendants) credited to the
        // reservoir must unwind too — the underlying clears no longer exist.
        //
        // Invariant when no reservoir→remaining spill happened in the child:
        //     pre_call_reservoir = child.reservoir + child.state_gas_spent
        // because every reservoir-funded `record_state_cost(c)` increments
        // state_gas_spent by `c` while decrementing reservoir by `c`, and every
        // `refill_reservoir(r)` does the opposite. Adding the (possibly negative)
        // state_gas_spent back to the final reservoir recovers the pre-call value
        // — discarding the negative branch (the old `.max(0)`) would leak
        // grandchild refill credits up through a reverting parent.
        parent_gas.set_reservoir(
            child_gas
                .reservoir()
                .saturating_add_signed(child_gas.state_gas_spent()),
        );
    }
}

/// Handles the result of a CREATE operation, including validation and state updates.
///
/// The EIP-8037 upfront CREATE state gas is charged on the parent's tracker by
/// the CREATE/CREATE2 opcode. On child failure (revert/halt/early-fail) it is
/// refunded to the parent in `return_result`. The child frame is NOT allowed to
/// borrow the upfront charge to pay for code deposit: it must cover code deposit
/// state gas from its own reservoir and remaining gas.
pub fn return_create<CTX: ContextTr>(
    context: &mut CTX,
    checkpoint: JournalCheckpoint,
    interpreter_result: &mut InterpreterResult,
    address: Address,
) {
    let (_, _, cfg, journal, _, local) = context.all_mut();

    let max_code_size = cfg.max_code_size();
    let is_eip3541_disabled = cfg.is_eip3541_disabled();
    let spec_id = cfg.spec().into();
    let is_amsterdam_eip8037 = cfg.is_amsterdam_eip8037_enabled();
    let cpsb = local.cpsb();
    let gas_params = cfg.gas_params();

    // If return is not ok revert and return.
    if !interpreter_result.result.is_ok() {
        journal.checkpoint_revert(checkpoint);
        return;
    }

    // EIP-170: Contract code size limit to 0x6000 (~25kb)
    // EIP-7954 increased this limit to 0x8000 (~32kb).
    // This must be checked BEFORE charging state gas for code deposit,
    // so that oversized code does not incur storage gas costs.
    if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size {
        journal.checkpoint_revert(checkpoint);
        interpreter_result.result = InstructionResult::CreateContractSizeLimit;
        return;
    }

    // Host error if present on execution
    // If ok, check contract creation limit and calculate gas deduction on output len.
    //
    // EIP-3541: Reject new contract code starting with the 0xEF byte
    if !is_eip3541_disabled
        && spec_id.is_enabled_in(LONDON)
        && interpreter_result.output.first() == Some(&0xEF)
    {
        journal.checkpoint_revert(checkpoint);
        interpreter_result.result = InstructionResult::CreateContractStartingWithEF;
        return;
    }

    // regular gas for code deposit. It is zero in EIP-8037.
    let gas_for_code = gas_params.code_deposit_cost(interpreter_result.output.len());
    if !interpreter_result.gas.record_regular_cost(gas_for_code) {
        // Record code deposit gas cost and check if we are out of gas.
        // EIP-2 point 3: If contract creation does not have enough gas to pay for the
        // final gas fee for adding the contract code to the state, the contract
        // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract.
        if spec_id.is_enabled_in(HOMESTEAD) {
            journal.checkpoint_revert(checkpoint);
            interpreter_result.result = InstructionResult::OutOfGas;
            return;
        } else {
            interpreter_result.output = Bytes::new();
        }
    }

    // EIP-8037: Hash cost for deployed bytecode (keccak256)
    // HASH_COST(L) = 6 × ceil(L / 32)
    // Both CREATE and CREATE2 must pay this cost: it covers hashing the deployed code
    // to compute the code_hash stored in the account. CREATE2's existing keccak256 charge
    // (in create2_cost) is for hashing the init code during address derivation, which is
    // a different hash.
    if is_amsterdam_eip8037 {
        let hash_cost = gas_params.keccak256_cost(interpreter_result.output.len());
        if !interpreter_result.gas.record_regular_cost(hash_cost) {
            journal.checkpoint_revert(checkpoint);
            interpreter_result.result = InstructionResult::OutOfGas;
            return;
        }
        // State gas for code deposit (EIP-8037).
        // Charged after size check: only code that passes validation incurs state gas cost.
        //
        // Note: This should be last operation before checkpoint commit as spending state before this messes
        // with refilling of state gas.
        let state_gas_for_code =
            gas_params.code_deposit_state_gas(interpreter_result.output.len(), cpsb);
        if state_gas_for_code > 0 && !interpreter_result.gas.record_state_cost(state_gas_for_code) {
            journal.checkpoint_revert(checkpoint);
            interpreter_result.result = InstructionResult::OutOfGas;
            return;
        }
    }

    // If we have enough gas we can commit changes.
    journal.checkpoint_commit();

    // Do analysis of bytecode straight away.
    let bytecode = Bytecode::new_legacy(interpreter_result.output.clone());

    // Set code
    journal.set_code(address, bytecode);

    interpreter_result.result = InstructionResult::Return;
}
