diff --git a/Cargo.lock b/Cargo.lock index 1624e7f6e0714..ca3d9e8e22a38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,7 +87,7 @@ dependencies = [ "k256", "once_cell", "rand 0.8.5", - "secp256k1", + "secp256k1 0.30.0", "serde", "serde_with", "thiserror 2.0.12", @@ -223,18 +223,19 @@ dependencies = [ [[package]] name = "alloy-evm" -version = "0.10.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "394b09cf3a32773eedf11828987f9c72dfa74545040be0422e3f5f09a2a3fab9" +checksum = "ef2d6e0448bfd057a4438226b3d2fd547a0530fa4226217dfb1682d09f108bd4" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-hardforks", "alloy-primitives", + "alloy-rpc-types-eth", "alloy-sol-types", "auto_impl", "derive_more 2.0.1", - "op-alloy-consensus", + "op-alloy-consensus 0.18.9", "op-revm", "revm", "thiserror 2.0.12", @@ -334,9 +335,9 @@ dependencies = [ [[package]] name = "alloy-op-evm" -version = "0.10.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f32538cc243ec5d4603da9845cc2f5254c6a3a78e82475beb1a2a1de6c0d36c" +checksum = "98354b9c3d50de701a63693d5b6a37e468a93b970b2224f934dd745c727ef998" dependencies = [ "alloy-consensus", "alloy-eips", @@ -344,7 +345,7 @@ dependencies = [ "alloy-op-hardforks", "alloy-primitives", "auto_impl", - "op-alloy-consensus", + "op-alloy-consensus 0.18.9", "op-revm", "revm", ] @@ -587,7 +588,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", - "itertools 0.13.0", + "itertools 0.14.0", "serde", "serde_json", "serde_with", @@ -1077,7 +1078,7 @@ dependencies = [ "futures", "hyper", "itertools 0.14.0", - "op-alloy-consensus", + "op-alloy-consensus 0.17.2", "op-alloy-rpc-types", "op-revm", "parking_lot", @@ -1110,7 +1111,7 @@ dependencies = [ "bytes", "foundry-common", "foundry-evm", - "op-alloy-consensus", + "op-alloy-consensus 0.17.2", "op-revm", "rand 0.9.1", "revm", @@ -2027,6 +2028,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "backtrace" version = "0.3.75" @@ -2404,7 +2411,7 @@ dependencies = [ "foundry-wallets", "futures", "itertools 0.14.0", - "op-alloy-consensus", + "op-alloy-consensus 0.17.2", "op-alloy-flz", "rand 0.8.5", "rand 0.9.1", @@ -4281,7 +4288,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.13.0", + "itertools 0.14.0", "path-slash", "rand 0.8.5", "rayon", @@ -4475,10 +4482,12 @@ dependencies = [ name = "foundry-evm-core" version = "1.2.3" dependencies = [ + "alloy-chains", "alloy-consensus", "alloy-dyn-abi", "alloy-evm", "alloy-genesis", + "alloy-hardforks", "alloy-json-abi", "alloy-network", "alloy-op-evm", @@ -4577,11 +4586,13 @@ dependencies = [ [[package]] name = "foundry-fork-db" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3ce9907d94f0371f17930a79ced2c2d9f09131da93f8678f21505ed43c1f39" +checksum = "8bdf390c3633b0eb14c6bb26a0aeb63ea0200f1350ccbe07493f23148f58c4a5" dependencies = [ + "alloy-chains", "alloy-consensus", + "alloy-hardforks", "alloy-primitives", "alloy-provider", "alloy-rpc-types", @@ -4935,6 +4946,16 @@ dependencies = [ "regex-syntax 0.8.5", ] +[[package]] +name = "gmp-mpfr-sys" +version = "1.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66d61197a68f6323b9afa616cf83d55d69191e1bf364d4eb7d35ae18defe776" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "group" version = "0.13.0" @@ -6034,9 +6055,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "mdbook" -version = "0.4.51" +version = "0.4.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a87e65420ab45ca9c1b8cdf698f95b710cc826d373fa550f0f7fad82beac9328" +checksum = "93c284d2855916af7c5919cf9ad897cfc77d3c2db6f55429c7cfb769182030ec" dependencies = [ "ammonia", "anyhow", @@ -6530,6 +6551,20 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "op-alloy-consensus" +version = "0.18.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8719d9b783b29cfa1cf8d591b894805786b9ab4940adc700a57fd0d5b721cf5" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more 2.0.1", + "thiserror 2.0.12", +] + [[package]] name = "op-alloy-flz" version = "0.13.1" @@ -6549,7 +6584,7 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-serde", "derive_more 2.0.1", - "op-alloy-consensus", + "op-alloy-consensus 0.17.2", "serde", "serde_json", "thiserror 2.0.12", @@ -6557,9 +6592,9 @@ dependencies = [ [[package]] name = "op-revm" -version = "5.0.1" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e8a3830a2be82166fbe9ead34361149ff4320743ed7ee5502ab779de221361" +checksum = "ee9ba9cab294a5ed02afd1a1060220762b3c52911acab635db33822e93f7276d" dependencies = [ "auto_impl", "once_cell", @@ -7191,7 +7226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.104", @@ -7627,9 +7662,9 @@ dependencies = [ [[package]] name = "revm" -version = "24.0.1" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d277408ff8d6f747665ad9e52150ab4caf8d5eaf0d787614cf84633c8337b4" +checksum = "70a84455f03d3480d4ed2e7271c15f2ec95b758e86d57cb8d258a8ff1c22e9a4" dependencies = [ "revm-bytecode", "revm-context", @@ -7646,9 +7681,9 @@ dependencies = [ [[package]] name = "revm-bytecode" -version = "4.1.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942fe4724cf552fd28db6b0a2ca5b79e884d40dd8288a4027ed1e9090e0c6f49" +checksum = "7a685758a4f375ae9392b571014b9779cfa63f0d8eb91afb4626ddd958b23615" dependencies = [ "bitvec", "once_cell", @@ -7659,9 +7694,9 @@ dependencies = [ [[package]] name = "revm-context" -version = "5.0.1" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01aad49e1233f94cebda48a4e5cef022f7c7ed29b4edf0d202b081af23435ef" +checksum = "a990abf66b47895ca3e915d5f3652bb7c6a4cff6e5351fdf0fc2795171fd411c" dependencies = [ "cfg-if", "derive-where", @@ -7675,9 +7710,9 @@ dependencies = [ [[package]] name = "revm-context-interface" -version = "5.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b844f48a411e62c7dde0f757bf5cce49c85b86d6fc1d3b2722c07f2bec4c3ce" +checksum = "a303a93102fceccec628265efd550ce49f2817b38ac3a492c53f7d524f18a1ca" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -7691,9 +7726,9 @@ dependencies = [ [[package]] name = "revm-database" -version = "4.0.1" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad3fbe34f6bb00a9c3155723b3718b9cb9f17066ba38f9eb101b678cd3626775" +checksum = "7db360729b61cc347f9c2f12adb9b5e14413aea58778cf9a3b7676c6a4afa115" dependencies = [ "alloy-eips", "revm-bytecode", @@ -7705,11 +7740,12 @@ dependencies = [ [[package]] name = "revm-database-interface" -version = "4.0.1" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8acd36784a6d95d5b9e1b7be3ce014f1e759abb59df1fa08396b30f71adc2a" +checksum = "b8500194cad0b9b1f0567d72370795fd1a5e0de9ec719b1607fa1566a23f039a" dependencies = [ "auto_impl", + "either", "revm-primitives", "revm-state", "serde", @@ -7717,11 +7753,12 @@ dependencies = [ [[package]] name = "revm-handler" -version = "5.0.1" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "481e8c3290ff4fa1c066592fdfeb2b172edfd14d12e6cade6f6f5588cad9359a" +checksum = "03c35a17a38203976f97109e20eccf6732447ce6c9c42973bae42732b2e957ff" dependencies = [ "auto_impl", + "derive-where", "revm-bytecode", "revm-context", "revm-context-interface", @@ -7735,11 +7772,12 @@ dependencies = [ [[package]] name = "revm-inspector" -version = "5.0.1" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc1167ef8937d8867888e63581d8ece729a72073d322119ef4627d813d99ecb" +checksum = "e69abf6a076741bd5cd87b7d6c1b48be2821acc58932f284572323e81a8d4179" dependencies = [ "auto_impl", + "either", "revm-context", "revm-database-interface", "revm-handler", @@ -7752,9 +7790,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.23.0" +version = "0.26.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b50ef375dbacefecfdacf8f02afc31df98acc5d8859a6f2b24d121ff2a740a8" +checksum = "c7b99a2332cf8eed9e9a22fffbf76dfadc99d2c45de6ae6431a1eb9f657dd97a" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -7770,9 +7808,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "20.0.0" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5ee65e57375c6639b0f50555e92a4f1b2434349dd32f52e2176f5c711171697" +checksum = "d95c4a9a1662d10b689b66b536ddc2eb1e89f5debfcabc1a2d7b8417a2fa47cd" dependencies = [ "revm-bytecode", "revm-context-interface", @@ -7782,15 +7820,16 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "21.0.0" +version = "24.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9311e735123d8d53a02af2aa81877bba185be7c141be7f931bb3d2f3af449c" +checksum = "b68d54a4733ac36bd29ee645c3c2e5e782fb63f199088d49e2c48c64a9fedc15" dependencies = [ "ark-bls12-381", "ark-bn254", "ark-ec", "ark-ff 0.5.0", "ark-serialize 0.5.0", + "arrayref", "aurora-engine-modexp", "blst", "c-kzg", @@ -7801,15 +7840,16 @@ dependencies = [ "p256", "revm-primitives", "ripemd", - "secp256k1", + "rug", + "secp256k1 0.31.1", "sha2 0.10.9", ] [[package]] name = "revm-primitives" -version = "19.2.0" +version = "20.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c1588093530ec4442461163be49c433c07a3235d1ca6f6799fef338dacc50d3" +checksum = "52cdf897b3418f2ee05bcade64985e5faed2dbaa349b2b5f27d3d6bfd10fff2a" dependencies = [ "alloy-primitives", "num_enum", @@ -7818,9 +7858,9 @@ dependencies = [ [[package]] name = "revm-state" -version = "4.0.1" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0040c61c30319254b34507383ba33d85f92949933adf6525a2cede05d165e1fa" +checksum = "106fec5c634420118c7d07a6c37110186ae7f23025ceac3a5dbe182eea548363" dependencies = [ "bitflags 2.9.1", "revm-bytecode", @@ -7901,6 +7941,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rug" +version = "1.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4207e8d668e5b8eb574bda8322088ccd0d7782d3d03c7e8d562e82ed82bdcbc3" +dependencies = [ + "az", + "gmp-mpfr-sys", + "libc", + "libm", +] + [[package]] name = "ruint" version = "1.15.0" @@ -8255,10 +8307,21 @@ checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", "rand 0.8.5", - "secp256k1-sys", + "secp256k1-sys 0.10.1", "serde", ] +[[package]] +name = "secp256k1" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" +dependencies = [ + "bitcoin_hashes", + "rand 0.9.1", + "secp256k1-sys 0.11.0", +] + [[package]] name = "secp256k1-sys" version = "0.10.1" @@ -8268,6 +8331,15 @@ dependencies = [ "cc", ] +[[package]] +name = "secp256k1-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" +dependencies = [ + "cc", +] + [[package]] name = "secret-vault-value" version = "0.3.9" @@ -8736,7 +8808,7 @@ dependencies = [ "derive_builder", "derive_more 2.0.1", "dunce", - "itertools 0.13.0", + "itertools 0.14.0", "itoa", "lasso", "match_cfg", @@ -8773,7 +8845,7 @@ dependencies = [ "alloy-primitives", "bitflags 2.9.1", "bumpalo", - "itertools 0.13.0", + "itertools 0.14.0", "memchr", "num-bigint", "num-rational", diff --git a/Cargo.toml b/Cargo.toml index 799005a20f448..80fee68f41d1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -206,7 +206,7 @@ foundry-linking = { path = "crates/linking" } # solc & compilation utilities foundry-block-explorers = { version = "0.19.1", default-features = false } foundry-compilers = { version = "0.17.4", default-features = false } -foundry-fork-db = "0.15" +foundry-fork-db = "0.16" solang-parser = { version = "=0.3.9", package = "foundry-solang-parser" } solar-ast = { version = "=0.1.4", default-features = false } solar-parse = { version = "=0.1.4", default-features = false } @@ -263,13 +263,13 @@ op-alloy-rpc-types = "0.17.2" op-alloy-flz = "0.13.1" ## revm -revm = { version = "24.0.1", default-features = false } -revm-inspectors = { version = "0.23.0", features = ["serde"] } -op-revm = { version = "5.0.1", default-features = false } +revm = { version = "27.0.3", default-features = false } +revm-inspectors = { version = "0.26.5", features = ["serde"] } +op-revm = { version = "8.0.3", default-features = false } ## alloy-evm -alloy-evm = "0.10.0" -alloy-op-evm = "0.10.0" +alloy-evm = "0.14.0" +alloy-op-evm = "0.14.0" ## cli anstream = "0.6" @@ -401,18 +401,18 @@ zip-extract = "=0.2.1" # alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "7fab7ee" } ## alloy-evm -# alloy-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "dce752f" } -# alloy-op-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "dce752f" } +# alloy-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "7762adc" } +# alloy-op-evm = { git = "https://github.com/alloy-rs/evm.git", rev = "7762adc" } ## revm # revm = { git = "https://github.com/bluealloy/revm.git", rev = "b5808253" } # op-revm = { git = "https://github.com/bluealloy/revm.git", rev = "b5808253" } -# revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors.git", rev = "a625c04" } +# revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors.git", rev = "956bc98" } ## foundry # foundry-block-explorers = { git = "https://github.com/foundry-rs/block-explorers.git", rev = "e09cb89" } # foundry-compilers = { git = "https://github.com/foundry-rs/compilers.git", rev = "e4a9b04" } -# foundry-fork-db = { git = "https://github.com/foundry-rs/foundry-fork-db", rev = "811a61a" } +# foundry-fork-db = { git = "https://github.com/foundry-rs/foundry-fork-db", rev = "4ed9afb" } ## solar # solar-ast = { git = "https://github.com/paradigmxyz/solar.git", branch = "main" } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index b01c5bcb3b6c8..f7eccaed0faf8 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -15,6 +15,7 @@ use crate::{ hardfork::{ChainHardfork, ethereum_hardfork_from_block_tag, spec_id_from_ethereum_hardfork}, mem::{self, in_memory_db::MemDb}, }; +use alloy_chains::Chain; use alloy_consensus::BlockHeader; use alloy_genesis::Genesis; use alloy_network::{AnyNetwork, TransactionResponse}; @@ -38,7 +39,7 @@ use foundry_config::Config; use foundry_evm::{ backend::{BlockchainDb, BlockchainDbMeta, SharedBackend}, constants::DEFAULT_CREATE2_DEPLOYER, - utils::apply_chain_and_block_specific_env_changes, + utils::{apply_chain_and_block_specific_env_changes, get_blob_base_fee_update_fraction}, }; use foundry_evm_core::AsEnvMut; use itertools::Itertools; @@ -513,14 +514,18 @@ impl NodeConfig { } pub fn get_blob_excess_gas_and_price(&self) -> BlobExcessGasAndPrice { - if let Some(blob_excess_gas_and_price) = &self.blob_excess_gas_and_price { - *blob_excess_gas_and_price - } else if let Some(excess_blob_gas) = self.genesis.as_ref().and_then(|g| g.excess_blob_gas) - { - BlobExcessGasAndPrice::new(excess_blob_gas, false) + if let Some(value) = self.blob_excess_gas_and_price { + value } else { - // If no excess blob gas is configured, default to 0 - BlobExcessGasAndPrice::new(0, false) + let excess_blob_gas = + self.genesis.as_ref().and_then(|g| g.excess_blob_gas).unwrap_or(0); + BlobExcessGasAndPrice::new( + excess_blob_gas, + get_blob_base_fee_update_fraction( + self.chain_id.unwrap_or(Chain::mainnet().id()), + self.get_genesis_timestamp(), + ), + ) } } @@ -1079,12 +1084,12 @@ impl NodeConfig { if self.chain_id.is_none() { env.evm_env.cfg_env.chain_id = genesis.config.chain_id; } - env.evm_env.block_env.timestamp = genesis.timestamp; + env.evm_env.block_env.timestamp = U256::from(genesis.timestamp); if let Some(base_fee) = genesis.base_fee_per_gas { env.evm_env.block_env.basefee = base_fee.try_into()?; } if let Some(number) = genesis.number { - env.evm_env.block_env.number = number; + env.evm_env.block_env.number = U256::from(number); } env.evm_env.block_env.beneficiary = genesis.coinbase; } @@ -1237,8 +1242,8 @@ latest block number: {latest_block}" self.gas_limit = Some(gas_limit); env.evm_env.block_env = BlockEnv { - number: fork_block_number, - timestamp: block.header.timestamp, + number: U256::from(fork_block_number), + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, // ensures prevrandao is set prevrandao: Some(block.header.mix_hash.unwrap_or_default()), @@ -1268,13 +1273,24 @@ latest block number: {latest_block}" if let (Some(blob_excess_gas), Some(blob_gas_used)) = (block.header.excess_blob_gas, block.header.blob_gas_used) { - env.evm_env.block_env.blob_excess_gas_and_price = - Some(BlobExcessGasAndPrice::new(blob_excess_gas, false)); + let blob_base_fee_update_fraction = get_blob_base_fee_update_fraction( + fork_chain_id + .unwrap_or_else(|| U256::from(Chain::mainnet().id())) + .saturating_to(), + block.header.timestamp, + ); + + env.evm_env.block_env.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( + blob_excess_gas, + blob_base_fee_update_fraction, + )); + let next_block_blob_excess_gas = fees.get_next_block_blob_excess_gas(blob_excess_gas, blob_gas_used); + fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new( next_block_blob_excess_gas, - false, + blob_base_fee_update_fraction, )); } } diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 344bec84ce8c2..d9971b07f28fc 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1,8 +1,5 @@ use super::{ - backend::{ - db::MaybeFullDatabase, - mem::{BlockRequest, State, state}, - }, + backend::mem::{BlockRequest, DatabaseRef, State, state}, sign::build_typed_transaction, }; use crate::{ @@ -83,7 +80,7 @@ use anvil_core::{ }; use anvil_rpc::{error::RpcError, response::ResponseResult}; use foundry_common::provider::ProviderBuilder; -use foundry_evm::{backend::DatabaseError, decode::RevertDecoder}; +use foundry_evm::decode::RevertDecoder; use futures::{ StreamExt, channel::{mpsc::Receiver, oneshot}, @@ -93,7 +90,7 @@ use revm::{ bytecode::Bytecode, context::BlockEnv, context_interface::{block::BlobExcessGasAndPrice, result::Output}, - database::{CacheDB, DatabaseRef}, + database::CacheDB, interpreter::{InstructionResult, return_ok, return_revert}, primitives::eip7702::PER_EMPTY_ACCOUNT_COST, }; @@ -2213,7 +2210,7 @@ impl EthApi { Ok(NodeInfo { current_block_number: self.backend.best_number(), - current_block_timestamp: env.evm_env.block_env.timestamp, + current_block_timestamp: env.evm_env.block_env.timestamp.saturating_to(), current_block_hash: self.backend.best_hash(), hard_fork: hard_fork.to_string(), transaction_order: match *tx_order { @@ -2955,7 +2952,7 @@ impl EthApi { if let Some(block_overrides) = overrides.block { state::apply_block_overrides(*block_overrides, &mut cache_db, &mut block); } - this.do_estimate_gas_with_state(request, cache_db.as_dyn(), block) + this.do_estimate_gas_with_state(request, &cache_db as &dyn DatabaseRef, block) }) .await? }) @@ -2968,7 +2965,7 @@ impl EthApi { fn do_estimate_gas_with_state( &self, mut request: WithOtherFields, - state: &dyn DatabaseRef, + state: &dyn DatabaseRef, block_env: BlockEnv, ) -> Result { // If the request is a simple native token transfer we can optimize @@ -3490,7 +3487,7 @@ impl TryFrom, u128, State)>> for GasEs } Err(err) => Err(err), Ok((exit, output, gas, _)) => match exit { - return_ok!() | InstructionResult::CallOrCreate => Ok(Self::Success(gas)), + return_ok!() => Ok(Self::Success(gas)), // Revert opcodes: InstructionResult::Revert => Ok(Self::Revert(output.map(|o| o.into_data()))), @@ -3525,13 +3522,7 @@ impl TryFrom, u128, State)>> for GasEs | InstructionResult::CreateContractSizeLimit | InstructionResult::CreateContractStartingWithEF | InstructionResult::CreateInitCodeSizeLimit - | InstructionResult::FatalExternalError - | InstructionResult::ReturnContractInNotInitEOF - | InstructionResult::EOFOpcodeDisabledInLegacy - | InstructionResult::SubRoutineStackOverflow - | InstructionResult::EofAuxDataOverflow - | InstructionResult::EofAuxDataTooSmall - | InstructionResult::InvalidEXTCALLTarget => Ok(Self::EvmError(exit)), + | InstructionResult::FatalExternalError => Ok(Self::EvmError(exit)), }, } } diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index 0f51f0356b247..4b26e23987da4 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -24,10 +24,14 @@ use serde::{ Deserialize, Deserializer, Serialize, de::{MapAccess, Visitor}, }; -use std::{collections::BTreeMap, fmt, path::Path}; +use std::{ + collections::BTreeMap, + fmt::{self, Debug}, + path::Path, +}; /// Helper trait get access to the full state data of the database -pub trait MaybeFullDatabase: DatabaseRef { +pub trait MaybeFullDatabase: DatabaseRef + Debug { /// Returns a reference to the database as a `dyn DatabaseRef`. // TODO: Required until trait upcasting is stabilized: fn as_dyn(&self) -> &dyn DatabaseRef; @@ -242,7 +246,7 @@ impl + Send + Sync + Clone + fmt::Debug> D } } -impl> MaybeFullDatabase for CacheDB { +impl + Debug> MaybeFullDatabase for CacheDB { fn as_dyn(&self) -> &dyn DatabaseRef { self } @@ -321,6 +325,7 @@ impl> MaybeForkedDatabase for CacheDB { } /// Represents a state at certain point +#[derive(Debug)] pub struct StateDb(pub(crate) Box); impl StateDb { diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index b28121a2546e6..5925e1db3e3f2 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -34,10 +34,13 @@ use revm::{ database::WrapDatabaseRef, handler::{EthPrecompiles, instructions::EthInstructions}, interpreter::InstructionResult, - precompile::{PrecompileSpecId, Precompiles, secp256r1::P256VERIFY}, + precompile::{ + PrecompileSpecId, Precompiles, + secp256r1::{P256VERIFY, P256VERIFY_BASE_GAS_FEE}, + }, primitives::hardfork::SpecId, }; -use std::sync::Arc; +use std::{fmt::Debug, sync::Arc}; /// Represents an executed transaction (transacted on the DB) #[derive(Debug)] @@ -229,10 +232,10 @@ impl TransactionExecutor<'_, DB, V> { receipts_root, logs_bloom: bloom, difficulty, - number: block_number, + number: block_number.saturating_to(), gas_limit, gas_used: cumulative_gas_used, - timestamp, + timestamp: timestamp.saturating_to(), extra_data: Default::default(), mix_hash: mix_hash.unwrap_or_default(), nonce: Default::default(), @@ -329,7 +332,7 @@ impl Iterator for &mut TransactionExec let mut evm = new_evm_with_inspector(&mut *self.db, &env, &mut inspector); if self.odyssey { - inject_precompiles(&mut evm, vec![P256VERIFY]); + inject_precompiles(&mut evm, vec![(P256VERIFY, P256VERIFY_BASE_GAS_FEE)]); } if let Some(factory) = &self.precompile_factory { @@ -428,7 +431,7 @@ pub fn new_evm_with_inspector( inspector: I, ) -> EitherEvm where - DB: Database, + DB: Database + Debug, I: Inspector> + Inspector>, { if env.is_optimism { @@ -500,7 +503,7 @@ pub fn new_evm_with_inspector_ref<'db, DB, I>( inspector: &'db mut I, ) -> EitherEvm, &'db mut I, PrecompilesMap> where - DB: DatabaseRef + 'db + ?Sized, + DB: DatabaseRef + Debug + 'db + ?Sized, I: Inspector>> + Inspector>>, WrapDatabaseRef<&'db DB>: Database, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 362f1d987dd48..aa37c09c7add0 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -90,6 +90,7 @@ use foundry_evm::{ decode::RevertDecoder, inspectors::AccessListInspector, traces::TracingInspectorConfig, + utils::{get_blob_base_fee_update_fraction, get_blob_base_fee_update_fraction_by_spec_id}, }; use foundry_evm_core::either_evm::EitherEvm; use futures::channel::mpsc::{UnboundedSender, unbounded}; @@ -105,15 +106,16 @@ use revm::{ block::BlobExcessGasAndPrice, result::{ExecutionResult, Output, ResultAndState}, }, - database::{CacheDB, DatabaseRef, WrapDatabaseRef}, + database::{CacheDB, WrapDatabaseRef}, interpreter::InstructionResult, - precompile::secp256r1::P256VERIFY, + precompile::secp256r1::{P256VERIFY, P256VERIFY_BASE_GAS_FEE}, primitives::{KECCAK_EMPTY, hardfork::SpecId}, state::AccountInfo, }; use revm_inspectors::transfer::TransferInspector; use std::{ collections::BTreeMap, + fmt::Debug, io::{Read, Write}, ops::Not, path::PathBuf, @@ -132,6 +134,13 @@ pub mod inspector; pub mod state; pub mod storage; +/// Helper trait that combines DatabaseRef with Debug. +/// This is needed because alloy-evm requires Debug on Database implementations. +/// Specific implementation for dyn Db since trait object upcasting is not stable. +pub trait DatabaseRef: revm::DatabaseRef + Debug {} +impl DatabaseRef for T where T: revm::DatabaseRef + Debug {} +impl DatabaseRef for dyn crate::eth::backend::db::Db {} + // Gas per transaction not creating a contract. pub const MIN_TRANSACTION_GAS: u128 = 21000; // Gas per transaction creating a contract. @@ -172,7 +181,7 @@ impl BlockRequest { } /// Gives access to the [revm::Database] -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Backend { /// Access to [`revm::Database`] abstraction. /// @@ -571,8 +580,8 @@ impl Backend { env.evm_env.cfg_env.chain_id = fork.chain_id(); env.evm_env.block_env = BlockEnv { - number: fork_block_number, - timestamp: fork_block.header.timestamp, + number: U256::from(fork_block_number), + timestamp: U256::from(fork_block.header.timestamp), gas_limit, difficulty: fork_block.header.difficulty, prevrandao: Some(fork_block.header.mix_hash.unwrap_or_default()), @@ -632,8 +641,8 @@ impl Backend { // Reset environment to genesis state { let mut env = self.env.write(); - env.evm_env.block_env.number = genesis_number; - env.evm_env.block_env.timestamp = genesis_timestamp; + env.evm_env.block_env.number = U256::from(genesis_number); + env.evm_env.block_env.timestamp = U256::from(genesis_timestamp); // Reset other block env fields to their defaults env.evm_env.block_env.basefee = self.fees.base_fee(); env.evm_env.block_env.prevrandao = Some(B256::ZERO); @@ -726,7 +735,7 @@ impl Backend { /// Sets the block number pub fn set_block_number(&self, number: u64) { let mut env = self.env.write(); - env.evm_env.block_env.number = number; + env.evm_env.block_env.number = U256::from(number); } /// Returns the client coinbase address. @@ -961,8 +970,8 @@ impl Backend { let mut env = self.env.write(); env.evm_env.block_env = BlockEnv { - number: num, - timestamp: block.header.timestamp, + number: U256::from(num), + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, // ensures prevrandao is set prevrandao: Some(block.header.mix_hash.unwrap_or_default()), @@ -1034,7 +1043,7 @@ impl Backend { // Defaults to block number for compatibility with existing state files. let fork_num_and_hash = self.get_fork().map(|f| (f.block_number(), f.block_hash())); - let best_number = state.best_block_number.unwrap_or(block.number); + let best_number = state.best_block_number.unwrap_or(block.number.saturating_to()); if let Some((number, hash)) = fork_num_and_hash { trace!(target: "backend", state_block_number=?best_number, fork_block_number=?number); // If the state.block_number is greater than the fork block number, set best number @@ -1086,9 +1095,13 @@ impl Backend { // update next base fee self.fees.set_base_fee(next_block_base_fee); + self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new( next_block_excess_blob_gas, - false, + get_blob_base_fee_update_fraction( + self.env.read().evm_env.cfg_env.chain_id, + header.timestamp, + ), )); } @@ -1129,33 +1142,29 @@ impl Backend { fn next_env(&self) -> Env { let mut env = self.env.read().clone(); // increase block number for this block - env.evm_env.block_env.number = env.evm_env.block_env.number.saturating_add(1); + env.evm_env.block_env.number = env.evm_env.block_env.number.saturating_add(U256::from(1)); env.evm_env.block_env.basefee = self.base_fee(); - env.evm_env.block_env.timestamp = self.time.current_call_timestamp(); + env.evm_env.block_env.timestamp = U256::from(self.time.current_call_timestamp()); env } /// Creates an EVM instance with optionally injected precompiles. - fn new_evm_with_inspector_ref<'db, I>( + fn new_evm_with_inspector_ref<'db, I, DB>( &self, - db: &'db dyn DatabaseRef, + db: &'db DB, env: &Env, inspector: &'db mut I, - ) -> EitherEvm< - WrapDatabaseRef<&'db dyn DatabaseRef>, - &'db mut I, - PrecompilesMap, - > + ) -> EitherEvm, &'db mut I, PrecompilesMap> where - I: Inspector>>> - + Inspector>>>, - WrapDatabaseRef<&'db dyn DatabaseRef>: - Database, + DB: DatabaseRef + ?Sized, + I: Inspector>> + + Inspector>>, + WrapDatabaseRef<&'db DB>: Database, { let mut evm = new_evm_with_inspector_ref(db, env, inspector); if self.odyssey { - inject_precompiles(&mut evm, vec![P256VERIFY]); + inject_precompiles(&mut evm, vec![(P256VERIFY, P256VERIFY_BASE_GAS_FEE)]); } if let Some(factory) = &self.precompile_factory { @@ -1183,7 +1192,7 @@ impl Backend { let db = self.db.read().await; let mut inspector = self.build_inspector(); - let mut evm = self.new_evm_with_inspector_ref(db.as_dyn(), &env, &mut inspector); + let mut evm = self.new_evm_with_inspector_ref(&**db, &env, &mut inspector); let ResultAndState { result, state } = evm.transact(env.tx)?; let (exit_reason, gas_used, out, logs) = match result { ExecutionResult::Success { reason, gas_used, logs, output, .. } => { @@ -1291,9 +1300,10 @@ impl Backend { // increase block number for this block if is_arbitrum(env.evm_env.cfg_env.chain_id) { // Temporary set `env.block.number` to `block_number` for Arbitrum chains. - env.evm_env.block_env.number = block_number; + env.evm_env.block_env.number = U256::from(block_number); } else { - env.evm_env.block_env.number = env.evm_env.block_env.number.saturating_add(1); + env.evm_env.block_env.number = + env.evm_env.block_env.number.saturating_add(U256::from(1)); } env.evm_env.block_env.basefee = current_base_fee; @@ -1316,7 +1326,7 @@ impl Backend { // finally set the next block timestamp, this is done just before execution, because // there can be concurrent requests that can delay acquiring the db lock and we want // to ensure the timestamp is as close as possible to the actual execution. - env.evm_env.block_env.timestamp = self.time.next_timestamp(); + env.evm_env.block_env.timestamp = U256::from(self.time.next_timestamp()); let executor = TransactionExecutor { db: &mut **db, @@ -1435,9 +1445,10 @@ impl Backend { // update next base fee self.fees.set_base_fee(next_block_base_fee); + self.fees.set_blob_excess_gas_and_price(BlobExcessGasAndPrice::new( next_block_excess_blob_gas, - false, + get_blob_base_fee_update_fraction_by_spec_id(*self.env.read().evm_env.spec_id()), )); // notify all listeners @@ -1468,7 +1479,7 @@ impl Backend { if let Some(block_overrides) = overrides.block { state::apply_block_overrides(*block_overrides, &mut cache_db, &mut block); } - self.call_with_state(cache_db.as_dyn(), request, fee_details, block) + self.call_with_state(&cache_db as &dyn DatabaseRef, request, fee_details, block) }?; trace!(target: "backend", "call return {:?} out: {:?} gas {} on block {}", exit, out, gas, block_number); Ok((exit, out, gas, state)) @@ -1678,7 +1689,7 @@ impl Backend { // recorded and included in logs let mut inspector = TransferInspector::new(false).with_logs(true); let mut evm= self.new_evm_with_inspector_ref( - cache_db.as_dyn(), + &cache_db as &dyn DatabaseRef, &env, &mut inspector, ); @@ -1688,7 +1699,7 @@ impl Backend { } else { let mut inspector = self.build_inspector(); let mut evm = self.new_evm_with_inspector_ref( - cache_db.as_dyn(), + &cache_db as &dyn DatabaseRef, &env, &mut inspector, ); @@ -1737,8 +1748,8 @@ impl Backend { .enumerate() .map(|(idx, log)| Log { inner: log, - block_number: Some(block_env.number), - block_timestamp: Some(block_env.timestamp), + block_number: Some(block_env.number.saturating_to()), + block_timestamp: Some(block_env.timestamp.saturating_to()), transaction_index: Some(req_idx as u64), log_index: Some((idx + log_index) as u64), removed: false, @@ -1765,10 +1776,10 @@ impl Backend { beneficiary: block_env.beneficiary, state_root: Default::default(), difficulty: Default::default(), - number: block_env.number, + number: block_env.number.saturating_to(), gas_limit: block_env.gas_limit, gas_used, - timestamp: block_env.timestamp, + timestamp: block_env.timestamp.saturating_to(), extra_data: Default::default(), mix_hash: Default::default(), nonce: Default::default(), @@ -1808,8 +1819,8 @@ impl Backend { }; // update block env - block_env.number += 1; - block_env.timestamp += 12; + block_env.number += U256::from(1); + block_env.timestamp += U256::from(12); block_env.basefee = simulated_block .inner .header @@ -1826,7 +1837,7 @@ impl Backend { pub fn call_with_state( &self, - state: &dyn DatabaseRef, + state: &dyn DatabaseRef, request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, @@ -1893,7 +1904,7 @@ impl Backend { let env = self.build_call_env(request, fee_details, block); let mut evm = self.new_evm_with_inspector_ref( - cache_db.as_dyn(), + &cache_db as &dyn DatabaseRef, &env, &mut inspector, ); @@ -1928,7 +1939,11 @@ impl Backend { .with_tracing_config(TracingInspectorConfig::from_geth_config(&config)); let env = self.build_call_env(request, fee_details, block); - let mut evm = self.new_evm_with_inspector_ref(cache_db.as_dyn(), &env, &mut inspector); + let mut evm = self.new_evm_with_inspector_ref( + &cache_db as &dyn DatabaseRef, + &env, + &mut inspector, + ); let ResultAndState { result, state: _ } = evm.transact(env.tx)?; let (exit_reason, gas_used, out) = match result { @@ -1961,7 +1976,7 @@ impl Backend { pub fn build_access_list_with_state( &self, - state: &dyn DatabaseRef, + state: &dyn DatabaseRef, request: WithOtherFields, fee_details: FeeDetails, block_env: BlockEnv, @@ -2357,9 +2372,9 @@ impl Backend { .with_pending_block(pool_transactions, |state, block| { let block = block.block; let block = BlockEnv { - number: block.header.number, + number: U256::from(block.header.number), beneficiary: block.header.beneficiary, - timestamp: block.header.timestamp, + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, prevrandao: Some(block.header.mix_hash), basefee: block.header.base_fee_per_gas.unwrap_or_default(), @@ -2376,7 +2391,7 @@ impl Backend { }; let block_number = self.convert_block_number(block_number); - if block_number < self.env.read().evm_env.block_env.number { + if block_number < self.env.read().evm_env.block_env.number.saturating_to() { if let Some((block_hash, block)) = self .block_by_number(BlockNumber::Number(block_number)) .await? @@ -2384,9 +2399,9 @@ impl Backend { && let Some(state) = self.states.write().get(&block_hash) { let block = BlockEnv { - number: block_number, + number: U256::from(block_number), beneficiary: block.header.beneficiary, - timestamp: block.header.timestamp, + timestamp: U256::from(block.header.timestamp), difficulty: block.header.difficulty, prevrandao: block.header.mix_hash, basefee: block.header.base_fee_per_gas.unwrap_or_default(), @@ -2398,7 +2413,7 @@ impl Backend { warn!(target: "backend", "Not historic state found for block={}", block_number); return Err(BlockchainError::BlockOutOfRange( - self.env.read().evm_env.block_env.number, + self.env.read().evm_env.block_env.number.saturating_to(), block_number, )); } @@ -3093,19 +3108,19 @@ impl Backend { // Set environment back to common block let mut env = self.env.write(); - env.evm_env.block_env.number = common_block.header.number; - env.evm_env.block_env.timestamp = common_block.header.timestamp; + env.evm_env.block_env.number = U256::from(common_block.header.number); + env.evm_env.block_env.timestamp = U256::from(common_block.header.timestamp); env.evm_env.block_env.gas_limit = common_block.header.gas_limit; env.evm_env.block_env.difficulty = common_block.header.difficulty; env.evm_env.block_env.prevrandao = Some(common_block.header.mix_hash); - self.time.reset(env.evm_env.block_env.timestamp); + self.time.reset(env.evm_env.block_env.timestamp.saturating_to()); } Ok(()) } } -/// Get max nonce from transaction pool by address +/// Get max nonce from transaction pool by address. fn get_pool_transactions_nonce( pool_transactions: &[Arc], address: Address, diff --git a/crates/anvil/src/eth/backend/mem/state.rs b/crates/anvil/src/eth/backend/mem/state.rs index 46e2eabd91bd1..437f7974c1754 100644 --- a/crates/anvil/src/eth/backend/mem/state.rs +++ b/crates/anvil/src/eth/backend/mem/state.rs @@ -154,7 +154,7 @@ pub fn apply_block_overrides( env.difficulty = difficulty; } if let Some(time) = time { - env.timestamp = time; + env.timestamp = U256::from(time); } if let Some(gas_limit) = gas_limit { env.gas_limit = gas_limit; diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 1ecd351384371..dc2247c959086 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -250,7 +250,7 @@ impl Default for InMemoryBlockStates { } /// Stores the blockchain data (blocks, transactions) -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BlockchainStorage { /// all stored blocks (block hash -> block) pub blocks: B256HashMap, @@ -435,7 +435,7 @@ impl BlockchainStorage { } /// A simple in-memory blockchain -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Blockchain { /// underlying storage that supports concurrent reads pub storage: Arc>, diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 756a9102a1732..a9bea5f098ce6 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -305,6 +305,8 @@ pub enum InvalidTransactionError { /// Thrown when an access list is used before the berlin hard fork. #[error("EIP-7702 authorization lists are not supported before the Prague hardfork")] AuthorizationListNotSupported, + #[error("Transaction gas limit is greater than the block gas limit, gas_limit: {0}, cap: {1}")] + TxGasLimitGreaterThanCap(u64, u64), /// Forwards error from the revm #[error(transparent)] Revm(revm::context_interface::result::InvalidTransaction), @@ -348,15 +350,19 @@ impl From for InvalidTransactionError { InvalidTransaction::AuthorizationListNotSupported => { Self::AuthorizationListNotSupported } + InvalidTransaction::TxGasLimitGreaterThanCap { gas_limit, cap } => { + Self::TxGasLimitGreaterThanCap(gas_limit, cap) + } + InvalidTransaction::AuthorizationListInvalidFields | InvalidTransaction::Eip1559NotSupported | InvalidTransaction::Eip2930NotSupported | InvalidTransaction::Eip4844NotSupported | InvalidTransaction::Eip7702NotSupported - | InvalidTransaction::EofCreateShouldHaveToAddress | InvalidTransaction::EmptyAuthorizationList | InvalidTransaction::Eip7873NotSupported - | InvalidTransaction::Eip7873MissingTarget => Self::Revm(err), + | InvalidTransaction::Eip7873MissingTarget + | InvalidTransaction::MissingChainId => Self::Revm(err), } } } diff --git a/crates/anvil/src/evm.rs b/crates/anvil/src/evm.rs index d426fdb4e925a..76e570268bb31 100644 --- a/crates/anvil/src/evm.rs +++ b/crates/anvil/src/evm.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use alloy_evm::{ Database, Evm, eth::EthEvmContext, - precompiles::{DynPrecompile, PrecompilesMap}, + precompiles::{DynPrecompile, PrecompileInput, PrecompilesMap}, }; use foundry_evm_core::either_evm::EitherEvm; use op_revm::OpContext; @@ -13,20 +13,21 @@ use revm::{Inspector, precompile::PrecompileWithAddress}; /// `anvil` as a library. pub trait PrecompileFactory: Send + Sync + Unpin + Debug { /// Returns a set of precompiles to extend the EVM with. - fn precompiles(&self) -> Vec; + fn precompiles(&self) -> Vec<(PrecompileWithAddress, u64)>; } /// Inject precompiles into the EVM dynamically. pub fn inject_precompiles( evm: &mut EitherEvm, - precompiles: Vec, + precompiles: Vec<(PrecompileWithAddress, u64)>, ) where DB: Database, I: Inspector> + Inspector>, { - for p in precompiles { - evm.precompiles_mut() - .apply_precompile(p.address(), |_| Some(DynPrecompile::from(*p.precompile()))); + for (PrecompileWithAddress(addr, func), gas) in precompiles { + evm.precompiles_mut().apply_precompile(&addr, move |_| { + Some(DynPrecompile::from(move |input: PrecompileInput<'_>| func(input.data, gas))) + }); } } @@ -70,11 +71,14 @@ mod tests { struct CustomPrecompileFactory; impl PrecompileFactory for CustomPrecompileFactory { - fn precompiles(&self) -> Vec { - vec![PrecompileWithAddress::from(( - PRECOMPILE_ADDR, - custom_echo_precompile as fn(&[u8], u64) -> PrecompileResult, - ))] + fn precompiles(&self) -> Vec<(PrecompileWithAddress, u64)> { + vec![( + PrecompileWithAddress::from(( + PRECOMPILE_ADDR, + custom_echo_precompile as fn(&[u8], u64) -> PrecompileResult, + )), + 1000, + )] } } diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 6d7b22e214b5d..6e9e55c4ae0db 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -61,7 +61,7 @@ op-alloy-consensus = { workspace = true, features = ["alloy-compat"] } chrono.workspace = true eyre.workspace = true futures.workspace = true -revm.workspace = true +revm = { workspace = true, features = ["optional_balance_check"] } rand.workspace = true rand_08.workspace = true rayon.workspace = true diff --git a/crates/cast/src/cmd/call.rs b/crates/cast/src/cmd/call.rs index 8a51dc2db60fa..7948d22b9eb83 100644 --- a/crates/cast/src/cmd/call.rs +++ b/crates/cast/src/cmd/call.rs @@ -270,7 +270,7 @@ impl CallArgs { env.evm_env.block_env.number = number.to(); } if let Some(time) = block_overrides.time { - env.evm_env.block_env.timestamp = time; + env.evm_env.block_env.timestamp = U256::from(time); } } diff --git a/crates/cast/src/cmd/run.rs b/crates/cast/src/cmd/run.rs index 8cc600aa10510..412dd970a89f1 100644 --- a/crates/cast/src/cmd/run.rs +++ b/crates/cast/src/cmd/run.rs @@ -1,7 +1,7 @@ use alloy_consensus::Transaction; use alloy_network::{AnyNetwork, TransactionResponse}; use alloy_primitives::{ - Address, Bytes, + Address, Bytes, U256, map::{HashMap, HashSet}, }; use alloy_provider::{Provider, RootProvider}; @@ -12,7 +12,7 @@ use foundry_cli::{ opts::{EtherscanOpts, RpcOpts}, utils::{TraceResult, handle_traces, init_progress}, }; -use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_known_system_sender, shell}; +use foundry_common::{SYSTEM_TRANSACTION_TYPE, is_impersonated_tx, is_known_system_sender, shell}; use foundry_compilers::artifacts::EvmVersion; use foundry_config::{ Config, @@ -157,10 +157,10 @@ impl RunArgs { let mut evm_version = self.evm_version; env.evm_env.cfg_env.disable_block_gas_limit = self.disable_block_gas_limit; - env.evm_env.block_env.number = tx_block_number; + env.evm_env.block_env.number = U256::from(tx_block_number); if let Some(block) = &block { - env.evm_env.block_env.timestamp = block.header.timestamp; + env.evm_env.block_env.timestamp = U256::from(block.header.timestamp); env.evm_env.block_env.beneficiary = block.header.beneficiary; env.evm_env.block_env.difficulty = block.header.difficulty; env.evm_env.block_env.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); @@ -232,6 +232,8 @@ impl RunArgs { configure_tx_env(&mut env.as_env_mut(), &tx.inner); + env.evm_env.cfg_env.disable_balance_check = true; + if let Some(to) = Transaction::to(tx) { trace!(tx=?tx.tx_hash(),?to, "executing previous call transaction"); executor.transact_with_env(env.clone()).wrap_err_with(|| { @@ -270,6 +272,9 @@ impl RunArgs { executor.set_trace_printer(self.trace_printer); configure_tx_env(&mut env.as_env_mut(), &tx.inner); + if is_impersonated_tx(tx.inner.inner.inner()) { + env.evm_env.cfg_env.disable_balance_check = true; + } if let Some(to) = Transaction::to(&tx) { trace!(tx=?tx.tx_hash(), to=?to, "executing call transaction"); diff --git a/crates/cheatcodes/src/evm.rs b/crates/cheatcodes/src/evm.rs index dac0ec734a9b1..7bb1084d03f1d 100644 --- a/crates/cheatcodes/src/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -15,6 +15,7 @@ use foundry_evm_core::{ ContextExt, backend::{DatabaseExt, RevertStateSnapshotAction}, constants::{CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, TEST_CONTRACT_ADDRESS}, + utils::get_blob_base_fee_update_fraction_by_spec_id, }; use foundry_evm_traces::StackSnapshotType; use itertools::Itertools; @@ -455,8 +456,7 @@ impl Cheatcode for getBlobhashesCall { impl Cheatcode for rollCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newHeight } = self; - ensure!(*newHeight <= U256::from(u64::MAX), "block height must be less than 2^64 - 1"); - ccx.ecx.block.number = newHeight.saturating_to(); + ccx.ecx.block.number = *newHeight; Ok(Default::default()) } } @@ -480,8 +480,7 @@ impl Cheatcode for txGasPriceCall { impl Cheatcode for warpCall { fn apply_stateful(&self, ccx: &mut CheatsCtxt) -> Result { let Self { newTimestamp } = self; - ensure!(*newTimestamp <= U256::from(u64::MAX), "timestamp must be less than 2^64 - 1"); - ccx.ecx.block.timestamp = newTimestamp.saturating_to(); + ccx.ecx.block.timestamp = *newTimestamp; Ok(Default::default()) } } @@ -501,8 +500,11 @@ impl Cheatcode for blobBaseFeeCall { "`blobBaseFee` is not supported before the Cancun hard fork; \ see EIP-4844: https://eips.ethereum.org/EIPS/eip-4844" ); - let is_prague = ccx.ecx.cfg.spec >= SpecId::PRAGUE; - ccx.ecx.block.set_blob_excess_gas_and_price((*newBlobBaseFee).to(), is_prague); + + ccx.ecx.block.set_blob_excess_gas_and_price( + (*newBlobBaseFee).to(), + get_blob_base_fee_update_fraction_by_spec_id(ccx.ecx.cfg.spec), + ); Ok(Default::default()) } } diff --git a/crates/cheatcodes/src/evm/record_debug_step.rs b/crates/cheatcodes/src/evm/record_debug_step.rs index 427e5ca20f419..ca9398b76c03e 100644 --- a/crates/cheatcodes/src/evm/record_debug_step.rs +++ b/crates/cheatcodes/src/evm/record_debug_step.rs @@ -71,11 +71,11 @@ pub(crate) fn convert_call_trace_to_debug_step(step: &CallTraceStep) -> DebugSte let memory = get_memory_input_for_opcode(opcode, step.stack.as_ref(), step.memory.as_ref()); - let is_out_of_gas = step.status == InstructionResult::OutOfGas - || step.status == InstructionResult::MemoryOOG - || step.status == InstructionResult::MemoryLimitOOG - || step.status == InstructionResult::PrecompileOOG - || step.status == InstructionResult::InvalidOperandOOG; + let is_out_of_gas = step.status == Some(InstructionResult::OutOfGas) + || step.status == Some(InstructionResult::MemoryOOG) + || step.status == Some(InstructionResult::MemoryLimitOOG) + || step.status == Some(InstructionResult::PrecompileOOG) + || step.status == Some(InstructionResult::InvalidOperandOOG); DebugStep { stack, diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 2bff4624244ea..126fdd2d63148 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -55,7 +55,7 @@ use revm::{ interpreter::{ CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas, Host, InstructionResult, Interpreter, InterpreterAction, InterpreterResult, - interpreter_types::{Jumps, MemoryTr}, + interpreter_types::{Jumps, LoopControl, MemoryTr}, }, state::EvmStorageSlot, }; @@ -96,7 +96,7 @@ pub trait CheatcodesExecutor { let frame = FrameInput::Create(Box::new(inputs)); let outcome = match evm.run_execution(frame)? { - FrameResult::Call(_) | FrameResult::EOFCreate(_) => unreachable!(), + FrameResult::Call(_) => unreachable!(), FrameResult::Create(create) => create, }; @@ -310,7 +310,7 @@ impl ArbitraryStorage { pub fn save(&mut self, ecx: Ecx, address: Address, slot: U256, data: U256) { self.values.get_mut(&address).expect("missing arbitrary address entry").insert(slot, data); if let Ok(mut account) = ecx.journaled_state.load_account(address) { - account.storage.insert(slot, EvmStorageSlot::new(data)); + account.storage.insert(slot, EvmStorageSlot::new(data, 0)); } } @@ -328,14 +328,14 @@ impl ArbitraryStorage { storage_cache.insert(slot, new_value); // Update source storage with new value. if let Ok(mut source_account) = ecx.journaled_state.load_account(*source) { - source_account.storage.insert(slot, EvmStorageSlot::new(new_value)); + source_account.storage.insert(slot, EvmStorageSlot::new(new_value, 0)); } new_value } }; // Update target storage with new value. if let Ok(mut target_account) = ecx.journaled_state.load_account(target) { - target_account.storage.insert(slot, EvmStorageSlot::new(value)); + target_account.storage.insert(slot, EvmStorageSlot::new(value, 0)); } value } @@ -782,7 +782,7 @@ impl Cheatcodes { // Apply delegate call, `call.caller`` will not equal `prank.prank_caller` if prank.delegate_call && curr_depth == prank.depth - && let CallScheme::DelegateCall | CallScheme::ExtDelegateCall = call.scheme + && let CallScheme::DelegateCall = call.scheme { call.target_address = prank.new_caller; call.caller = prank.new_caller; @@ -940,9 +940,6 @@ impl Cheatcodes { CallScheme::CallCode => crate::Vm::AccountAccessKind::CallCode, CallScheme::DelegateCall => crate::Vm::AccountAccessKind::DelegateCall, CallScheme::StaticCall => crate::Vm::AccountAccessKind::StaticCall, - CallScheme::ExtCall => crate::Vm::AccountAccessKind::Call, - CallScheme::ExtStaticCall => crate::Vm::AccountAccessKind::StaticCall, - CallScheme::ExtDelegateCall => crate::Vm::AccountAccessKind::DelegateCall, }; // Record this call by pushing it to a new pending vector; all subsequent calls at // that depth will be pushed to the same vector. When the call ends, the @@ -1055,7 +1052,7 @@ impl Inspector> for Cheatcodes { // Record gas for current frame. if self.gas_metering.paused { - self.gas_metering.paused_frames.push(interpreter.control.gas); + self.gas_metering.paused_frames.push(interpreter.gas); } // `expectRevert`: track the max call depth during `expectRevert` @@ -1814,36 +1811,33 @@ impl Cheatcodes { if let Some(paused_gas) = self.gas_metering.paused_frames.last() { // Keep gas constant if paused. // Make sure we record the memory changes so that memory expansion is not paused. - let memory = *interpreter.control.gas.memory(); - interpreter.control.gas = *paused_gas; - interpreter.control.gas.memory_mut().words_num = memory.words_num; - interpreter.control.gas.memory_mut().expansion_cost = memory.expansion_cost; + let memory = *interpreter.gas.memory(); + interpreter.gas = *paused_gas; + interpreter.gas.memory_mut().words_num = memory.words_num; + interpreter.gas.memory_mut().expansion_cost = memory.expansion_cost; } else { // Record frame paused gas. - self.gas_metering.paused_frames.push(interpreter.control.gas); + self.gas_metering.paused_frames.push(interpreter.gas); } } #[cold] fn meter_gas_record(&mut self, interpreter: &mut Interpreter, ecx: Ecx) { - if matches!(interpreter.control.instruction_result, InstructionResult::Continue) { + if interpreter.bytecode.action.as_ref().and_then(|i| i.instruction_result()).is_none() { self.gas_metering.gas_records.iter_mut().for_each(|record| { let curr_depth = ecx.journaled_state.depth(); if curr_depth == record.depth { // Skip the first opcode of the first call frame as it includes the gas cost of // creating the snapshot. if self.gas_metering.last_gas_used != 0 { - let gas_diff = interpreter - .control - .gas - .spent() - .saturating_sub(self.gas_metering.last_gas_used); + let gas_diff = + interpreter.gas.spent().saturating_sub(self.gas_metering.last_gas_used); record.gas_used = record.gas_used.saturating_add(gas_diff); } // Update `last_gas_used` to the current spent gas for the next iteration to // compare against. - self.gas_metering.last_gas_used = interpreter.control.gas.spent(); + self.gas_metering.last_gas_used = interpreter.gas.spent(); } }); } @@ -1852,27 +1846,31 @@ impl Cheatcodes { #[cold] fn meter_gas_end(&mut self, interpreter: &mut Interpreter) { // Remove recorded gas if we exit frame. - if will_exit(interpreter.control.instruction_result) { + if let Some(interpreter_action) = interpreter.bytecode.action.as_ref() + && will_exit(interpreter_action) + { self.gas_metering.paused_frames.pop(); } } #[cold] fn meter_gas_reset(&mut self, interpreter: &mut Interpreter) { - interpreter.control.gas = Gas::new(interpreter.control.gas.limit()); + interpreter.gas = Gas::new(interpreter.gas.limit()); self.gas_metering.reset = false; } #[cold] fn meter_gas_check(&mut self, interpreter: &mut Interpreter) { - if will_exit(interpreter.control.instruction_result) { + if let Some(interpreter_action) = interpreter.bytecode.action.as_ref() + && will_exit(interpreter_action) + { // Reset gas if spent is less than refunded. // This can happen if gas was paused / resumed or reset. // https://github.com/foundry-rs/foundry/issues/4370 - if interpreter.control.gas.spent() - < u64::try_from(interpreter.control.gas.refunded()).unwrap_or_default() + if interpreter.gas.spent() + < u64::try_from(interpreter.gas.refunded()).unwrap_or_default() { - interpreter.control.gas = Gas::new(interpreter.control.gas.limit()); + interpreter.gas = Gas::new(interpreter.gas.limit()); } } } @@ -2292,14 +2290,11 @@ fn disallowed_mem_write( ranges.iter().map(|r| format!("(0x{:02X}, 0x{:02X}]", r.start, r.end)).join(" U ") ); - interpreter.control.instruction_result = InstructionResult::Revert; - interpreter.control.next_action = InterpreterAction::Return { - result: InterpreterResult { - output: Error::encode(revert_string), - gas: interpreter.control.gas, - result: InstructionResult::Revert, - }, - }; + interpreter.bytecode.set_action(InterpreterAction::new_return( + InstructionResult::Revert, + Bytes::from(revert_string.into_bytes()), + interpreter.gas, + )); } // Determines if the gas limit on a given call was manually set in the script and should therefore @@ -2427,6 +2422,11 @@ fn calls_as_dyn_cheatcode(calls: &Vm::VmCalls) -> &dyn DynCheatcode { } /// Helper function to check if frame execution will exit. -fn will_exit(ir: InstructionResult) -> bool { - !matches!(ir, InstructionResult::Continue | InstructionResult::CallOrCreate) +fn will_exit(action: &InterpreterAction) -> bool { + match action { + InterpreterAction::Return(result) => { + result.result.is_ok_or_revert() || result.result.is_error() + } + _ => false, + } } diff --git a/crates/cheatcodes/src/test/expect.rs b/crates/cheatcodes/src/test/expect.rs index 5e65fb453d91c..5dc68dc9230b3 100644 --- a/crates/cheatcodes/src/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -10,7 +10,9 @@ use alloy_primitives::{ }; use revm::{ context::JournalTr, - interpreter::{InstructionResult, Interpreter, InterpreterAction, InterpreterResult}, + interpreter::{ + InstructionResult, Interpreter, InterpreterAction, interpreter_types::LoopControl, + }, }; use super::revert_handlers::RevertParameters; @@ -824,14 +826,11 @@ pub(crate) fn handle_expect_emit( .expected_emits .insert(index_to_fill_or_check, (event_to_fill_or_check, count_map)); } else { - interpreter.control.instruction_result = InstructionResult::Revert; - interpreter.control.next_action = InterpreterAction::Return { - result: InterpreterResult { - output: Error::encode("use vm.expectEmitAnonymous to match anonymous events"), - gas: interpreter.control.gas, - result: InstructionResult::Revert, - }, - }; + interpreter.bytecode.set_action(InterpreterAction::new_return( + InstructionResult::Revert, + Error::encode("use vm.expectEmitAnonymous to match anonymous events"), + interpreter.gas, + )); } return; }; diff --git a/crates/chisel/src/runner.rs b/crates/chisel/src/runner.rs index 5339f3df45232..c2d44f0f51fff 100644 --- a/crates/chisel/src/runner.rs +++ b/crates/chisel/src/runner.rs @@ -48,7 +48,7 @@ pub struct ChiselResult { /// Called address pub address: Option
, /// EVM State at the final instruction of the `run()` function - pub state: Option<(Vec, Vec, InstructionResult)>, + pub state: Option<(Vec, Vec, Option)>, } /// ChiselRunner implementation @@ -135,7 +135,7 @@ impl ChiselRunner { let mut res = self.executor.call_raw(from, to, calldata.clone(), value)?; let mut gas_used = res.gas_used; - if matches!(res.exit_reason, return_ok!()) { + if matches!(res.exit_reason, Some(return_ok!())) { // store the current gas limit and reset it later let init_gas_limit = self.executor.env().tx.gas_limit; @@ -151,9 +151,9 @@ impl ChiselRunner { self.executor.env_mut().tx.gas_limit = mid_gas_limit; let res = self.executor.call_raw(from, to, calldata.clone(), value)?; match res.exit_reason { - InstructionResult::Revert - | InstructionResult::OutOfGas - | InstructionResult::OutOfFunds => { + Some(InstructionResult::Revert) + | Some(InstructionResult::OutOfGas) + | Some(InstructionResult::OutOfFunds) => { lowest_gas_limit = mid_gas_limit; } _ => { diff --git a/crates/common/src/constants.rs b/crates/common/src/constants.rs index d127cc0548b06..4d15967fe590a 100644 --- a/crates/common/src/constants.rs +++ b/crates/common/src/constants.rs @@ -1,6 +1,8 @@ //! Commonly used constants. -use alloy_primitives::{Address, address}; +use alloy_eips::Typed2718; +use alloy_network::AnyTxEnvelope; +use alloy_primitives::{Address, B256, Signature, address}; use std::time::Duration; /// The dev chain-id, inherited from hardhat @@ -56,6 +58,24 @@ pub fn is_known_system_sender(sender: Address) -> bool { [ARBITRUM_SENDER, OPTIMISM_SYSTEM_ADDRESS, Address::ZERO].contains(&sender) } +pub fn is_impersonated_tx(tx: &AnyTxEnvelope) -> bool { + if let AnyTxEnvelope::Ethereum(tx) = tx { + return is_impersonated_sig(tx.signature(), tx.ty()); + } + false +} + +pub fn is_impersonated_sig(sig: &Signature, ty: u8) -> bool { + let impersonated_sig = + Signature::from_scalars_and_parity(B256::with_last_byte(1), B256::with_last_byte(1), false); + if ty != SYSTEM_TRANSACTION_TYPE + && (sig == &impersonated_sig || sig.r() == impersonated_sig.r()) + { + return true; + } + false +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d3ba91f077101..d48bfd66f59e2 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -346,7 +346,11 @@ pub struct Config { /// the initial balance of each deployed test contract pub initial_balance: U256, /// the block.number value during EVM execution - pub block_number: u64, + #[serde( + deserialize_with = "crate::deserialize_u64_to_u256", + serialize_with = "crate::serialize_u64_or_u256" + )] + pub block_number: U256, /// pins the block number for the state fork pub fork_block_number: Option, /// The chain name or EIP-155 chain ID. @@ -366,7 +370,11 @@ pub struct Config { /// The `block.coinbase` value during EVM execution. pub block_coinbase: Address, /// The `block.timestamp` value during EVM execution. - pub block_timestamp: u64, + #[serde( + deserialize_with = "crate::deserialize_u64_to_u256", + serialize_with = "crate::serialize_u64_or_u256" + )] + pub block_timestamp: U256, /// The `block.difficulty` value during EVM execution. pub block_difficulty: u64, /// Before merge the `block.max_hash`, after merge it is `block.prevrandao`. @@ -1679,8 +1687,8 @@ impl Config { pub fn dapptools() -> Self { Self { chain: Some(Chain::from_id(99)), - block_timestamp: 0, - block_number: 0, + block_timestamp: U256::ZERO, + block_number: U256::ZERO, ..Self::default() } } @@ -2370,7 +2378,7 @@ impl Default for Config { sender: Self::DEFAULT_SENDER, tx_origin: Self::DEFAULT_SENDER, initial_balance: U256::from((1u128 << 96) - 1), - block_number: 1, + block_number: U256::from(1), fork_block_number: None, chain: None, gas_limit: (1u64 << 30).into(), // ~1B @@ -2378,7 +2386,7 @@ impl Default for Config { gas_price: None, block_base_fee_per_gas: 0, block_coinbase: Address::ZERO, - block_timestamp: 1, + block_timestamp: U256::from(1), block_difficulty: 0, block_prevrandao: Default::default(), block_gas_limit: None, @@ -4174,7 +4182,7 @@ mod tests { let config = Config::load().unwrap(); - assert_eq!(config.block_number, 1337); + assert_eq!(config.block_number, U256::from(1337)); assert_eq!(config.sender, addr); assert_eq!(config.fuzz.runs, 420); assert_eq!(config.invariant.depth, 20); diff --git a/crates/config/src/utils.rs b/crates/config/src/utils.rs index d9a834fdff1db..8b316c0da9ca0 100644 --- a/crates/config/src/utils.rs +++ b/crates/config/src/utils.rs @@ -8,7 +8,7 @@ use foundry_compilers::artifacts::{ remappings::{Remapping, RemappingError}, }; use revm::primitives::hardfork::SpecId; -use serde::{Deserialize, Deserializer, de::Error}; +use serde::{Deserialize, Deserializer, Serializer, de::Error}; use std::{ io, path::{Path, PathBuf}, @@ -213,6 +213,44 @@ where deserialize_u64_or_max(deserializer)?.try_into().map_err(D::Error::custom) } +/// Deserialize into `U256` from either a `u64` or a `U256` hex string. +pub fn deserialize_u64_to_u256<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum NumericValue { + U256(U256), + U64(u64), + } + + match NumericValue::deserialize(deserializer)? { + NumericValue::U64(n) => Ok(U256::from(n)), + NumericValue::U256(n) => Ok(n), + } +} + +/// Serialize `U256` as `u64` if it fits, otherwise as a hex string. +/// If the number fits into a i64, serialize it as number without quotation marks. +/// If the number fits into a u64, serialize it as a stringified number with quotation marks. +/// Otherwise, serialize it as a hex string with quotation marks. +pub fn serialize_u64_or_u256(n: &U256, serializer: S) -> Result +where + S: Serializer, +{ + // The TOML specification handles integers as i64 so the number representation is limited to + // i64. If the number is larger than `i64::MAX` and up to `u64::MAX`, we serialize it as a + // string to avoid losing precision. + if let Ok(n_i64) = i64::try_from(*n) { + serializer.serialize_i64(n_i64) + } else if let Ok(n_u64) = u64::try_from(*n) { + serializer.serialize_str(&n_u64.to_string()) + } else { + serializer.serialize_str(&format!("{n:#x}")) + } +} + /// Helper type to parse both `u64` and `U256` #[derive(Clone, Copy, Deserialize)] #[serde(untagged)] diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 45097ca47f1fa..222bf550c5ba3 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -19,9 +19,11 @@ foundry-common.workspace = true foundry-config.workspace = true foundry-evm-abi.workspace = true +alloy-chains.workspace = true alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-evm.workspace = true alloy-genesis.workspace = true +alloy-hardforks.workspace = true alloy-json-abi.workspace = true alloy-primitives = { workspace = true, features = [ "serde", @@ -46,7 +48,6 @@ revm = { workspace = true, features = [ "arbitrary", "c-kzg", "blst", - "secp256r1", ] } revm-inspectors.workspace = true op-revm.workspace = true diff --git a/crates/evm/core/src/backend/in_memory_db.rs b/crates/evm/core/src/backend/in_memory_db.rs index cf8eea1aa4355..4e35c84da59e2 100644 --- a/crates/evm/core/src/backend/in_memory_db.rs +++ b/crates/evm/core/src/backend/in_memory_db.rs @@ -133,16 +133,16 @@ mod tests { // call `basic` on a non-existing account let info = Database::basic(&mut db, address).unwrap(); assert!(info.is_none()); + let mut info = info.unwrap_or_default(); info.balance = U256::from(500u64); // insert the modified account info db.insert_account_info(address, info); - // when fetching again, the `AccountInfo` is still `None` because the state of the account - // is `AccountState::NotExisting`, see + // now we can call `basic` again and it should return the inserted account info let info = Database::basic(&mut db, address).unwrap(); - assert!(info.is_none()); + assert!(info.is_some()); } /// Demonstrates how to insert a new account but not mark it as non-existing diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index c130e835ea1d0..abe2448523c6e 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -6,7 +6,7 @@ use crate::{ evm::new_evm_with_inspector, fork::{CreateFork, ForkId, MultiFork}, state_snapshot::StateSnapshots, - utils::{configure_tx_env, configure_tx_req_env}, + utils::{configure_tx_env, configure_tx_req_env, get_blob_base_fee_update_fraction_by_spec_id}, }; use alloy_consensus::Typed2718; use alloy_evm::Evm; @@ -30,6 +30,7 @@ use revm::{ }; use std::{ collections::{BTreeMap, HashMap, HashSet}, + fmt::Debug, time::Instant, }; @@ -76,7 +77,7 @@ pub type JournaledState = JournalInner; /// An extension trait that allows us to easily extend the `revm::Inspector` capabilities #[auto_impl::auto_impl(&mut)] -pub trait DatabaseExt: Database + DatabaseCommit { +pub trait DatabaseExt: Database + DatabaseCommit + Debug { /// Creates a new state snapshot at the current point of execution. /// /// A state snapshot is associated with a new unique id that's created for the snapshot. @@ -880,7 +881,8 @@ impl Backend { let fork_id = self.ensure_fork_id(id)?.clone(); let fork = self.inner.get_fork_by_id_mut(id)?; - let full_block = fork.db.db.get_full_block(env.evm_env.block_env.number)?; + let full_block = + fork.db.db.get_full_block(env.evm_env.block_env.number.saturating_to::())?; for tx in full_block.inner.transactions.txns() { // System transactions such as on L2s don't contain any pricing info so we skip them @@ -1447,6 +1449,7 @@ impl DatabaseExt for Backend { .map(|s| s.present_value) .unwrap_or_default(), U256::from_be_bytes(value.0), + 0, ), ) }) @@ -1942,16 +1945,19 @@ fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool /// Updates the env's block with the block's data fn update_env_block(env: &mut EnvMut<'_>, block: &AnyRpcBlock) { - env.block.timestamp = block.header.timestamp; + env.block.timestamp = U256::from(block.header.timestamp); env.block.beneficiary = block.header.beneficiary; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default()); env.block.basefee = block.header.base_fee_per_gas.unwrap_or_default(); env.block.gas_limit = block.header.gas_limit; - env.block.number = block.header.number; + env.block.number = U256::from(block.header.number); + if let Some(excess_blob_gas) = block.header.excess_blob_gas { - env.block.blob_excess_gas_and_price = - Some(BlobExcessGasAndPrice::new(excess_blob_gas, false)); + env.block.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new( + excess_blob_gas, + get_blob_base_fee_update_fraction_by_spec_id(env.cfg.spec), + )); } } @@ -2071,7 +2077,11 @@ mod tests { } drop(backend); - let meta = BlockchainDbMeta { block_env: env.evm_env.block_env, hosts: Default::default() }; + let meta = BlockchainDbMeta { + chain: None, + block_env: env.evm_env.block_env, + hosts: Default::default(), + }; let db = BlockchainDb::new( meta, diff --git a/crates/evm/core/src/either_evm.rs b/crates/evm/core/src/either_evm.rs index e958eeb94df34..785a73b8d4354 100644 --- a/crates/evm/core/src/either_evm.rs +++ b/crates/evm/core/src/either_evm.rs @@ -6,7 +6,7 @@ use revm::{ DatabaseCommit, Inspector, context::{ BlockEnv, TxEnv, - result::{EVMError, ExecutionResult, HaltReason, ResultAndState}, + result::{EVMError, ExecResultAndState, ExecutionResult, ResultAndState}, }, handler::PrecompileProvider, interpreter::InterpreterResult, @@ -54,13 +54,13 @@ where /// Converts the [`EthEvm::transact`] result to [`EitherEvmResult`]. fn map_eth_result( &self, - result: Result, EVMError>, + result: Result, EVMError>, ) -> EitherEvmResult { match result { - Ok(result) => { - // Map the halt reason - Ok(result.map_haltreason(OpHaltReason::Base)) - } + Ok(result) => Ok(ResultAndState { + result: result.result.map_haltreason(OpHaltReason::Base), + state: result.state, + }), Err(e) => Err(self.map_eth_err(e)), } } diff --git a/crates/evm/core/src/evm.rs b/crates/evm/core/src/evm.rs index 1cd91d0aceee7..a40bcd44d442f 100644 --- a/crates/evm/core/src/evm.rs +++ b/crates/evm/core/src/evm.rs @@ -1,4 +1,7 @@ -use std::ops::{Deref, DerefMut}; +use std::{ + marker::PhantomData, + ops::{Deref, DerefMut}, +}; use crate::{ Env, InspectorExt, backend::DatabaseExt, constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH, @@ -7,27 +10,31 @@ use alloy_consensus::constants::KECCAK_EMPTY; use alloy_evm::{ Evm, EvmEnv, eth::EthEvmContext, - precompiles::{DynPrecompile, PrecompilesMap}, + precompiles::{DynPrecompile, PrecompileInput, PrecompilesMap}, }; use alloy_primitives::{Address, Bytes, U256}; use foundry_fork_db::DatabaseError; use revm::{ - Context, ExecuteEvm, Journal, + Context, Journal, context::{ - BlockEnv, CfgEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContext, TxEnv, - result::{EVMError, HaltReason, ResultAndState}, + BlockEnv, CfgEnv, ContextTr, CreateScheme, Evm as RevmEvm, JournalTr, LocalContext, + LocalContextTr, TxEnv, + result::{EVMError, ExecResultAndState, ExecutionResult, HaltReason, ResultAndState}, }, handler::{ - EthFrame, EthPrecompiles, FrameInitOrResult, FrameResult, Handler, ItemOrResult, - MainnetHandler, instructions::EthInstructions, + EthFrame, EthPrecompiles, EvmTr, FrameResult, FrameTr, Handler, ItemOrResult, + instructions::EthInstructions, }, - inspector::InspectorHandler, + inspector::{InspectorEvmTr, InspectorHandler}, interpreter::{ CallInput, CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, - FrameInput, Gas, InstructionResult, InterpreterResult, interpreter::EthInterpreter, - return_ok, + FrameInput, Gas, InstructionResult, InterpreterResult, SharedMemory, + interpreter::EthInterpreter, interpreter_action::FrameInit, return_ok, + }, + precompile::{ + PrecompileSpecId, Precompiles, + secp256r1::{P256VERIFY, P256VERIFY_BASE_GAS_FEE}, }, - precompile::{PrecompileSpecId, Precompiles, secp256r1::P256VERIFY}, primitives::hardfork::SpecId, }; @@ -36,7 +43,7 @@ pub fn new_evm_with_inspector<'i, 'db, I: InspectorExt + ?Sized>( env: Env, inspector: &'i mut I, ) -> FoundryEvm<'db, &'i mut I> { - let ctx = EthEvmContext { + let mut ctx = EthEvmContext { journaled_state: { let mut journal = Journal::new(db); journal.set_spec_id(env.evm_env.cfg_env.spec); @@ -49,6 +56,7 @@ pub fn new_evm_with_inspector<'i, 'db, I: InspectorExt + ?Sized>( local: LocalContext::default(), error: Ok(()), }; + ctx.cfg.tx_chain_id_check = true; let spec = ctx.cfg.spec; let mut evm = FoundryEvm { @@ -89,7 +97,11 @@ pub fn new_evm_with_existing_context<'a>( fn inject_precompiles(evm: &mut FoundryEvm<'_, impl InspectorExt>) { if evm.inspector().is_odyssey() { evm.precompiles_mut().apply_precompile(P256VERIFY.address(), |_| { - Some(DynPrecompile::from(P256VERIFY.precompile())) + // Create a wrapper function that adapts the new API + let precompile_fn = |input: PrecompileInput<'_>| -> Result<_, _> { + P256VERIFY.precompile()(input.data, P256VERIFY_BASE_GAS_FEE) + }; + Some(DynPrecompile::from(precompile_fn)) }); } } @@ -122,7 +134,6 @@ fn get_create2_factory_call_inputs( gas_limit: inputs.gas_limit, is_static: false, return_memory_offset: 0..0, - is_eof: false, } } @@ -133,22 +144,26 @@ pub struct FoundryEvm<'db, I: InspectorExt> { I, EthInstructions>, PrecompilesMap, + EthFrame, >, } - impl FoundryEvm<'_, I> { pub fn run_execution( &mut self, frame: FrameInput, ) -> Result> { - let mut handler = FoundryHandler::<_>::default(); + let mut handler = FoundryHandler::::default(); - // Create first frame action - let frame = handler.inspect_first_frame_init(&mut self.inner, frame)?; - let frame_result = match frame { - ItemOrResult::Item(frame) => handler.inspect_run_exec_loop(&mut self.inner, frame)?, - ItemOrResult::Result(result) => result, - }; + // Create first frame + let memory = + SharedMemory::new_with_buffer(self.inner.ctx().local().shared_memory_buffer().clone()); + let first_frame_input = FrameInit { depth: 0, memory, frame_input: frame }; + + // Run execution loop + let mut frame_result = handler.inspect_run_exec_loop(&mut self.inner, first_frame_input)?; + + // Handle last frame result + handler.last_frame_result(&mut self.inner, &mut frame_result)?; Ok(frame_result) } @@ -172,7 +187,7 @@ impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> { } fn db_mut(&mut self) -> &mut Self::DB { - self.inner.db() + &mut self.inner.ctx.journaled_state.database } fn precompiles(&self) -> &Self::Precompiles { @@ -199,9 +214,12 @@ impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> { &mut self, tx: Self::Tx, ) -> Result, Self::Error> { - let mut handler = FoundryHandler::<_>::default(); - self.inner.set_tx(tx); - handler.inspect_run(&mut self.inner) + self.inner.ctx.tx = tx; + + let mut handler = FoundryHandler::::default(); + let result = handler.inspect_run(&mut self.inner)?; + + Ok(ResultAndState::new(result, self.inner.ctx.journaled_state.inner.state.clone())) } fn transact_system_call( @@ -209,7 +227,7 @@ impl<'db, I: InspectorExt> Evm for FoundryEvm<'db, I> { _caller: Address, _contract: Address, _data: Bytes, - ) -> Result, Self::Error> { + ) -> Result, Self::Error> { unimplemented!() } @@ -238,76 +256,103 @@ impl DerefMut for FoundryEvm<'_, I> { } pub struct FoundryHandler<'db, I: InspectorExt> { - #[allow(clippy::type_complexity)] - inner: MainnetHandler< - RevmEvm< - EthEvmContext<&'db mut dyn DatabaseExt>, - I, - EthInstructions>, - PrecompilesMap, - >, - EVMError, - EthFrame< - RevmEvm< - EthEvmContext<&'db mut dyn DatabaseExt>, - I, - EthInstructions>, - PrecompilesMap, - >, - EVMError, - EthInterpreter, - >, - >, create2_overrides: Vec<(usize, CallInputs)>, + _phantom: PhantomData<(&'db mut dyn DatabaseExt, I)>, } impl Default for FoundryHandler<'_, I> { fn default() -> Self { - Self { inner: MainnetHandler::default(), create2_overrides: Vec::new() } + Self { create2_overrides: Vec::new(), _phantom: PhantomData } } } +// Blanket Handler implementation for FoundryHandler, needed for implementing the InspectorHandler +// trait. impl<'db, I: InspectorExt> Handler for FoundryHandler<'db, I> { type Evm = RevmEvm< EthEvmContext<&'db mut dyn DatabaseExt>, I, EthInstructions>, PrecompilesMap, + EthFrame, >; type Error = EVMError; - type Frame = EthFrame< - RevmEvm< - EthEvmContext<&'db mut dyn DatabaseExt>, - I, - EthInstructions>, - PrecompilesMap, - >, - EVMError, - EthInterpreter, - >; type HaltReason = HaltReason; +} - fn frame_return_result( +impl<'db, I: InspectorExt> FoundryHandler<'db, I> { + /// Handles CREATE2 frame initialization, potentially transforming it to use the CREATE2 + /// factory. + fn handle_create_frame( &mut self, - frame: &mut Self::Frame, - evm: &mut Self::Evm, - result: ::FrameResult, - ) -> Result<(), Self::Error> { - let result = if self - .create2_overrides - .last() - .is_some_and(|(depth, _)| *depth == evm.journal().depth) + evm: &mut ::Evm, + init: &mut FrameInit, + ) -> Result, ::Error> { + if let FrameInput::Create(inputs) = &init.frame_input + && let CreateScheme::Create2 { salt } = inputs.scheme { + let (ctx, inspector) = evm.ctx_inspector(); + + if inspector.should_use_create2_factory(ctx, inputs) { + let gas_limit = inputs.gas_limit; + + // Get CREATE2 deployer. + let create2_deployer = evm.inspector().create2_deployer(); + + // Generate call inputs for CREATE2 factory. + let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer); + + // Push data about current override to the stack. + self.create2_overrides.push((evm.journal().depth(), call_inputs.clone())); + + // Sanity check that CREATE2 deployer exists. + let code_hash = evm.journal_mut().load_account(create2_deployer)?.info.code_hash; + if code_hash == KECCAK_EMPTY { + return Ok(Some(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: Bytes::copy_from_slice( + format!("missing CREATE2 deployer: {create2_deployer}").as_bytes(), + ), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + }))); + } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH { + return Ok(Some(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: InstructionResult::Revert, + output: "invalid CREATE2 deployer bytecode".into(), + gas: Gas::new(gas_limit), + }, + memory_offset: 0..0, + }))); + } + + // Rewrite the frame init + init.frame_input = FrameInput::Call(Box::new(call_inputs)); + } + } + Ok(None) + } + + /// Transforms CREATE2 factory call results back into CREATE outcomes. + fn handle_create2_override( + &mut self, + evm: &mut ::Evm, + result: FrameResult, + ) -> FrameResult { + if self.create2_overrides.last().is_some_and(|(depth, _)| *depth == evm.journal().depth()) { let (_, call_inputs) = self.create2_overrides.pop().unwrap(); - let FrameResult::Call(mut result) = result else { + let FrameResult::Call(mut call) = result else { unreachable!("create2 override should be a call frame"); }; // Decode address from output. - let address = match result.instruction_result() { - return_ok!() => Address::try_from(result.output().as_ref()) + let address = match call.instruction_result() { + return_ok!() => Address::try_from(call.output().as_ref()) .map_err(|_| { - result.result = InterpreterResult { + call.result = InterpreterResult { result: InstructionResult::Revert, output: "invalid CREATE2 factory output".into(), gas: Gas::new(call_inputs.gas_limit), @@ -317,71 +362,51 @@ impl<'db, I: InspectorExt> Handler for FoundryHandler<'db, I> { _ => None, }; - FrameResult::Create(CreateOutcome { result: result.result, address }) + FrameResult::Create(CreateOutcome { result: call.result, address }) } else { result - }; - - self.inner.frame_return_result(frame, evm, result) + } } } impl InspectorHandler for FoundryHandler<'_, I> { type IT = EthInterpreter; - fn inspect_frame_call( + fn inspect_run_exec_loop( &mut self, - frame: &mut Self::Frame, evm: &mut Self::Evm, - ) -> Result, Self::Error> { - let frame_or_result = self.inner.inspect_frame_call(frame, evm)?; + first_frame_input: <::Frame as FrameTr>::FrameInit, + ) -> Result { + let res = evm.inspect_frame_init(first_frame_input)?; - let ItemOrResult::Item(FrameInput::Create(inputs)) = &frame_or_result else { - return Ok(frame_or_result); - }; + if let ItemOrResult::Result(frame_result) = res { + return Ok(frame_result); + } - let CreateScheme::Create2 { salt } = inputs.scheme else { return Ok(frame_or_result) }; + loop { + let call_or_result = evm.inspect_frame_run()?; + + let result = match call_or_result { + ItemOrResult::Item(mut init) => { + // Handle CREATE/CREATE2 frame initialization + if let Some(frame_result) = self.handle_create_frame(evm, &mut init)? { + return Ok(frame_result); + } + + match evm.inspect_frame_init(init)? { + ItemOrResult::Item(_) => continue, + ItemOrResult::Result(result) => result, + } + } + ItemOrResult::Result(result) => result, + }; - if !evm.inspector.should_use_create2_factory(&mut evm.ctx, inputs) { - return Ok(frame_or_result); - } + // Handle CREATE2 override transformation if needed + let result = self.handle_create2_override(evm, result); - let gas_limit = inputs.gas_limit; - - // Get CREATE2 deployer. - let create2_deployer = evm.inspector.create2_deployer(); - - // Generate call inputs for CREATE2 factory. - let call_inputs = get_create2_factory_call_inputs(salt, inputs, create2_deployer); - - // Push data about current override to the stack. - self.create2_overrides.push((evm.journal().depth(), call_inputs.clone())); - - // Sanity check that CREATE2 deployer exists. - let code_hash = evm.journal().load_account(create2_deployer)?.info.code_hash; - if code_hash == KECCAK_EMPTY { - return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome { - result: InterpreterResult { - result: InstructionResult::Revert, - output: Bytes::copy_from_slice( - format!("missing CREATE2 deployer: {create2_deployer}").as_bytes(), - ), - gas: Gas::new(gas_limit), - }, - memory_offset: 0..0, - }))); - } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH { - return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome { - result: InterpreterResult { - result: InstructionResult::Revert, - output: "invalid CREATE2 deployer bytecode".into(), - gas: Gas::new(gas_limit), - }, - memory_offset: 0..0, - }))); + if let Some(result) = evm.frame_return_result(result)? { + return Ok(result); + } } - - // Return the created CALL frame instead - Ok(ItemOrResult::Item(FrameInput::Call(Box::new(call_inputs)))) } } diff --git a/crates/evm/core/src/fork/database.rs b/crates/evm/core/src/fork/database.rs index 8780bac3333fe..6a53a19843b1b 100644 --- a/crates/evm/core/src/fork/database.rs +++ b/crates/evm/core/src/fork/database.rs @@ -272,7 +272,6 @@ mod tests { use super::*; use crate::backend::BlockchainDbMeta; use foundry_common::provider::get_http_provider; - use std::collections::BTreeSet; /// Demonstrates that `Database::basic` for `ForkedDatabase` will always return the /// `AccountInfo` @@ -280,7 +279,8 @@ mod tests { async fn fork_db_insert_basic_default() { let rpc = foundry_test_utils::rpc::next_http_rpc_endpoint(); let provider = get_http_provider(rpc.clone()); - let meta = BlockchainDbMeta { block_env: Default::default(), hosts: BTreeSet::from([rpc]) }; + let meta = BlockchainDbMeta::new(Default::default(), rpc); + let db = BlockchainDb::new(meta, None); let backend = SharedBackend::spawn_backend(Arc::new(provider), db.clone(), None).await; diff --git a/crates/evm/core/src/fork/init.rs b/crates/evm/core/src/fork/init.rs index 70a12d38ec72c..4742d46fcaad2 100644 --- a/crates/evm/core/src/fork/init.rs +++ b/crates/evm/core/src/fork/init.rs @@ -1,6 +1,6 @@ use crate::{AsEnvMut, Env, EvmEnv, utils::apply_chain_and_block_specific_env_changes}; use alloy_consensus::BlockHeader; -use alloy_primitives::Address; +use alloy_primitives::{Address, U256}; use alloy_provider::{Network, Provider, network::BlockResponse}; use alloy_rpc_types::BlockNumberOrTag; use eyre::WrapErr; @@ -56,8 +56,8 @@ pub async fn environment>( evm_env: EvmEnv { cfg_env: cfg, block_env: BlockEnv { - number: block.header().number(), - timestamp: block.header().timestamp(), + number: U256::from(block.header().number()), + timestamp: U256::from(block.header().timestamp()), beneficiary: block.header().beneficiary(), difficulty: block.header().difficulty(), prevrandao: block.header().mix_hash(), diff --git a/crates/evm/core/src/fork/multi.rs b/crates/evm/core/src/fork/multi.rs index 0431f63bb77e4..ea5e425c3ba63 100644 --- a/crates/evm/core/src/fork/multi.rs +++ b/crates/evm/core/src/fork/multi.rs @@ -6,7 +6,7 @@ use super::CreateFork; use crate::Env; use alloy_consensus::BlockHeader; -use alloy_primitives::map::HashMap; +use alloy_primitives::{U256, map::HashMap}; use alloy_provider::network::BlockResponse; use foundry_common::provider::{ProviderBuilder, RetryProvider}; use foundry_config::Config; @@ -151,7 +151,7 @@ impl MultiFork { } /// Updates block number and timestamp of given fork with new values. - pub fn update_block(&self, fork: ForkId, number: u64, timestamp: u64) -> eyre::Result<()> { + pub fn update_block(&self, fork: ForkId, number: U256, timestamp: U256) -> eyre::Result<()> { trace!(?fork, ?number, ?timestamp, "update fork block"); self.handler .clone() @@ -213,7 +213,7 @@ enum Request { /// Returns the environment of the fork. GetEnv(ForkId, GetEnvSender), /// Updates the block number and timestamp of the fork. - UpdateBlock(ForkId, u64, u64), + UpdateBlock(ForkId, U256, U256), /// Updates the block the entire block env, UpdateEnv(ForkId, BlockEnv), /// Shutdowns the entire `MultiForkHandler`, see `ShutDownMultiFork` @@ -323,7 +323,7 @@ impl MultiForkHandler { } /// Update fork block number and timestamp. Used to preserve values set by `roll` and `warp` /// cheatcodes when new fork selected. - fn update_block(&mut self, fork_id: ForkId, block_number: u64, block_timestamp: u64) { + fn update_block(&mut self, fork_id: ForkId, block_number: U256, block_timestamp: U256) { if let Some(fork) = self.forks.get_mut(&fork_id) { fork.opts.env.evm_env.block_env.number = block_number; fork.opts.env.evm_env.block_env.timestamp = block_timestamp; @@ -559,7 +559,7 @@ async fn create_fork(mut fork: CreateFork) -> eyre::Result<(ForkId, CreatedFork, let db = BlockchainDb::new(meta, cache_path); let (backend, handler) = SharedBackend::new(provider, db, Some(number.into())); let fork = CreatedFork::new(fork, backend); - let fork_id = ForkId::new(&fork.opts.url, number.into()); + let fork_id = ForkId::new(&fork.opts.url, Some(number)); Ok((fork_id, fork, handler)) } diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 1b071e25f4617..5cf7321647acb 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -278,10 +278,18 @@ pub struct Env { pub block_coinbase: Address, /// the block.timestamp value during EVM execution - pub block_timestamp: u64, + #[serde( + deserialize_with = "foundry_config::deserialize_u64_to_u256", + serialize_with = "foundry_config::serialize_u64_or_u256" + )] + pub block_timestamp: U256, /// the block.number value during EVM execution" - pub block_number: u64, + #[serde( + deserialize_with = "foundry_config::deserialize_u64_to_u256", + serialize_with = "foundry_config::serialize_u64_or_u256" + )] + pub block_number: U256, /// the block.difficulty value during EVM execution pub block_difficulty: u64, diff --git a/crates/evm/core/src/utils.rs b/crates/evm/core/src/utils.rs index f1c3f418d212e..778802f395875 100644 --- a/crates/evm/core/src/utils.rs +++ b/crates/evm/core/src/utils.rs @@ -1,11 +1,17 @@ use crate::EnvMut; +use alloy_chains::Chain; use alloy_consensus::BlockHeader; +use alloy_hardforks::EthereumHardfork; use alloy_json_abi::{Function, JsonAbi}; use alloy_network::{AnyTxEnvelope, TransactionResponse}; -use alloy_primitives::{Address, B256, Selector, TxKind, U256}; +use alloy_primitives::{Address, B256, ChainId, Selector, TxKind, U256}; use alloy_provider::{Network, network::BlockResponse}; use alloy_rpc_types::{Transaction, TransactionRequest}; use foundry_config::NamedChain; +use revm::primitives::{ + eip4844::{BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE}, + hardfork::SpecId, +}; pub use revm::state::EvmState as StateChangeset; /// Depending on the configured chain id and block number this should apply any specific changes @@ -71,6 +77,28 @@ pub fn apply_chain_and_block_specific_env_changes( } } +/// Derive the blob base fee update fraction based on the chain and timestamp by checking the +/// hardfork. +pub fn get_blob_base_fee_update_fraction(chain_id: ChainId, timestamp: u64) -> u64 { + let hardfork = EthereumHardfork::from_chain_and_timestamp(Chain::from_id(chain_id), timestamp) + .unwrap_or_default(); + + if hardfork >= EthereumHardfork::Prague { + BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE + } else { + BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN + } +} + +/// Returns the blob base fee update fraction based on the spec id. +pub fn get_blob_base_fee_update_fraction_by_spec_id(spec: SpecId) -> u64 { + if spec >= SpecId::PRAGUE { + BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE + } else { + BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN + } +} + /// Given an ABI and selector, it tries to find the respective function. pub fn get_function<'a>( contract_name: &str, diff --git a/crates/evm/evm/src/executors/fuzz/mod.rs b/crates/evm/evm/src/executors/fuzz/mod.rs index f056285cc4c8a..cdd638ff4d183 100644 --- a/crates/evm/evm/src/executors/fuzz/mod.rs +++ b/crates/evm/evm/src/executors/fuzz/mod.rs @@ -150,7 +150,7 @@ impl FuzzedExecutor { // since that input represents the last run case, which may not correspond with // our failure - when a fuzz case fails, proptest will try to run at least one // more case to find a minimal failure case. - let reason = rd.maybe_decode(&outcome.1.result, Some(status)); + let reason = rd.maybe_decode(&outcome.1.result, status); execution_data.borrow_mut().logs.extend(outcome.1.logs.clone()); execution_data.borrow_mut().counterexample = outcome; // HACK: we have to use an empty string here to denote `None`. diff --git a/crates/evm/evm/src/executors/fuzz/types.rs b/crates/evm/evm/src/executors/fuzz/types.rs index 890702aa44cca..11f658f4bed8b 100644 --- a/crates/evm/evm/src/executors/fuzz/types.rs +++ b/crates/evm/evm/src/executors/fuzz/types.rs @@ -29,7 +29,7 @@ pub struct CounterExampleOutcome { /// Minimal reproduction test case for failing test. pub counterexample: (Bytes, RawCallResult), /// The status of the call. - pub exit_reason: InstructionResult, + pub exit_reason: Option, /// Breakpoints char pc map. pub breakpoints: Breakpoints, } diff --git a/crates/evm/evm/src/executors/invariant/error.rs b/crates/evm/evm/src/executors/invariant/error.rs index f9b5984a60f46..061d9ed8d0ddc 100644 --- a/crates/evm/evm/src/executors/invariant/error.rs +++ b/crates/evm/evm/src/executors/invariant/error.rs @@ -80,7 +80,7 @@ impl FailedInvariantCaseData { let revert_reason = RevertDecoder::new() .with_abis(targeted_contracts.targets.lock().values().map(|c| &c.abi)) .with_abi(invariant_contract.abi) - .decode(call_result.result.as_ref(), Some(call_result.exit_reason)); + .decode(call_result.result.as_ref(), call_result.exit_reason); let func = invariant_contract.invariant_function; debug_assert!(func.inputs.is_empty()); diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index e70c6801f38d7..dbfdc7059cb4a 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -809,7 +809,7 @@ impl From for RawCallResult { #[derive(Debug)] pub struct RawCallResult { /// The status of the call - pub exit_reason: InstructionResult, + pub exit_reason: Option, /// Whether the call reverted or not pub reverted: bool, /// Whether the call includes a snapshot failure @@ -846,14 +846,14 @@ pub struct RawCallResult { /// The raw output of the execution pub out: Option, /// The chisel state - pub chisel_state: Option<(Vec, Vec, InstructionResult)>, + pub chisel_state: Option<(Vec, Vec, Option)>, pub reverter: Option
, } impl Default for RawCallResult { fn default() -> Self { Self { - exit_reason: InstructionResult::Continue, + exit_reason: None, reverted: false, has_state_snapshot_failure: false, result: Bytes::new(), @@ -899,7 +899,7 @@ impl RawCallResult { if let Some(reason) = SkipReason::decode(&self.result) { return EvmError::Skip(reason); } - let reason = rd.unwrap_or_default().decode(&self.result, Some(self.exit_reason)); + let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason); EvmError::Execution(Box::new(self.into_execution_error(reason))) } @@ -910,7 +910,13 @@ impl RawCallResult { /// Returns an `EvmError` if the call failed, otherwise returns `self`. pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result { - if self.exit_reason.is_ok() { Ok(self) } else { Err(self.into_evm_error(rd)) } + if let Some(reason) = self.exit_reason + && reason.is_ok() + { + Ok(self) + } else { + Err(self.into_evm_error(rd)) + } } /// Decodes the result of the call with the given function. @@ -1051,7 +1057,7 @@ fn convert_executed_result( .filter(|txs| !txs.is_empty()); Ok(RawCallResult { - exit_reason, + exit_reason: Some(exit_reason), reverted: !matches!(exit_reason, return_ok!()), has_state_snapshot_failure, result, diff --git a/crates/evm/evm/src/inspectors/chisel_state.rs b/crates/evm/evm/src/inspectors/chisel_state.rs index 600c6ca3c41c7..f99ae4116fd32 100644 --- a/crates/evm/evm/src/inspectors/chisel_state.rs +++ b/crates/evm/evm/src/inspectors/chisel_state.rs @@ -5,7 +5,9 @@ use revm::{ context::ContextTr, inspector::JournalExt, interpreter::{ - InstructionResult, Interpreter, interpreter::EthInterpreter, interpreter_types::Jumps, + InstructionResult, Interpreter, + interpreter::EthInterpreter, + interpreter_types::{Jumps, LoopControl}, }, }; @@ -15,7 +17,7 @@ pub struct ChiselState { /// The PC of the final instruction pub final_pc: usize, /// The final state of the REPL contract call - pub state: Option<(Vec, Vec, InstructionResult)>, + pub state: Option<(Vec, Vec, Option)>, } impl ChiselState { @@ -33,14 +35,14 @@ where CTX::Journal: JournalExt, { #[cold] - fn step_end(&mut self, interp: &mut Interpreter, _context: &mut CTX) { + fn step_end(&mut self, interpreter: &mut Interpreter, _context: &mut CTX) { // If we are at the final pc of the REPL contract execution, set the state. // Subtraction can't overflow because `pc` is always at least 1 in `step_end`. - if self.final_pc == interp.bytecode.pc() - 1 { + if self.final_pc == interpreter.bytecode.pc() - 1 { self.state = Some(( - interp.stack.data().clone(), - interp.memory.context_memory().to_vec(), - interp.control.instruction_result, + interpreter.stack.data().clone(), + interpreter.memory.context_memory().to_vec(), + interpreter.bytecode.instruction_result(), )) } } diff --git a/crates/evm/evm/src/inspectors/custom_printer.rs b/crates/evm/evm/src/inspectors/custom_printer.rs index 4d14798ba417d..2d39f33b955b3 100644 --- a/crates/evm/evm/src/inspectors/custom_printer.rs +++ b/crates/evm/evm/src/inspectors/custom_printer.rs @@ -11,7 +11,7 @@ use revm::{ interpreter::{ CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, interpreter::EthInterpreter, - interpreter_types::{Jumps, LoopControl, MemoryTr}, + interpreter_types::{Jumps, MemoryTr}, }, primitives::{Address, U256}, }; @@ -31,7 +31,7 @@ where CTX::Journal: JournalExt, { fn initialize_interp(&mut self, interp: &mut Interpreter, _context: &mut CTX) { - self.gas_inspector.initialize_interp(&interp.control.gas); + self.gas_inspector.initialize_interp(&interp.gas); } // get opcode by calling `interp.contract.opcode(interp.program_counter())`. @@ -52,17 +52,17 @@ where gas_remaining, name, opcode, - interp.control.gas.refunded(), - interp.control.gas.refunded(), + interp.gas.refunded(), + interp.gas.refunded(), interp.stack.data(), memory_size, ); - self.gas_inspector.step(&interp.control.gas); + self.gas_inspector.step(&interp.gas); } - fn step_end(&mut self, interp: &mut Interpreter, _context: &mut CTX) { - self.gas_inspector.step_end(interp.control.gas_mut()); + fn step_end(&mut self, interpreter: &mut Interpreter, _context: &mut CTX) { + self.gas_inspector.step_end(&mut interpreter.gas); } fn call_end(&mut self, _context: &mut CTX, _inputs: &CallInputs, outcome: &mut CallOutcome) { diff --git a/crates/evm/evm/src/inspectors/revert_diagnostic.rs b/crates/evm/evm/src/inspectors/revert_diagnostic.rs index c1add5530621e..79c9b1754d511 100644 --- a/crates/evm/evm/src/inspectors/revert_diagnostic.rs +++ b/crates/evm/evm/src/inspectors/revert_diagnostic.rs @@ -11,7 +11,8 @@ use revm::{ inspector::JournalExt, interpreter::{ CallInputs, CallOutcome, CallScheme, InstructionResult, Interpreter, InterpreterAction, - InterpreterResult, interpreter::EthInterpreter, interpreter_types::Jumps, + interpreter::EthInterpreter, + interpreter_types::{Jumps, LoopControl}, }, }; use std::fmt; @@ -20,7 +21,7 @@ const IGNORE: [Address; 2] = [HARDHAT_CONSOLE_ADDRESS, CHEATCODE_ADDRESS]; /// Checks if the call scheme corresponds to any sort of delegate call pub fn is_delegatecall(scheme: CallScheme) -> bool { - matches!(scheme, CallScheme::DelegateCall | CallScheme::ExtDelegateCall | CallScheme::CallCode) + matches!(scheme, CallScheme::DelegateCall | CallScheme::CallCode) } #[derive(Debug, Clone, Copy)] @@ -97,16 +98,13 @@ impl RevertDiagnostic { } /// Injects the revert diagnostic into the debug traces. Should only be called after a revert. - fn broadcast_diagnostic(&self, interp: &mut Interpreter) { + fn broadcast_diagnostic(&self, interpreter: &mut Interpreter) { if let Some(reason) = self.reason() { - interp.control.instruction_result = InstructionResult::Revert; - interp.control.next_action = InterpreterAction::Return { - result: InterpreterResult { - output: reason.to_string().abi_encode().into(), - gas: interp.control.gas, - result: InstructionResult::Revert, - }, - }; + interpreter.bytecode.set_action(InterpreterAction::new_return( + InstructionResult::Revert, + reason.to_string().abi_encode().into(), + interpreter.gas, + )); } } @@ -199,7 +197,7 @@ where return None; } - if let Ok(state) = ctx.journal().code(target) + if let Ok(state) = ctx.journal_mut().code(target) && state.is_empty() && !inputs.input.is_empty() { @@ -210,6 +208,11 @@ where /// Handles `REVERT` and `EXTCODESIZE` opcodes for diagnostics. fn step(&mut self, interp: &mut Interpreter, ctx: &mut CTX) { + // Check if an action has already been set (which would null the instruction pointer) + if interp.bytecode.action.is_some() { + return; + } + match interp.bytecode.opcode() { opcode::REVERT => self.handle_revert(interp, ctx), opcode::EXTCODESIZE => self.handle_extcodesize(interp, ctx), diff --git a/crates/evm/evm/src/inspectors/script.rs b/crates/evm/evm/src/inspectors/script.rs index 3d094f4c22897..a7703fa32cd1d 100644 --- a/crates/evm/evm/src/inspectors/script.rs +++ b/crates/evm/evm/src/inspectors/script.rs @@ -1,6 +1,5 @@ use alloy_evm::Database; -use alloy_primitives::Address; -use foundry_common::sh_err; +use alloy_primitives::{Address, Bytes}; use foundry_evm_core::backend::DatabaseError; use revm::{ Inspector, @@ -8,7 +7,9 @@ use revm::{ context::ContextTr, inspector::JournalExt, interpreter::{ - InstructionResult, Interpreter, interpreter::EthInterpreter, interpreter_types::Jumps, + InstructionResult, Interpreter, InterpreterAction, + interpreter::EthInterpreter, + interpreter_types::{Jumps, LoopControl}, }, }; @@ -35,12 +36,11 @@ where && interpreter.input.target_address == self.script_address && interpreter.input.bytecode_address == Some(self.script_address) { - // Log the reason for revert - let _ = sh_err!( - "Usage of `address(this)` detected in script contract. Script contracts are ephemeral and their addresses should not be relied upon." - ); - // Set the instruction result to Revert to stop execution - interpreter.control.instruction_result = InstructionResult::Revert; + interpreter.bytecode.set_action(InterpreterAction::new_return( + InstructionResult::Revert, + Bytes::from("Usage of `address(this)` detected in script contract. Script contracts are ephemeral and their addresses should not be relied upon."), + interpreter.gas, + )); } // Note: We don't return anything here as step returns void. // The original check returned InstructionResult::Continue, but that's the default diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 2daca5dd26049..4d4c39c7c360d 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -262,7 +262,7 @@ pub struct InspectorData { pub line_coverage: Option, pub edge_coverage: Option>, pub cheatcodes: Option, - pub chisel_state: Option<(Vec, Vec, InstructionResult)>, + pub chisel_state: Option<(Vec, Vec, Option)>, pub reverter: Option
, } @@ -279,8 +279,8 @@ pub struct InnerContextData { /// An inspector that calls multiple inspectors in sequence. /// -/// If a call to an inspector returns a value other than [InstructionResult::Continue] (or -/// equivalent) the remaining inspectors are not called. +/// If a call to an inspector returns a value (indicating a stop or revert) the remaining inspectors +/// are not called. /// /// Stack is divided into [Cheatcodes] and `InspectorStackInner`. This is done to allow assembling /// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. This gives @@ -881,9 +881,7 @@ impl Inspector> for InspectorStackRefMut<'_> ], |inspector| { let mut out = None; - if let Some(output) = inspector.call(ecx, call) - && output.result.result != InstructionResult::Continue - { + if let Some(output) = inspector.call(ecx, call) { out = Some(Some(output)); } out @@ -902,9 +900,7 @@ impl Inspector> for InspectorStackRefMut<'_> } } - if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) - && output.result.result != InstructionResult::Continue - { + if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) { return Some(output); } } @@ -912,7 +908,7 @@ impl Inspector> for InspectorStackRefMut<'_> if self.enable_isolation && !self.in_inner_context && ecx.journaled_state.depth == 1 { match call.scheme { // Isolate CALLs - CallScheme::Call | CallScheme::ExtCall => { + CallScheme::Call => { let input = call.input.bytes(ecx); let (result, _) = self.transact_inner( ecx, @@ -928,7 +924,7 @@ impl Inspector> for InspectorStackRefMut<'_> }); } // Mark accounts and storage cold before STATICCALLs - CallScheme::StaticCall | CallScheme::ExtStaticCall => { + CallScheme::StaticCall => { let JournaledState { state, warm_preloaded_addresses, .. } = &mut ecx.journaled_state.inner; for (addr, acc_mut) in state { @@ -949,7 +945,7 @@ impl Inspector> for InspectorStackRefMut<'_> } } // Process other variants as usual - CallScheme::CallCode | CallScheme::DelegateCall | CallScheme::ExtDelegateCall => {} + CallScheme::CallCode | CallScheme::DelegateCall => {} } } diff --git a/crates/evm/traces/src/decoder/mod.rs b/crates/evm/traces/src/decoder/mod.rs index 5f5ebd18575b1..616e78acbdad3 100644 --- a/crates/evm/traces/src/decoder/mod.rs +++ b/crates/evm/traces/src/decoder/mod.rs @@ -396,7 +396,7 @@ impl CallTraceDecoder { && (!cdata.is_empty() || !self.receive_contracts.contains(&trace.address)) { let return_data = if !trace.success { - let revert_msg = self.revert_decoder.decode(&trace.output, Some(trace.status)); + let revert_msg = self.revert_decoder.decode(&trace.output, trace.status); if trace.output.is_empty() || revert_msg.contains("EvmError: Revert") { Some(format!( @@ -667,7 +667,14 @@ impl CallTraceDecoder { /// The default decoded return data for a trace. fn default_return_data(&self, trace: &CallTrace) -> Option { - (!trace.success).then(|| self.revert_decoder.decode(&trace.output, Some(trace.status))) + // For calls with status None or successful status, don't decode revert data + // This is due to trace.status is derived from the revm_interpreter::InstructionResult in + // revm-inspectors status will `None` post revm 27, as `InstructionResult::Continue` does + // not exists anymore. + if trace.status.is_none() || trace.status.is_some_and(|s| s.is_ok()) { + return None; + } + (!trace.success).then(|| self.revert_decoder.decode(&trace.output, trace.status)) } /// Decodes an event. diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 8d2d42bdd8736..bcfaca725678e 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -101,7 +101,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { sender: "00a329c0648769A73afAc7F9381D08FB43dBEA72".parse().unwrap(), tx_origin: "00a329c0648769A73afAc7F9F81E08FB43dBEA72".parse().unwrap(), initial_balance: U256::from(0xffffffffffffffffffffffffu128), - block_number: 10, + block_number: U256::from(10), fork_block_number: Some(200), chain: Some(9999.into()), gas_limit: 99_000_000u64.into(), @@ -109,7 +109,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { gas_price: Some(999), block_base_fee_per_gas: 10, block_coinbase: Address::random(), - block_timestamp: 10, + block_timestamp: U256::from(10), block_difficulty: 10, block_prevrandao: B256::random(), block_gas_limit: Some(100u64.into()), diff --git a/crates/forge/tests/cli/script.rs b/crates/forge/tests/cli/script.rs index 76413e528a02e..1339a6ec9791e 100644 --- a/crates/forge/tests/cli/script.rs +++ b/crates/forge/tests/cli/script.rs @@ -2665,10 +2665,7 @@ forgetest_init!(should_revert_on_address_opcode, |prj, cmd| { .unwrap(); cmd.arg("script").arg("ScriptWithAddress").assert_failure().stderr_eq(str![[r#" -... -Error: Usage of `address(this)` detected in script contract. Script contracts are ephemeral and their addresses should not be relied upon. -Error: script failed: -... +Error: script failed: Usage of `address(this)` detected in script contract. Script contracts are ephemeral and their addresses should not be relied upon. "#]]); diff --git a/crates/forge/tests/it/repros.rs b/crates/forge/tests/it/repros.rs index 0a56eddaabde6..b9ed5a2711916 100644 --- a/crates/forge/tests/it/repros.rs +++ b/crates/forge/tests/it/repros.rs @@ -272,7 +272,7 @@ test_repro!(6501, false, None, |res| { assert_eq!(test_call.idx, 0); assert_eq!(test_call.children, [1, 2, 3]); assert_eq!(test_call.trace.depth, 0); - assert!(test_call.trace.success); + assert!(!test_call.trace.is_error()); let expected = [ ("log(string)", vec!["\"a\""]), @@ -286,7 +286,7 @@ test_repro!(6501, false, None, |res| { assert_eq!(trace.address, HARDHAT_CONSOLE_ADDRESS); assert_eq!(decoded.label, Some("console".into())); assert_eq!(trace.depth, 1); - assert!(trace.success); + assert!(!trace.is_error()); assert_eq!( decoded.call_data, Some(DecodedCallData { diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 6512987509856..87723919e400f 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -100,8 +100,8 @@ impl ForgeTestProfile { config.gas_limit = u64::MAX.into(); config.chain = None; config.tx_origin = CALLER; - config.block_number = 1; - config.block_timestamp = 1; + config.block_number = U256::from(1); + config.block_timestamp = U256::from(1); config.sender = CALLER; config.initial_balance = U256::MAX; diff --git a/crates/script/src/runner.rs b/crates/script/src/runner.rs index 4ce766555bfce..2af31ef752e55 100644 --- a/crates/script/src/runner.rs +++ b/crates/script/src/runner.rs @@ -359,7 +359,7 @@ impl ScriptRunner { value: U256, ) -> Result { let mut gas_used = res.gas_used; - if matches!(res.exit_reason, return_ok!()) { + if matches!(res.exit_reason, Some(return_ok!())) { // Store the current gas limit and reset it later. let init_gas_limit = self.executor.env().tx.gas_limit; @@ -371,9 +371,9 @@ impl ScriptRunner { self.executor.env_mut().tx.gas_limit = mid_gas_limit; let res = self.executor.call_raw(from, to, calldata.0.clone().into(), value)?; match res.exit_reason { - InstructionResult::Revert - | InstructionResult::OutOfGas - | InstructionResult::OutOfFunds => { + Some(InstructionResult::Revert) + | Some(InstructionResult::OutOfGas) + | Some(InstructionResult::OutOfFunds) => { lowest_gas_limit = mid_gas_limit; } _ => { diff --git a/crates/script/src/simulate.rs b/crates/script/src/simulate.rs index c9d53addb2496..a98dd1e0f6dbe 100644 --- a/crates/script/src/simulate.rs +++ b/crates/script/src/simulate.rs @@ -11,7 +11,7 @@ use crate::{ }; use alloy_chains::NamedChain; use alloy_network::TransactionBuilder; -use alloy_primitives::{Address, Bytes, TxKind, map::HashMap, utils::format_units}; +use alloy_primitives::{Address, Bytes, TxKind, U256, map::HashMap, utils::format_units}; use dialoguer::Confirm; use eyre::{Context, Result}; use forge_script_sequence::{ScriptSequence, TransactionWithMetadata}; @@ -139,7 +139,7 @@ impl PreSimulationState { // Simulate mining the transaction if the user passes `--slow`. if self.args.slow { - runner.executor.env_mut().evm_env.block_env.number += 1; + runner.executor.env_mut().evm_env.block_env.number += U256::from(1); } let is_noop_tx = if let Some(to) = to { diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index f9f552f689208..f94daf8c4e4af 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -247,7 +247,7 @@ impl VerifyBytecodeArgs { ) .await?; - env.evm_env.block_env.number = 0; + env.evm_env.block_env.number = U256::ZERO; let genesis_block = provider.get_block(gen_blk_num.into()).full().await?; // Setup genesis tx and env. @@ -465,7 +465,7 @@ impl VerifyBytecodeArgs { evm_opts, ) .await?; - env.evm_env.block_env.number = simulation_block; + env.evm_env.block_env.number = U256::from(simulation_block); let block = provider.get_block(simulation_block.into()).full().await?; // Workaround for the NonceTooHigh issue as we're not simulating prior txs of the same diff --git a/crates/verify/src/utils.rs b/crates/verify/src/utils.rs index 71fd2dbea39a3..4e98e55053f62 100644 --- a/crates/verify/src/utils.rs +++ b/crates/verify/src/utils.rs @@ -1,6 +1,6 @@ use crate::{bytecode::VerifyBytecodeArgs, types::VerificationType}; use alloy_dyn_abi::DynSolValue; -use alloy_primitives::{Address, Bytes, TxKind}; +use alloy_primitives::{Address, Bytes, TxKind, U256}; use alloy_provider::{Provider, network::AnyRpcBlock}; use alloy_rpc_types::BlockId; use clap::ValueEnum; @@ -328,7 +328,7 @@ pub async fn get_tracing_executor( } pub fn configure_env_block(env: &mut EnvMut<'_>, block: &AnyRpcBlock) { - env.block.timestamp = block.header.timestamp; + env.block.timestamp = U256::from(block.header.timestamp); env.block.beneficiary = block.header.beneficiary; env.block.difficulty = block.header.difficulty; env.block.prevrandao = Some(block.header.mix_hash.unwrap_or_default());