use hashbrown::HashSet;
use std::{
    ops::{Div, Rem, Shl, Shr},
    time::{SystemTime, UNIX_EPOCH},
};

use alloy::primitives::{keccak256, Address, I256, U256};
use eyre::{OptionExt, Result};
use heimdall_common::utils::strings::sign_uint;

#[cfg(feature = "step-tracing")]
use std::time::Instant;
#[cfg(feature = "step-tracing")]
use tracing::trace;

use crate::core::opcodes::OpCodeInfo;

use super::{
    constants::{COINBASE_ADDRESS, CREATE2_ADDRESS, CREATE_ADDRESS},
    log::Log,
    memory::Memory,
    opcodes::{WrappedInput, WrappedOpcode},
    stack::{Stack, StackFrame},
    storage::Storage,
};

/// The [`VM`] struct represents an EVM instance. \
/// It contains the EVM's [`Stack`], [`Memory`], [`Storage`], and other state variables needed to
/// emulate EVM execution.
#[derive(Clone, Debug)]
pub struct VM {
    /// The EVM stack that holds values during execution.
    pub stack: Stack,

    /// The EVM memory space that can be read from and written to.
    pub memory: Memory,

    /// The contract's persistent storage.
    pub storage: Storage,

    /// The current instruction pointer (program counter).
    pub instruction: u128,

    /// The compiled bytecode being executed.
    pub bytecode: Vec<u8>,

    /// The input data provided to the contract call.
    pub calldata: Vec<u8>,

    /// The address of the executing contract.
    pub address: Address,

    /// The address that originated the transaction.
    pub origin: Address,

    /// The address that directly called this contract.
    pub caller: Address,

    /// The amount of ether sent with the call (in wei).
    pub value: u128,

    /// The amount of gas remaining for execution.
    pub gas_remaining: u128,

    /// The amount of gas used so far during execution.
    pub gas_used: u128,

    /// The events (logs) emitted during execution.
    pub events: Vec<Log>,

    /// The data returned by the execution.
    pub returndata: Vec<u8>,

    /// The exit code of the execution (0 for success, non-zero for errors).
    pub exitcode: u128,

    /// A set of addresses that have been accessed during execution (used for gas calculation).
    pub address_access_set: HashSet<U256>,

    /// Counter for operations executed (only available with step-tracing feature).
    #[cfg(feature = "step-tracing")]
    pub operation_count: u128,

    /// The time when execution started (only available with step-tracing feature).
    #[cfg(feature = "step-tracing")]
    pub start_time: Instant,
}

/// [`ExecutionResult`] is the result of a single contract execution.
#[derive(Clone, Debug)]
pub struct ExecutionResult {
    /// The amount of gas consumed during the execution.
    pub gas_used: u128,

    /// The amount of gas left after execution completes.
    pub gas_remaining: u128,

    /// The data returned by the execution.
    pub returndata: Vec<u8>,

    /// The exit code of the execution (0 for success, non-zero for errors).
    pub exitcode: u128,

    /// The events (logs) emitted during execution.
    pub events: Vec<Log>,

    /// The final instruction pointer value after execution.
    pub instruction: u128,
}

/// [`State`] is the state of the EVM after executing a single instruction. It is returned by the
/// [`VM::step`] function, and is used by heimdall for tracing contract execution.
#[derive(Clone, Debug)]
pub struct State {
    /// The instruction that was just executed.
    pub last_instruction: Instruction,

    /// The total amount of gas used so far during execution.
    pub gas_used: u128,

    /// The amount of gas remaining for execution.
    pub gas_remaining: u128,

    /// The current state of the EVM stack.
    pub stack: Stack,

    /// The current state of the EVM memory.
    pub memory: Memory,

    /// The current state of the contract storage.
    pub storage: Storage,

    /// The events (logs) emitted so far during execution.
    pub events: Vec<Log>,
}

/// [`Instruction`] is a single EVM instruction. It is returned by the [`VM::step`] function, and
/// contains necessary tracing information, such as the opcode executed, it's inputs and outputs, as
/// well as their parent operations.
#[derive(Clone, Debug)]
pub struct Instruction {
    /// The position of this instruction in the bytecode.
    pub instruction: u128,

    /// The opcode value of the instruction.
    pub opcode: u8,

    /// The raw values of the inputs to this instruction.
    pub inputs: Vec<U256>,

    /// The raw values of the outputs produced by this instruction.
    pub outputs: Vec<U256>,

    /// The wrapped operations that produced the inputs to this instruction.
    /// This allows for tracking data flow and operation dependencies.
    pub input_operations: Vec<WrappedOpcode>,

    /// The wrapped operations that will consume the outputs of this instruction.
    /// This allows for forward tracking of data flow.
    pub output_operations: Vec<WrappedOpcode>,
}

impl VM {
    /// Creates a new [`VM`] instance with the given bytecode, calldata, address, origin, caller,
    /// value, and gas limit.
    ///
    /// ```
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    /// ```
    pub fn new(
        bytecode: &[u8],
        calldata: &[u8],
        address: Address,
        origin: Address,
        caller: Address,
        value: u128,
        gas_limit: u128,
    ) -> VM {
        VM {
            stack: Stack::new(),
            memory: Memory::new(),
            storage: Storage::new(),
            instruction: 1,
            bytecode: bytecode.to_vec(),
            calldata: calldata.to_vec(),
            address,
            origin,
            caller,
            value,
            gas_remaining: gas_limit.max(21000) - 21000,
            gas_used: 21000,
            events: Vec::new(),
            returndata: Vec::new(),
            exitcode: 255,
            address_access_set: HashSet::new(),
            #[cfg(feature = "step-tracing")]
            operation_count: 0,
            #[cfg(feature = "step-tracing")]
            start_time: Instant::now(),
        }
    }

    /// Exits current execution with the given code and returndata.
    ///
    /// ```
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let mut vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    ///
    /// vm.exit(0xff, Vec::new());
    /// assert_eq!(vm.exitcode, 0xff);
    /// ```
    pub fn exit(&mut self, code: u128, returndata: Vec<u8>) {
        self.exitcode = code;
        self.returndata = returndata;
    }

    /// Consume gas units, halting execution if out of gas
    ///
    /// ```
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let mut vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    ///
    /// vm.consume_gas(100);
    /// assert_eq!(vm.gas_remaining, 999999999999978900);
    ///
    /// vm.consume_gas(1000000000000000000);
    /// assert_eq!(vm.gas_remaining, 0);
    /// assert_eq!(vm.exitcode, 9);
    /// ```
    pub fn consume_gas(&mut self, amount: u128) -> bool {
        // REVERT if out of gas
        if amount > self.gas_remaining {
            self.gas_used += self.gas_remaining;
            self.gas_remaining = 0;
            self.exit(9, Vec::new());
            return false;
        }

        self.gas_remaining = self.gas_remaining.saturating_sub(amount);
        self.gas_used = self.gas_used.saturating_add(amount);
        true
    }

    /// Executes the next instruction in the bytecode. Returns information about the instruction
    /// executed.
    ///
    /// ```no_run
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let mut vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    ///
    /// // vm._step(); // 0x00 EXIT
    /// // assert_eq!(vm.exitcode, 10);
    /// ```

    fn push_boolean(&mut self, condition: bool, operation: WrappedOpcode) {
        let value = if condition { U256::from(1u8) } else { U256::ZERO };
        self.stack.push(value, operation);
    }

    fn address_to_u256(address: &Address) -> U256 {
        let mut result = [0u8; 32];
        result[12..].copy_from_slice(address.as_ref());
        U256::from_be_bytes(result)
    }

    fn push_with_optimization(
        &mut self,
        result: U256,
        a: &StackFrame,
        b: &StackFrame,
        operation: WrappedOpcode,
    ) {
        let simplified_operation = if (0x5f..=0x7f).contains(&a.operation.opcode) &&
            (0x5f..=0x7f).contains(&b.operation.opcode)
        {
            WrappedOpcode::new(0x7f, vec![WrappedInput::Raw(result)])
        } else {
            operation
        };
        self.stack.push(result, simplified_operation);
    }

    fn push_with_optimization_single(
        &mut self,
        result: U256,
        a: &StackFrame,
        operation: WrappedOpcode,
    ) {
        let simplified_operation = if (0x5f..=0x7f).contains(&a.operation.opcode) {
            WrappedOpcode::new(0x7f, vec![WrappedInput::Raw(result)])
        } else {
            operation
        };
        self.stack.push(result, simplified_operation);
    }

    fn push_with_optimization_signed(
        &mut self,
        result: I256,
        a: &StackFrame,
        b: &StackFrame,
        operation: WrappedOpcode,
    ) {
        let simplified_operation = if (0x5f..=0x7f).contains(&a.operation.opcode) &&
            (0x5f..=0x7f).contains(&b.operation.opcode)
        {
            WrappedOpcode::new(0x7f, vec![WrappedInput::Raw(result.into_raw())])
        } else {
            operation
        };
        self.stack.push(result.into_raw(), simplified_operation);
    }

    fn safe_copy_data(source: &[u8], offset: usize, size: usize) -> Vec<u8> {
        let end_offset = offset.saturating_add(size).min(source.len());
        let mut value = source.get(offset..end_offset).unwrap_or(&[]).to_owned();
        if value.len() < size {
            value.resize(size, 0u8);
        }
        value
    }

    fn _step(&mut self) -> Result<Instruction> {
        // sanity check
        if self.bytecode.len() < self.instruction as usize {
            self.exit(2, Vec::new());
            return Ok(Instruction {
                instruction: self.instruction,
                opcode: 0xff,
                inputs: Vec::new(),
                outputs: Vec::new(),
                input_operations: Vec::new(),
                output_operations: Vec::new(),
            });
        }

        // get the opcode at the current instruction
        let opcode = self
            .bytecode
            .get((self.instruction - 1) as usize)
            .ok_or_eyre(format!("invalid jumpdest: {}", self.instruction - 1))?
            .to_owned();
        let last_instruction = self.instruction;
        self.instruction += 1;
        #[cfg(feature = "step-tracing")]
        {
            self.operation_count += 1;
        }
        #[cfg(feature = "step-tracing")]
        let start_time = Instant::now();

        // add the opcode to the trace
        let opcode_info = OpCodeInfo::from(opcode);
        let input_frames = self.stack.peek_n(opcode_info.inputs() as usize);
        let input_operations =
            input_frames.iter().map(|x| x.operation.clone()).collect::<Vec<WrappedOpcode>>();
        let inputs = input_frames.iter().map(|x| x.value).collect::<Vec<U256>>();

        // Consume the minimum gas for the opcode
        let gas_cost = opcode_info.min_gas();
        self.consume_gas(gas_cost.into());

        // convert inputs to WrappedInputs
        let wrapped_inputs = input_operations
            .iter()
            .map(|x| WrappedInput::Opcode(x.to_owned()))
            .collect::<Vec<WrappedInput>>();
        let mut operation = WrappedOpcode::new(opcode, wrapped_inputs);

        // if step-tracing feature is enabled, print the current operation
        #[cfg(feature = "step-tracing")]
        trace!(
            pc = self.instruction - 1,
            opcode = opcode_info.name(),
            inputs = ?inputs
                .iter()
                .map(|x| format!("{x:#x}"))
                .collect::<Vec<String>>(),
            "executing opcode"
        );

        // execute the operation
        match opcode {
            // STOP
            0x00 => {
                self.exit(10, Vec::new());
                return Ok(Instruction {
                    instruction: last_instruction,
                    opcode,
                    inputs,
                    outputs: Vec::new(),
                    input_operations,
                    output_operations: Vec::new(),
                });
            }

            // ADD
            0x01 => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let result = a.value.overflowing_add(b.value).0;
                self.push_with_optimization(result, &a, &b, operation);
            }

            // MUL
            0x02 => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let result = a.value.overflowing_mul(b.value).0;
                self.push_with_optimization(result, &a, &b, operation);
            }

            // SUB
            0x03 => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let result = a.value.overflowing_sub(b.value).0;
                self.push_with_optimization(result, &a, &b, operation);
            }

            // DIV
            0x04 => {
                let numerator = self.stack.pop()?;
                let denominator = self.stack.pop()?;
                let result = if !denominator.value.is_zero() {
                    numerator.value.div(denominator.value)
                } else {
                    U256::ZERO
                };
                self.push_with_optimization(result, &numerator, &denominator, operation);
            }

            // SDIV
            0x05 => {
                let numerator = self.stack.pop()?;
                let denominator = self.stack.pop()?;
                let result = if !denominator.value.is_zero() {
                    sign_uint(numerator.value).div(sign_uint(denominator.value))
                } else {
                    I256::ZERO
                };
                self.push_with_optimization_signed(result, &numerator, &denominator, operation);
            }

            // MOD
            0x06 => {
                let a = self.stack.pop()?;
                let modulus = self.stack.pop()?;
                let result =
                    if !modulus.value.is_zero() { a.value.rem(modulus.value) } else { U256::ZERO };
                self.push_with_optimization(result, &a, &modulus, operation);
            }

            // SMOD
            0x07 => {
                let a = self.stack.pop()?;
                let modulus = self.stack.pop()?;
                let result = if !modulus.value.is_zero() {
                    sign_uint(a.value).rem(sign_uint(modulus.value))
                } else {
                    I256::ZERO
                };
                self.push_with_optimization_signed(result, &a, &modulus, operation);
            }

            // ADDMOD
            0x08 => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let modulus = self.stack.pop()?;
                let result = if !modulus.value.is_zero() {
                    a.value.overflowing_add(b.value).0.rem(modulus.value)
                } else {
                    U256::ZERO
                };
                self.push_with_optimization(result, &a, &b, operation);
            }

            // MULMOD
            0x09 => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let modulus = self.stack.pop()?;
                let result = if !modulus.value.is_zero() {
                    a.value.overflowing_mul(b.value).0.rem(modulus.value)
                } else {
                    U256::ZERO
                };
                self.push_with_optimization(result, &a, &b, operation);
            }

            // EXP
            0x0A => {
                let a = self.stack.pop()?;
                let exponent = self.stack.pop()?;
                let result = a.value.overflowing_pow(exponent.value).0;

                // consume dynamic gas
                let exponent_byte_size = exponent.value.bit_len() / 8;
                let gas_cost = 50 * exponent_byte_size;
                self.consume_gas(gas_cost as u128);

                self.push_with_optimization(result, &a, &exponent, operation);
            }

            // SIGNEXTEND
            0x0B => {
                let x = self.stack.pop()?.value;
                let b = self.stack.pop()?.value;

                let t = x * U256::from(8u32) + U256::from(7u32);
                let sign_bit = U256::from(1u32) << t;

                // (b & sign_bit - 1) - (b & sign_bit)
                let result = (b & (sign_bit.overflowing_sub(U256::from(1u32)).0))
                    .overflowing_sub(b & sign_bit)
                    .0;

                self.stack.push(result, operation)
            }

            // LT
            0x10 => {
                let a = self.stack.pop()?.value;
                let b = self.stack.pop()?.value;
                self.push_boolean(a.lt(&b), operation);
            }

            // GT
            0x11 => {
                let a = self.stack.pop()?.value;
                let b = self.stack.pop()?.value;
                self.push_boolean(a.gt(&b), operation);
            }

            // SLT
            0x12 => {
                let a = self.stack.pop()?.value;
                let b = self.stack.pop()?.value;
                self.push_boolean(sign_uint(a).lt(&sign_uint(b)), operation);
            }

            // SGT
            0x13 => {
                let a = self.stack.pop()?.value;
                let b = self.stack.pop()?.value;
                self.push_boolean(sign_uint(a).gt(&sign_uint(b)), operation);
            }

            // EQ
            0x14 => {
                let a = self.stack.pop()?.value;
                let b = self.stack.pop()?.value;
                self.push_boolean(a.eq(&b), operation);
            }

            // ISZERO
            0x15 => {
                let a = self.stack.pop()?.value;
                self.push_boolean(a.is_zero(), operation);
            }

            // AND
            0x16 => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let result = a.value & b.value;
                self.push_with_optimization(result, &a, &b, operation);
            }

            // OR
            0x17 => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let result = a.value | b.value;
                self.push_with_optimization(result, &a, &b, operation);
            }

            // XOR
            0x18 => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let result = a.value ^ b.value;
                self.push_with_optimization(result, &a, &b, operation);
            }

            // NOT
            0x19 => {
                let a = self.stack.pop()?;
                let result = !a.value;
                self.push_with_optimization_single(result, &a, operation);
            }

            // BYTE
            0x1A => {
                let b = self.stack.pop()?.value;
                let a = self.stack.pop()?.value;
                let result = if b >= U256::from(32u32) {
                    U256::ZERO
                } else {
                    a / (U256::from(256u32).pow(U256::from(31u32) - b)) % U256::from(256u32)
                };
                self.stack.push(result, operation);
            }

            // SHL
            0x1B => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let result =
                    if a.value > U256::from(255u8) { U256::ZERO } else { b.value.shl(a.value) };
                self.push_with_optimization(result, &a, &b, operation);
            }

            // SHR
            0x1C => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let result =
                    if a.value > U256::from(255u8) { U256::ZERO } else { b.value.shr(a.value) };
                self.push_with_optimization(result, &a, &b, operation);
            }

            // SAR
            0x1D => {
                let a = self.stack.pop()?;
                let b = self.stack.pop()?;
                let usize_a: usize = a.value.try_into().unwrap_or(usize::MAX);
                let result =
                    if !b.value.is_zero() { sign_uint(b.value).shr(usize_a) } else { I256::ZERO };
                self.push_with_optimization_signed(result, &a, &b, operation);
            }

            // SHA3
            0x20 => {
                let offset = self.stack.pop()?.value;
                let size = self.stack.pop()?.value;

                // Safely convert U256 to usize
                let offset: usize = offset.try_into().unwrap_or(usize::MAX);
                let size: usize = size.try_into().unwrap_or(usize::MAX);

                let data = self.memory.read(offset, size);
                let result = keccak256(data);

                // consume dynamic gas
                let minimum_word_size = size.div_ceil(32) as u128;
                let gas_cost = 6 * minimum_word_size + self.memory.expansion_cost(offset, size);
                self.consume_gas(gas_cost);

                self.stack.push(U256::from_be_bytes(result.0), operation);
            }

            // ADDRESS
            0x30 => {
                self.stack.push(Self::address_to_u256(&self.address), operation);
            }

            // BALANCE
            0x31 => {
                let address = self.stack.pop()?.value;

                // consume dynamic gas
                if !self.address_access_set.contains(&address) {
                    self.consume_gas(2600);
                    self.address_access_set.insert(address);
                } else {
                    self.consume_gas(100);
                }

                // balance is set to 1 wei because we won't run into div by 0 errors
                self.stack.push(U256::from(1), operation);
            }

            // ORIGIN
            0x32 => {
                self.stack.push(Self::address_to_u256(&self.origin), operation);
            }

            // CALLER
            0x33 => {
                self.stack.push(Self::address_to_u256(&self.caller), operation);
            }

            // CALLVALUE
            0x34 => {
                self.stack.push(U256::from(self.value), operation);
            }

            // CALLDATALOAD
            0x35 => {
                let i = self.stack.pop()?.value;

                // Safely convert U256 to usize
                let i: usize = i.try_into().unwrap_or(usize::MAX);

                let result = if i.saturating_add(32) > self.calldata.len() {
                    let mut value = [0u8; 32];

                    if i <= self.calldata.len() {
                        value[..self.calldata.len() - i].copy_from_slice(&self.calldata[i..]);
                    }

                    U256::from_be_bytes(value)
                } else {
                    U256::from_be_slice(&self.calldata[i..i + 32])
                };

                self.stack.push(result, operation);
            }

            // CALLDATASIZE
            0x36 => {
                let result = U256::from(self.calldata.len());

                self.stack.push(result, operation);
            }

            // CALLDATACOPY
            0x37 => {
                let dest_offset = self.stack.pop()?.value;
                let offset = self.stack.pop()?.value;
                let size = self.stack.pop()?.value;

                let dest_offset: usize = dest_offset.try_into().unwrap_or(usize::MAX);
                let offset: usize = offset.try_into().unwrap_or(usize::MAX);
                let size: usize =
                    size.try_into().unwrap_or(self.calldata.len()).min(self.calldata.len());

                let value = Self::safe_copy_data(&self.calldata, offset, size);

                // consume dynamic gas
                let minimum_word_size = size.div_ceil(32) as u128;
                let gas_cost = 3 * minimum_word_size + self.memory.expansion_cost(offset, size);
                self.consume_gas(gas_cost);

                self.memory.store_with_opcode(
                    dest_offset,
                    size,
                    &value,
                    #[cfg(feature = "experimental")]
                    operation,
                );
            }

            // CODESIZE
            0x38 => {
                let result = U256::from(self.bytecode.len() as u128);

                self.stack.push(result, operation);
            }

            // CODECOPY
            0x39 => {
                let dest_offset = self.stack.pop()?.value;
                let offset = self.stack.pop()?.value;
                let size = self.stack.pop()?.value;

                let dest_offset: usize = dest_offset.try_into().unwrap_or(usize::MAX);
                let offset: usize = offset.try_into().unwrap_or(usize::MAX);
                let size: usize =
                    size.try_into().unwrap_or(self.bytecode.len()).min(self.bytecode.len());

                let value = Self::safe_copy_data(&self.bytecode, offset, size);

                // consume dynamic gas
                let minimum_word_size = size.div_ceil(32) as u128;
                let gas_cost = 3 * minimum_word_size + self.memory.expansion_cost(offset, size);
                self.consume_gas(gas_cost);

                self.memory.store_with_opcode(
                    dest_offset,
                    size,
                    &value,
                    #[cfg(feature = "experimental")]
                    operation,
                );
            }

            // GASPRICE
            0x3A => {
                self.stack.push(U256::from(1), operation);
            }

            // EXTCODESIZE
            0x3B => {
                let address = self.stack.pop()?.value;

                // consume dynamic gas
                if !self.address_access_set.contains(&address) {
                    self.consume_gas(2600);
                    self.address_access_set.insert(address);
                } else {
                    self.consume_gas(100);
                }

                self.stack.push(U256::from(1), operation);
            }

            // EXTCODECOPY
            0x3C => {
                let address = self.stack.pop()?.value;
                let dest_offset = self.stack.pop()?.value;
                self.stack.pop()?;
                let size = self.stack.pop()?.value;

                // Safely convert U256 to usize
                let dest_offset: usize = dest_offset.try_into().unwrap_or(0);
                let mut size: usize = size.try_into().unwrap_or(256);
                size = size.max(256);

                let mut value = Vec::with_capacity(size);
                value.fill(0xff);

                // consume dynamic gas
                let minimum_word_size = size.div_ceil(32) as u128;
                let gas_cost =
                    3 * minimum_word_size + self.memory.expansion_cost(dest_offset, size);
                self.consume_gas(gas_cost);
                if !self.address_access_set.contains(&address) {
                    self.consume_gas(2600);
                    self.address_access_set.insert(address);
                } else {
                    self.consume_gas(100);
                }

                self.memory.store_with_opcode(
                    dest_offset,
                    size,
                    &value,
                    #[cfg(feature = "experimental")]
                    operation,
                );
            }

            // RETURNDATASIZE
            0x3D => {
                self.stack.push(U256::from(1u8), operation);
            }

            // RETURNDATACOPY
            0x3E => {
                let dest_offset = self.stack.pop()?.value;
                self.stack.pop()?;
                let size = self.stack.pop()?.value;

                // Safely convert U256 to usize
                let dest_offset: usize = dest_offset.try_into().unwrap_or(0);
                let size: usize = size.try_into().unwrap_or(256);

                let mut value = Vec::with_capacity(size);
                value.fill(0xff);

                // consume dynamic gas
                let minimum_word_size = size.div_ceil(32) as u128;
                let gas_cost =
                    3 * minimum_word_size + self.memory.expansion_cost(dest_offset, size);
                self.consume_gas(gas_cost);

                self.memory.store_with_opcode(
                    dest_offset,
                    size,
                    &value,
                    #[cfg(feature = "experimental")]
                    operation,
                );
            }

            // EXTCODEHASH and BLOCKHASH
            0x3F | 0x40 => {
                let address = self.stack.pop()?.value;

                // consume dynamic gas
                if opcode == 0x3f && !self.address_access_set.contains(&address) {
                    self.consume_gas(2600);
                    self.address_access_set.insert(address);
                } else if opcode == 0x3f {
                    self.consume_gas(100);
                }

                self.stack.push(U256::ZERO, operation);
            }

            // COINBASE
            0x41 => {
                self.stack.push(*COINBASE_ADDRESS, operation);
            }

            // TIMESTAMP
            0x42 => {
                let timestamp =
                    SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_secs();

                self.stack.push(U256::from(timestamp), operation);
            }

            // NUMBER -> BLOBBASEFEE
            (0x43..=0x4a) => {
                self.stack.push(U256::from(1u8), operation);
            }

            // POP
            0x50 => {
                self.stack.pop()?;
            }

            // MLOAD
            0x51 => {
                let i = self.stack.pop()?.value;
                let i: usize = i.try_into().unwrap_or(usize::MAX);

                let result = U256::from_be_slice(self.memory.read(i, 32).as_slice());

                // consume dynamic gas
                let gas_cost = self.memory.expansion_cost(i, 32);
                self.consume_gas(gas_cost);

                self.stack.push(result, operation);
            }

            // MSTORE
            0x52 => {
                let offset = self.stack.pop()?.value;
                let value = self.stack.pop()?.value;

                // Safely convert U256 to usize
                let offset: usize = offset.try_into().unwrap_or(usize::MAX);

                // consume dynamic gas
                let gas_cost = self.memory.expansion_cost(offset, 32);
                self.consume_gas(gas_cost);

                self.memory.store_with_opcode(
                    offset,
                    32,
                    &value.to_be_bytes_vec(),
                    #[cfg(feature = "experimental")]
                    operation,
                );
            }

            // MSTORE8
            0x53 => {
                let offset = self.stack.pop()?.value;
                let value = self.stack.pop()?.value;

                // Safely convert U256 to usize
                let offset: usize = offset.try_into().unwrap_or(usize::MAX);

                // consume dynamic gas
                let gas_cost = self.memory.expansion_cost(offset, 1);
                self.consume_gas(gas_cost);

                self.memory.store_with_opcode(
                    offset,
                    1,
                    &[value.to_be_bytes_vec()[31]],
                    #[cfg(feature = "experimental")]
                    operation,
                );
            }

            // SLOAD
            0x54 => {
                let key = self.stack.pop()?.value;

                // consume dynamic gas
                let gas_cost = self.storage.access_cost(key);
                self.consume_gas(gas_cost);

                self.stack.push(U256::from(self.storage.load(key)), operation)
            }

            // SSTORE
            0x55 => {
                let key = self.stack.pop()?.value;
                let value = self.stack.pop()?.value;

                // consume dynamic gas
                let gas_cost = self.storage.storage_cost(key, value);
                self.consume_gas(gas_cost);

                self.storage.store(key, value);
            }

            // JUMP
            0x56 => {
                let pc = self.stack.pop()?.value;

                // Safely convert U256 to u128
                let pc: u128 = pc.try_into().unwrap_or(u128::MAX);

                // Check if JUMPDEST is valid and throw with 790 if not (invalid jump destination)
                if (pc <=
                    self.bytecode
                        .len()
                        .try_into()
                        .expect("impossible case: bytecode is larger than u128::MAX")) &&
                    (self.bytecode[pc as usize] != 0x5b)
                {
                    self.exit(790, Vec::new());
                    return Ok(Instruction {
                        instruction: last_instruction,
                        opcode,
                        inputs,
                        outputs: Vec::new(),
                        input_operations,
                        output_operations: Vec::new(),
                    });
                } else {
                    self.instruction = pc + 1;
                }
            }

            // JUMPI
            0x57 => {
                let pc = self.stack.pop()?.value;
                let condition = self.stack.pop()?.value;

                // Safely convert U256 to u128
                let pc: u128 = pc.try_into().unwrap_or(u128::MAX);

                if !condition.is_zero() {
                    // Check if JUMPDEST is valid and throw with 790 if not (invalid jump
                    // destination)
                    if (pc <
                        self.bytecode
                            .len()
                            .try_into()
                            .expect("impossible case: bytecode is larger than u128::MAX")) &&
                        (self.bytecode[pc as usize] != 0x5b)
                    {
                        self.exit(790, Vec::new());
                        return Ok(Instruction {
                            instruction: last_instruction,
                            opcode,
                            inputs,
                            outputs: Vec::new(),
                            input_operations,
                            output_operations: Vec::new(),
                        });
                    } else {
                        self.instruction = pc + 1;
                    }
                }
            }

            // JUMPDEST
            0x5B => {}

            // TLOAD
            0x5C => {
                let key = self.stack.pop()?.value;
                self.stack.push(U256::from(self.storage.tload(key)), operation)
            }

            // TSTORE
            0x5D => {
                let key = self.stack.pop()?.value;
                let value = self.stack.pop()?.value;
                self.storage.tstore(key, value);
            }

            // MCOPY
            0x5E => {
                let dest_offset = self.stack.pop()?.value;
                let offset = self.stack.pop()?.value;
                let size = self.stack.pop()?.value;

                let dest_offset: usize = dest_offset.try_into().unwrap_or(u128::MAX as usize);
                let offset: usize = offset.try_into().unwrap_or(u128::MAX as usize);
                let memory_size: usize =
                    self.memory.size().try_into().expect("failed to convert u128 to usize");
                let size: usize = size.try_into().unwrap_or(memory_size).min(memory_size);

                let value = Self::safe_copy_data(&self.memory.memory, offset, size);

                // consume dynamic gas
                let minimum_word_size = size.div_ceil(32) as u128;
                let gas_cost = 3 * minimum_word_size + self.memory.expansion_cost(offset, size);
                self.consume_gas(gas_cost);

                self.memory.store_with_opcode(
                    dest_offset,
                    size,
                    &value,
                    #[cfg(feature = "experimental")]
                    operation,
                );
            }

            // PC
            0x58 => {
                self.stack.push(U256::from(self.instruction), operation);
            }

            // MSIZE
            0x59 => {
                self.stack.push(U256::from(self.memory.size()), operation);
            }

            // GAS
            0x5a => {
                self.stack.push(U256::from(self.gas_remaining), operation);
            }

            // PUSH0
            0x5f => {
                self.stack.push(U256::ZERO, operation);
            }

            // PUSH1 -> PUSH32
            (0x60..=0x7F) => {
                // Get the number of bytes to push
                let num_bytes = (opcode - 95) as u128;

                // Get the bytes to push from bytecode
                let bytes = &self.bytecode
                    [(self.instruction - 1) as usize..(self.instruction - 1 + num_bytes) as usize];
                self.instruction += num_bytes;

                // update the operation's inputs
                let new_operation_inputs = vec![WrappedInput::Raw(U256::from_be_slice(bytes))];

                operation.inputs = new_operation_inputs;

                // Push the bytes to the stack
                self.stack.push(U256::from_be_slice(bytes), operation);
            }

            // DUP1 -> DUP16
            (0x80..=0x8F) => {
                // Get the number of items to swap
                let index = opcode - 127;

                // Perform the swap
                self.stack.dup(index as usize);
            }

            // SWAP1 -> SWAP16
            (0x90..=0x9F) => {
                // Get the number of items to swap
                let index = opcode - 143;

                // Perform the swap
                self.stack.swap(index as usize);
            }

            // LOG0 -> LOG4
            (0xA0..=0xA4) => {
                let topic_count = opcode - 160;
                let offset = self.stack.pop()?.value;
                let size = self.stack.pop()?.value;
                let topics =
                    self.stack.pop_n(topic_count as usize).iter().map(|x| x.value).collect();

                // Safely convert U256 to usize
                let offset: usize = offset.try_into().unwrap_or(usize::MAX);
                let size: usize = size.try_into().unwrap_or(usize::MAX);

                let data = self.memory.read(offset, size);

                // consume dynamic gas
                let gas_cost = (375 * (topic_count as u128)) +
                    8 * (size as u128) +
                    self.memory.expansion_cost(offset, size);
                self.consume_gas(gas_cost);

                // no need for a panic check because the length of events should never be larger
                // than a u128
                self.events.push(Log::new(
                    self.events
                        .len()
                        .try_into()
                        .expect("impossible case: log_index is larger than u128::MAX"),
                    topics,
                    &data,
                ))
            }

            // CREATE
            0xF0 => {
                self.stack.pop_n(3);

                self.stack.push(*CREATE_ADDRESS, operation);
            }

            // CALL, CALLCODE
            0xF1 | 0xF2 => {
                let address = self.stack.pop()?.value;
                self.stack.pop_n(6);

                // consume dynamic gas
                if !self.address_access_set.contains(&address) {
                    self.consume_gas(2600);
                    self.address_access_set.insert(address);
                } else {
                    self.consume_gas(100);
                }

                self.stack.push(U256::from(1u8), operation);
            }

            // RETURN
            0xF3 => {
                let offset = self.stack.pop()?.value;
                let size = self.stack.pop()?.value;

                // Safely convert U256 to usize
                let offset: usize = offset.try_into().unwrap_or(usize::MAX);
                let size: usize = size.try_into().unwrap_or(usize::MAX);

                // consume dynamic gas
                let gas_cost = self.memory.expansion_cost(offset, size);
                self.consume_gas(gas_cost);

                self.exit(0, self.memory.read(offset, size));
            }

            // DELEGATECALL, STATICCALL
            0xF4 | 0xFA => {
                let address = self.stack.pop()?.value;
                self.stack.pop_n(5);

                // consume dynamic gas
                if !self.address_access_set.contains(&address) {
                    self.consume_gas(2600);
                    self.address_access_set.insert(address);
                } else {
                    self.consume_gas(100);
                }

                self.stack.push(U256::from(1u8), operation);
            }

            // CREATE2
            0xF5 => {
                self.stack.pop_n(4);

                self.stack.push(*CREATE2_ADDRESS, operation);
            }

            // REVERT
            0xFD => {
                let offset = self.stack.pop()?.value;
                let size = self.stack.pop()?.value;

                // Safely convert U256 to usize
                let offset: usize = offset.try_into().unwrap_or(usize::MAX);
                let size: usize = size.try_into().unwrap_or(usize::MAX);

                self.exit(1, self.memory.read(offset, size));
            }

            // INVALID & SELFDESTRUCT
            _ => {
                self.exit(1, Vec::new());
            }
        }

        // get outputs
        let output_frames = self.stack.peek_n(opcode_info.outputs() as usize);
        let output_operations =
            output_frames.iter().map(|x| x.operation.clone()).collect::<Vec<WrappedOpcode>>();
        let outputs = output_frames.iter().map(|x| x.value).collect::<Vec<U256>>();

        // if step-tracing feature is enabled, print the current operation
        #[cfg(feature = "step-tracing")]
        trace!(
            pc = self.instruction - 1,
            opcode = opcode_info.name(),
            outputs = ?outputs
                .iter()
                .map(|x| format!("{x:#x}"))
                .collect::<Vec<String>>(),
            elapsed = ?Instant::now().duration_since(start_time),
            "done executing opcode"
        );

        #[cfg(feature = "step-tracing")]
        {
            trace!(
                ops_per_sec =
                    (self.operation_count as f64 / self.start_time.elapsed().as_secs_f64()),
                mem_size = self.memory.size(),
                stack_size = self.stack.size(),
                "_step.end"
            );
        }

        Ok(Instruction {
            instruction: last_instruction,
            opcode,
            inputs,
            outputs,
            input_operations,
            output_operations,
        })
    }

    /// Executes the next instruction in the VM and returns a snapshot of the VM state after
    /// executing the instruction
    ///
    /// ```
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let mut vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    ///
    /// vm.step(); // 0x00 EXIT
    /// assert_eq!(vm.exitcode, 10);
    /// ```
    pub fn step(&mut self) -> Result<State> {
        let instruction = self._step()?;

        Ok(State {
            last_instruction: instruction,
            gas_used: self.gas_used,
            gas_remaining: self.gas_remaining,
            stack: self.stack.clone(),
            memory: self.memory.clone(),
            storage: self.storage.clone(),
            events: self.events.clone(),
        })
    }

    /// View the next n instructions without executing them
    ///
    /// ```
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let mut vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    ///
    /// vm.peek(1); // 0x00 EXIT (not executed)
    /// assert_eq!(vm.exitcode, 255);
    /// ```
    pub fn peek(&mut self, n: usize) -> Result<Vec<State>> {
        let mut states = Vec::new();
        let mut vm_clone = self.clone();

        for _ in 0..n {
            if vm_clone.bytecode.len() < vm_clone.instruction as usize ||
                vm_clone.exitcode != 255 ||
                !vm_clone.returndata.is_empty()
            {
                break;
            }
            states.push(vm_clone.step()?);
        }

        Ok(states)
    }

    /// Resets the VM state for a new execution
    ///
    /// ```
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let mut vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    ///
    /// vm.step(); // 0x00 EXIT (not executed)
    /// assert_eq!(vm.exitcode, 10);
    ///
    /// vm.reset();
    /// assert_eq!(vm.exitcode, 255);
    /// ```
    pub fn reset(&mut self) {
        self.stack = Stack::new();
        self.memory = Memory::new();
        self.instruction = 1;
        self.gas_remaining = (self.gas_used + self.gas_remaining).max(21000) - 21000;
        self.gas_used = 21000;
        self.events = Vec::new();
        self.returndata = Vec::new();
        self.exitcode = 255;
    }

    /// Executes the code until finished
    ///
    /// ```
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let mut vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    ///
    /// vm.execute().expect("execution failed!"); // 0x00 EXIT (not executed)
    /// assert_eq!(vm.exitcode, 10);
    /// ```
    pub fn execute(&mut self) -> Result<ExecutionResult> {
        while self.bytecode.len() >= self.instruction as usize {
            self.step()?;

            if self.exitcode != 255 || !self.returndata.is_empty() {
                break;
            }
        }

        Ok(ExecutionResult {
            gas_used: self.gas_used,
            gas_remaining: self.gas_remaining,
            returndata: self.returndata.to_owned(),
            exitcode: self.exitcode,
            events: self.events.clone(),
            instruction: self.instruction,
        })
    }

    /// Executes provided calldata until finished
    ///
    /// ```
    /// use heimdall_vm::core::vm::VM;
    /// use alloy::primitives::Address;
    ///
    /// let mut vm = VM::new(
    ///     &vec![0x00],
    ///     &vec![],
    ///     "0x0000000000000000000000000000000000000000".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000001".parse::<Address>().expect("failed to parse Address"),
    ///     "0x0000000000000000000000000000000000000002".parse::<Address>().expect("failed to parse Address"),
    ///     0,
    ///     1000000000000000000,
    /// );
    ///
    /// vm.call(&vec![], 0);
    /// assert_eq!(vm.exitcode, 10);
    /// ```
    pub fn call(&mut self, calldata: &[u8], value: u128) -> Result<ExecutionResult> {
        // reset the VM temp state
        self.reset();
        calldata.clone_into(&mut self.calldata);
        self.value = value;

        self.execute()
    }
}

#[cfg(test)]
mod tests {

    use std::str::FromStr;

    use heimdall_common::utils::strings::decode_hex;

    use super::*;

    // creates a new test VM with calldata.
    fn new_test_vm(bytecode: &str) -> VM {
        VM::new(
            &decode_hex(bytecode).expect("failed to decode bytecode"),
            &decode_hex("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
                .expect("failed to decode calldata"),
            "0x6865696d64616c6c000000000061646472657373"
                .parse::<Address>()
                .expect("failed to parse Address"),
            "0x6865696d64616c6c0000000000006f726967696e"
                .parse::<Address>()
                .expect("failed to parse Address"),
            "0x6865696d64616c6c00000000000063616c6c6572"
                .parse::<Address>()
                .expect("failed to parse Address"),
            0,
            9999999999,
        )
    }

    #[test]
    fn test_stop_vm() {
        let mut vm = new_test_vm("0x00");
        vm.execute().expect("execution failed!");

        assert!(vm.returndata.is_empty());
        assert_eq!(vm.exitcode, 10);
    }

    #[test]
    fn test_pc_out_of_range() {
        let mut vm = new_test_vm("0x");
        vm.execute().expect("execution failed!");

        assert!(vm.returndata.is_empty());
        assert_eq!(vm.exitcode, 255);
    }

    #[test]
    fn test_add() {
        let mut vm = new_test_vm(
            "0x600a600a017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600101",
        );
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x14").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_mul() {
        let mut vm = new_test_vm(
            "0x600a600a027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600202",
        );
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x64").expect("failed to parse hex"));
        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_sub() {
        let mut vm = new_test_vm("0x600a600a036001600003");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").expect("failed to parse hex"));
        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_div() {
        let mut vm = new_test_vm("0x600a600a046002600104");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_div_by_zero() {
        let mut vm = new_test_vm("0x6002600004");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_sdiv() {
        let mut vm = new_test_vm("0x600a600a057fFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7fFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE05");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x02").expect("failed to parse hex"));
    }

    #[test]
    fn test_sdiv_by_zero() {
        let mut vm = new_test_vm("0x6002600005");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_mod() {
        let mut vm = new_test_vm("0x6003600a066005601106");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x02").expect("failed to parse hex"));
    }

    #[test]
    fn test_mod_by_zero() {
        let mut vm = new_test_vm("0x6002600006");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_smod() {
        let mut vm = new_test_vm("0x6003600a077ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff807");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_smod_by_zero() {
        let mut vm = new_test_vm("0x6002600007");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_addmod() {
        let mut vm = new_test_vm("0x6008600a600a08600260027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x04").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").expect("failed to parse hex"));
    }

    #[test]
    fn test_addmod_by_zero() {
        let mut vm = new_test_vm("0x60026000600008");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_mulmod() {
        let mut vm = new_test_vm("0x6008600a600a09600c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x04").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").expect("failed to parse hex"));
    }

    #[test]
    fn test_mulmod_by_zero() {
        let mut vm = new_test_vm("0x60026000600009");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_exp() {
        let mut vm = new_test_vm("0x6002600a0a600260020a");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x64").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x04").expect("failed to parse hex"));
    }

    #[test]
    fn test_signextend() {
        let mut vm = new_test_vm("0x60ff60000b607f60000b");
        vm.execute().expect("execution failed!");

        assert_eq!(
            vm.stack.peek(1).value,
            U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
                .expect("failed to parse hex")
        );
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x7f").expect("failed to parse hex"));
    }

    #[test]
    fn test_lt() {
        let mut vm = new_test_vm("0x600a600910600a600a10");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_gt() {
        let mut vm = new_test_vm("0x6009600a11600a600a10");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_slt() {
        let mut vm = new_test_vm(
            "0x60097fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff12600a600a12",
        );
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_sgt() {
        let mut vm = new_test_vm(
            "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600913600a600a13",
        );
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_eq() {
        let mut vm = new_test_vm("0x600a600a14600a600514");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_iszero() {
        let mut vm = new_test_vm("0x600015600a15");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_and() {
        let mut vm = new_test_vm("0x600f600f16600060ff1600");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x0F").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_or() {
        let mut vm = new_test_vm("0x600f60f01760ff60ff17");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff").expect("failed to parse hex"));
    }

    #[test]
    fn test_xor() {
        let mut vm = new_test_vm("0x600f60f01860ff60ff18");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_not() {
        let mut vm = new_test_vm("0x600019");
        vm.execute().expect("execution failed!");

        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_byte() {
        let mut vm = new_test_vm("0x60ff601f1a61ff00601e1a");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff").expect("failed to parse hex"));
    }

    #[test]
    fn test_shl() {
        let mut vm = new_test_vm(
            "600160011b7fFF0000000000000000000000000000000000000000000000000000000000000060041b",
        );
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x02").expect("failed to parse hex"));
        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0xF000000000000000000000000000000000000000000000000000000000000000")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_shl_gt_255() {
        let mut vm = new_test_vm(
            "600161ffff1b7fFF0000000000000000000000000000000000000000000000000000000000000060041b",
        );
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").expect("failed to parse hex"));
        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0xF000000000000000000000000000000000000000000000000000000000000000")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_shr() {
        let mut vm = new_test_vm("600260011c60ff60041c");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x01").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0f").expect("failed to parse hex"));
    }

    #[test]
    fn test_shr_gt_256() {
        let mut vm = new_test_vm("600261ffff1c61ffff60041c");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x00").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0fff").expect("failed to parse hex"));
    }

    #[test]
    fn test_shr_zero() {
        let mut vm = new_test_vm("0x600060011c");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_sar() {
        let mut vm = new_test_vm("600260011d");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x01").expect("failed to parse hex"));
    }

    #[test]
    fn test_sar_zero() {
        let mut vm = new_test_vm("0x600060011d");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_sha3() {
        let mut vm = new_test_vm(
            "0x7fffffffff000000000000000000000000000000000000000000000000000000006000526004600020",
        );
        vm.execute().expect("execution failed!");

        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0x29045A592007D0C246EF02C2223570DA9522D0CF0F73282C79A1BC8F0BB2C238")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_address() {
        let mut vm = new_test_vm("0x30");
        vm.execute().expect("execution failed!");

        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0x6865696d64616c6c000000000061646472657373")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_calldataload() {
        let mut vm = new_test_vm("600035601f35");
        vm.execute().expect("execution failed!");

        assert_eq!(
            vm.stack.peek(1).value,
            U256::from_str("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
                .expect("failed to parse hex")
        );
        assert_eq!(
            vm.stack.peek(0).value,
            U256::from_str("0xFF00000000000000000000000000000000000000000000000000000000000000")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_calldatasize() {
        let mut vm = new_test_vm("0x36");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x20").expect("failed to parse hex"));
    }

    #[test]
    fn test_xdatacopy() {
        // returndatacopy, calldatacopy, etc share same code.
        let mut vm = new_test_vm("0x60ff6000600037");
        vm.execute().expect("execution failed!");
        assert_eq!(
            vm.memory.read(0, 32),
            decode_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_mcopy() {
        let mut vm = new_test_vm("0x7f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f6020526020602060005e");
        vm.execute().expect("execution failed!");
        assert_eq!(
            vm.memory.read(0, 64),
            decode_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
                .expect("failed to parse hex")
        );
    }

    #[test]
    fn test_mcopy_clamping_source_beyond_memory() {
        // Test copying from offset beyond current memory size
        // Store 32 bytes at memory[0x20], then try to copy from offset 0x40 (beyond memory)
        let mut vm = new_test_vm("0x7f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f6020526020604060005e");
        vm.execute().expect("execution failed!");

        // Should copy zeros since source is beyond memory
        let result = vm.memory.read(0, 32);
        assert_eq!(result, vec![0u8; 32]);
    }

    #[test]
    fn test_mcopy_clamping_partial_source_beyond_memory() {
        // Test copying where source starts in memory but extends beyond it
        // Store 32 bytes at memory[0x20], then copy 64 bytes from offset 0x30
        let mut vm = new_test_vm("0x7f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f6020526040603060005e");
        vm.execute().expect("execution failed!");

        // Should copy the available 16 bytes from memory[0x30-0x3F] then pad with zeros
        let result = vm.memory.read(0, 64);
        let expected = [
            &decode_hex("101112131415161718191a1b1c1d1e1f").expect("failed to parse hex")[..],
            &vec![0u8; 48][..],
        ]
        .concat();
        assert_eq!(result, expected);
    }

    #[test]
    fn test_mcopy_clamping_zero_size() {
        // Test copying zero bytes
        let mut vm = new_test_vm("0x7f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f6020526000602060005e");
        vm.execute().expect("execution failed!");

        // Memory should only contain the original store at 0x20, destination at 0x00 should be
        // unchanged
        let result = vm.memory.read(0, 64);
        let expected = [
            &vec![0u8; 32][..],
            &decode_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
                .expect("failed to parse hex")[..],
        ]
        .concat();
        assert_eq!(result, expected);
    }

    #[test]
    fn test_mcopy_clamping_size_exceeds_memory() {
        // Test copying more bytes than available memory
        // Store 32 bytes at 0x20, then copy 64 bytes from 0x20 to 0x00
        // The copy overlaps, so source data at 0x20+ gets overwritten by destination
        let mut vm = new_test_vm("0x7f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f6020526040602060005e");
        vm.execute().expect("execution failed!");

        // Since we copy 64 bytes from 0x20 to 0x00, and only 32 bytes exist at source:
        // - Bytes 0x00-0x1F get the original data from 0x20-0x3F
        // - Bytes 0x20-0x3F get overwritten with zeros (padding from beyond source)
        let result = vm.memory.read(0, 64);
        let expected = [
            &decode_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
                .expect("failed to parse hex")[..], // First 32 bytes: copied data
            &vec![0u8; 32][..], // Next 32 bytes: zeros (padding that overwrote the source)
        ]
        .concat();
        assert_eq!(result, expected);
    }

    #[test]
    fn test_mcopy_clamping_simple_overlap() {
        // Test a simpler overlapping copy case
        // Store data, then copy within the same memory region
        let mut vm = new_test_vm("0x7f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f6000526010601060005e");
        vm.execute().expect("execution failed!");

        // Original data at 0x00: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
        // Copy 16 bytes from 0x10 to 0x00: should copy 101112131415161718191a1b1c1d1e1f
        let result = vm.memory.read(0, 32);
        let expected = [
            &decode_hex("101112131415161718191a1b1c1d1e1f").expect("failed to parse hex")[..], // 0x00-0x0F: copied data
            &decode_hex("101112131415161718191a1b1c1d1e1f").expect("failed to parse hex")[..] // 0x10-0x1F: original data
        ].concat();
        assert_eq!(result, expected);
    }

    #[test]
    fn test_mcopy_clamping_large_offsets() {
        // Test with very large U256 offsets that get clamped to usize::MAX
        // This tests the try_into().unwrap_or() clamping behavior
        let mut vm = new_test_vm("0x7f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f6020526020608060005e");
        vm.execute().expect("execution failed!");

        // Should handle large offset gracefully and copy zeros (since source is beyond memory)
        let result = vm.memory.read(0, 32);
        assert_eq!(result, vec![0u8; 32]);
    }

    #[test]
    fn test_mcopy_clamping_exact_memory_boundary() {
        // Test copying exactly at memory boundary
        // Store 32 bytes, then copy from the last valid offset
        let mut vm = new_test_vm("0x7f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f6020526001603f60005e");
        vm.execute().expect("execution failed!");

        // Should copy the last byte of memory and pad with zero
        let result = vm.memory.read(0, 32);
        let expected = [
            &[0x1f][..],        // Last byte of the stored data
            &vec![0u8; 31][..], // Padding
        ]
        .concat();
        assert_eq!(result, expected);
    }

    #[test]
    fn test_mcopy_clamping_empty_memory() {
        // Test copying from empty memory (no prior stores)
        let mut vm = new_test_vm("0x6020602060005e");
        vm.execute().expect("execution failed!");

        // Should copy all zeros
        let result = vm.memory.read(0, 64);
        assert_eq!(result, vec![0u8; 64]);
    }

    #[test]
    fn test_codesize() {
        let mut vm = new_test_vm("0x60ff60ff60ff60ff60ff38");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x0B").expect("failed to parse hex"));
    }

    #[test]
    fn test_mload_mstore() {
        let mut vm = new_test_vm("0x7f00000000000000000000000000000000000000000000000000000000000000FF600052600051600151");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0xff00").expect("failed to parse hex"));
    }

    #[test]
    fn test_mstore8() {
        let mut vm = new_test_vm("0x60ff600053");
        vm.execute().expect("execution failed!");

        assert_eq!(
            vm.memory.read(0, 32),
            decode_hex("ff00000000000000000000000000000000000000000000000000000000000000")
                .expect("failed to parse hex")
        )
    }

    #[test]
    fn test_msize() {
        let mut vm = new_test_vm("0x60ff60005359");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x20").expect("failed to parse hex"));
    }

    #[test]
    fn test_sload_sstore() {
        let mut vm = new_test_vm("0x602e600055600054600154");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x2e").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_tload_tstore() {
        let mut vm = new_test_vm("0x602e60005d60005c60015c");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0x2e").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x00").expect("failed to parse hex"));
    }

    #[test]
    fn test_sstore_tstore_independence() {
        let mut vm = new_test_vm("0x60ff60015560fe60015d60015460015c");
        vm.execute().expect("execution failed!");

        assert_eq!(vm.stack.peek(1).value, U256::from_str("0xff").expect("failed to parse hex"));
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0xfe").expect("failed to parse hex"));
    }

    #[test]
    fn test_jump() {
        let mut vm = new_test_vm("0x60fe56");
        vm.execute().expect("execution failed!");

        assert_eq!(
            U256::from(vm.instruction),
            U256::from_str("0xff").expect("failed to parse hex")
        );
    }

    #[test]
    fn test_jumpi() {
        let mut vm = new_test_vm("0x600160fe57");
        vm.execute().expect("execution failed!");

        assert_eq!(
            U256::from(vm.instruction),
            U256::from_str("0xff").expect("failed to parse hex")
        );

        let mut vm = new_test_vm("0x600060fe5758");
        vm.execute().expect("execution failed!");

        assert_eq!(
            U256::from(vm.instruction),
            U256::from_str("0x07").expect("failed to parse hex")
        );

        // PC test
        assert_eq!(vm.stack.peek(0).value, U256::from_str("0x07").expect("failed to parse hex"));
    }

    #[test]
    fn test_usdt_sim() {
        // this execution should return the name of the USDT contract
        let mut vm = new_test_vm(" ");
        vm.calldata = [0x06, 0xfd, 0xde, 0x03].to_vec();
        vm.execute().expect("execution failed!");

        assert_eq!(
            vm.returndata,
            vec![
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 10, 85, 110, 105, 115, 119, 97, 112, 32, 86, 50, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
            ]
        );
    }
}
