diff --git a/Cargo.lock b/Cargo.lock index a6c101fc7ecd3..71814f3a29e33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2797,37 +2797,129 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", - "alloy-sol-types", - "auto_impl", "bytes", "const-hex", "ethers", "eyre", "foundry-abi", - "foundry-block-explorers", "foundry-common", "foundry-compilers", "foundry-config", + "foundry-evm-coverage", + "foundry-evm-executors", + "foundry-evm-fuzz", + "foundry-evm-traces", "foundry-macros", "foundry-utils", - "futures", "hashbrown 0.14.2", "itertools 0.11.0", "jsonpath_lib", "once_cell", - "ordered-float", "parking_lot", "proptest", "revm", + "serde", + "serde_json", + "thiserror", + "tracing", + "walkdir", +] + +[[package]] +name = "foundry-evm-coverage" +version = "0.2.0" +dependencies = [ + "alloy-primitives", + "eyre", + "foundry-common", + "foundry-compilers", + "foundry-evm-executors", + "revm", "semver 1.0.20", + "tracing", +] + +[[package]] +name = "foundry-evm-executors" +version = "0.2.0" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-types", + "auto_impl", + "const-hex", + "ethers", + "eyre", + "foundry-abi", + "foundry-common", + "foundry-compilers", + "foundry-config", + "foundry-utils", + "futures", + "itertools 0.11.0", + "once_cell", + "parking_lot", + "revm", "serde", "serde_json", - "tempfile", "thiserror", "tokio", "tracing", "url", - "walkdir", +] + +[[package]] +name = "foundry-evm-fuzz" +version = "0.2.0" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "ethers", + "eyre", + "foundry-common", + "foundry-compilers", + "foundry-config", + "foundry-evm-coverage", + "foundry-evm-executors", + "foundry-evm-traces", + "foundry-utils", + "hashbrown 0.14.2", + "parking_lot", + "proptest", + "revm", + "serde", + "thiserror", + "tracing", +] + +[[package]] +name = "foundry-evm-traces" +version = "0.2.0" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "const-hex", + "ethers", + "eyre", + "foundry-block-explorers", + "foundry-common", + "foundry-compilers", + "foundry-config", + "foundry-evm-executors", + "foundry-utils", + "futures", + "hashbrown 0.14.2", + "itertools 0.11.0", + "once_cell", + "ordered-float", + "revm", + "serde", + "tempfile", + "tokio", + "tracing", "yansi 0.5.1", ] @@ -2878,6 +2970,7 @@ name = "foundry-utils" version = "0.2.0" dependencies = [ "alloy-dyn-abi", + "alloy-json-abi", "alloy-primitives", "dunce", "ethers-addressbook", diff --git a/Cargo.toml b/Cargo.toml index 72636473ad957..50d15728d76fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,11 +130,18 @@ foundry-common = { path = "crates/common" } foundry-cheatcodes = { path = "crates/cheatcodes" } foundry-config = { path = "crates/config" } foundry-evm = { path = "crates/evm" } +foundry-evm-coverage = { path = "crates/evm-coverage" } +foundry-evm-executors = { path = "crates/evm-executors" } +foundry-evm-fuzz = { path = "crates/evm-fuzz" } +foundry-evm-traces = { path = "crates/evm-traces" } foundry-macros = { path = "crates/macros" } foundry-test-utils = { path = "crates/test-utils" } foundry-utils = { path = "crates/utils" } foundry-debugger = { path = "crates/debugger" } +foundry-compilers = { version = "0.1", default-features = false } +foundry-block-explorers = { version = "0.1", default-features = false } + ## revm # no default features to avoid c-kzg revm = { version = "3", default-features = false } # @@ -159,11 +166,6 @@ alloy-json-abi = "0.4.1" alloy-sol-types = "0.4.1" syn-solidity = "0.4.1" -# solc utils -foundry-compilers = { version = "0.1", default-features = false } -# block explorer utils -foundry-block-explorers = { version = "0.1", default-features = false } - solang-parser = "=0.3.2" ## misc @@ -204,7 +206,7 @@ ethers-etherscan = { git = "https://github.com/gakonst/ethers-rs", rev = "9754f2 ethers-solc = { git = "https://github.com/gakonst/ethers-rs", rev = "9754f22d06a2defe5608c4c9b809cc361443a3dc" } foundry-compilers = { git = "https://github.com/foundry-rs/compilers" } -foundry-block-explorers = { git = "https://github.com/foundry-rs/block-explorers"} +foundry-block-explorers = { git = "https://github.com/foundry-rs/block-explorers" } alloy-dyn-abi = { git = "https://github.com/alloy-rs/core/" } alloy-primitives = { git = "https://github.com/alloy-rs/core/" } diff --git a/crates/anvil/core/src/eth/subscription.rs b/crates/anvil/core/src/eth/subscription.rs index c363f52ffc2bf..791bac2526951 100644 --- a/crates/anvil/core/src/eth/subscription.rs +++ b/crates/anvil/core/src/eth/subscription.rs @@ -103,7 +103,7 @@ impl fmt::Display for SubscriptionId { } impl fmt::Debug for SubscriptionId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { SubscriptionId::Number(num) => num.fmt(f), SubscriptionId::String(s) => s.fmt(f), diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index 6cbd7e8a464a9..4edc89d08a541 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -14,7 +14,7 @@ use ethers_core::{ rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}, }, }; -use foundry_evm::trace::CallTraceArena; +use foundry_evm::traces::CallTraceArena; use foundry_utils::types::ToAlloy; use revm::{ interpreter::InstructionResult, diff --git a/crates/anvil/core/src/types.rs b/crates/anvil/core/src/types.rs index 8725b9f4a384d..0488bbe12c9f3 100644 --- a/crates/anvil/core/src/types.rs +++ b/crates/anvil/core/src/types.rs @@ -129,7 +129,7 @@ impl<'a> serde::Deserialize<'a> for Index { impl<'a> serde::de::Visitor<'a> for IndexVisitor { type Value = Index; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "hex-encoded or decimal index") } diff --git a/crates/anvil/rpc/src/error.rs b/crates/anvil/rpc/src/error.rs index eb95f8413ecab..427acd384ced4 100644 --- a/crates/anvil/rpc/src/error.rs +++ b/crates/anvil/rpc/src/error.rs @@ -69,7 +69,7 @@ impl RpcError { } impl fmt::Display for RpcError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}: {}", self.code.message(), self.message) } } diff --git a/crates/anvil/src/config.rs b/crates/anvil/src/config.rs index 265fbd972650f..151282e417261 100644 --- a/crates/anvil/src/config.rs +++ b/crates/anvil/src/config.rs @@ -32,9 +32,9 @@ use foundry_common::{ }; use foundry_config::Config; use foundry_evm::{ - executor::{ + executors::{ fork::{BlockchainDb, BlockchainDbMeta, SharedBackend}, - inspector::DEFAULT_CREATE2_DEPLOYER, + DEFAULT_CREATE2_DEPLOYER, }, revm, revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv, U256 as rU256}, diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 313ecfb4efb9a..35219b7d364ad 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -59,8 +59,9 @@ use ethers::{ }; use foundry_common::ProviderBuilder; use foundry_evm::{ - executor::{backend::DatabaseError, DatabaseRef}, + executors::backend::DatabaseError, revm::{ + db::DatabaseRef, interpreter::{return_ok, return_revert, InstructionResult}, primitives::BlockEnv, }, diff --git a/crates/anvil/src/eth/backend/db.rs b/crates/anvil/src/eth/backend/db.rs index ff924eff848ca..aa6494f2b03a3 100644 --- a/crates/anvil/src/eth/backend/db.rs +++ b/crates/anvil/src/eth/backend/db.rs @@ -10,17 +10,16 @@ use ethers::{ }; use foundry_common::errors::FsPathError; use foundry_evm::{ - executor::{ - backend::{snapshot::StateSnapshot, DatabaseError, DatabaseResult, MemDb}, + executors::{ + backend::{DatabaseError, DatabaseResult, MemDb, StateSnapshot}, fork::BlockchainDb, - DatabaseRef, }, + hashbrown::HashMap, revm::{ - db::{CacheDB, DbAccount}, + db::{CacheDB, DatabaseRef, DbAccount}, primitives::{Bytecode, KECCAK_EMPTY}, Database, DatabaseCommit, }, - HashMap, }; use foundry_utils::types::ToAlloy; use hash_db::HashDB; diff --git a/crates/anvil/src/eth/backend/executor.rs b/crates/anvil/src/eth/backend/executor.rs index 8cbf6381163df..fc9cff994a57f 100644 --- a/crates/anvil/src/eth/backend/executor.rs +++ b/crates/anvil/src/eth/backend/executor.rs @@ -18,13 +18,13 @@ use ethers::{ utils::rlp, }; use foundry_evm::{ - executor::backend::DatabaseError, + executors::backend::DatabaseError, revm, revm::{ interpreter::InstructionResult, primitives::{BlockEnv, CfgEnv, EVMError, Env, ExecutionResult, Output, SpecId}, }, - trace::{node::CallTraceNode, CallTraceArena}, + traces::{node::CallTraceNode, CallTraceArena}, utils::{eval_to_instruction_result, halt_to_instruction_result}, }; use foundry_utils::types::{ToAlloy, ToEthers}; diff --git a/crates/anvil/src/eth/backend/genesis.rs b/crates/anvil/src/eth/backend/genesis.rs index ade5ac6f5c79e..e1a4a2ceca24b 100644 --- a/crates/anvil/src/eth/backend/genesis.rs +++ b/crates/anvil/src/eth/backend/genesis.rs @@ -10,11 +10,11 @@ use ethers::{ types::{Address, H256}, }; use foundry_evm::{ - executor::{ - backend::{snapshot::StateSnapshot, DatabaseError, DatabaseResult}, - DatabaseRef, + executors::backend::{DatabaseError, DatabaseResult, StateSnapshot}, + revm::{ + db::DatabaseRef, + primitives::{AccountInfo, Bytecode, KECCAK_EMPTY}, }, - revm::primitives::{AccountInfo, Bytecode, KECCAK_EMPTY}, }; use foundry_utils::types::{ToAlloy, ToEthers}; use parking_lot::Mutex; diff --git a/crates/anvil/src/eth/backend/mem/cache.rs b/crates/anvil/src/eth/backend/mem/cache.rs index 0703cb78fb5ec..649f1c4ba6377 100644 --- a/crates/anvil/src/eth/backend/mem/cache.rs +++ b/crates/anvil/src/eth/backend/mem/cache.rs @@ -1,6 +1,6 @@ use crate::config::anvil_tmp_dir; use ethers::prelude::H256; -use foundry_evm::executor::backend::snapshot::StateSnapshot; +use foundry_evm::executors::backend::StateSnapshot; use std::{ io, path::{Path, PathBuf}, diff --git a/crates/anvil/src/eth/backend/mem/fork_db.rs b/crates/anvil/src/eth/backend/mem/fork_db.rs index 8039442a4286b..bba62fc4a1095 100644 --- a/crates/anvil/src/eth/backend/mem/fork_db.rs +++ b/crates/anvil/src/eth/backend/mem/fork_db.rs @@ -7,16 +7,17 @@ use crate::{ Address, U256, }; use ethers::{prelude::H256, types::BlockId}; -pub use foundry_evm::executor::fork::database::ForkedDatabase; use foundry_evm::{ - executor::{ - backend::{snapshot::StateSnapshot, DatabaseResult}, + executors::{ + backend::{DatabaseResult, StateSnapshot}, fork::{database::ForkDbSnapshot, BlockchainDb}, }, revm::Database, }; use foundry_utils::types::{ToAlloy, ToEthers}; +pub use foundry_evm::executors::fork::database::ForkedDatabase; + /// Implement the helper for the fork database impl Db for ForkedDatabase { fn insert_account(&mut self, address: Address, account: AccountInfo) { diff --git a/crates/anvil/src/eth/backend/mem/in_memory_db.rs b/crates/anvil/src/eth/backend/mem/in_memory_db.rs index ebba405fc806c..62cf70ff222dd 100644 --- a/crates/anvil/src/eth/backend/mem/in_memory_db.rs +++ b/crates/anvil/src/eth/backend/mem/in_memory_db.rs @@ -5,21 +5,20 @@ use crate::{ AsHashDB, Db, MaybeForkedDatabase, MaybeHashDatabase, SerializableAccountRecord, SerializableState, StateDb, }, - mem::state::{state_merkle_trie_root, trie_hash_db}, + mem::state::{state_merkle_trie_root, storage_trie_db, trie_hash_db}, revm::primitives::AccountInfo, Address, U256, }; use ethers::{prelude::H256, types::BlockId}; +use foundry_evm::executors::{ + backend::{DatabaseResult, StateSnapshot}, + fork::BlockchainDb, +}; use foundry_utils::types::{ToAlloy, ToEthers}; use tracing::{trace, warn}; // reexport for convenience -use crate::mem::state::storage_trie_db; -pub use foundry_evm::executor::{backend::MemDb, DatabaseRef}; -use foundry_evm::executor::{ - backend::{snapshot::StateSnapshot, DatabaseResult}, - fork::BlockchainDb, -}; +pub use foundry_evm::{executors::backend::MemDb, revm::db::DatabaseRef}; impl Db for MemDb { fn insert_account(&mut self, address: Address, account: AccountInfo) { @@ -135,6 +134,7 @@ impl MaybeForkedDatabase for MemDb { #[cfg(test)] mod tests { + use super::*; use crate::{ eth::backend::db::{Db, SerializableAccountRecord, SerializableState}, revm::primitives::AccountInfo, @@ -143,7 +143,7 @@ mod tests { use alloy_primitives::{Bytes, U256 as rU256}; use ethers::types::U256; use foundry_evm::{ - executor::{backend::MemDb, DatabaseRef}, + executors::backend::MemDb, revm::primitives::{Bytecode, KECCAK_EMPTY}, }; use foundry_utils::types::ToAlloy; diff --git a/crates/anvil/src/eth/backend/mem/inspector.rs b/crates/anvil/src/eth/backend/mem/inspector.rs index ee46ea101d14c..a4e2d0d94f4db 100644 --- a/crates/anvil/src/eth/backend/mem/inspector.rs +++ b/crates/anvil/src/eth/backend/mem/inspector.rs @@ -5,7 +5,7 @@ use ethers::types::Log; use foundry_evm::{ call_inspectors, decode::decode_console_logs, - executor::inspector::{LogCollector, Tracer}, + inspectors::{LogCollector, Tracer}, revm, revm::{ interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}, diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index a8cd2b9e5a7c1..456d874c5b1fc 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -59,11 +59,11 @@ use flate2::{read::GzDecoder, write::GzEncoder, Compression}; use foundry_common::abi::format_token; use foundry_evm::{ decode::{decode_custom_error_args, decode_revert}, - executor::{ + executors::{ backend::{DatabaseError, DatabaseResult}, - inspector::AccessListTracer, DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE, }, + inspectors::AccessListTracer, revm::{ self, db::CacheDB, @@ -103,7 +103,8 @@ pub const MIN_TRANSACTION_GAS: U256 = U256([21_000, 0, 0, 0]); // Gas per transaction creating a contract. pub const MIN_CREATE_GAS: U256 = U256([53_000, 0, 0, 0]); -pub type State = foundry_evm::HashMap; +// TODO: This is the same as foundry_evm::executors::StateChangeset but with ethers H160 +pub type State = foundry_evm::hashbrown::HashMap; /// A block request, which includes the Pool Transactions if it's Pending #[derive(Debug)] diff --git a/crates/anvil/src/eth/backend/mem/state.rs b/crates/anvil/src/eth/backend/mem/state.rs index c0d5271ab85bd..8e3c23a2d5124 100644 --- a/crates/anvil/src/eth/backend/mem/state.rs +++ b/crates/anvil/src/eth/backend/mem/state.rs @@ -8,12 +8,12 @@ use ethers::{ utils::{rlp, rlp::RlpStream}, }; use foundry_evm::{ - executor::{backend::DatabaseError, DatabaseRef}, + executors::backend::DatabaseError, + hashbrown::HashMap as Map, revm::{ - db::{CacheDB, DbAccount}, + db::{CacheDB, DatabaseRef, DbAccount}, primitives::{AccountInfo, Bytecode, Log}, }, - HashMap as Map, }; use foundry_utils::types::{ToAlloy, ToEthers}; use memory_db::HashKey; diff --git a/crates/anvil/src/eth/backend/mem/storage.rs b/crates/anvil/src/eth/backend/mem/storage.rs index 4d693ed060119..d973fbf27ab72 100644 --- a/crates/anvil/src/eth/backend/mem/storage.rs +++ b/crates/anvil/src/eth/backend/mem/storage.rs @@ -425,7 +425,7 @@ mod tests { use crate::eth::backend::db::Db; use ethers::{abi::ethereum_types::BigEndianHash, types::Address}; use foundry_evm::{ - executor::backend::MemDb, + executors::backend::MemDb, revm::{ db::DatabaseRef, primitives::{AccountInfo, U256 as rU256}, diff --git a/crates/anvil/src/eth/error.rs b/crates/anvil/src/eth/error.rs index 3fa9e6c732d35..8e48511b02ab7 100644 --- a/crates/anvil/src/eth/error.rs +++ b/crates/anvil/src/eth/error.rs @@ -13,7 +13,7 @@ use ethers::{ }; use foundry_common::SELECTOR_LEN; use foundry_evm::{ - executor::backend::DatabaseError, + executors::backend::DatabaseError, revm::{ self, interpreter::InstructionResult, diff --git a/crates/anvil/src/eth/fees.rs b/crates/anvil/src/eth/fees.rs index 5b1e54679a4fc..97acabb1df36a 100644 --- a/crates/anvil/src/eth/fees.rs +++ b/crates/anvil/src/eth/fees.rs @@ -405,7 +405,7 @@ impl FeeDetails { } impl fmt::Debug for FeeDetails { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "Fees {{ ")?; write!(fmt, "gaPrice: {:?}, ", self.gas_price)?; write!(fmt, "max_fee_per_gas: {:?}, ", self.max_fee_per_gas)?; diff --git a/crates/anvil/src/eth/otterscan/types.rs b/crates/anvil/src/eth/otterscan/types.rs index bd2567d749a56..3f49d0df1ce87 100644 --- a/crates/anvil/src/eth/otterscan/types.rs +++ b/crates/anvil/src/eth/otterscan/types.rs @@ -2,7 +2,7 @@ use alloy_primitives::U256 as rU256; use ethers::types::{ Action, Address, Block, Bytes, CallType, Trace, Transaction, TransactionReceipt, H256, U256, }; -use foundry_evm::{executor::InstructionResult, CallKind}; +use foundry_evm::{revm::interpreter::InstructionResult, utils::CallKind}; use foundry_utils::types::ToEthers; use futures::future::join_all; use serde::{de::DeserializeOwned, Serialize}; diff --git a/crates/anvil/src/eth/pool/mod.rs b/crates/anvil/src/eth/pool/mod.rs index ee43d8556eda8..842015837d71e 100644 --- a/crates/anvil/src/eth/pool/mod.rs +++ b/crates/anvil/src/eth/pool/mod.rs @@ -358,7 +358,7 @@ pub struct PruneResult { } impl fmt::Debug for PruneResult { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "PruneResult {{ ")?; write!( fmt, diff --git a/crates/anvil/src/eth/pool/transactions.rs b/crates/anvil/src/eth/pool/transactions.rs index 39b0d1cc1fe13..d0c0337c6b0f2 100644 --- a/crates/anvil/src/eth/pool/transactions.rs +++ b/crates/anvil/src/eth/pool/transactions.rs @@ -99,7 +99,7 @@ impl PoolTransaction { } impl fmt::Debug for PoolTransaction { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "Transaction {{ ")?; write!(fmt, "hash: {:?}, ", &self.pending_transaction.hash())?; write!(fmt, "requires: [{}], ", hex_fmt_many(self.requires.iter()))?; @@ -283,7 +283,7 @@ impl PendingPoolTransaction { } impl fmt::Debug for PendingPoolTransaction { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "PendingTransaction {{ ")?; write!(fmt, "added_at: {:?}, ", self.added_at)?; write!(fmt, "tx: {:?}, ", self.transaction)?; diff --git a/crates/anvil/src/eth/util.rs b/crates/anvil/src/eth/util.rs index 078971c577884..5fdf3dc6ae1c4 100644 --- a/crates/anvil/src/eth/util.rs +++ b/crates/anvil/src/eth/util.rs @@ -31,7 +31,7 @@ impl<'a> HexDisplay<'a> { } impl<'a> fmt::Display for HexDisplay<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.0.len() < 1027 { for byte in self.0 { f.write_fmt(format_args!("{byte:02x}"))?; @@ -50,7 +50,7 @@ impl<'a> fmt::Display for HexDisplay<'a> { } impl<'a> fmt::Debug for HexDisplay<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for byte in self.0 { f.write_fmt(format_args!("{byte:02x}"))?; } diff --git a/crates/cast/bin/cmd/call.rs b/crates/cast/bin/cmd/call.rs index 30c8fa4c20f88..9c1b2b58d6830 100644 --- a/crates/cast/bin/cmd/call.rs +++ b/crates/cast/bin/cmd/call.rs @@ -10,7 +10,7 @@ use foundry_cli::{ use foundry_common::runtime_client::RuntimeClient; use foundry_compilers::EvmVersion; use foundry_config::{find_project_root_path, Config}; -use foundry_evm::{executor::opts::EvmOpts, trace::TracingExecutor}; +use foundry_evm::executors::{opts::EvmOpts, TracingExecutor}; use foundry_utils::types::{ToAlloy, ToEthers}; use std::str::FromStr; @@ -151,7 +151,7 @@ impl CallArgs { TracingExecutor::get_fork_material(&config, evm_opts).await?; let mut executor = - foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) + foundry_evm::executors::TracingExecutor::new(env, fork, evm_version, debug) .await; let trace = match executor.deploy( @@ -186,7 +186,7 @@ impl CallArgs { TracingExecutor::get_fork_material(&config, evm_opts).await?; let mut executor = - foundry_evm::trace::TracingExecutor::new(env, fork, evm_version, debug) + foundry_evm::executors::TracingExecutor::new(env, fork, evm_version, debug) .await; let (tx, _) = builder.build(); diff --git a/crates/cast/bin/cmd/run.rs b/crates/cast/bin/cmd/run.rs index 2ba8016ff1250..d628d1ea379e0 100644 --- a/crates/cast/bin/cmd/run.rs +++ b/crates/cast/bin/cmd/run.rs @@ -1,3 +1,4 @@ +use alloy_primitives::U256; use clap::Parser; use ethers::prelude::Middleware; use eyre::{Result, WrapErr}; @@ -11,9 +12,8 @@ use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use foundry_compilers::EvmVersion; use foundry_config::{find_project_root_path, Config}; use foundry_evm::{ - executor::{inspector::cheatcodes::util::configure_tx_env, opts::EvmOpts, EvmError}, - revm::primitives::U256 as rU256, - trace::TracingExecutor, + executors::{opts::EvmOpts, EvmError, TracingExecutor}, + utils::configure_tx_env, }; use foundry_utils::types::ToAlloy; use tracing::trace; @@ -121,7 +121,7 @@ impl RunArgs { let mut executor = TracingExecutor::new(env.clone(), fork, self.evm_version, self.debug).await; - env.block.number = rU256::from(tx_block_number); + env.block.number = U256::from(tx_block_number); let block = provider.get_block_with_txs(tx_block_number).await?; if let Some(ref block) = block { diff --git a/crates/chisel/Cargo.toml b/crates/chisel/Cargo.toml index 51f9930786e78..21314dafd8496 100644 --- a/crates/chisel/Cargo.toml +++ b/crates/chisel/Cargo.toml @@ -26,7 +26,7 @@ foundry-common.workspace = true foundry-utils.workspace = true forge-fmt.workspace = true -foundry-compilers = { workspace = true, default-features = false, features = ["project-util", "full"]} +foundry-compilers = { workspace = true, features = ["project-util", "full"] } # ethers ethers.workspace = true diff --git a/crates/chisel/benches/session_source.rs b/crates/chisel/benches/session_source.rs index 784de2c4b03df..38f0c04b75140 100644 --- a/crates/chisel/benches/session_source.rs +++ b/crates/chisel/benches/session_source.rs @@ -2,7 +2,7 @@ use chisel::session_source::{SessionSource, SessionSourceConfig}; use criterion::{criterion_group, Criterion}; use foundry_compilers::Solc; use foundry_config::Config; -use foundry_evm::executor::opts::EvmOpts; +use foundry_evm::executors::opts::EvmOpts; use once_cell::sync::Lazy; use std::hint::black_box; use tokio::runtime::Runtime; diff --git a/crates/chisel/src/dispatcher.rs b/crates/chisel/src/dispatcher.rs index db8950ca1323d..927cc2e2f6089 100644 --- a/crates/chisel/src/dispatcher.rs +++ b/crates/chisel/src/dispatcher.rs @@ -13,7 +13,7 @@ use forge_fmt::FormatterConfig; use foundry_config::{Config, RpcEndpoint}; use foundry_evm::{ decode::decode_console_logs, - trace::{ + traces::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, }, diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 5979743bcbc50..af0ead61d48b2 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -13,7 +13,8 @@ use eyre::{Result, WrapErr}; use foundry_compilers::Artifact; use foundry_evm::{ decode::decode_console_logs, - executor::{inspector::CheatsConfig, Backend, ExecutorBuilder}, + executors::{Backend, ExecutorBuilder}, + inspectors::CheatsConfig, }; use foundry_utils::types::ToEthers; use solang_parser::pt::{self, CodeLocation}; diff --git a/crates/chisel/src/runner.rs b/crates/chisel/src/runner.rs index 043cde85c7bc3..cb0ed68452512 100644 --- a/crates/chisel/src/runner.rs +++ b/crates/chisel/src/runner.rs @@ -7,8 +7,8 @@ use alloy_primitives::{Address, Bytes, U256}; use ethers::types::Log; use eyre::Result; use foundry_evm::{ - executor::{DeployResult, Executor, RawCallResult}, - trace::{CallTraceArena, TraceKind}, + executors::{DeployResult, Executor, RawCallResult}, + traces::{CallTraceArena, TraceKind}, }; use revm::interpreter::{return_ok, InstructionResult}; use std::collections::BTreeMap; diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index 5fbb0a1e104a8..08ab4cd8f0a65 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -11,7 +11,7 @@ use foundry_compilers::{ CompilerInput, CompilerOutput, EvmVersion, Solc, }; use foundry_config::{Config, SolcReq}; -use foundry_evm::executor::{opts::EvmOpts, Backend}; +use foundry_evm::executors::{opts::EvmOpts, Backend}; use semver::Version; use serde::{Deserialize, Serialize}; use solang_parser::pt; diff --git a/crates/chisel/tests/cache.rs b/crates/chisel/tests/cache.rs index 7ea13c64de986..d51a4fd32600d 100644 --- a/crates/chisel/tests/cache.rs +++ b/crates/chisel/tests/cache.rs @@ -1,7 +1,7 @@ use chisel::session::ChiselSession; use foundry_compilers::EvmVersion; use foundry_config::Config; -use foundry_evm::executor::opts::EvmOpts; +use foundry_evm::executors::opts::EvmOpts; use serial_test::serial; use std::path::Path; diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index d0b253499d9fc..f0f314edd3f2c 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -17,7 +17,7 @@ foundry-evm.workspace = true foundry-debugger.workspace = true foundry-utils.workspace = true -foundry-compilers = { workspace = true, default-features = false, features = ["full"] } +foundry-compilers = { workspace = true, features = ["full"] } # aws rusoto_core = { version = "0.48", default-features = false } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index e43c0e2968608..890d0dd3c8f90 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -14,8 +14,8 @@ use foundry_config::{error::ExtractConfigError, figment::Figment, Chain as Confi use foundry_debugger::DebuggerArgs; use foundry_evm::{ debug::DebugArena, - executor::{opts::EvmOpts, DeployResult, EvmError, ExecutionErr, RawCallResult}, - trace::{ + executors::{opts::EvmOpts, DeployResult, EvmError, ExecutionErr, RawCallResult}, + traces::{ identifier::{EtherscanIdentifier, SignaturesIdentifier}, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces, }, diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index e2ab1df43f31c..f12289aaa459b 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -24,7 +24,8 @@ foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } alloy-json-abi.workspace = true -foundry-compilers = { workspace = true, default-features = false } +foundry-compilers.workspace = true + # io reqwest = { version = "0.11", default-features = false } diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index a08287beac1d2..a990035244d33 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -203,7 +203,7 @@ impl SizeReport { } impl Display for SizeReport { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { let mut table = Table::new(); table.load_preset(ASCII_MARKDOWN); table.set_header(vec![ diff --git a/crates/debugger/src/debugger.rs b/crates/debugger/src/debugger.rs index eb59780194b0f..e65556bd5f24a 100644 --- a/crates/debugger/src/debugger.rs +++ b/crates/debugger/src/debugger.rs @@ -1,6 +1,6 @@ use crate::{TUIExitReason, Tui, Ui}; use foundry_common::{compile::ContractSources, evm::Breakpoints, get_contract_name}; -use foundry_evm::{debug::DebugArena, trace::CallTraceDecoder}; +use foundry_evm::{debug::DebugArena, traces::CallTraceDecoder}; use tracing::{error, trace}; /// Standardized way of firing up the debugger diff --git a/crates/debugger/src/lib.rs b/crates/debugger/src/lib.rs index 7fc74325dcb7f..c6f645e8a2c07 100644 --- a/crates/debugger/src/lib.rs +++ b/crates/debugger/src/lib.rs @@ -13,8 +13,7 @@ use eyre::Result; use foundry_common::{compile::ContractSources, evm::Breakpoints}; use foundry_evm::{ debug::{DebugStep, Instruction}, - utils::{build_pc_ic_map, PCICMap}, - CallKind, + utils::{build_pc_ic_map, CallKind, PCICMap}, }; use ratatui::{ backend::{Backend, CrosstermBackend}, diff --git a/crates/doc/Cargo.toml b/crates/doc/Cargo.toml index fb5f8dcc14f1b..cb0db0d18bfdc 100644 --- a/crates/doc/Cargo.toml +++ b/crates/doc/Cargo.toml @@ -16,11 +16,11 @@ forge-fmt.workspace = true foundry-config.workspace = true foundry-utils.workspace = true +foundry-compilers = { workspace = true, features = ["async"] } + # ethers ethers-core.workspace = true -foundry-compilers = { workspace = true, features = ["async"] } - # tracing tracing.workspace = true diff --git a/crates/evm-coverage/Cargo.toml b/crates/evm-coverage/Cargo.toml new file mode 100644 index 0000000000000..4e9404873996d --- /dev/null +++ b/crates/evm-coverage/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "foundry-evm-coverage" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +foundry-common.workspace = true +foundry-compilers.workspace = true +foundry-evm-executors.workspace = true + +alloy-primitives.workspace = true +eyre = "0.6" +revm.workspace = true +semver = "1" +tracing = "0.1" diff --git a/crates/evm/src/coverage/analysis.rs b/crates/evm-coverage/src/analysis.rs similarity index 100% rename from crates/evm/src/coverage/analysis.rs rename to crates/evm-coverage/src/analysis.rs diff --git a/crates/evm/src/coverage/anchors.rs b/crates/evm-coverage/src/anchors.rs similarity index 99% rename from crates/evm/src/coverage/anchors.rs rename to crates/evm-coverage/src/anchors.rs index bbba9544286ce..ae4453f638d5c 100644 --- a/crates/evm/src/coverage/anchors.rs +++ b/crates/evm-coverage/src/anchors.rs @@ -1,7 +1,7 @@ use super::{CoverageItem, CoverageItemKind, ItemAnchor, SourceLocation}; -use crate::utils::ICPCMap; use alloy_primitives::Bytes; use foundry_compilers::sourcemap::{SourceElement, SourceMap}; +use foundry_evm_executors::utils::ICPCMap; use revm::{ interpreter::opcode::{self, spec_opcode_gas}, primitives::SpecId, diff --git a/crates/evm/src/executor/inspector/coverage.rs b/crates/evm-coverage/src/inspector.rs similarity index 94% rename from crates/evm/src/executor/inspector/coverage.rs rename to crates/evm-coverage/src/inspector.rs index 3f06c490e4160..3ee7ec98cce9b 100644 --- a/crates/evm/src/executor/inspector/coverage.rs +++ b/crates/evm-coverage/src/inspector.rs @@ -1,5 +1,5 @@ -use crate::coverage::{HitMap, HitMaps}; -use bytes::Bytes; +use crate::{HitMap, HitMaps}; +use alloy_primitives::Bytes; use revm::{ interpreter::{InstructionResult, Interpreter}, Database, EVMData, Inspector, diff --git a/crates/evm/src/coverage/mod.rs b/crates/evm-coverage/src/lib.rs similarity index 97% rename from crates/evm/src/coverage/mod.rs rename to crates/evm-coverage/src/lib.rs index 7d78e9b519478..0ae3d6ea4ae73 100644 --- a/crates/evm/src/coverage/mod.rs +++ b/crates/evm-coverage/src/lib.rs @@ -1,8 +1,13 @@ -pub mod analysis; -pub mod anchors; +//! # foundry-evm-coverage +//! +//! EVM bytecode coverage analysis. + +#![warn(unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] + +#[macro_use] +extern crate tracing; -use alloy_primitives::B256; -use bytes::Bytes; +use alloy_primitives::{Bytes, B256}; use semver::Version; use std::{ collections::{BTreeMap, HashMap}, @@ -10,6 +15,12 @@ use std::{ ops::{AddAssign, Deref, DerefMut}, }; +pub mod analysis; +pub mod anchors; + +mod inspector; +pub use inspector::CoverageCollector; + /// A coverage report. /// /// A coverage report contains coverage items and opcodes corresponding to those items (called diff --git a/crates/evm-executors/Cargo.toml b/crates/evm-executors/Cargo.toml new file mode 100644 index 0000000000000..ad5983f513a2e --- /dev/null +++ b/crates/evm-executors/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "foundry-evm-executors" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +foundry-abi.workspace = true +foundry-common.workspace = true +foundry-compilers.workspace = true +foundry-config.workspace = true +foundry-utils.workspace = true + +# EVM +revm = { workspace = true, default-features = false, features = [ + "std", + "serde", + "memory_limit", + "optional_eip3607", + "optional_block_gas_limit", + "optional_no_base_fee", + "arbitrary", +] } +alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } +alloy-json-abi = { workspace = true } +alloy-sol-types.workspace = true +ethers = { workspace = true, features = ["ethers-solc"] } + +# Encoding/decoding +serde_json = "1" +serde = "1" +hex.workspace = true + +# Error handling +eyre = "0.6" +thiserror = "1" + +# Logging +tracing = "0.1" + +# Threading/futures +tokio = { version = "1", features = ["time", "macros"] } +parking_lot = "0.12" +futures = "0.3" +once_cell = "1" + +# Misc +url = "2" +auto_impl = "1" +itertools.workspace = true diff --git a/crates/evm-executors/src/abi/mod.rs b/crates/evm-executors/src/abi/mod.rs new file mode 100644 index 0000000000000..b3620e50e5062 --- /dev/null +++ b/crates/evm-executors/src/abi/mod.rs @@ -0,0 +1,573 @@ +//! Several ABI-related utilities for executors. + +use alloy_primitives::Address; +pub use foundry_abi::{ + console::{self, ConsoleEvents, CONSOLE_ABI}, + hardhat_console::{self, HardhatConsoleCalls, HARDHATCONSOLE_ABI as HARDHAT_CONSOLE_ABI}, + hevm::{self, HEVMCalls, HEVM_ABI}, +}; +use once_cell::sync::Lazy; +use std::collections::HashMap; + +/// The cheatcode handler address (0x7109709ECfa91a80626fF3989D68f67F5b1DD12D). +/// +/// This is the same address as the one used in DappTools's HEVM. +/// `address(bytes20(uint160(uint256(keccak256('hevm cheat code')))))` +pub const CHEATCODE_ADDRESS: Address = Address::new([ + 0x71, 0x09, 0x70, 0x9E, 0xcf, 0xa9, 0x1a, 0x80, 0x62, 0x6f, 0xf3, 0x98, 0x9d, 0x68, 0xf6, 0x7f, + 0x5b, 0x1d, 0xd1, 0x2d, +]); + +/// The Hardhat console address (0x000000000000000000636F6e736F6c652e6c6f67). +/// +/// See: https://github.com/nomiclabs/hardhat/blob/master/packages/hardhat-core/console.sol +pub const HARDHAT_CONSOLE_ADDRESS: Address = Address::new([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, + 0x2e, 0x6c, 0x6f, 0x67, +]); + +/// If the input starts with a known `hardhat/console.log` `uint` selector, then this will replace +/// it with the selector `abigen!` bindings expect. +pub fn patch_hardhat_console_selector(input: &mut Vec) { + if input.len() < 4 { + return + } + let selector = unsafe { &mut *(input.get_unchecked_mut(..4) as *mut [u8] as *mut [u8; 4]) }; + if let Some(abigen_selector) = HARDHAT_CONSOLE_SELECTOR_PATCHES.get(selector) { + *selector = *abigen_selector; + } +} + +/// This contains a map with all the `hardhat/console.log` log selectors that use `uint` or `int` +/// as key and the selector of the call with `uint256`, +/// +/// This is a bit terrible but a workaround for the differing selectors used by hardhat and the call +/// bindings which `abigen!` creates. `hardhat/console.log` logs its events in functions that accept +/// `uint` manually as `abi.encodeWithSignature("log(int)", p0)`, but `abigen!` uses `uint256` for +/// its call bindings (`HardhatConsoleCalls`) as generated by solc. +pub static HARDHAT_CONSOLE_SELECTOR_PATCHES: Lazy> = Lazy::new(|| { + HashMap::from([ + // log(bool,uint256,uint256,address) + ([241, 97, 178, 33], [0, 221, 135, 185]), + // log(uint256,address,address,string) + ([121, 67, 220, 102], [3, 28, 111, 115]), + // log(uint256,bool,address,uint256) + ([65, 181, 239, 59], [7, 130, 135, 245]), + // log(bool,address,bool,uint256) + ([76, 182, 15, 209], [7, 131, 21, 2]), + // log(bool,uint256,address) + ([196, 210, 53, 7], [8, 142, 249, 210]), + // log(uint256,address,address,bool) + ([1, 85, 11, 4], [9, 31, 250, 245]), + // log(address,bool,uint256,string) + ([155, 88, 142, 204], [10, 166, 207, 173]), + // log(bool,bool,uint256,uint256) + ([70, 103, 222, 142], [11, 176, 14, 171]), + // log(bool,address,address,uint256) + ([82, 132, 189, 108], [12, 102, 209, 190]), + // log(uint256,address,uint256,uint256) + ([202, 154, 62, 180], [12, 156, 217, 193]), + // log(string,address,uint256) + ([7, 200, 18, 23], [13, 38, 185, 37]), + // log(address,string,uint256,bool) + ([126, 37, 13, 91], [14, 247, 224, 80]), + // log(address,uint256,address,uint256) + ([165, 217, 135, 104], [16, 15, 101, 14]), + // log(string,string,uint256,address) + ([93, 79, 70, 128], [16, 35, 247, 178]), + // log(bool,string,uint256) + ([192, 56, 42, 172], [16, 147, 238, 17]), + // log(bool,bool,uint256) + ([176, 19, 101, 187], [18, 242, 22, 2]), + // log(bool,address,uint256,address) + ([104, 241, 88, 181], [19, 107, 5, 221]), + // log(bool,uint256,address,uint256) + ([202, 165, 35, 106], [21, 55, 220, 135]), + // log(bool,string,uint256,address) + ([91, 34, 185, 56], [21, 150, 161, 206]), + // log(address,string,string,uint256) + ([161, 79, 208, 57], [21, 159, 137, 39]), + // log(uint256,address,uint256,address) + ([253, 178, 236, 212], [21, 193, 39, 181]), + // log(uint256,uint256,address,bool) + ([168, 232, 32, 174], [21, 202, 196, 118]), + // log(bool,string,bool,uint256) + ([141, 111, 156, 165], [22, 6, 163, 147]), + // log(address,address,uint256) + ([108, 54, 109, 114], [23, 254, 97, 133]), + // log(uint256,uint256,uint256,uint256) + ([92, 160, 173, 62], [25, 63, 184, 0]), + // log(bool,string,uint256,string) + ([119, 161, 171, 237], [26, 217, 109, 230]), + // log(bool,uint256,address,string) + ([24, 9, 19, 65], [27, 179, 176, 154]), + // log(string,uint256,address) + ([227, 132, 159, 121], [28, 126, 196, 72]), + // log(uint256,bool) + ([30, 109, 212, 236], [28, 157, 126, 179]), + // log(address,uint256,address,string) + ([93, 113, 243, 158], [29, 169, 134, 234]), + // log(address,string,uint256,uint256) + ([164, 201, 42, 96], [29, 200, 225, 184]), + // log(uint256,bool,uint256) + ([90, 77, 153, 34], [32, 9, 128, 20]), + // log(uint256,bool,bool) + ([213, 206, 172, 224], [32, 113, 134, 80]), + // log(address,uint256,uint256,address) + ([30, 246, 52, 52], [32, 227, 152, 77]), + // log(uint256,string,string,string) + ([87, 221, 10, 17], [33, 173, 6, 131]), + // log(address,uint256,bool,uint256) + ([105, 143, 67, 146], [34, 246, 185, 153]), + // log(uint256,address,address,address) + ([85, 71, 69, 249], [36, 136, 180, 20]), + // log(string,bool,string,uint256) + ([52, 203, 48, 141], [36, 249, 20, 101]), + // log(bool,uint256,address,address) + ([138, 47, 144, 170], [38, 245, 96, 168]), + // log(uint256,uint256,string,string) + ([124, 3, 42, 50], [39, 216, 175, 210]), + // log(bool,string,uint256,uint256) + ([142, 74, 232, 110], [40, 134, 63, 203]), + // log(uint256,bool,string,uint256) + ([145, 95, 219, 40], [44, 29, 7, 70]), + // log(address,uint256,uint256,uint256) + ([61, 14, 157, 228], [52, 240, 230, 54]), + // log(uint256,bool,address) + ([66, 78, 255, 191], [53, 8, 95, 123]), + // log(string,uint256,bool,bool) + ([227, 127, 243, 208], [53, 76, 54, 214]), + // log(bool,uint256,uint256) + ([59, 92, 3, 224], [55, 16, 51, 103]), + // log(bool,uint256,uint256,uint256) + ([50, 223, 165, 36], [55, 75, 180, 178]), + // log(uint256,string,uint256) + ([91, 109, 232, 63], [55, 170, 125, 76]), + // log(address,bool,uint256,uint256) + ([194, 16, 160, 30], [56, 111, 245, 244]), + // log(address,address,bool,uint256) + ([149, 214, 95, 17], [57, 113, 231, 140]), + // log(bool,uint256) + ([54, 75, 106, 146], [57, 145, 116, 211]), + // log(uint256,string,uint256,address) + ([171, 123, 217, 253], [59, 34, 121, 180]), + // log(address,uint256,bool,bool) + ([254, 161, 213, 90], [59, 245, 229, 55]), + // log(uint256,address,string,string) + ([141, 119, 134, 36], [62, 18, 140, 163]), + // log(string,address,bool,uint256) + ([197, 209, 187, 139], [62, 159, 134, 106]), + // log(uint256,uint256,string,address) + ([67, 50, 133, 162], [66, 210, 29, 183]), + // log(address,string,uint256,string) + ([93, 19, 101, 201], [68, 136, 48, 168]), + // log(uint256,bool,address,bool) + ([145, 251, 18, 66], [69, 77, 84, 165]), + // log(address,string,address,uint256) + ([140, 25, 51, 169], [69, 127, 227, 207]), + // log(uint256,address,string,uint256) + ([160, 196, 20, 232], [70, 130, 107, 93]), + // log(uint256,uint256,bool) + ([103, 87, 15, 247], [71, 102, 218, 114]), + // log(address,uint256,address,address) + ([236, 36, 132, 111], [71, 141, 28, 98]), + // log(address,uint256,uint256,string) + ([137, 52, 13, 171], [74, 40, 192, 23]), + // log(bool,bool,address,uint256) + ([96, 147, 134, 231], [76, 18, 61, 87]), + // log(uint256,string,bool) + ([70, 167, 208, 206], [76, 237, 167, 90]), + // log(string,uint256,address,uint256) + ([88, 73, 122, 254], [79, 4, 253, 198]), + // log(address,string,bool,uint256) + ([231, 32, 82, 28], [81, 94, 56, 182]), + // log(bool,address,uint256,string) + ([160, 104, 88, 51], [81, 240, 159, 248]), + // log(bool,bool,uint256,address) + ([11, 255, 149, 13], [84, 167, 169, 160]), + // log(uint256,uint256,address,address) + ([202, 147, 155, 32], [86, 165, 209, 177]), + // log(string,string,uint256) + ([243, 98, 202, 89], [88, 33, 239, 161]), + // log(string,uint256,string) + ([163, 245, 199, 57], [89, 112, 224, 137]), + // log(uint256,uint256,uint256,string) + ([120, 173, 122, 12], [89, 207, 203, 227]), + // log(string,address,uint256,string) + ([76, 85, 242, 52], [90, 71, 118, 50]), + // log(uint256,address,uint256) + ([136, 67, 67, 170], [90, 155, 94, 213]), + // log(string,uint256,string,string) + ([108, 152, 218, 226], [90, 184, 78, 31]), + // log(uint256,address,bool,uint256) + ([123, 8, 232, 235], [90, 189, 153, 42]), + // log(address,uint256,string,address) + ([220, 121, 38, 4], [92, 67, 13, 71]), + // log(uint256,uint256,address) + ([190, 51, 73, 27], [92, 150, 179, 49]), + // log(string,bool,address,uint256) + ([40, 223, 78, 150], [93, 8, 187, 5]), + // log(string,string,uint256,string) + ([141, 20, 44, 221], [93, 26, 151, 26]), + // log(uint256,uint256,string,uint256) + ([56, 148, 22, 61], [93, 162, 151, 235]), + // log(string,uint256,address,address) + ([234, 200, 146, 129], [94, 162, 183, 174]), + // log(uint256,address,uint256,bool) + ([25, 246, 115, 105], [95, 116, 58, 124]), + // log(bool,address,uint256) + ([235, 112, 75, 175], [95, 123, 154, 251]), + // log(uint256,string,address,address) + ([127, 165, 69, 139], [97, 104, 237, 97]), + // log(bool,bool,uint256,bool) + ([171, 92, 193, 196], [97, 158, 77, 14]), + // log(address,string,uint256,address) + ([223, 215, 216, 11], [99, 24, 54, 120]), + // log(uint256,address,string) + ([206, 131, 4, 123], [99, 203, 65, 249]), + // log(string,address,uint256,address) + ([163, 102, 236, 128], [99, 251, 139, 197]), + // log(uint256,string) + ([15, 163, 243, 69], [100, 63, 208, 223]), + // log(string,bool,uint256,uint256) + ([93, 191, 240, 56], [100, 181, 187, 103]), + // log(address,uint256,uint256,bool) + ([236, 75, 168, 162], [102, 241, 188, 103]), + // log(address,uint256,bool) + ([229, 74, 225, 68], [103, 130, 9, 168]), + // log(address,string,uint256) + ([28, 218, 242, 138], [103, 221, 111, 241]), + // log(uint256,bool,string,string) + ([164, 51, 252, 253], [104, 200, 184, 189]), + // log(uint256,string,uint256,bool) + ([135, 90, 110, 46], [105, 26, 143, 116]), + // log(uint256,address) + ([88, 235, 134, 12], [105, 39, 108, 134]), + // log(uint256,bool,bool,address) + ([83, 6, 34, 93], [105, 100, 11, 89]), + // log(bool,uint256,string,uint256) + ([65, 128, 1, 27], [106, 17, 153, 226]), + // log(bool,string,uint256,bool) + ([32, 187, 201, 175], [107, 14, 93, 83]), + // log(uint256,uint256,address,string) + ([214, 162, 209, 222], [108, 222, 64, 184]), + // log(bool,bool,bool,uint256) + ([194, 72, 131, 77], [109, 112, 69, 193]), + // log(uint256,uint256,string) + ([125, 105, 14, 230], [113, 208, 74, 242]), + // log(uint256,address,address,uint256) + ([154, 60, 191, 150], [115, 110, 251, 182]), + // log(string,bool,uint256,string) + ([66, 185, 162, 39], [116, 45, 110, 231]), + // log(uint256,bool,bool,uint256) + ([189, 37, 173, 89], [116, 100, 206, 35]), + // log(string,uint256,uint256,bool) + ([247, 60, 126, 61], [118, 38, 219, 146]), + // log(uint256,uint256,string,bool) + ([178, 46, 175, 6], [122, 246, 171, 37]), + // log(uint256,string,address) + ([31, 144, 242, 74], [122, 250, 201, 89]), + // log(address,uint256,address) + ([151, 236, 163, 148], [123, 192, 216, 72]), + // log(bool,string,string,uint256) + ([93, 219, 37, 146], [123, 224, 195, 235]), + // log(bool,address,uint256,uint256) + ([155, 254, 114, 188], [123, 241, 129, 161]), + // log(string,uint256,string,address) + ([187, 114, 53, 233], [124, 70, 50, 164]), + // log(string,string,address,uint256) + ([74, 129, 165, 106], [124, 195, 198, 7]), + // log(string,uint256,string,bool) + ([233, 159, 130, 207], [125, 36, 73, 29]), + // log(bool,bool,uint256,string) + ([80, 97, 137, 55], [125, 212, 208, 224]), + // log(bool,uint256,bool,uint256) + ([211, 222, 85, 147], [127, 155, 188, 162]), + // log(address,bool,string,uint256) + ([158, 18, 123, 110], [128, 230, 162, 11]), + // log(string,uint256,address,bool) + ([17, 6, 168, 247], [130, 17, 42, 66]), + // log(uint256,string,uint256,uint256) + ([192, 4, 56, 7], [130, 194, 91, 116]), + // log(address,uint256) + ([34, 67, 207, 163], [131, 9, 232, 168]), + // log(string,uint256,uint256,string) + ([165, 78, 212, 189], [133, 75, 52, 150]), + // log(uint256,bool,string) + ([139, 14, 20, 254], [133, 119, 80, 33]), + // log(address,uint256,string,string) + ([126, 86, 198, 147], [136, 168, 196, 6]), + // log(uint256,bool,uint256,address) + ([79, 64, 5, 142], [136, 203, 96, 65]), + // log(uint256,uint256,address,uint256) + ([97, 11, 168, 192], [136, 246, 228, 178]), + // log(string,bool,uint256,bool) + ([60, 197, 181, 211], [138, 247, 207, 138]), + // log(address,bool,bool,uint256) + ([207, 181, 135, 86], [140, 78, 93, 230]), + // log(address,address,uint256,address) + ([214, 198, 82, 118], [141, 166, 222, 245]), + // log(string,bool,bool,uint256) + ([128, 117, 49, 232], [142, 63, 120, 169]), + // log(bool,uint256,uint256,string) + ([218, 6, 102, 200], [142, 105, 251, 93]), + // log(string,string,string,uint256) + ([159, 208, 9, 245], [142, 175, 176, 43]), + // log(string,address,address,uint256) + ([110, 183, 148, 61], [142, 243, 243, 153]), + // log(uint256,string,address,bool) + ([249, 63, 255, 55], [144, 195, 10, 86]), + // log(uint256,address,bool,string) + ([99, 240, 226, 66], [144, 251, 6, 170]), + // log(bool,uint256,bool,string) + ([182, 213, 105, 212], [145, 67, 219, 177]), + // log(uint256,bool,uint256,bool) + ([210, 171, 196, 253], [145, 160, 46, 42]), + // log(string,address,string,uint256) + ([143, 98, 75, 233], [145, 209, 17, 46]), + // log(string,bool,uint256,address) + ([113, 211, 133, 13], [147, 94, 9, 191]), + // log(address,address,address,uint256) + ([237, 94, 172, 135], [148, 37, 13, 119]), + // log(uint256,uint256,bool,address) + ([225, 23, 116, 79], [154, 129, 106, 131]), + // log(bool,uint256,bool,address) + ([66, 103, 199, 248], [154, 205, 54, 22]), + // log(address,address,uint256,bool) + ([194, 246, 136, 236], [155, 66, 84, 226]), + // log(uint256,address,bool) + ([122, 208, 18, 142], [155, 110, 192, 66]), + // log(uint256,string,address,string) + ([248, 152, 87, 127], [156, 58, 223, 161]), + // log(address,bool,uint256) + ([44, 70, 141, 21], [156, 79, 153, 251]), + // log(uint256,address,string,address) + ([203, 229, 142, 253], [156, 186, 143, 255]), + // log(string,uint256,address,string) + ([50, 84, 194, 232], [159, 251, 47, 147]), + // log(address,uint256,address,bool) + ([241, 129, 161, 233], [161, 188, 201, 179]), + // log(uint256,bool,address,address) + ([134, 237, 193, 12], [161, 239, 76, 187]), + // log(address,uint256,string) + ([186, 249, 104, 73], [161, 242, 232, 170]), + // log(address,uint256,bool,address) + ([35, 229, 73, 114], [163, 27, 253, 204]), + // log(uint256,uint256,bool,string) + ([239, 217, 203, 238], [165, 180, 252, 153]), + // log(bool,string,address,uint256) + ([27, 11, 149, 91], [165, 202, 218, 148]), + // log(address,bool,address,uint256) + ([220, 113, 22, 210], [167, 92, 89, 222]), + // log(string,uint256,uint256,uint256) + ([8, 238, 86, 102], [167, 168, 120, 83]), + // log(uint256,uint256,bool,bool) + ([148, 190, 59, 177], [171, 8, 90, 230]), + // log(string,uint256,bool,string) + ([118, 204, 96, 100], [171, 247, 58, 152]), + // log(uint256,bool,address,string) + ([162, 48, 118, 30], [173, 224, 82, 199]), + // log(uint256,string,bool,address) + ([121, 111, 40, 160], [174, 46, 197, 129]), + // log(uint256,string,string,uint256) + ([118, 236, 99, 94], [176, 40, 201, 189]), + // log(uint256,string,string) + ([63, 87, 194, 149], [177, 21, 97, 31]), + // log(uint256,string,string,bool) + ([18, 134, 43, 152], [179, 166, 182, 189]), + // log(bool,uint256,address,bool) + ([101, 173, 244, 8], [180, 195, 20, 255]), + // log(string,uint256) + ([151, 16, 169, 208], [182, 14, 114, 204]), + // log(address,uint256,uint256) + ([135, 134, 19, 94], [182, 155, 202, 246]), + // log(uint256,bool,bool,bool) + ([78, 108, 83, 21], [182, 245, 119, 161]), + // log(uint256,string,uint256,string) + ([162, 188, 12, 153], [183, 185, 20, 202]), + // log(uint256,string,bool,bool) + ([81, 188, 43, 193], [186, 83, 93, 156]), + // log(uint256,address,address) + ([125, 119, 166, 27], [188, 253, 155, 224]), + // log(address,address,uint256,uint256) + ([84, 253, 243, 228], [190, 85, 52, 129]), + // log(bool,uint256,uint256,bool) + ([164, 29, 129, 222], [190, 152, 67, 83]), + // log(address,uint256,string,uint256) + ([245, 18, 207, 155], [191, 1, 248, 145]), + // log(bool,address,string,uint256) + ([11, 153, 252, 34], [194, 31, 100, 199]), + // log(string,string,uint256,bool) + ([230, 86, 88, 202], [195, 168, 166, 84]), + // log(bool,uint256,string) + ([200, 57, 126, 176], [195, 252, 57, 112]), + // log(address,bool,uint256,bool) + ([133, 205, 197, 175], [196, 100, 62, 32]), + // log(uint256,uint256,uint256,bool) + ([100, 82, 185, 203], [197, 152, 209, 133]), + // log(address,uint256,bool,string) + ([142, 142, 78, 117], [197, 173, 133, 249]), + // log(string,uint256,string,uint256) + ([160, 196, 178, 37], [198, 126, 169, 209]), + // log(uint256,bool,uint256,uint256) + ([86, 130, 141, 164], [198, 172, 199, 168]), + // log(string,bool,uint256) + ([41, 27, 185, 208], [201, 89, 88, 214]), + // log(string,uint256,uint256) + ([150, 156, 221, 3], [202, 71, 196, 235]), + // log(string,uint256,bool) + ([241, 2, 238, 5], [202, 119, 51, 177]), + // log(uint256,address,string,bool) + ([34, 164, 121, 166], [204, 50, 171, 7]), + // log(address,bool,uint256,address) + ([13, 140, 230, 30], [204, 247, 144, 161]), + // log(bool,uint256,bool,bool) + ([158, 1, 247, 65], [206, 181, 244, 215]), + // log(uint256,string,bool,uint256) + ([164, 180, 138, 127], [207, 0, 152, 128]), + // log(address,uint256,string,bool) + ([164, 2, 79, 17], [207, 24, 16, 92]), + // log(uint256,uint256,uint256) + ([231, 130, 10, 116], [209, 237, 122, 60]), + // log(uint256,string,bool,string) + ([141, 72, 156, 160], [210, 212, 35, 205]), + // log(uint256,string,string,address) + ([204, 152, 138, 160], [213, 131, 198, 2]), + // log(bool,address,uint256,bool) + ([238, 141, 134, 114], [214, 1, 159, 28]), + // log(string,string,bool,uint256) + ([134, 129, 138, 122], [214, 174, 250, 210]), + // log(uint256,address,uint256,string) + ([62, 211, 189, 40], [221, 176, 101, 33]), + // log(uint256,bool,bool,string) + ([49, 138, 229, 155], [221, 219, 149, 97]), + // log(uint256,bool,uint256,string) + ([232, 221, 188, 86], [222, 3, 231, 116]), + // log(string,uint256,bool,address) + ([229, 84, 157, 145], [224, 233, 91, 152]), + // log(string,uint256,uint256,address) + ([190, 215, 40, 191], [226, 29, 226, 120]), + // log(uint256,address,bool,bool) + ([126, 39, 65, 13], [227, 81, 20, 15]), + // log(bool,bool,string,uint256) + ([23, 139, 70, 133], [227, 169, 202, 47]), + // log(string,uint256,bool,uint256) + ([85, 14, 110, 245], [228, 27, 111, 111]), + // log(bool,uint256,string,bool) + ([145, 210, 248, 19], [229, 231, 11, 43]), + // log(uint256,string,address,uint256) + ([152, 231, 243, 243], [232, 211, 1, 141]), + // log(bool,uint256,bool) + ([27, 173, 201, 235], [232, 222, 251, 169]), + // log(uint256,uint256,bool,uint256) + ([108, 100, 124, 140], [235, 127, 111, 210]), + // log(uint256,bool,string,bool) + ([52, 110, 184, 199], [235, 146, 141, 127]), + // log(address,address,string,uint256) + ([4, 40, 147, 0], [239, 28, 239, 231]), + // log(uint256,bool,string,address) + ([73, 110, 43, 180], [239, 82, 144, 24]), + // log(uint256,address,bool,address) + ([182, 49, 48, 148], [239, 114, 197, 19]), + // log(string,string,uint256,uint256) + ([213, 207, 23, 208], [244, 93, 125, 44]), + // log(bool,uint256,string,string) + ([211, 42, 101, 72], [245, 188, 34, 73]), + // log(uint256,uint256) + ([108, 15, 105, 128], [246, 102, 113, 90]), + // log(uint256) and logUint(uint256) + ([245, 177, 187, 169], [248, 44, 80, 241]), + // log(string,address,uint256,uint256) + ([218, 163, 148, 189], [248, 245, 27, 30]), + // log(uint256,uint256,uint256,address) + ([224, 133, 63, 105], [250, 129, 133, 175]), + // log(string,address,uint256,bool) + ([90, 193, 193, 60], [252, 72, 69, 240]), + // log(address,address,uint256,string) + ([157, 209, 46, 173], [253, 180, 249, 144]), + // log(bool,uint256,string,address) + ([165, 199, 13, 41], [254, 221, 31, 255]), + // logInt(int256) + ([78, 12, 29, 29], [101, 37, 181, 245]), + // logBytes(bytes) + ([11, 231, 127, 86], [225, 123, 249, 86]), + // logBytes1(bytes1) + ([110, 24, 161, 40], [111, 65, 113, 201]), + // logBytes2(bytes2) + ([233, 182, 34, 150], [155, 94, 148, 62]), + // logBytes3(bytes3) + ([45, 131, 73, 38], [119, 130, 250, 45]), + // logBytes4(bytes4) + ([224, 95, 72, 209], [251, 163, 173, 57]), + // logBytes5(bytes5) + ([166, 132, 128, 141], [85, 131, 190, 46]), + // logBytes6(bytes6) + ([174, 132, 165, 145], [73, 66, 173, 198]), + // logBytes7(bytes7) + ([78, 213, 126, 40], [69, 116, 175, 171]), + // logBytes8(bytes8) + ([79, 132, 37, 46], [153, 2, 228, 127]), + // logBytes9(bytes9) + ([144, 189, 140, 208], [80, 161, 56, 223]), + // logBytes10(bytes10) + ([1, 61, 23, 139], [157, 194, 168, 151]), + // logBytes11(bytes11) + ([4, 0, 74, 46], [220, 8, 182, 167]), + // logBytes12(bytes12) + ([134, 160, 106, 189], [118, 86, 214, 199]), + // logBytes13(bytes13) + ([148, 82, 158, 52], [52, 193, 216, 27]), + // logBytes14(bytes14) + ([146, 102, 240, 127], [60, 234, 186, 101]), + // logBytes15(bytes15) + ([218, 149, 116, 224], [89, 26, 61, 162]), + // logBytes16(bytes16) + ([102, 92, 97, 4], [31, 141, 115, 18]), + // logBytes17(bytes17) + ([51, 159, 103, 58], [248, 154, 83, 47]), + // logBytes18(bytes18) + ([196, 210, 61, 154], [216, 101, 38, 66]), + // logBytes19(bytes19) + ([94, 107, 90, 51], [0, 245, 107, 201]), + // logBytes20(bytes20) + ([81, 136, 227, 233], [236, 184, 86, 126]), + // logBytes21(bytes21) + ([233, 218, 53, 96], [48, 82, 192, 143]), + // logBytes22(bytes22) + ([213, 250, 232, 156], [128, 122, 180, 52]), + // logBytes23(bytes23) + ([171, 161, 207, 13], [73, 121, 176, 55]), + // logBytes24(bytes24) + ([241, 179, 91, 52], [9, 119, 174, 252]), + // logBytes25(bytes25) + ([11, 132, 188, 88], [174, 169, 150, 63]), + // logBytes26(bytes26) + ([248, 177, 73, 241], [211, 99, 86, 40]), + // logBytes27(bytes27) + ([58, 55, 87, 221], [252, 55, 47, 159]), + // logBytes28(bytes28) + ([200, 42, 234, 238], [56, 47, 154, 52]), + // logBytes29(bytes29) + ([75, 105, 195, 213], [122, 24, 118, 65]), + // logBytes30(bytes30) + ([238, 18, 196, 237], [196, 52, 14, 246]), + // logBytes31(bytes31) + ([194, 133, 77, 146], [129, 252, 134, 72]), + // logBytes32(bytes32) + ([39, 183, 207, 133], [45, 33, 214, 247]), + ]) +}); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hardhat_console_path_works() { + for (hh, abigen) in HARDHAT_CONSOLE_SELECTOR_PATCHES.iter() { + let mut hh = (*hh).to_vec(); + patch_hardhat_console_selector(&mut hh); + assert_eq!((*abigen).to_vec(), hh); + } + } +} diff --git a/crates/evm/src/executor/backend/diagnostic.rs b/crates/evm-executors/src/backend/diagnostic.rs similarity index 80% rename from crates/evm/src/executor/backend/diagnostic.rs rename to crates/evm-executors/src/backend/diagnostic.rs index ff179460e3008..1e2b0b0d26672 100644 --- a/crates/evm/src/executor/backend/diagnostic.rs +++ b/crates/evm-executors/src/backend/diagnostic.rs @@ -1,9 +1,7 @@ -use crate::executor::{backend::LocalForkId, inspector::Cheatcodes}; +use crate::backend::LocalForkId; use alloy_primitives::Address; -use foundry_common::fmt::UIfmt; - -use foundry_utils::types::ToEthers; use itertools::Itertools; +use std::collections::BTreeMap; /// Represents possible diagnostic cases on revert #[derive(Debug, Clone)] @@ -21,14 +19,11 @@ pub enum RevertDiagnostic { }, } -// === impl RevertDiagnostic === - impl RevertDiagnostic { /// Converts the diagnostic to a readable error message - pub fn to_error_msg(&self, cheats: &Cheatcodes) -> String { - let get_label = |addr: &Address| { - cheats.labels.get(addr).cloned().unwrap_or_else(|| addr.to_ethers().pretty()) - }; + pub fn to_error_msg(&self, labels: &BTreeMap) -> String { + let get_label = + |addr: &Address| labels.get(addr).cloned().unwrap_or_else(|| addr.to_string()); match self { RevertDiagnostic::ContractExistsOnOtherForks { contract, active, available_on } => { diff --git a/crates/evm/src/executor/backend/error.rs b/crates/evm-executors/src/backend/error.rs similarity index 98% rename from crates/evm/src/executor/backend/error.rs rename to crates/evm-executors/src/backend/error.rs index a23a16c3828cd..209ecf4b01ef0 100644 --- a/crates/evm/src/executor/backend/error.rs +++ b/crates/evm-executors/src/backend/error.rs @@ -99,7 +99,7 @@ impl From for DatabaseError { /// Error thrown when the address is not allowed to execute cheatcodes /// -/// See also [`DatabaseExt`](crate::executor::DatabaseExt) +/// See also [`DatabaseExt`](crate::DatabaseExt) #[derive(Debug, Clone, Copy)] pub struct NoCheatcodeAccessError(pub Address); diff --git a/crates/evm/src/executor/backend/fuzz.rs b/crates/evm-executors/src/backend/fuzz.rs similarity index 97% rename from crates/evm/src/executor/backend/fuzz.rs rename to crates/evm-executors/src/backend/fuzz.rs index 738fa8a34c2a7..12d0f6ce781ed 100644 --- a/crates/evm/src/executor/backend/fuzz.rs +++ b/crates/evm-executors/src/backend/fuzz.rs @@ -1,11 +1,10 @@ //! A wrapper around `Backend` that is clone-on-write used for fuzzing. -use crate::executor::{ +use crate::{ backend::{ diagnostic::RevertDiagnostic, error::DatabaseError, Backend, DatabaseExt, LocalForkId, }, fork::{CreateFork, ForkId}, - inspector::cheatcodes::Cheatcodes, }; use alloy_primitives::{Address, B256, U256}; use revm::{ @@ -46,8 +45,6 @@ pub struct FuzzBackendWrapper<'a> { has_snapshot_failure: bool, } -// === impl FuzzBackendWrapper === - impl<'a> FuzzBackendWrapper<'a> { pub fn new(backend: &'a Backend) -> Self { Self { backend: Cow::Borrowed(backend), is_initialized: false, has_snapshot_failure: false } @@ -165,16 +162,16 @@ impl<'a> DatabaseExt for FuzzBackendWrapper<'a> { self.backend_mut(env).roll_fork_to_transaction(id, transaction, env, journaled_state) } - fn transact( + fn transact>( &mut self, id: Option, transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - cheatcodes_inspector: Option<&mut Cheatcodes>, + inspector: I, ) -> eyre::Result<()> { trace!(?id, ?transaction, "fuzz: execute transaction"); - self.backend_mut(env).transact(id, transaction, env, journaled_state, cheatcodes_inspector) + self.backend_mut(env).transact(id, transaction, env, journaled_state, inspector) } fn active_fork_id(&self) -> Option { diff --git a/crates/evm/src/executor/backend/in_memory_db.rs b/crates/evm-executors/src/backend/in_memory_db.rs similarity index 98% rename from crates/evm/src/executor/backend/in_memory_db.rs rename to crates/evm-executors/src/backend/in_memory_db.rs index 4687db7804e95..9041258593455 100644 --- a/crates/evm/src/executor/backend/in_memory_db.rs +++ b/crates/evm-executors/src/backend/in_memory_db.rs @@ -1,5 +1,5 @@ //! The in memory DB -use crate::executor::backend::error::DatabaseError; +use crate::{backend::error::DatabaseError, snapshot::Snapshots}; use alloy_primitives::{Address, B256, U256}; use revm::{ db::{CacheDB, DatabaseRef, EmptyDB}, @@ -7,8 +7,6 @@ use revm::{ Database, DatabaseCommit, }; -use crate::executor::snapshot::Snapshots; - /// Type alias for an in memory database /// /// See `EmptyDBWrapper` diff --git a/crates/evm/src/executor/backend/mod.rs b/crates/evm-executors/src/backend/mod.rs similarity index 98% rename from crates/evm/src/executor/backend/mod.rs rename to crates/evm-executors/src/backend/mod.rs index f159ef82c6dbe..49d464e4e2d46 100644 --- a/crates/evm/src/executor/backend/mod.rs +++ b/crates/evm-executors/src/backend/mod.rs @@ -1,20 +1,10 @@ //! Foundry's main executor backend abstraction and implementation. use crate::{ - abi::CHEATCODE_ADDRESS, - executor::{ - backend::{ - error::NoCheatcodeAccessError, in_memory_db::FoundryEvmInMemoryDB, - snapshot::BackendSnapshot, - }, - fork::{CreateFork, ForkId, MultiFork, SharedBackend}, - inspector::{ - cheatcodes::{util::configure_tx_env, Cheatcodes}, - DEFAULT_CREATE2_DEPLOYER, - }, - snapshot::Snapshots, - }, - CALLER, TEST_CONTRACT_ADDRESS, + fork::{CreateFork, ForkId, MultiFork, SharedBackend}, + snapshot::Snapshots, + utils::configure_tx_env, + CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, TEST_CONTRACT_ADDRESS, }; use alloy_primitives::{b256, keccak256, Address, B256, U256, U64}; use ethers::{ @@ -23,9 +13,9 @@ use ethers::{ }; use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE}; use foundry_utils::types::{ToAlloy, ToEthers}; -pub use in_memory_db::MemDb; use revm::{ db::{CacheDB, DatabaseRef}, + inspectors::NoOpInspector, precompile::{Precompiles, SpecId}, primitives::{ Account, AccountInfo, Bytecode, CreateScheme, Env, HashMap as Map, Log, ResultAndState, @@ -41,17 +31,20 @@ use std::{ }, }; -mod fuzz; -pub mod snapshot; -pub use fuzz::FuzzBackendWrapper; mod diagnostic; - pub use diagnostic::RevertDiagnostic; -pub mod error; -pub use error::{DatabaseError, DatabaseResult}; +mod error; +pub use error::{DatabaseError, DatabaseResult, NoCheatcodeAccessError}; + +mod fuzz; +pub use fuzz::FuzzBackendWrapper; mod in_memory_db; +pub use in_memory_db::{EmptyDBWrapper, FoundryEvmInMemoryDB, MemDb}; + +mod snapshot; +pub use snapshot::{BackendSnapshot, StateSnapshot}; // A `revm::Database` that is used in forking mode type ForkDB = CacheDB; @@ -83,6 +76,7 @@ pub trait DatabaseExt: Database { /// Snapshots can be reverted: [DatabaseExt::revert], however a snapshot can only be reverted /// once. After a successful revert, the same snapshot id cannot be used again. fn snapshot(&mut self, journaled_state: &JournaledState, env: &Env) -> U256; + /// Reverts the snapshot if it exists /// /// Returns `true` if the snapshot was successfully reverted, `false` if no snapshot for that id @@ -187,13 +181,13 @@ pub trait DatabaseExt: Database { ) -> eyre::Result<()>; /// Fetches the given transaction for the fork and executes it, committing the state in the DB - fn transact( + fn transact>( &mut self, id: Option, transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - cheatcodes_inspector: Option<&mut Cheatcodes>, + inspector: I, ) -> eyre::Result<()>; /// Returns the `ForkId` that's currently used in the database, if fork mode is on @@ -877,7 +871,7 @@ impl Backend { } trace!(tx=?tx.hash, "committing transaction"); - commit_transaction(tx, env.clone(), journaled_state, fork, &fork_id, None)?; + commit_transaction(tx, env.clone(), journaled_state, fork, &fork_id, NoOpInspector)?; } Ok(None) @@ -1176,13 +1170,13 @@ impl DatabaseExt for Backend { Ok(()) } - fn transact( + fn transact>( &mut self, maybe_id: Option, transaction: B256, env: &mut Env, journaled_state: &mut JournaledState, - cheatcodes_inspector: Option<&mut Cheatcodes>, + inspector: I, ) -> eyre::Result<()> { trace!(?maybe_id, ?transaction, "execute transaction"); let id = self.ensure_fork(maybe_id)?; @@ -1199,9 +1193,7 @@ impl DatabaseExt for Backend { let fork = self.inner.get_fork_by_id_mut(id)?; let tx = fork.db.db.get_transaction(transaction)?; - commit_transaction(tx, env, journaled_state, fork, &fork_id, cheatcodes_inspector)?; - - Ok(()) + commit_transaction(tx, env, journaled_state, fork, &fork_id, inspector) } fn active_fork_id(&self) -> Option { @@ -1803,13 +1795,13 @@ fn is_contract_in_state(journaled_state: &JournaledState, acc: Address) -> bool /// Executes the given transaction and commits state changes to the database _and_ the journaled /// state, with an optional inspector -fn commit_transaction( +fn commit_transaction>( tx: Transaction, mut env: Env, journaled_state: &mut JournaledState, fork: &mut Fork, fork_id: &ForkId, - cheatcodes_inspector: Option<&mut Cheatcodes>, + inspector: I, ) -> eyre::Result<()> { configure_tx_env(&mut env, &tx); @@ -1823,16 +1815,9 @@ fn commit_transaction( .block_on(async move { Backend::new_with_fork(fork_id, fork, journaled_state).await }); evm.database(db); - if let Some(inspector) = cheatcodes_inspector { - match evm.inspect(inspector) { - Ok(res) => res.state, - Err(e) => eyre::bail!("backend: failed committing transaction: {:?}", e), - } - } else { - match evm.transact() { - Ok(res) => res.state, - Err(e) => eyre::bail!("backend: failed committing transaction: {:?}", e), - } + match evm.inspect(inspector) { + Ok(res) => res.state, + Err(e) => eyre::bail!("backend: failed committing transaction: {:?}", e), } }; diff --git a/crates/evm/src/executor/backend/snapshot.rs b/crates/evm-executors/src/backend/snapshot.rs similarity index 100% rename from crates/evm/src/executor/backend/snapshot.rs rename to crates/evm-executors/src/backend/snapshot.rs diff --git a/crates/evm-executors/src/constants.rs b/crates/evm-executors/src/constants.rs new file mode 100644 index 0000000000000..55a0458ed0649 --- /dev/null +++ b/crates/evm-executors/src/constants.rs @@ -0,0 +1,37 @@ +use alloy_primitives::{address, hex, Address}; + +/// The cheatcode handler address. +/// +/// This is the same address as the one used in DappTools's HEVM. +/// It is calculated as: +/// `address(bytes20(uint160(uint256(keccak256('hevm cheat code')))))` +pub const CHEATCODE_ADDRESS: Address = address!("7109709ECfa91a80626fF3989D68f67F5b1DD12D"); + +/// The Hardhat console address. +/// +/// See: +pub const HARDHAT_CONSOLE_ADDRESS: Address = address!("000000000000000000636F6e736F6c652e6c6f67"); + +/// Stores the caller address to be used as *sender* account for: +/// - deploying Test contracts +/// - deploying Script contracts +/// +/// Derived from `address(uint160(uint256(keccak256("foundry default caller"))))`, +/// which is equal to `0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38`. +pub const CALLER: Address = address!("1804c8AB1F12E6bbf3894d4083f33e07309d1f38"); + +/// The default test contract address. +pub const TEST_CONTRACT_ADDRESS: Address = address!("b4c79daB8f259C7Aee6E5b2Aa729821864227e84"); + +/// Magic return value returned by the `assume` cheatcode. +pub const ASSUME_MAGIC_RETURN_CODE: &[u8] = b"FOUNDRY::ASSUME"; + +/// Magic return value returned by the `skip` cheatcode. +pub const MAGIC_SKIP_BYTES: &[u8] = b"FOUNDRY::SKIP"; + +/// The default CREATE2 deployer. +pub const DEFAULT_CREATE2_DEPLOYER: Address = address!("4e59b44847b379578588920ca78fbf26c0b4956c"); +/// The initcode of the default CREATE2 deployer. +pub const DEFAULT_CREATE2_DEPLOYER_CODE: &[u8] = &hex!("604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"); +/// The runtime code of the default CREATE2 deployer. +pub const DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE: &[u8] = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"); diff --git a/crates/evm/src/debug.rs b/crates/evm-executors/src/debug.rs similarity index 98% rename from crates/evm/src/debug.rs rename to crates/evm-executors/src/debug.rs index d4c97fedf95be..be6252109e72c 100644 --- a/crates/evm/src/debug.rs +++ b/crates/evm-executors/src/debug.rs @@ -180,7 +180,7 @@ impl Display for Instruction { "VM_{}", &*HEVM_ABI .functions() - .find(|func| func.selector() == *cheat) + .find(|func| func.short_signature() == *cheat) .expect("unknown cheatcode found in debugger") .name .to_uppercase() diff --git a/crates/evm/src/decode.rs b/crates/evm-executors/src/decode.rs similarity index 99% rename from crates/evm/src/decode.rs rename to crates/evm-executors/src/decode.rs index 9386c098548f2..943e3144778e8 100644 --- a/crates/evm/src/decode.rs +++ b/crates/evm-executors/src/decode.rs @@ -1,5 +1,6 @@ -//! Various utilities to decode test results -use crate::executor::inspector::cheatcodes::util::MAGIC_SKIP_BYTES; +//! Various utilities to decode test results. + +use crate::MAGIC_SKIP_BYTES; use alloy_dyn_abi::{DynSolType, DynSolValue, JsonAbiExt}; use alloy_json_abi::JsonAbi; use alloy_primitives::{B256, U256}; diff --git a/crates/evm/src/executor/fork/backend.rs b/crates/evm-executors/src/fork/backend.rs similarity index 98% rename from crates/evm/src/executor/fork/backend.rs rename to crates/evm-executors/src/fork/backend.rs index f296796b7097f..e781601560e3b 100644 --- a/crates/evm/src/executor/fork/backend.rs +++ b/crates/evm-executors/src/fork/backend.rs @@ -1,6 +1,6 @@ //! Smart caching and deduplication of requests when using a forking provider -use crate::executor::{ - backend::error::{DatabaseError, DatabaseResult}, +use crate::{ + backend::{DatabaseError, DatabaseResult}, fork::{cache::FlushJsonBlockCacheDB, BlockchainDb}, }; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; @@ -346,19 +346,17 @@ where }; // convert it to revm-style types - let (code, code_hash) = if !code.0.is_empty() { - (Some(code.0.clone()), keccak256(&code)) + let (code, code_hash) = if !code.is_empty() { + (code.clone(), keccak256(&code)) } else { - (Some(bytes::Bytes::default()), KECCAK_EMPTY) + (Bytes::default(), KECCAK_EMPTY) }; // update the cache let acc = AccountInfo { nonce: nonce.to(), balance, - code: code.map(|bytes| { - Bytecode::new_raw(alloy_primitives::Bytes(bytes)).to_checked() - }), + code: Some(Bytecode::new_raw(code).to_checked()), code_hash, }; pin.db.accounts().write().insert(addr, acc.clone()); @@ -696,7 +694,7 @@ impl DatabaseRef for SharedBackend { #[cfg(test)] mod tests { use super::*; - use crate::executor::{ + use crate::{ fork::{BlockchainDbMeta, CreateFork, JsonBlockCacheDB}, opts::EvmOpts, Backend, diff --git a/crates/evm/src/executor/fork/cache.rs b/crates/evm-executors/src/fork/cache.rs similarity index 99% rename from crates/evm/src/executor/fork/cache.rs rename to crates/evm-executors/src/fork/cache.rs index f9a96772ce34d..f2203fc5de9c4 100644 --- a/crates/evm/src/executor/fork/cache.rs +++ b/crates/evm-executors/src/fork/cache.rs @@ -1,5 +1,5 @@ //! Cache related abstraction -use crate::executor::backend::snapshot::StateSnapshot; +use crate::backend::StateSnapshot; use alloy_primitives::{Address, B256, U256}; use parking_lot::RwLock; use revm::{ diff --git a/crates/evm/src/executor/fork/database.rs b/crates/evm-executors/src/fork/database.rs similarity index 96% rename from crates/evm/src/executor/fork/database.rs rename to crates/evm-executors/src/fork/database.rs index 8da324b24cf29..13596fee4796f 100644 --- a/crates/evm/src/executor/fork/database.rs +++ b/crates/evm-executors/src/fork/database.rs @@ -1,18 +1,15 @@ //! A revm database that forks off a remote client use crate::{ - executor::{ - backend::{error::DatabaseError, snapshot::StateSnapshot}, - fork::{BlockchainDb, SharedBackend}, - snapshot::Snapshots, - }, - revm::db::CacheDB, + backend::{DatabaseError, StateSnapshot}, + fork::{BlockchainDb, SharedBackend}, + snapshot::Snapshots, }; use alloy_primitives::{Address, B256, U256}; use ethers::types::BlockId; use parking_lot::Mutex; use revm::{ - db::DatabaseRef, + db::{CacheDB, DatabaseRef}, primitives::{Account, AccountInfo, Bytecode, HashMap as Map}, Database, DatabaseCommit, }; @@ -155,7 +152,7 @@ impl Database for ForkedDatabase { fn basic(&mut self, address: Address) -> Result, Self::Error> { // Note: this will always return Some, since the `SharedBackend` will always load the // account, this differs from `::basic`, See also - // [MemDb::ensure_loaded](crate::executor::backend::MemDb::ensure_loaded) + // [MemDb::ensure_loaded](crate::backend::MemDb::ensure_loaded) Database::basic(&mut self.cache_db, address) } @@ -266,7 +263,7 @@ impl DatabaseRef for ForkDbSnapshot { #[cfg(test)] mod tests { use super::*; - use crate::executor::fork::BlockchainDbMeta; + use crate::fork::BlockchainDbMeta; use foundry_common::get_http_provider; use std::collections::BTreeSet; diff --git a/crates/evm/src/executor/fork/init.rs b/crates/evm-executors/src/fork/init.rs similarity index 100% rename from crates/evm/src/executor/fork/init.rs rename to crates/evm-executors/src/fork/init.rs diff --git a/crates/evm/src/executor/fork/mod.rs b/crates/evm-executors/src/fork/mod.rs similarity index 100% rename from crates/evm/src/executor/fork/mod.rs rename to crates/evm-executors/src/fork/mod.rs diff --git a/crates/evm/src/executor/fork/multi.rs b/crates/evm-executors/src/fork/multi.rs similarity index 99% rename from crates/evm/src/executor/fork/multi.rs rename to crates/evm-executors/src/fork/multi.rs index 8be587090b67e..4795634e5035e 100644 --- a/crates/evm/src/executor/fork/multi.rs +++ b/crates/evm-executors/src/fork/multi.rs @@ -3,9 +3,7 @@ //! The design is similar to the single `SharedBackend`, `BackendHandler` but supports multiple //! concurrently active pairs at once. -use crate::executor::fork::{ - BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend, -}; +use crate::fork::{BackendHandler, BlockchainDb, BlockchainDbMeta, CreateFork, SharedBackend}; use ethers::{ providers::Provider, types::{BlockId, BlockNumber}, diff --git a/crates/evm-executors/src/lib.rs b/crates/evm-executors/src/lib.rs new file mode 100644 index 0000000000000..c3b9aee60a68e --- /dev/null +++ b/crates/evm-executors/src/lib.rs @@ -0,0 +1,31 @@ +//! # foundry-evm-executors +//! +//! EVM executor abstractions, which can execute calls. +//! +//! Used for running tests, scripts, and interacting with the inner backend which holds the state. + +#![warn(unused_crate_dependencies)] + +#[macro_use] +extern crate tracing; + +pub mod abi; +pub mod backend; +pub use backend::Backend; +pub mod constants; +pub mod debug; +pub mod decode; +pub mod fork; +pub mod opts; +pub mod snapshot; +pub mod utils; + +pub use revm::primitives::State as StateChangeset; + +// TODO: Remove these re-exports +pub use abi::{ + patch_hardhat_console_selector, HardhatConsoleCalls, CHEATCODE_ADDRESS, CONSOLE_ABI, + HARDHAT_CONSOLE_ABI, HARDHAT_CONSOLE_ADDRESS, +}; +pub use constants::*; +pub use utils::CallKind; diff --git a/crates/evm/src/executor/opts.rs b/crates/evm-executors/src/opts.rs similarity index 99% rename from crates/evm/src/executor/opts.rs rename to crates/evm-executors/src/opts.rs index a09e7c44d2087..b3f2b1bd0b642 100644 --- a/crates/evm/src/executor/opts.rs +++ b/crates/evm-executors/src/opts.rs @@ -1,4 +1,5 @@ -use crate::{executor::fork::CreateFork, utils::RuntimeOrHandle}; +use super::fork::environment; +use crate::fork::CreateFork; use alloy_primitives::{Address, B256, U256}; use ethers::{ providers::{Middleware, Provider}, @@ -6,12 +7,11 @@ use ethers::{ }; use eyre::WrapErr; use foundry_common::{self, ProviderBuilder, RpcUrl, ALCHEMY_FREE_TIER_CUPS}; +use foundry_compilers::utils::RuntimeOrHandle; use foundry_config::Config; use revm::primitives::{BlockEnv, CfgEnv, SpecId, TxEnv}; use serde::{Deserialize, Deserializer, Serialize}; -use super::fork::environment; - #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct EvmOpts { #[serde(flatten)] diff --git a/crates/evm/src/executor/snapshot.rs b/crates/evm-executors/src/snapshot.rs similarity index 98% rename from crates/evm/src/executor/snapshot.rs rename to crates/evm-executors/src/snapshot.rs index d931e45f7e145..bcd1c92913191 100644 --- a/crates/evm/src/executor/snapshot.rs +++ b/crates/evm-executors/src/snapshot.rs @@ -10,8 +10,6 @@ pub struct Snapshots { snapshots: HashMap, } -// === impl Snapshots === - impl Snapshots { fn next_id(&mut self) -> U256 { let id = self.id; diff --git a/crates/evm/src/utils.rs b/crates/evm-executors/src/utils.rs similarity index 65% rename from crates/evm/src/utils.rs rename to crates/evm-executors/src/utils.rs index 78d6b98b59b1d..166f30650de1f 100644 --- a/crates/evm/src/utils.rs +++ b/crates/evm-executors/src/utils.rs @@ -1,14 +1,75 @@ use alloy_json_abi::{Function, JsonAbi as Abi}; -use alloy_primitives::FixedBytes; -use ethers::types::{Block, Chain, H256, U256}; +use alloy_primitives::{Address, FixedBytes, B256}; +use ethers::types::{ActionType, Block, CallType, Chain, Transaction, H256, U256}; use eyre::ContextCompat; use foundry_utils::types::ToAlloy; use revm::{ - interpreter::{opcode, opcode::spec_opcode_gas, InstructionResult}, - primitives::{Eval, Halt, SpecId}, + interpreter::{opcode, opcode::spec_opcode_gas, CallScheme, CreateInputs, InstructionResult}, + primitives::{CreateScheme, Eval, Halt, SpecId, TransactTo}, }; +use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; +pub use foundry_compilers::utils::RuntimeOrHandle; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +#[derive(Default)] +pub enum CallKind { + #[default] + Call, + StaticCall, + CallCode, + DelegateCall, + Create, + Create2, +} + +impl From for CallKind { + fn from(scheme: CallScheme) -> Self { + match scheme { + CallScheme::Call => CallKind::Call, + CallScheme::StaticCall => CallKind::StaticCall, + CallScheme::CallCode => CallKind::CallCode, + CallScheme::DelegateCall => CallKind::DelegateCall, + } + } +} + +impl From for CallKind { + fn from(create: CreateScheme) -> Self { + match create { + CreateScheme::Create => CallKind::Create, + CreateScheme::Create2 { .. } => CallKind::Create2, + } + } +} + +impl From for ActionType { + fn from(kind: CallKind) -> Self { + match kind { + CallKind::Call | CallKind::StaticCall | CallKind::DelegateCall | CallKind::CallCode => { + ActionType::Call + } + CallKind::Create => ActionType::Create, + CallKind::Create2 => ActionType::Create, + } + } +} + +impl From for CallType { + fn from(ty: CallKind) -> Self { + match ty { + CallKind::Call => CallType::Call, + CallKind::StaticCall => CallType::StaticCall, + CallKind::CallCode => CallType::CallCode, + CallKind::DelegateCall => CallType::DelegateCall, + CallKind::Create => CallType::None, + CallKind::Create2 => CallType::None, + } + } +} + /// Small helper function to convert [U256] into [H256]. #[inline] pub fn u256_to_h256_le(u: U256) -> H256 { @@ -179,37 +240,44 @@ pub fn get_function( .wrap_err(format!("{contract_name} does not have the selector {selector:?}")) } -// TODO: Add this once solc is removed from this crate -pub use foundry_compilers::utils::RuntimeOrHandle; - -/* -use tokio::runtime::{Handle, Runtime}; - -#[derive(Debug)] -pub enum RuntimeOrHandle { - Runtime(Runtime), - Handle(Handle), -} - -impl Default for RuntimeOrHandle { - fn default() -> Self { - Self::new() - } +/// Configures the env for the transaction +pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { + env.tx.caller = tx.from.to_alloy(); + env.tx.gas_limit = tx.gas.as_u64(); + env.tx.gas_price = tx.gas_price.unwrap_or_default().to_alloy(); + env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(|g| g.to_alloy()); + env.tx.nonce = Some(tx.nonce.as_u64()); + env.tx.access_list = tx + .access_list + .clone() + .unwrap_or_default() + .0 + .into_iter() + .map(|item| { + ( + item.address.to_alloy(), + item.storage_keys.into_iter().map(h256_to_u256_be).map(|g| g.to_alloy()).collect(), + ) + }) + .collect(); + env.tx.value = tx.value.to_alloy(); + env.tx.data = alloy_primitives::Bytes(tx.input.0.clone()); + env.tx.transact_to = + tx.to.map(|tx| tx.to_alloy()).map(TransactTo::Call).unwrap_or_else(TransactTo::create) } -impl RuntimeOrHandle { - pub fn new() -> RuntimeOrHandle { - match Handle::try_current() { - Ok(handle) => RuntimeOrHandle::Handle(handle), - Err(_) => RuntimeOrHandle::Runtime(Runtime::new().expect("Failed to start runtime")), +/// Get the address of a contract creation +pub fn get_create_address(call: &CreateInputs, nonce: u64) -> Address { + match call.scheme { + CreateScheme::Create => call.caller.create(nonce), + CreateScheme::Create2 { salt } => { + call.caller.create2_from_code(B256::from(salt), &call.init_code) } } +} - pub fn block_on(&self, f: F) -> F::Output { - match &self { - RuntimeOrHandle::Runtime(runtime) => runtime.block_on(f), - RuntimeOrHandle::Handle(handle) => tokio::task::block_in_place(|| handle.block_on(f)), - } - } +/// Get the gas used, accounting for refunds +pub fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 { + let refund_quotient = if SpecId::enabled(spec, SpecId::LONDON) { 5 } else { 2 }; + spent - (refunded).min(spent / refund_quotient) } -*/ diff --git a/crates/evm/test-data/storage.json b/crates/evm-executors/test-data/storage.json similarity index 100% rename from crates/evm/test-data/storage.json rename to crates/evm-executors/test-data/storage.json diff --git a/crates/evm-fuzz/Cargo.toml b/crates/evm-fuzz/Cargo.toml new file mode 100644 index 0000000000000..c7afb6681349c --- /dev/null +++ b/crates/evm-fuzz/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "foundry-evm-fuzz" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +foundry-common.workspace = true +foundry-compilers.workspace = true +foundry-config.workspace = true +foundry-evm-coverage.workspace = true +foundry-evm-executors.workspace = true +foundry-evm-traces.workspace = true +foundry-utils.workspace = true + +alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } +alloy-json-abi = { workspace = true } +alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +ethers = { workspace = true, features = ["ethers-solc"] } +revm = { workspace = true, default-features = false, features = [ + "std", + "serde", + "memory_limit", + "optional_eip3607", + "optional_block_gas_limit", + "optional_no_base_fee", + "arbitrary", +] } + +eyre = "0.6" +hashbrown = { version = "0.14", features = ["serde"] } +parking_lot = "0.12" +proptest = "1" +serde = "1" +thiserror = "1" +tracing = "0.1" diff --git a/crates/evm/src/fuzz/error.rs b/crates/evm-fuzz/src/error.rs similarity index 84% rename from crates/evm/src/fuzz/error.rs rename to crates/evm-fuzz/src/error.rs index fc3e6279600bd..145afa5e72743 100644 --- a/crates/evm/src/fuzz/error.rs +++ b/crates/evm-fuzz/src/error.rs @@ -1,9 +1,6 @@ //! errors related to fuzz tests use proptest::test_runner::Reason; -/// Magic return code for the `assume` cheatcode -pub const ASSUME_MAGIC_RETURN_CODE: &[u8] = b"FOUNDRY::ASSUME"; - /// Possible errors when running fuzz tests #[derive(Debug, thiserror::Error)] pub enum FuzzError { diff --git a/crates/evm/src/executor/inspector/fuzzer.rs b/crates/evm-fuzz/src/inspector.rs similarity index 97% rename from crates/evm/src/executor/inspector/fuzzer.rs rename to crates/evm-fuzz/src/inspector.rs index f738a26e1a8b4..25274dfae87fe 100644 --- a/crates/evm/src/executor/inspector/fuzzer.rs +++ b/crates/evm-fuzz/src/inspector.rs @@ -1,8 +1,6 @@ -use crate::{ - fuzz::{invariant::RandomCallGenerator, strategies::EvmFuzzState}, - utils, -}; +use crate::{invariant::RandomCallGenerator, strategies::EvmFuzzState}; use alloy_primitives::Bytes; +use foundry_evm_executors::utils; use foundry_utils::types::ToEthers; use revm::{ interpreter::{CallInputs, CallScheme, Gas, InstructionResult, Interpreter}, diff --git a/crates/evm/src/fuzz/invariant/call_override.rs b/crates/evm-fuzz/src/invariant/call_override.rs similarity index 87% rename from crates/evm/src/fuzz/invariant/call_override.rs rename to crates/evm-fuzz/src/invariant/call_override.rs index 2739e79fbc155..c9a0b4ff10357 100644 --- a/crates/evm/src/fuzz/invariant/call_override.rs +++ b/crates/evm-fuzz/src/invariant/call_override.rs @@ -1,5 +1,4 @@ use super::BasicTxDetails; -use crate::executor::Executor; use alloy_primitives::{Address, Bytes}; use parking_lot::{Mutex, RwLock}; use proptest::{ @@ -90,13 +89,3 @@ impl RandomCallGenerator { } } } - -/// Sets up the calls generated by the internal fuzzer, if they exist. -pub fn set_up_inner_replay(executor: &mut Executor, inner_sequence: &[Option]) { - if let Some(fuzzer) = &mut executor.inspector.fuzzer { - if let Some(call_generator) = &mut fuzzer.call_generator { - call_generator.last_sequence = Arc::new(RwLock::new(inner_sequence.to_owned())); - call_generator.set_replay(true); - } - } -} diff --git a/crates/evm/src/fuzz/invariant/filters.rs b/crates/evm-fuzz/src/invariant/filters.rs similarity index 98% rename from crates/evm/src/fuzz/invariant/filters.rs rename to crates/evm-fuzz/src/invariant/filters.rs index 32c8704ca197d..b94f6193992cd 100644 --- a/crates/evm/src/fuzz/invariant/filters.rs +++ b/crates/evm-fuzz/src/invariant/filters.rs @@ -1,7 +1,7 @@ -use crate::utils::get_function; use alloy_json_abi::{Function, JsonAbi as Abi}; use alloy_primitives::{Address, Selector}; use foundry_compilers::ArtifactId; +use foundry_evm_executors::utils::get_function; use std::collections::BTreeMap; /// Contains which contracts are to be targeted or excluded on an invariant test through their diff --git a/crates/evm-fuzz/src/invariant/mod.rs b/crates/evm-fuzz/src/invariant/mod.rs new file mode 100644 index 0000000000000..d7fbf09ec0c90 --- /dev/null +++ b/crates/evm-fuzz/src/invariant/mod.rs @@ -0,0 +1,27 @@ +use alloy_json_abi::{Function, JsonAbi as Abi}; +use alloy_primitives::{Address, Bytes}; +use parking_lot::Mutex; +use std::{collections::BTreeMap, sync::Arc}; + +mod call_override; +pub use call_override::RandomCallGenerator; + +mod filters; +pub use filters::{ArtifactFilters, SenderFilters}; + +pub type TargetedContracts = BTreeMap)>; +pub type FuzzRunIdentifiedContracts = Arc>; + +/// (Sender, (TargetContract, Calldata)) +pub type BasicTxDetails = (Address, (Address, Bytes)); + +/// Test contract which is testing its invariants. +#[derive(Debug, Clone)] +pub struct InvariantContract<'a> { + /// Address of the test contract. + pub address: Address, + /// Invariant functions present in the test contract. + pub invariant_function: &'a Function, + /// Abi of the test contract. + pub abi: &'a Abi, +} diff --git a/crates/evm-fuzz/src/lib.rs b/crates/evm-fuzz/src/lib.rs new file mode 100644 index 0000000000000..847ac9abfb94d --- /dev/null +++ b/crates/evm-fuzz/src/lib.rs @@ -0,0 +1,275 @@ +//! # foundry-evm-fuzz +//! +//! [`proptest`] EVM executor, used for fuzzing. + +#![warn(unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] + +#[macro_use] +extern crate tracing; + +use alloy_dyn_abi::{DynSolValue, JsonAbiExt}; +use alloy_primitives::{Address, Bytes, U256}; +use ethers::types::Log; +use foundry_common::{calc, contracts::ContractsByAddress}; +use foundry_evm_coverage::HitMaps; +use foundry_evm_traces::CallTraceArena; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, fmt}; + +pub use proptest::test_runner::{Config as FuzzConfig, Reason}; + +mod error; +pub use error::FuzzError; + +pub mod invariant; +pub mod strategies; + +mod inspector; +pub use inspector::Fuzzer; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum CounterExample { + /// Call used as a counter example for fuzz tests. + Single(BaseCounterExample), + /// Sequence of calls used as a counter example for invariant tests. + Sequence(Vec), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct BaseCounterExample { + /// Address which makes the call + pub sender: Option
, + /// Address to which to call to + pub addr: Option
, + /// The data to provide + pub calldata: Bytes, + /// Function signature if it exists + pub signature: Option, + /// Contract name if it exists + pub contract_name: Option, + /// Traces + pub traces: Option, + #[serde(skip)] + pub args: Vec, +} + +impl BaseCounterExample { + pub fn create( + sender: Address, + addr: Address, + bytes: &Bytes, + contracts: &ContractsByAddress, + traces: Option, + ) -> Self { + if let Some((name, abi)) = &contracts.get(&addr) { + if let Some(func) = abi.functions().find(|f| f.selector() == bytes[..4]) { + // skip the function selector when decoding + if let Ok(args) = func.abi_decode_input(&bytes[4..], false) { + return BaseCounterExample { + sender: Some(sender), + addr: Some(addr), + calldata: bytes.clone(), + signature: Some(func.signature()), + contract_name: Some(name.clone()), + traces, + args, + } + } + } + } + + BaseCounterExample { + sender: Some(sender), + addr: Some(addr), + calldata: bytes.clone(), + signature: None, + contract_name: None, + traces, + args: vec![], + } + } +} + +impl fmt::Display for BaseCounterExample { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let args = foundry_common::abi::format_tokens(&self.args).collect::>().join(", "); + + if let Some(sender) = self.sender { + write!(f, "sender={sender} addr=")? + } + + if let Some(name) = &self.contract_name { + write!(f, "[{name}]")? + } + + if let Some(addr) = &self.addr { + write!(f, "{addr} ")? + } + + if let Some(sig) = &self.signature { + write!(f, "calldata={sig}")? + } else { + write!(f, "calldata=0x{}", self.calldata)? + } + + write!(f, ", args=[{args}]") + } +} + +/// The outcome of a fuzz test +#[derive(Debug)] +pub struct FuzzTestResult { + /// we keep this for the debugger + pub first_case: FuzzCase, + /// Gas usage (gas_used, call_stipend) per cases + pub gas_by_case: Vec<(u64, u64)>, + /// Whether the test case was successful. This means that the transaction executed + /// properly, or that there was a revert and that the test was expected to fail + /// (prefixed with `testFail`) + pub success: bool, + + /// If there was a revert, this field will be populated. Note that the test can + /// still be successful (i.e self.success == true) when it's expected to fail. + pub reason: Option, + + /// Minimal reproduction test case for failing fuzz tests + pub counterexample: Option, + + /// Any captured & parsed as strings logs along the test's execution which should + /// be printed to the user. + pub logs: Vec, + + /// The decoded DSTest logging events and Hardhat's `console.log` from [logs](Self::logs). + pub decoded_logs: Vec, + + /// Labeled addresses + pub labeled_addresses: BTreeMap, + + /// Exemplary traces for a fuzz run of the test function + /// + /// **Note** We only store a single trace of a successful fuzz call, otherwise we would get + /// `num(fuzz_cases)` traces, one for each run, which is neither helpful nor performant. + pub traces: Option, + + /// Raw coverage info + pub coverage: Option, +} + +impl FuzzTestResult { + /// Returns the median gas of all test cases + pub fn median_gas(&self, with_stipend: bool) -> u64 { + let mut values = + self.gas_values(with_stipend).into_iter().map(U256::from).collect::>(); + values.sort_unstable(); + calc::median_sorted(&values).to::() + } + + /// Returns the average gas use of all test cases + pub fn mean_gas(&self, with_stipend: bool) -> u64 { + let mut values = + self.gas_values(with_stipend).into_iter().map(U256::from).collect::>(); + values.sort_unstable(); + calc::mean(&values).to::() + } + + fn gas_values(&self, with_stipend: bool) -> Vec { + self.gas_by_case + .iter() + .map(|gas| if with_stipend { gas.0 } else { gas.0.saturating_sub(gas.1) }) + .collect() + } +} + +/// Data of a single fuzz test case +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct FuzzCase { + /// The calldata used for this fuzz test + pub calldata: Bytes, + /// Consumed gas + pub gas: u64, + /// The initial gas stipend for the transaction + pub stipend: u64, +} + +/// Container type for all successful test cases +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(transparent)] +pub struct FuzzedCases { + cases: Vec, +} + +impl FuzzedCases { + #[inline] + pub fn new(mut cases: Vec) -> Self { + cases.sort_by_key(|c| c.gas); + Self { cases } + } + + #[inline] + pub fn cases(&self) -> &[FuzzCase] { + &self.cases + } + + #[inline] + pub fn into_cases(self) -> Vec { + self.cases + } + + /// Get the last [FuzzCase] + #[inline] + pub fn last(&self) -> Option<&FuzzCase> { + self.cases.last() + } + + /// Returns the median gas of all test cases + #[inline] + pub fn median_gas(&self, with_stipend: bool) -> u64 { + let mut values = + self.gas_values(with_stipend).into_iter().map(U256::from).collect::>(); + values.sort_unstable(); + calc::median_sorted(&values).to::() + } + + /// Returns the average gas use of all test cases + #[inline] + pub fn mean_gas(&self, with_stipend: bool) -> u64 { + let mut values = + self.gas_values(with_stipend).into_iter().map(U256::from).collect::>(); + values.sort_unstable(); + calc::mean(&values).to::() + } + + #[inline] + fn gas_values(&self, with_stipend: bool) -> Vec { + self.cases + .iter() + .map(|c| if with_stipend { c.gas } else { c.gas.saturating_sub(c.stipend) }) + .collect() + } + + /// Returns the case with the highest gas usage + #[inline] + pub fn highest(&self) -> Option<&FuzzCase> { + self.cases.last() + } + + /// Returns the case with the lowest gas usage + #[inline] + pub fn lowest(&self) -> Option<&FuzzCase> { + self.cases.first() + } + + /// Returns the highest amount of gas spent on a fuzz case + #[inline] + pub fn highest_gas(&self, with_stipend: bool) -> u64 { + self.highest() + .map(|c| if with_stipend { c.gas } else { c.gas - c.stipend }) + .unwrap_or_default() + } + + /// Returns the lowest amount of gas spent on a fuzz case + #[inline] + pub fn lowest_gas(&self) -> u64 { + self.lowest().map(|c| c.gas).unwrap_or_default() + } +} diff --git a/crates/evm/src/fuzz/strategies/calldata.rs b/crates/evm-fuzz/src/strategies/calldata.rs similarity index 81% rename from crates/evm/src/fuzz/strategies/calldata.rs rename to crates/evm-fuzz/src/strategies/calldata.rs index 93d914543b73e..2d07e83ac9465 100644 --- a/crates/evm/src/fuzz/strategies/calldata.rs +++ b/crates/evm-fuzz/src/strategies/calldata.rs @@ -1,7 +1,5 @@ -use std::str::FromStr; - use super::fuzz_param; -use alloy_dyn_abi::{DynSolType, JsonAbiExt}; +use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::Function; use alloy_primitives::Bytes; use proptest::prelude::{BoxedStrategy, Strategy}; @@ -14,7 +12,7 @@ pub fn fuzz_calldata(func: Function) -> BoxedStrategy { let strats = func .inputs .iter() - .map(|input| fuzz_param(&DynSolType::from_str(&input.selector_type()).unwrap())) + .map(|input| fuzz_param(&input.selector_type().parse().unwrap())) .collect::>(); strats diff --git a/crates/evm/src/fuzz/strategies/int.rs b/crates/evm-fuzz/src/strategies/int.rs similarity index 100% rename from crates/evm/src/fuzz/strategies/int.rs rename to crates/evm-fuzz/src/strategies/int.rs diff --git a/crates/evm/src/fuzz/strategies/invariants.rs b/crates/evm-fuzz/src/strategies/invariants.rs similarity index 97% rename from crates/evm/src/fuzz/strategies/invariants.rs rename to crates/evm-fuzz/src/strategies/invariants.rs index c842fc45ce729..62be1b6eb57d4 100644 --- a/crates/evm/src/fuzz/strategies/invariants.rs +++ b/crates/evm-fuzz/src/strategies/invariants.rs @@ -1,16 +1,14 @@ use super::fuzz_param_from_state; -use crate::fuzz::{ - fuzz_calldata, fuzz_calldata_from_state, +use crate::{ invariant::{BasicTxDetails, FuzzRunIdentifiedContracts, SenderFilters}, - strategies::fuzz_param, - EvmFuzzState, + strategies::{fuzz_calldata, fuzz_calldata_from_state, fuzz_param, EvmFuzzState}, }; use alloy_json_abi::{Function, JsonAbi as Abi}; use alloy_primitives::{Address, Bytes}; use parking_lot::RwLock; use proptest::prelude::*; -pub use proptest::test_runner::Config as FuzzConfig; use std::{rc::Rc, sync::Arc}; + /// Given a target address, we generate random calldata. pub fn override_call_strat( fuzz_state: EvmFuzzState, @@ -37,6 +35,7 @@ pub fn override_call_strat( }) .sboxed() } + /// Creates the invariant strategy. /// /// Given the known and future contracts, it generates the next call by fuzzing the `caller`, @@ -57,6 +56,7 @@ pub fn invariant_strat( // state generate_call(fuzz_state, senders, contracts, dictionary_weight).prop_map(|x| vec![x]) } + /// Strategy to generate a transaction where the `sender`, `target` and `calldata` are all generated /// through specific strategies. fn generate_call( @@ -80,6 +80,7 @@ fn generate_call( }) .boxed() } + /// Strategy to select a sender address: /// * If `senders` is empty, then it's either a random address (10%) or from the dictionary (90%). /// * If `senders` is not empty, a random address is chosen from the list of senders. @@ -114,6 +115,7 @@ fn select_random_sender( fuzz_strategy } } + /// Strategy to randomly select a contract from the `contracts` list that has at least 1 function fn select_random_contract( contracts: FuzzRunIdentifiedContracts, @@ -126,6 +128,7 @@ fn select_random_contract( (*addr, abi.clone(), functions.clone()) }) } + /// Strategy to select a random mutable function from the abi. /// /// If `targeted_functions` is not empty, select one from it. Otherwise, take any @@ -154,6 +157,7 @@ fn select_random_function(abi: Abi, targeted_functions: Vec) -> BoxedS total_random.boxed() } } + /// Given a function, it returns a proptest strategy which generates valid abi-encoded calldata /// for that function's input types. pub fn fuzz_contract_with_calldata( diff --git a/crates/evm/src/fuzz/strategies/mod.rs b/crates/evm-fuzz/src/strategies/mod.rs similarity index 79% rename from crates/evm/src/fuzz/strategies/mod.rs rename to crates/evm-fuzz/src/strategies/mod.rs index 8add232c89532..a795905de95ac 100644 --- a/crates/evm/src/fuzz/strategies/mod.rs +++ b/crates/evm-fuzz/src/strategies/mod.rs @@ -1,6 +1,7 @@ mod int; -mod uint; pub use int::IntStrategy; + +mod uint; pub use uint::UintStrategy; mod param; @@ -16,4 +17,4 @@ pub use state::{ }; mod invariants; -pub use invariants::*; +pub use invariants::{fuzz_contract_with_calldata, invariant_strat, override_call_strat}; diff --git a/crates/evm/src/fuzz/strategies/param.rs b/crates/evm-fuzz/src/strategies/param.rs similarity index 98% rename from crates/evm/src/fuzz/strategies/param.rs rename to crates/evm-fuzz/src/strategies/param.rs index 9e4df3a8ede32..a23d6574c31ce 100644 --- a/crates/evm/src/fuzz/strategies/param.rs +++ b/crates/evm-fuzz/src/strategies/param.rs @@ -4,7 +4,8 @@ use alloy_primitives::{Address, FixedBytes, I256, U256}; use proptest::prelude::*; /// The max length of arrays we fuzz for is 256. -pub const MAX_ARRAY_LEN: usize = 256; +const MAX_ARRAY_LEN: usize = 256; + /// Given a parameter type, returns a strategy for generating values for that type. /// /// Works with ABI Encoder v2 tuples. @@ -159,7 +160,7 @@ pub fn fuzz_param_from_state( #[cfg(test)] mod tests { - use crate::fuzz::strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state}; + use crate::strategies::{build_initial_state, fuzz_calldata, fuzz_calldata_from_state}; use alloy_json_abi::Function; use foundry_config::FuzzDictionaryConfig; use revm::db::{CacheDB, EmptyDB}; diff --git a/crates/evm/src/fuzz/strategies/state.rs b/crates/evm-fuzz/src/strategies/state.rs similarity index 96% rename from crates/evm/src/fuzz/strategies/state.rs rename to crates/evm-fuzz/src/strategies/state.rs index 44f32c2498da2..76ea37c40cd37 100644 --- a/crates/evm/src/fuzz/strategies/state.rs +++ b/crates/evm-fuzz/src/strategies/state.rs @@ -1,15 +1,12 @@ use super::fuzz_param_from_state; -use crate::{ - executor::StateChangeset, - fuzz::invariant::{ArtifactFilters, FuzzRunIdentifiedContracts}, -}; +use crate::invariant::{ArtifactFilters, FuzzRunIdentifiedContracts}; use alloy_dyn_abi::{DynSolType, JsonAbiExt}; use alloy_json_abi::Function; -use alloy_primitives::{Address, B256, U256}; -use bytes::Bytes; +use alloy_primitives::{Address, Bytes, B256, U256}; use ethers::types::Log; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::FuzzDictionaryConfig; +use foundry_evm_executors::StateChangeset; use foundry_utils::types::ToEthers; use hashbrown::HashSet; use parking_lot::RwLock; @@ -25,6 +22,7 @@ use std::{fmt, io::Write, str::FromStr, sync::Arc}; /// /// Wrapped in a shareable container. pub type EvmFuzzState = Arc>; + #[derive(Default)] pub struct FuzzDictionary { /// Collected state values. @@ -32,6 +30,7 @@ pub struct FuzzDictionary { /// Addresses that already had their PUSH bytes collected. addresses: HashSet
, } + impl fmt::Debug for FuzzDictionary { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FuzzDictionary") @@ -40,30 +39,32 @@ impl fmt::Debug for FuzzDictionary { .finish() } } + impl FuzzDictionary { #[inline] pub fn values(&self) -> &HashSet<[u8; 32]> { &self.state_values } + #[inline] pub fn values_mut(&mut self) -> &mut HashSet<[u8; 32]> { &mut self.state_values } + #[inline] pub fn addresses(&mut self) -> &HashSet
{ &self.addresses } + #[inline] pub fn addresses_mut(&mut self) -> &mut HashSet
{ &mut self.addresses } } + /// Given a function and some state, it returns a strategy which generated valid calldata for the /// given function's input types, based on state taken from the EVM. -pub fn fuzz_calldata_from_state( - func: Function, - state: EvmFuzzState, -) -> BoxedStrategy { +pub fn fuzz_calldata_from_state(func: Function, state: EvmFuzzState) -> BoxedStrategy { let strats = func .inputs .iter() @@ -89,6 +90,7 @@ pub fn fuzz_calldata_from_state( .no_shrink() .boxed() } + /// Builds the initial [EvmFuzzState] from a database. pub fn build_initial_state( db: &CacheDB, @@ -105,12 +107,13 @@ pub fn build_initial_state( if config.include_push_bytes { if let Some(code) = &account.info.code { if state.addresses_mut().insert(address) { - for push_byte in collect_push_bytes(code.bytes().clone().0) { + for push_byte in collect_push_bytes(code.bytes()) { state.values_mut().insert(push_byte); } } } } + if config.include_storage { // Insert storage for (slot, value) in &account.storage { @@ -128,6 +131,7 @@ pub fn build_initial_state( } } } + // need at least some state data if db is empty otherwise we can't select random data for state // fuzzing if state.values().is_empty() { @@ -137,6 +141,7 @@ pub fn build_initial_state( Arc::new(RwLock::new(state)) } + /// Collects state changes from a [StateChangeset] and logs into an [EvmFuzzState] according to the /// given [FuzzDictionaryConfig]. pub fn collect_state_from_call( @@ -156,12 +161,13 @@ pub fn collect_state_from_call( // Insert push bytes if let Some(code) = &account.info.code { if state.addresses_mut().insert(*address) { - for push_byte in collect_push_bytes(code.bytes().clone().0) { + for push_byte in collect_push_bytes(code.bytes()) { state.values_mut().insert(push_byte); } } } } + if config.include_storage && state.state_values.len() < config.max_fuzz_dictionary_values { // Insert storage for (slot, value) in &account.storage { @@ -181,6 +187,7 @@ pub fn collect_state_from_call( } else { return } + // Insert log topics and data for log in logs { log.topics.iter().for_each(|topic| { @@ -196,13 +203,15 @@ pub fn collect_state_from_call( } } } + /// The maximum number of bytes we will look at in bytecodes to find push bytes (24 KiB). /// /// This is to limit the performance impact of fuzz tests that might deploy arbitrarily sized /// bytecode (as is the case with Solmate). const PUSH_BYTE_ANALYSIS_LIMIT: usize = 24 * 1024; + /// Collects all push bytes from the given bytecode. -fn collect_push_bytes(code: Bytes) -> Vec<[u8; 32]> { +fn collect_push_bytes(code: &[u8]) -> Vec<[u8; 32]> { let mut bytes: Vec<[u8; 32]> = Vec::new(); // We use [SpecId::LATEST] since we do not really care what spec it is - we are not interested // in gas costs. @@ -236,6 +245,7 @@ fn collect_push_bytes(code: Bytes) -> Vec<[u8; 32]> { } bytes } + /// Collects all created contracts from a StateChangeset which haven't been discovered yet. Stores /// them at `targeted_contracts` and `created_contracts`. pub fn collect_created_contracts( diff --git a/crates/evm/src/fuzz/strategies/uint.rs b/crates/evm-fuzz/src/strategies/uint.rs similarity index 100% rename from crates/evm/src/fuzz/strategies/uint.rs rename to crates/evm-fuzz/src/strategies/uint.rs diff --git a/crates/evm-traces/Cargo.toml b/crates/evm-traces/Cargo.toml new file mode 100644 index 0000000000000..78d1c3d6df00f --- /dev/null +++ b/crates/evm-traces/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "foundry-evm-traces" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +foundry-block-explorers.workspace = true +foundry-common.workspace = true +foundry-compilers.workspace = true +foundry-config.workspace = true +foundry-evm-executors.workspace = true +foundry-utils.workspace = true + +alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } +alloy-json-abi = { workspace = true } +alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } +ethers = { workspace = true, features = ["ethers-solc"] } +revm = { workspace = true, default-features = false, features = [ + "std", + "serde", + "memory_limit", + "optional_eip3607", + "optional_block_gas_limit", + "optional_no_base_fee", + "arbitrary", +] } + +eyre = "0.6" +futures = "0.3" +hashbrown = "0.14" +hex.workspace = true +itertools.workspace = true +once_cell = "1" +ordered-float = "4" +serde = "1" +tokio = { version = "1", features = ["time", "macros"] } +tracing = "0.1" +yansi = "0.5" + +[dev-dependencies] +ethers = { workspace = true, features = ["ethers-solc", "rustls"] } +foundry-utils.workspace = true +tempfile = "3" diff --git a/crates/evm/src/trace/decoder.rs b/crates/evm-traces/src/decoder.rs similarity index 82% rename from crates/evm/src/trace/decoder.rs rename to crates/evm-traces/src/decoder.rs index 652760c7c040f..d834735688b1a 100644 --- a/crates/evm/src/trace/decoder.rs +++ b/crates/evm-traces/src/decoder.rs @@ -1,18 +1,18 @@ -use super::{ - identifier::{AddressIdentity, SingleSignaturesIdentifier, TraceIdentifier}, - CallTraceArena, RawOrDecodedCall, RawOrDecodedLog, RawOrDecodedReturnData, -}; use crate::{ - abi::{CHEATCODE_ADDRESS, CONSOLE_ABI, HARDHAT_CONSOLE_ABI, HARDHAT_CONSOLE_ADDRESS, HEVM_ABI}, - decode, - executor::inspector::DEFAULT_CREATE2_DEPLOYER, - trace::{node::CallTraceNode, utils}, - CALLER, TEST_CONTRACT_ADDRESS, + identifier::{AddressIdentity, SingleSignaturesIdentifier, TraceIdentifier}, + node::CallTraceNode, + utils, CallTraceArena, RawOrDecodedCall, RawOrDecodedLog, RawOrDecodedReturnData, }; -use alloy_dyn_abi::{DynSolType, DynSolValue, EventExt}; -use alloy_json_abi::{Event, Function, JsonAbi as Abi, Param}; +use alloy_dyn_abi::{DynSolValue, EventExt}; +use alloy_json_abi::{Event, Function, JsonAbi as Abi}; use alloy_primitives::{Address, FixedBytes, B256}; use foundry_common::{abi::get_indexed_event, SELECTOR_LEN}; +use foundry_evm_executors::{ + abi::{CONSOLE_ABI, HARDHAT_CONSOLE_ABI, HEVM_ABI}, + decode, CALLER, CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS, + TEST_CONTRACT_ADDRESS, +}; +use foundry_utils::types::ToAlloy; use once_cell::sync::OnceCell; use std::collections::{BTreeMap, HashMap}; @@ -106,15 +106,17 @@ pub struct CallTraceDecoder { macro_rules! precompiles { ($($number:literal : $name:ident($( $name_in:ident : $in:expr ),* $(,)?) -> ($( $name_out:ident : $out:expr ),* $(,)?)),+ $(,)?) => {{ use std::string::String as RustString; + use ethers::abi::ParamType::*; [$( ( - Address::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, $number]), + alloy_primitives::Address::with_last_byte($number), #[allow(deprecated)] - Function { + ethers::abi::Function { name: RustString::from(stringify!($name)), - inputs: vec![$(Param { name: RustString::from(stringify!($name_in)), ty: $in, components: vec![], internal_type: None, }),*], - outputs: vec![$(Param { name: RustString::from(stringify!($name_out)), ty: $out, components: vec![], internal_type: None, }),*], - state_mutability: alloy_json_abi::StateMutability::Pure, + inputs: vec![$(ethers::abi::Param { name: RustString::from(stringify!($name_in)), kind: $in, internal_type: None, }),*], + outputs: vec![$(ethers::abi::Param { name: RustString::from(stringify!($name_out)), kind: $out, internal_type: None, }),*], + constant: None, + state_mutability: ethers::abi::StateMutability::Pure, }, ), )+] @@ -138,16 +140,16 @@ impl CallTraceDecoder { // TODO: These are the Ethereum precompiles. We should add a way to support precompiles // for other networks, too. precompiles: precompiles!( - 0x01: ecrecover(hash: format!("bytes32"), v: format!("uint256"), r: format!("uint256"), s: format!("uint256")) -> (publicAddress: format!("address")), - 0x02: sha256(data: format!("bytes")) -> (hash: format!("bytes32")), - 0x03: ripemd(data: format!("bytes")) -> (hash: format!("bytes32")), - 0x04: identity(data: format!("bytes")) -> (data: format!("bytes")), - 0x05: modexp(Bsize: format!("uint256"), Esize: format!("uint256"), Msize: format!("uint256"), BEM: format!("bytes")) -> (value: format!("bytes")), - 0x06: ecadd(x1: format!("uint256"), y1: format!("uint256"), x2: format!("uint256"), y2: format!("uint256")) -> (x: format!("uint256"), y: format!("uint256")), - 0x07: ecmul(x1: format!("uint256"), y1: format!("uint256"), s: format!("uint256")) -> (x: format!("uint256"), y: format!("uint256")), - 0x08: ecpairing(x1: format!("uint256"), y1: format!("uint256"), x2: format!("uint256"), y2: format!("uint256"), x3: format!("uint256"), y3: format!("uint256")) -> (success: format!("uint256")), - 0x09: blake2f(rounds: DynSolType::Uint(4).to_string(), h: DynSolType::FixedBytes(64).to_string(), m: DynSolType::FixedBytes(128).to_string(), t: DynSolType::FixedBytes(16).to_string(), f: DynSolType::FixedBytes(1).to_string()) -> (h: DynSolType::FixedBytes(64).to_string()), - ).into(), + 0x01: ecrecover(hash: FixedBytes(32), v: Uint(256), r: Uint(256), s: Uint(256)) -> (publicAddress: Address), + 0x02: sha256(data: Bytes) -> (hash: FixedBytes(32)), + 0x03: ripemd(data: Bytes) -> (hash: FixedBytes(32)), + 0x04: identity(data: Bytes) -> (data: Bytes), + 0x05: modexp(Bsize: Uint(256), Esize: Uint(256), Msize: Uint(256), BEM: Bytes) -> (value: Bytes), + 0x06: ecadd(x1: Uint(256), y1: Uint(256), x2: Uint(256), y2: Uint(256)) -> (x: Uint(256), y: Uint(256)), + 0x07: ecmul(x1: Uint(256), y1: Uint(256), s: Uint(256)) -> (x: Uint(256), y: Uint(256)), + 0x08: ecpairing(x1: Uint(256), y1: Uint(256), x2: Uint(256), y2: Uint(256), x3: Uint(256), y3: Uint(256)) -> (success: Uint(256)), + 0x09: blake2f(rounds: Uint(4), h: FixedBytes(64), m: FixedBytes(128), t: FixedBytes(16), f: FixedBytes(1)) -> (h: FixedBytes(64)), + ).into_iter().map(|(addr, func)| (addr, func.to_alloy())).collect(), contracts: Default::default(), @@ -163,12 +165,18 @@ impl CallTraceDecoder { functions: HARDHAT_CONSOLE_ABI .functions() .chain(HEVM_ABI.functions()) - .map(|func| (func.selector(), vec![func.clone()])) + .map(|func| { + let func = func.clone().to_alloy(); + (func.selector(), vec![func]) + }) .collect(), events: CONSOLE_ABI .events() - .map(|event| ((event.selector(), indexed_inputs(event)), vec![event.clone()])) + .map(|event| { + let event = event.clone().to_alloy(); + ((event.selector(), indexed_inputs(&event)), vec![event]) + }) .collect(), errors: Default::default(), @@ -196,7 +204,7 @@ impl CallTraceDecoder { }) } - fn collect_identities(&mut self, identities: Vec) { + fn collect_identities(&mut self, identities: Vec>) { for identity in identities { let address = identity.address; diff --git a/crates/evm/src/trace/identifier/etherscan.rs b/crates/evm-traces/src/identifier/etherscan.rs similarity index 98% rename from crates/evm/src/trace/identifier/etherscan.rs rename to crates/evm-traces/src/identifier/etherscan.rs index 40b71dfd42dc6..6f1f0db4b5f41 100644 --- a/crates/evm/src/trace/identifier/etherscan.rs +++ b/crates/evm-traces/src/identifier/etherscan.rs @@ -1,5 +1,4 @@ use super::{AddressIdentity, TraceIdentifier}; -use crate::utils::RuntimeOrHandle; use alloy_primitives::Address; use foundry_block_explorers::{ contract::{ContractMetadata, Metadata}, @@ -7,6 +6,7 @@ use foundry_block_explorers::{ }; use foundry_common::compile::{self, ContractSources}; use foundry_config::{Chain, Config}; +use foundry_evm_executors::utils::RuntimeOrHandle; use futures::{ future::{join_all, Future}, stream::{FuturesUnordered, Stream, StreamExt}, @@ -97,7 +97,7 @@ impl EtherscanIdentifier { } impl TraceIdentifier for EtherscanIdentifier { - fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec + fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> where A: Iterator)>, { @@ -152,7 +152,7 @@ type EtherscanFuture = /// A rate limit aware Etherscan client. /// /// Fetches information about multiple addresses concurrently, while respecting rate limits. -pub struct EtherscanFetcher { +struct EtherscanFetcher { /// The Etherscan client client: Arc, /// The time we wait if we hit the rate limit @@ -170,7 +170,7 @@ pub struct EtherscanFetcher { } impl EtherscanFetcher { - pub fn new( + fn new( client: Arc, timeout: Duration, concurrency: usize, @@ -187,7 +187,7 @@ impl EtherscanFetcher { } } - pub fn push(&mut self, address: Address) { + fn push(&mut self, address: Address) { self.queue.push(address); } diff --git a/crates/evm/src/trace/identifier/local.rs b/crates/evm-traces/src/identifier/local.rs similarity index 98% rename from crates/evm/src/trace/identifier/local.rs rename to crates/evm-traces/src/identifier/local.rs index cb2100b019d2a..8943ba7517d91 100644 --- a/crates/evm/src/trace/identifier/local.rs +++ b/crates/evm-traces/src/identifier/local.rs @@ -22,7 +22,7 @@ impl<'a> LocalTraceIdentifier<'a> { } impl TraceIdentifier for LocalTraceIdentifier<'_> { - fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec + fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> where A: Iterator)>, { diff --git a/crates/evm/src/trace/identifier/mod.rs b/crates/evm-traces/src/identifier/mod.rs similarity index 96% rename from crates/evm/src/trace/identifier/mod.rs rename to crates/evm-traces/src/identifier/mod.rs index 7362c07e4b140..3743cde8d6b64 100644 --- a/crates/evm/src/trace/identifier/mod.rs +++ b/crates/evm-traces/src/identifier/mod.rs @@ -1,3 +1,8 @@ +use alloy_json_abi::JsonAbi as Abi; +use alloy_primitives::Address; +use foundry_compilers::ArtifactId; +use std::borrow::Cow; + mod local; pub use local::LocalTraceIdentifier; @@ -7,11 +12,6 @@ pub use etherscan::EtherscanIdentifier; mod signatures; pub use signatures::{SignaturesIdentifier, SingleSignaturesIdentifier}; -use alloy_json_abi::JsonAbi as Abi; -use alloy_primitives::Address; -use foundry_compilers::ArtifactId; -use std::borrow::Cow; - /// An address identity pub struct AddressIdentity<'a> { /// The address this identity belongs to @@ -30,9 +30,8 @@ pub struct AddressIdentity<'a> { /// Trace identifiers figure out what ABIs and labels belong to all the addresses of the trace. pub trait TraceIdentifier { - // TODO: Update docs /// Attempts to identify an address in one or more call traces. - fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec + fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec> where A: Iterator)>; } diff --git a/crates/evm/src/trace/identifier/signatures.rs b/crates/evm-traces/src/identifier/signatures.rs similarity index 98% rename from crates/evm/src/trace/identifier/signatures.rs rename to crates/evm-traces/src/identifier/signatures.rs index d78ec03e8991c..5b5014af021cc 100644 --- a/crates/evm/src/trace/identifier/signatures.rs +++ b/crates/evm-traces/src/identifier/signatures.rs @@ -11,6 +11,12 @@ use tokio::sync::RwLock; pub type SingleSignaturesIdentifier = Arc>; +#[derive(Debug, Deserialize, Serialize, Default)] +struct CachedSignatures { + events: BTreeMap, + functions: BTreeMap, +} + /// An identifier that tries to identify functions and events using signatures found at /// `https://openchain.xyz`. #[derive(Debug)] @@ -151,12 +157,6 @@ impl Drop for SignaturesIdentifier { } } -#[derive(Debug, Deserialize, Serialize, Default)] -pub struct CachedSignatures { - pub events: BTreeMap, - pub functions: BTreeMap, -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/evm/src/executor/inspector/tracer.rs b/crates/evm-traces/src/inspector.rs similarity index 97% rename from crates/evm/src/executor/inspector/tracer.rs rename to crates/evm-traces/src/inspector.rs index 9e2848498b731..259da355b10a1 100644 --- a/crates/evm/src/executor/inspector/tracer.rs +++ b/crates/evm-traces/src/inspector.rs @@ -1,13 +1,13 @@ use crate::{ + CallTrace, CallTraceArena, CallTraceStep, LogCallOrder, RawOrDecodedCall, RawOrDecodedLog, + RawOrDecodedReturnData, +}; +use alloy_primitives::{Address, Bytes, Log as RawLog, B256, U256}; +use foundry_evm_executors::{ debug::Instruction::OpCode, - executor::inspector::utils::{gas_used, get_create_address}, - trace::{ - CallTrace, CallTraceArena, CallTraceStep, LogCallOrder, RawOrDecodedCall, RawOrDecodedLog, - RawOrDecodedReturnData, - }, + utils::{gas_used, get_create_address}, CallKind, }; -use alloy_primitives::{Address, Bytes, Log as RawLog, B256, U256}; use revm::{ interpreter::{ opcode, return_ok, CallInputs, CallScheme, CreateInputs, Gas, InstructionResult, diff --git a/crates/evm/src/trace/mod.rs b/crates/evm-traces/src/lib.rs similarity index 97% rename from crates/evm/src/trace/mod.rs rename to crates/evm-traces/src/lib.rs index 88bfc6d73a5db..3e91a13be0dea 100644 --- a/crates/evm/src/trace/mod.rs +++ b/crates/evm-traces/src/lib.rs @@ -1,11 +1,16 @@ -use crate::{ - abi::CHEATCODE_ADDRESS, debug::Instruction, trace::identifier::LocalTraceIdentifier, CallKind, -}; +//! # foundry-evm-traces +//! +//! EVM trace identifying and decoding. + +#![warn(unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] + +#[macro_use] +extern crate tracing; + use alloy_primitives::{Address, Bytes, Log as RawLog, B256, U256}; -pub use decoder::{CallTraceDecoder, CallTraceDecoderBuilder}; use ethers::types::{DefaultFrame, GethDebugTracingOptions, StructLog}; -pub use executor::TracingExecutor; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; +use foundry_evm_executors::{debug::Instruction, CallKind, CHEATCODE_ADDRESS}; use foundry_utils::types::ToEthers; use hashbrown::HashMap; use itertools::Itertools; @@ -22,9 +27,14 @@ use yansi::{Color, Paint}; /// /// Identifiers figure out what ABIs and labels belong to all the addresses of the trace. pub mod identifier; +use identifier::LocalTraceIdentifier; mod decoder; -mod executor; +pub use decoder::{CallTraceDecoder, CallTraceDecoderBuilder}; + +mod inspector; +pub use inspector::Tracer; + pub mod node; pub mod utils; @@ -112,7 +122,6 @@ impl CallTraceArena { contract_storage.insert(B256::from(key), B256::from(value)); log.storage = Some( contract_storage - .clone() .into_iter() .map(|t| (t.0.to_ethers(), t.1.to_ethers())) .collect(), @@ -192,7 +201,7 @@ const CALL: &str = "→ "; const RETURN: &str = "← "; impl fmt::Display for CallTraceArena { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn inner( arena: &CallTraceArena, writer: &mut (impl Write + ?Sized), @@ -276,7 +285,7 @@ pub enum RawOrDecodedLog { } impl fmt::Display for RawOrDecodedLog { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { RawOrDecodedLog::Raw(log) => { for (i, topic) in log.topics().iter().enumerate() { @@ -372,7 +381,7 @@ impl Default for RawOrDecodedReturnData { } impl fmt::Display for RawOrDecodedReturnData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self { RawOrDecodedReturnData::Raw(bytes) => { if bytes.is_empty() { @@ -508,7 +517,7 @@ impl Default for CallTrace { } impl fmt::Display for CallTrace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let address = self.address.to_checksum(None); if self.created() { write!( diff --git a/crates/evm/src/trace/node.rs b/crates/evm-traces/src/node.rs similarity index 96% rename from crates/evm/src/trace/node.rs rename to crates/evm-traces/src/node.rs index 2f0deb93684fb..dec50ed028dc2 100644 --- a/crates/evm/src/trace/node.rs +++ b/crates/evm-traces/src/node.rs @@ -1,18 +1,13 @@ use crate::{ - decode, - executor::CHEATCODE_ADDRESS, - trace::{ - utils, utils::decode_cheatcode_outputs, CallTrace, LogCallOrder, RawOrDecodedCall, - RawOrDecodedLog, RawOrDecodedReturnData, - }, - CallKind, + utils, utils::decode_cheatcode_outputs, CallTrace, LogCallOrder, RawOrDecodedCall, + RawOrDecodedLog, RawOrDecodedReturnData, }; use alloy_dyn_abi::{FunctionExt, JsonAbiExt}; use alloy_json_abi::{Function, JsonAbi as Abi}; use alloy_primitives::Address; use ethers::types::{Action, Call, CallResult, Create, CreateResult, Res, Suicide}; -// use super::trace_types::{Action, Call, CallResult, Create, CreateResult, Res, Suicide}; use foundry_common::SELECTOR_LEN; +use foundry_evm_executors::{decode, CallKind, CHEATCODE_ADDRESS}; use foundry_utils::types::ToEthers; use revm::interpreter::InstructionResult; use serde::{Deserialize, Serialize}; diff --git a/crates/evm/src/trace/utils.rs b/crates/evm-traces/src/utils.rs similarity index 99% rename from crates/evm/src/trace/utils.rs rename to crates/evm-traces/src/utils.rs index 6ad1879126b57..7be8fd64f3743 100644 --- a/crates/evm/src/trace/utils.rs +++ b/crates/evm-traces/src/utils.rs @@ -1,10 +1,10 @@ //! utilities used within tracing -use crate::decode; use alloy_dyn_abi::{DynSolType, DynSolValue, JsonAbiExt}; use alloy_json_abi::{Function, JsonAbi as Abi}; use alloy_primitives::Address; use foundry_common::{abi::format_token, SELECTOR_LEN}; +use foundry_evm_executors::decode; use std::collections::HashMap; /// Returns the label for the given [DynSolValue] diff --git a/crates/evm/Cargo.toml b/crates/evm/Cargo.toml index 2716133d914a5..bbe709d3cad3c 100644 --- a/crates/evm/Cargo.toml +++ b/crates/evm/Cargo.toml @@ -11,15 +11,16 @@ repository.workspace = true [dependencies] foundry-abi.workspace = true -foundry-utils.workspace = true +# foundry-cheatcodes.workspace = true foundry-common.workspace = true +foundry-compilers.workspace = true foundry-config.workspace = true +foundry-evm-coverage.workspace = true +foundry-evm-executors.workspace = true +foundry-evm-fuzz.workspace = true +foundry-evm-traces.workspace = true foundry-macros.workspace = true - -ethers = { workspace = true, features = ["ethers-solc"]} - -foundry-compilers = { workspace = true, default-features = false } -foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } +foundry-utils.workspace = true # Encoding/decoding serde_json = "1" @@ -35,45 +36,27 @@ thiserror = "1" tracing = "0.1" # Threading/futures -tokio = { version = "1", features = ["time", "macros"] } -parking_lot = "0.12" -futures = "0.3" once_cell = "1" # EVM -bytes = "1" hashbrown = { version = "0.14", features = ["serde"] } revm = { workspace = true, default-features = false, features = [ - "std", - "serde", - "memory_limit", - "optional_eip3607", - "optional_block_gas_limit", - "optional_no_base_fee", - "arbitrary", + "std", + "serde", + "memory_limit", + "optional_eip3607", + "optional_block_gas_limit", + "optional_no_base_fee", + "arbitrary", ] } alloy-primitives = { workspace = true, features = ["serde", "getrandom", "arbitrary", "rlp"] } alloy-dyn-abi = { workspace = true, features = ["arbitrary", "eip712"] } -alloy-json-abi = { workspace = true } -alloy-sol-types.workspace = true - -# Fuzzer -proptest = "1" - -# Display -yansi = "0.5" +alloy-json-abi.workspace = true +ethers = { workspace = true, features = ["ethers-solc"] } # Misc -url = "2" -auto_impl = "1" +bytes = "1.5" itertools.workspace = true -ordered-float = "4" +parking_lot = "0.12" +proptest = "1.3" walkdir = "2" - -# Coverage -semver = "1" - -[dev-dependencies] -ethers = { workspace = true, features = ["ethers-solc", "rustls"] } -foundry-utils.workspace = true -tempfile = "3" diff --git a/crates/evm/src/executor/abi/mod.rs b/crates/evm/src/executor/abi/mod.rs deleted file mode 100644 index ecd17a870beb2..0000000000000 --- a/crates/evm/src/executor/abi/mod.rs +++ /dev/null @@ -1,1212 +0,0 @@ -//! Several ABI-related utilities for executors. - -use alloy_json_abi::JsonAbi; -use alloy_primitives::Address; -pub use foundry_abi::{ - console::{self, ConsoleEvents}, - hardhat_console::{self, HardhatConsoleCalls}, - hevm::{self, HEVMCalls}, -}; -use once_cell::sync::Lazy; -use std::collections::HashMap; - -/// The cheatcode handler address (0x7109709ECfa91a80626fF3989D68f67F5b1DD12D). -/// -/// This is the same address as the one used in DappTools's HEVM. -/// `address(bytes20(uint160(uint256(keccak256('hevm cheat code')))))` -pub const CHEATCODE_ADDRESS: Address = Address::new([ - 0x71, 0x09, 0x70, 0x9E, 0xcf, 0xa9, 0x1a, 0x80, 0x62, 0x6f, 0xf3, 0x98, 0x9d, 0x68, 0xf6, 0x7f, - 0x5b, 0x1d, 0xd1, 0x2d, -]); - -/// The Hardhat console address (0x000000000000000000636F6e736F6c652e6c6f67). -/// -/// See: https://github.com/nomiclabs/hardhat/blob/master/packages/hardhat-core/console.sol -pub const HARDHAT_CONSOLE_ADDRESS: Address = Address::new([ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, - 0x2e, 0x6c, 0x6f, 0x67, -]); - -/// If the input starts with a known `hardhat/console.log` `uint` selector, then this will replace -/// it with the selector `abigen!` bindings expect. -pub fn patch_hardhat_console_selector(input: &mut Vec) { - if input.len() < 4 { - return - } - let selector = unsafe { &mut *(input.get_unchecked_mut(..4) as *mut [u8] as *mut [u8; 4]) }; - if let Some(abigen_selector) = HARDHAT_CONSOLE_SELECTOR_PATCHES.get(selector) { - *selector = *abigen_selector; - } -} - -pub static HEVM_ABI: Lazy = Lazy::new(|| { - // TODO: Move to file & find a way to sync with actual HEVM.sol - JsonAbi::parse([ - "function allowCheatcodes(address)", -"function tryFfi(string[])(tuple(int32,bytes,bytes))", -"function ffi(string[])(bytes)", -"function breakpoint(string)", -"function breakpoint(string,bool)", -"function roll(uint256)", -"function warp(uint256)", -"function difficulty(uint256)", -"function prevrandao(bytes32)", -"function fee(uint256)", -"function coinbase(address)", -"function store(address,bytes32,bytes32)", -"function load(address,bytes32)(bytes32)", -"function cool(address)", -"function setEnv(string,string)", -"function envBool(string)(bool)", -"function envUint(string)(uint256)", -"function envInt(string)(int256)", -"function envAddress(string)(address)", -"function envBytes32(string)(bytes32)", -"function envString(string)(string)", -"function envBytes(string)(bytes)", -"function envBool(string,string)(bool[])", -"function envUint(string,string)(uint256[])", -"function envInt(string,string)(int256[])", -"function envAddress(string,string)(address[])", -"function envBytes32(string,string)(bytes32[])", -"function envString(string,string)(string[])", -"function envBytes(string,string)(bytes[])", -"function envOr(string,bool)(bool)", -"function envOr(string,uint256)(uint256)", -"function envOr(string,int256)(int256)", -"function envOr(string,address)(address)", -"function envOr(string,bytes32)(bytes32)", -"function envOr(string,string)(string)", -"function envOr(string,bytes)(bytes)", -"function envOr(string,string,bool[])(bool[])", -"function envOr(string,string,uint256[])(uint256[])", -"function envOr(string,string,int256[])(int256[])", -"function envOr(string,string,address[])(address[])", -"function envOr(string,string,bytes32[])(bytes32[])", -"function envOr(string,string,string[])(string[])", -"function envOr(string,string,bytes[])(bytes[])", -"function addr(uint256)(address)", -"function sign(uint256,bytes32)(uint8,bytes32,bytes32)", -"function deriveKey(string,uint32)(uint256)", -"function deriveKey(string,string,uint32)(uint256)", -"function deriveKey(string,uint32,string)(uint256)", -"function deriveKey(string,string,uint32,string)(uint256)", -"function rememberKey(uint256)(address)", -"function createWallet(string)(tuple(address,uint256,uint256,uint256))", -"function createWallet(uint256)(tuple(address,uint256,uint256,uint256))", -"function createWallet(uint256,string)(tuple(address,uint256,uint256,uint256))", -"function sign(tuple(address,uint256,uint256,uint256),bytes32)(uint8,bytes32,bytes32)", -"function getNonce(tuple(address,uint256,uint256,uint256))(uint64)", -"function prank(address)", -"function prank(address,address)", -"function readCallers()(uint256,address,address)", -"function startPrank(address)", -"function startPrank(address,address)", -"function stopPrank()", -"function deal(address,uint256)", -"function etch(address,bytes)", -"function expectRevert()", -"function expectRevert(bytes)", -"function expectRevert(bytes4)", -"function record()", -"function accesses(address)(bytes32[], bytes32[])", -"function skip(bool)", -"function recordLogs()", -"function getRecordedLogs()(tuple(bytes32,bytes)[])", -"function expectEmit()", -"function expectEmit(address)", -"function expectEmit(bool,bool,bool,bool)", -"function expectEmit(bool,bool,bool,bool,address)", -"function mockCall(address,bytes,bytes)", -"function mockCall(address,uint256,bytes,bytes)", -"function mockCallRevert(address,bytes,bytes)", -"function mockCallRevert(address,uint256,bytes,bytes)", -"function clearMockedCalls()", -"function expectCall(address,bytes)", -"function expectCall(address,bytes,uint64)", -"function expectCall(address,uint256,bytes)", -"function expectCall(address,uint256,bytes,uint64)", -"function expectCall(address,uint256,uint64,bytes)", -"function expectCall(address,uint256,uint64,bytes,uint64)", -"function expectCallMinGas(address,uint256,uint64,bytes)", -"function expectCallMinGas(address,uint256,uint64,bytes,uint64)", -"function expectSafeMemory(uint64,uint64)", -"function expectSafeMemoryCall(uint64,uint64)", -"function getCode(string)", -"function getDeployedCode(string)", -"function label(address,string)", -"function getLabel(address)(string)", -"function assume(bool)", -"function setNonce(address,uint64)", -"function getNonce(address)", -"function resetNonce(address)", -"function setNonceUnsafe(address,uint64)", -"function chainId(uint256)", -"function txGasPrice(uint256)", -"function broadcast()", -"function broadcast(address)", -"function broadcast(uint256)", -"function startBroadcast()", -"function startBroadcast(address)", -"function startBroadcast(uint256)", -"function stopBroadcast()", -"function projectRoot()(string)", -"function openFile(string)", -"function readFile(string)(string)", -"function readFileBinary(string)(bytes)", -"function readLine(string)(string)", -"function writeFile(string,string)", -"function writeFileBinary(string,bytes)", -"function writeLine(string,string)", -"function copyFile(string,string)", -"function closeFile(string)", -"function removeFile(string)", -"function createDir(string, bool)", -"function removeDir(string, bool)", -"function readDir(string)(tuple(string,string,uint64,bool,bool)[])", -"function readDir(string, uint64)(tuple(string,string,uint64,bool,bool)[])", -"function readDir(string, uint64, bool)(tuple(string,string,uint64,bool,bool)[])", -"function readLink(string)(string)", -"function fsMetadata(string)(tuple(bool,bool,uint256,bool,uint256,uint256,uint256))", -"function exists(string)(bool)", -"function isFile(string)(bool)", -"function isDir(string)(bool)", -"function toString(bytes)", -"function toString(address)", -"function toString(uint256)", -"function toString(int256)", -"function toString(bytes32)", -"function toString(bool)", -"function parseBytes(string)(bytes)", -"function parseAddress(string)(address)", -"function parseUint(string)(uint256)", -"function parseInt(string)(int256)", -"function parseBytes32(string)(bytes32)", -"function parseBool(string)(bool)", -"function snapshot()(uint256)", -"function revertTo(uint256)(bool)", -"function createFork(string,uint256)(uint256)", -"function createFork(string,bytes32)(uint256)", -"function createFork(string)(uint256)", -"function createSelectFork(string,uint256)(uint256)", -"function createSelectFork(string,bytes32)(uint256)", -"function createSelectFork(string)(uint256)", -"function selectFork(uint256)", -"function activeFork()(uint256)", -"function transact(bytes32)", -"function transact(uint256,bytes32)", -"function makePersistent(address)", -"function makePersistent(address,address)", -"function makePersistent(address,address,address)", -"function makePersistent(address[])", -"function revokePersistent(address)", -"function revokePersistent(address[])", -"function isPersistent(address)(bool)", -"function rollFork(uint256)", -"function rollFork(bytes32)", -"function rollFork(uint256,uint256)", -"function rollFork(uint256,bytes32)", -"function rpcUrl(string)(string)", -"function rpcUrls()(string[2][])", -"function rpcUrlStructs()(tuple(string,string)[])", -"function eth_getLogs(uint256,uint256,address,bytes32[])(tuple(address,bytes32[],bytes,uint256,bytes32,uint256,bytes32,uint256,bool)[])", -"function rpc(string,string)(bytes)", -"function writeJson(string, string)", -"function writeJson(string, string, string)", -"function parseJson(string)(bytes)", -"function parseJson(string, string)(bytes)", -"function parseJsonKeys(string, string)(string[])", -"function parseJsonUint(string, string)(uint256)", -"function parseJsonUintArray(string, string)(uint256[])", -"function parseJsonInt(string, string)(int256)", -"function parseJsonIntArray(string, string)(int256[])", -"function parseJsonString(string, string)(string)", -"function parseJsonStringArray(string, string)(string[])", -"function parseJsonAddress(string, string)(address)", -"function parseJsonAddressArray(string, string)(address[])", -"function parseJsonBool(string, string)(bool)", -"function parseJsonBoolArray(string, string)(bool[])", -"function parseJsonBytes(string, string)(bytes)", -"function parseJsonBytesArray(string, string)(bytes[])", -"function parseJsonBytes32(string, string)(bytes32)", -"function parseJsonBytes32Array(string, string)(bytes32[])", -"function serializeJson(string,string)(string)", -"function serializeBool(string,string,bool)(string)", -"function serializeBool(string,string,bool[])(string)", -"function serializeUint(string,string,uint256)(string)", -"function serializeUint(string,string,uint256[])(string)", -"function serializeInt(string,string,int256)(string)", -"function serializeInt(string,string,int256[])(string)", -"function serializeAddress(string,string,address)(string)", -"function serializeAddress(string,string,address[])(string)", -"function serializeBytes32(string,string,bytes32)(string)", -"function serializeBytes32(string,string,bytes32[])(string)", -"function serializeString(string,string,string)(string)", -"function serializeString(string,string,string[])(string)", -"function serializeBytes(string,string,bytes)(string)", -"function serializeBytes(string,string,bytes[])(string)", -"function keyExists(string,string)(bool)", -"function pauseGasMetering()", -"function resumeGasMetering()", -"function startMappingRecording()", -"function stopMappingRecording()", -"function getMappingLength(address,bytes32)", -"function getMappingSlotAt(address,bytes32,uint256)", -"function getMappingKeyAndParentOf(address,bytes32)", -"function sleep(uint256)", -"function unixTime()(uint256)", - ]).expect("Could not parse HEVM abi") -}); - -pub static HARDHAT_CONSOLE_ABI: Lazy = Lazy::new(|| { - // TODO: Move to file - JsonAbi::parse([ - "function log(address p0, address p1, string p2)", - "function log(bool p0, uint256 p1, uint256 p2, address p3)", - "function log(address p0, address p1, address p2)", - "function log(uint256 p0, address p1, address p2, string p3)", - "function log(string p0, address p1, bool p2, string p3)", - "function log(uint256 p0, bool p1, address p2, uint256 p3)", - "function log(bool p0, address p1, bool p2, uint256 p3)", - "function log(bool p0, uint256 p1, address p2)", - "function log(uint256 p0, address p1, address p2, bool p3)", - "function log(address p0, bool p1, uint256 p2, string p3)", - "function log(bool p0, bool p1, uint256 p2, uint256 p3)", - "function log(bool p0, address p1, address p2, uint256 p3)", - "function log(uint256 p0, address p1, uint256 p2, uint256 p3)", - "function log(string p0, address p1, uint256 p2)", - "function log(address p0, string p1, address p2, address p3)", - "function log(address p0, string p1, address p2, bool p3)", - "function log(address p0, address p1, address p2, bool p3)", - "function log(address p0, string p1, uint256 p2, bool p3)", - "function log(address p0, uint256 p1, address p2, uint256 p3)", - "function log(string p0, string p1, uint256 p2, address p3)", - "function log(bool p0, bool p1, address p2)", - "function log(bool p0, string p1, uint256 p2)", - "function log(bool p0, string p1, address p2, string p3)", - "function log(bool p0, bool p1, uint256 p2)", - "function log(bool p0, address p1, uint256 p2, address p3)", - "function log(bool p0, uint256 p1, address p2, uint256 p3)", - "function log(bool p0, string p1, uint256 p2, address p3)", - "function log(address p0, string p1, string p2, uint256 p3)", - "function log(uint256 p0, address p1, uint256 p2, address p3)", - "function log(uint256 p0, uint256 p1, address p2, bool p3)", - "function log(bool p0, string p1, bool p2, uint256 p3)", - "function log(bool p0, string p1, string p2, string p3)", - "function log(address p0, address p1, uint256 p2)", - "function log(bool p0, address p1, bool p2)", - "function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3)", - "function log(address p0, bool p1, string p2, address p3)", - "function log(bool p0, string p1, uint256 p2, string p3)", - "function log(bool p0, uint256 p1, address p2, string p3)", - "function log(bool p0, address p1, bool p2, address p3)", - "function log(string p0, uint256 p1, address p2)", - "function log(uint256 p0, bool p1)", - "function log(bool p0, address p1, address p2, address p3)", - "function log(address p0, uint256 p1, address p2, string p3)", - "function log(address p0, string p1, uint256 p2, uint256 p3)", - "function log(bool p0, string p1, string p2, bool p3)", - "function log(uint256 p0, bool p1, uint256 p2)", - "function log(address p0, string p1, bool p2, address p3)", - "function log(uint256 p0, bool p1, bool p2)", - "function log(address p0, uint256 p1, uint256 p2, address p3)", - "function log(address p0, bool p1, string p2)", - "function log(uint256 p0, string p1, string p2, string p3)", - "function log(address p0, address p1, string p2, string p3)", - "function log(string p0, address p1, bool p2, address p3)", - "function log(address p0, uint256 p1, bool p2, uint256 p3)", - "function log(string p0, address p1, string p2, string p3)", - "function log(uint256 p0, address p1, address p2, address p3)", - "function log(string p0, bool p1, string p2, uint256 p3)", - "function log(bool p0, bool p1, string p2)", - "function log(bool p0, uint256 p1, address p2, address p3)", - "function log(uint256 p0, uint256 p1, string p2, string p3)", - "function log(bool p0, string p1, uint256 p2, uint256 p3)", - "function log(bool p0, bool p1)", - "function log(bool p0, bool p1, bool p2, string p3)", - "function log(bool p0, string p1, address p2, address p3)", - "function log(string p0, string p1, string p2, bool p3)", - "function log(uint256 p0, bool p1, string p2, uint256 p3)", - "function log(address p0)", - "function log(address p0, address p1, bool p2, bool p3)", - "function log(string p0, string p1, string p2)", - "function log(string p0, bool p1, address p2, string p3)", - "function log(address p0, bool p1, address p2, string p3)", - "function log(string p0, address p1)", - "function log(bool p0)", - "function log(string p0, bool p1, address p2, address p3)", - "function log(address p0, uint256 p1, uint256 p2, uint256 p3)", - "function log(uint256 p0, bool p1, address p2)", - "function log(string p0, uint256 p1, bool p2, bool p3)", - "function log(address p0, string p1, string p2, bool p3)", - "function log(bool p0, uint256 p1, uint256 p2)", - "function log(bool p0, uint256 p1, uint256 p2, uint256 p3)", - "function log(uint256 p0, string p1, uint256 p2)", - "function log(address p0, bool p1, uint256 p2, uint256 p3)", - "function log(address p0, address p1, bool p2, uint256 p3)", - "function log(bool p0, uint256 p1)", - "function log(uint256 p0, string p1, uint256 p2, address p3)", - "function log(bool p0, bool p1, bool p2, bool p3)", - "function log(address p0, uint256 p1, bool p2, bool p3)", - "function log(uint256 p0, address p1, string p2, string p3)", - "function log(string p0, address p1, bool p2, uint256 p3)", - "function log(string p0, bool p1, string p2, bool p3)", - "function log(string p0, string p1, bool p2, bool p3)", - "function log(string p0)", - "function log(uint256 p0, uint256 p1, string p2, address p3)", - "function log(string p0, string p1, address p2, address p3)", - "function log(address p0, string p1, uint256 p2, string p3)", - "function log(uint256 p0, bool p1, address p2, bool p3)", - "function log(address p0, string p1, address p2, uint256 p3)", - "function log(bool p0, address p1, address p2, bool p3)", - "function log(uint256 p0, address p1, string p2, uint256 p3)", - "function log(address p0, bool p1, string p2, string p3)", - "function log(uint256 p0, uint256 p1, bool p2)", - "function log(address p0, uint256 p1, address p2, address p3)", - "function log(bool p0, string p1, bool p2, string p3)", - "function log(address p0, uint256 p1, uint256 p2, string p3)", - "function log(bool p0, address p1, bool p2, string p3)", - "function log(string p0, string p1)", - "function log(bool p0, bool p1, address p2, uint256 p3)", - "function log(uint256 p0, string p1, bool p2)", - "function log(string p0, uint256 p1, address p2, uint256 p3)", - "function log(bool p0, bool p1, bool p2)", - "function log(address p0, bool p1, string p2, bool p3)", - "function log(address p0, string p1, bool p2, uint256 p3)", - "function log()", - "function log(bool p0, address p1, uint256 p2, string p3)", - "function log(bool p0, string p1, bool p2, address p3)", - "function log(bool p0, bool p1, uint256 p2, address p3)", - "function log(uint256 p0, uint256 p1, address p2, address p3)", - "function log(string p0, string p1, uint256 p2)", - "function log(string p0, uint256 p1, string p2)", - "function log(uint256 p0, uint256 p1, uint256 p2, string p3)", - "function log(string p0, address p1, uint256 p2, string p3)", - "function log(uint256 p0, address p1, uint256 p2)", - "function log(string p0, uint256 p1, string p2, string p3)", - "function log(uint256 p0, address p1, bool p2, uint256 p3)", - "function log(address p0, uint256 p1, string p2, address p3)", - "function log(uint256 p0, uint256 p1, address p2)", - "function log(string p0, string p1, address p2, bool p3)", - "function log(address p0, string p1, string p2, string p3)", - "function log(string p0, bool p1, address p2, uint256 p3)", - "function log(string p0, string p1, uint256 p2, string p3)", - "function log(uint256 p0, uint256 p1, string p2, uint256 p3)", - "function log(string p0, string p1, bool p2, string p3)", - "function log(string p0, uint256 p1, address p2, address p3)", - "function log(string p0, address p1, string p2, bool p3)", - "function log(address p0, string p1, bool p2, bool p3)", - "function log(uint256 p0, address p1, uint256 p2, bool p3)", - "function log(bool p0, address p1, uint256 p2)", - "function log(uint256 p0, string p1, address p2, address p3)", - "function log(bool p0, bool p1, uint256 p2, bool p3)", - "function log(address p0, string p1, uint256 p2, address p3)", - "function log(uint256 p0, address p1, string p2)", - "function log(string p0, address p1, uint256 p2, address p3)", - "function log(uint256 p0, string p1)", - "function log(string p0, bool p1, uint256 p2, uint256 p3)", - "function log(address p0, bool p1, address p2, address p3)", - "function log(address p0, address p1, address p2, address p3)", - "function log(address p0, uint256 p1, uint256 p2, bool p3)", - "function log(address p0, uint256 p1, bool p2)", - "function log(address p0, string p1, uint256 p2)", - "function log(uint256 p0, bool p1, string p2, string p3)", - "function log(uint256 p0, string p1, uint256 p2, bool p3)", - "function log(uint256 p0, address p1)", - "function log(uint256 p0, bool p1, bool p2, address p3)", - "function log(bool p0, uint256 p1, string p2, uint256 p3)", - "function log(bool p0, address p1, bool p2, bool p3)", - "function log(bool p0, string p1, uint256 p2, bool p3)", - "function log(uint256 p0, uint256 p1, address p2, string p3)", - "function log(bool p0, bool p1, string p2, string p3)", - "function log(string p0, string p1, string p2, address p3)", - "function log(bool p0, bool p1, bool p2, uint256 p3)", - "function log(bool p0, string p1, address p2, bool p3)", - "function log(address p0, address p1, string p2, bool p3)", - "function log(bool p0, address p1, string p2, address p3)", - "function log(string p0, bool p1, bool p2, address p3)", - "function log(uint256 p0, uint256 p1, string p2)", - "function log(uint256 p0, address p1, address p2, uint256 p3)", - "function log(string p0, bool p1, uint256 p2, string p3)", - "function log(uint256 p0, bool p1, bool p2, uint256 p3)", - "function log(address p0, string p1)", - "function log(address p0, bool p1)", - "function log(string p0, uint256 p1, uint256 p2, bool p3)", - "function log(string p0, address p1, bool p2, bool p3)", - "function log(uint256 p0, uint256 p1, string p2, bool p3)", - "function log(uint256 p0, string p1, address p2)", - "function log(address p0, uint256 p1, address p2)", - "function log(bool p0, string p1, string p2, uint256 p3)", - "function log(bool p0, address p1, uint256 p2, uint256 p3)", - "function log(string p0, uint256 p1, string p2, address p3)", - "function log(string p0, string p1, address p2, uint256 p3)", - "function log(string p0, uint256 p1, string p2, bool p3)", - "function log(bool p0, bool p1, uint256 p2, string p3)", - "function log(bool p0, uint256 p1, bool p2, uint256 p3)", - "function log(string p0, address p1, address p2, string p3)", - "function log(address p0, bool p1, string p2, uint256 p3)", - "function log(string p0, uint256 p1, address p2, bool p3)", - "function log(uint256 p0, string p1, uint256 p2, uint256 p3)", - "function log(address p0, uint256 p1)", - "function log(string p0, bool p1, bool p2)", - "function log(bool p0, address p1)", - "function log(string p0, uint256 p1, uint256 p2, string p3)", - "function log(uint256 p0, bool p1, string p2)", - "function log(address p0, uint256 p1, string p2, string p3)", - "function log(uint256 p0, bool p1, uint256 p2, address p3)", - "function log(uint256 p0, uint256 p1, address p2, uint256 p3)", - "function log(string p0, bool p1, bool p2, bool p3)", - "function log(string p0, bool p1, uint256 p2, bool p3)", - "function log(bool p0, bool p1, bool p2, address p3)", - "function log(address p0, bool p1, bool p2, uint256 p3)", - "function log(address p0, address p1, uint256 p2, address p3)", - "function log(string p0, bool p1, bool p2, uint256 p3)", - "function log(bool p0, uint256 p1, uint256 p2, string p3)", - "function log(string p0, string p1, string p2, uint256 p3)", - "function log(string p0, address p1, address p2, uint256 p3)", - "function log(address p0, address p1, string p2, address p3)", - "function log(bool p0, string p1)", - "function log(uint256 p0, string p1, address p2, bool p3)", - "function log(uint256 p0, address p1, bool p2, string p3)", - "function log(bool p0, uint256 p1, bool p2, string p3)", - "function log(uint256 p0, bool p1, uint256 p2, bool p3)", - "function log(string p0, address p1, string p2, uint256 p3)", - "function log(string p0, bool p1, address p2)", - "function log(string p0, bool p1, uint256 p2, address p3)", - "function log(address p0, address p1, address p2, uint256 p3)", - "function log(string p0, bool p1, address p2, bool p3)", - "function log(bool p0, string p1, address p2)", - "function log(string p0, string p1, address p2)", - "function log(bool p0, string p1, string p2, address p3)", - "function log(uint256 p0, uint256 p1, bool p2, address p3)", - "function log(bool p0, uint256 p1, bool p2, address p3)", - "function log(address p0, address p1, uint256 p2, bool p3)", - "function log(uint256 p0, address p1, bool p2)", - "function log(uint256 p0, string p1, address p2, string p3)", - "function log(address p0, bool p1, uint256 p2)", - "function log(uint256 p0, address p1, string p2, address p3)", - "function log(string p0, bool p1, bool p2, string p3)", - "function log(address p0, address p1, bool p2, address p3)", - "function log(string p0, uint256 p1, address p2, string p3)", - "function log(address p0, string p1, string p2, address p3)", - "function log(bool p0, bool p1, address p2, string p3)", - "function log(address p0, uint256 p1, address p2, bool p3)", - "function log(uint256 p0, bool p1, address p2, address p3)", - "function log(address p0, uint256 p1, string p2)", - "function log(address p0, uint256 p1, bool p2, address p3)", - "function log(uint256 p0, uint256 p1, bool p2, string p3)", - "function log(bool p0, string p1, address p2, uint256 p3)", - "function log(address p0, bool p1, address p2, bool p3)", - "function log(bool p0, address p1, string p2, string p3)", - "function log(address p0, bool p1, address p2, uint256 p3)", - "function log(string p0, uint256 p1, uint256 p2, uint256 p3)", - "function log(string p0, bool p1, string p2, string p3)", - "function log(address p0, address p1, bool p2, string p3)", - "function log(string p0, address p1, string p2, address p3)", - "function log(uint256 p0, uint256 p1, bool p2, bool p3)", - "function log(string p0, uint256 p1, bool p2, string p3)", - "function log(uint256 p0, bool p1, address p2, string p3)", - "function log(uint256 p0, string p1, bool p2, address p3)", - "function log(uint256 p0, string p1, string p2, uint256 p3)", - "function log(bool p0, string p1, string p2)", - "function log(string p0, string p1, bool p2)", - "function log(uint256 p0, string p1, string p2)", - "function log(uint256 p0, string p1, string p2, bool p3)", - "function log(bool p0, uint256 p1, address p2, bool p3)", - "function log(string p0, address p1, address p2, bool p3)", - "function log(string p0, uint256 p1)", - "function log(address p0, uint256 p1, uint256 p2)", - "function log(uint256 p0, bool p1, bool p2, bool p3)", - "function log(uint256 p0, string p1, uint256 p2, string p3)", - "function log(bool p0, bool p1, string p2, bool p3)", - "function log(uint256 p0, string p1, bool p2, bool p3)", - "function log(address p0, string p1, bool p2, string p3)", - "function log(uint256 p0, address p1, address p2)", - "function log(address p0, address p1, uint256 p2, uint256 p3)", - "function log(bool p0, uint256 p1, uint256 p2, bool p3)", - "function log(address p0, uint256 p1, string p2, uint256 p3)", - "function log(bool p0, bool p1, address p2, bool p3)", - "function log(bool p0, address p1, string p2, uint256 p3)", - "function log(string p0, string p1, bool p2, address p3)", - "function log(string p0, string p1, uint256 p2, bool p3)", - "function log(string p0, bool p1)", - "function log(bool p0, uint256 p1, string p2)", - "function log(address p0, bool p1, uint256 p2, bool p3)", - "function log(uint256 p0, uint256 p1, uint256 p2, bool p3)", - "function log(address p0, uint256 p1, bool p2, string p3)", - "function log(string p0, uint256 p1, string p2, uint256 p3)", - "function log(uint256 p0, bool p1, uint256 p2, uint256 p3)", - "function log(string p0, address p1, bool p2)", - "function log(string p0, bool p1, uint256 p2)", - "function log(string p0, uint256 p1, uint256 p2)", - "function log(string p0, uint256 p1, bool p2)", - "function log(address p0, bool p1, bool p2, bool p3)", - "function log(uint256 p0, address p1, string p2, bool p3)", - "function log(address p0, bool p1, uint256 p2, address p3)", - "function log(bool p0, uint256 p1, bool p2, bool p3)", - "function log(uint256 p0, string p1, bool p2, uint256 p3)", - "function log(address p0, string p1, bool p2)", - "function log(address p0, uint256 p1, string p2, bool p3)", - "function log(address p0, bool p1, bool p2, address p3)", - "function log(uint256 p0, uint256 p1, uint256 p2)", - "function log(bool p0, address p1, address p2)", - "function log(uint256 p0, string p1, bool p2, string p3)", - "function log(uint256 p0, string p1, string p2, address p3)", - "function log(bool p0, address p1, uint256 p2, bool p3)", - "function log(string p0, string p1, bool p2, uint256 p3)", - "function log(bool p0, address p1, address p2, string p3)", - "function log(address p0, address p1)", - "function log(bool p0, string p1, bool p2)", - "function log(bool p0, string p1, bool p2, bool p3)", - "function log(uint256 p0, address p1, uint256 p2, string p3)", - "function log(uint256 p0, bool p1, bool p2, string p3)", - "function log(uint256 p0, bool p1, uint256 p2, string p3)", - "function log(string p0, string p1, string p2, string p3)", - "function log(bool p0, address p1, string p2)", - "function log(address p0, bool p1, bool p2, string p3)", - "function log(string p0, bool p1, string p2, address p3)", - "function log(string p0, uint256 p1, bool p2, address p3)", - "function log(string p0, address p1, string p2)", - "function log(string p0, uint256 p1, uint256 p2, address p3)", - "function log(string p0, bool p1, string p2)", - "function log(bool p0, address p1, string p2, bool p3)", - "function log(uint256 p0, address p1, bool p2, bool p3)", - "function log(bool p0, bool p1, string p2, uint256 p3)", - "function log(string p0, uint256 p1, bool p2, uint256 p3)", - "function log(bool p0, uint256 p1, string p2, bool p3)", - "function log(uint256 p0, string p1, address p2, uint256 p3)", - "function log(bool p0, uint256 p1, bool p2)", - "function log(string p0, string p1, address p2, string p3)", - "function log(uint256 p0, uint256 p1, bool p2, uint256 p3)", - "function log(address p0, bool p1, bool p2)", - "function log(uint256 p0, bool p1, string p2, bool p3)", - "function log(string p0, address p1, address p2, address p3)", - "function log(address p0, address p1, string p2, uint256 p3)", - "function log(uint256 p0, bool p1, string p2, address p3)", - "function log(uint256 p0, address p1, bool p2, address p3)", - "function log(address p0, string p1, address p2)", - "function log(address p0, bool p1, address p2)", - "function log(address p0, address p1, bool p2)", - "function log(string p0, string p1, uint256 p2, uint256 p3)", - "function log(bool p0, bool p1, address p2, address p3)", - "function log(bool p0, uint256 p1, string p2, string p3)", - "function log(uint256 p0, uint256 p1)", - "function log(address p0, string p1, address p2, string p3)", - "function log(address p0, address p1, address p2, string p3)", - "function log(uint256 p0)", - "function log(string p0, address p1, uint256 p2, uint256 p3)", - "function log(bool p0, bool p1, string p2, address p3)", - "function log(uint256 p0, uint256 p1, uint256 p2, address p3)", - "function log(address p0, string p1, string p2)", - "function log(string p0, address p1, uint256 p2, bool p3)", - "function log(string p0, address p1, address p2)", - "function log(address p0, address p1, uint256 p2, string p3)", - "function log(bool p0, uint256 p1, string p2, address p3)", - "function logAddress(address p0)", - "function logBool(bool p0)", - "function logBytes(bytes p0)", - "function logBytes1(bytes1 p0)", - "function logBytes10(bytes10 p0)", - "function logBytes11(bytes11 p0)", - "function logBytes12(bytes12 p0)", - "function logBytes13(bytes13 p0)", - "function logBytes14(bytes14 p0)", - "function logBytes15(bytes15 p0)", - "function logBytes16(bytes16 p0)", - "function logBytes17(bytes17 p0)", - "function logBytes18(bytes18 p0)", - "function logBytes19(bytes19 p0)", - "function logBytes2(bytes2 p0)", - "function logBytes20(bytes20 p0)", - "function logBytes21(bytes21 p0)", - "function logBytes22(bytes22 p0)", - "function logBytes23(bytes23 p0)", - "function logBytes24(bytes24 p0)", - "function logBytes25(bytes25 p0)", - "function logBytes26(bytes26 p0)", - "function logBytes27(bytes27 p0)", - "function logBytes28(bytes28 p0)", - "function logBytes29(bytes29 p0)", - "function logBytes3(bytes3 p0)", - "function logBytes30(bytes30 p0)", - "function logBytes31(bytes31 p0)", - "function logBytes32(bytes32 p0)", - "function logBytes4(bytes4 p0)", - "function logBytes5(bytes5 p0)", - "function logBytes6(bytes6 p0)", - "function logBytes7(bytes7 p0)", - "function logBytes8(bytes8 p0)", - "function logBytes9(bytes9 p0)", - "function logInt(int256 p0)", - "function logString(string p0)", - "function logUint(uint256 p0)", - "function log(int256 p0)", - "function log(string p0, int256 p1)", - ]) - .expect("Could not parse HardhatConsole abi") -}); - -pub static CONSOLE_ABI: Lazy = Lazy::new(|| { - JsonAbi::parse([ - "event log(string)", - "event logs (bytes)", - "event log_address (address)", - "event log_bytes32 (bytes32)", - "event log_int (int)", - "event log_uint (uint)", - "event log_bytes (bytes)", - "event log_string (string)", - "event log_array (uint256[] val)", - "event log_array (int256[] val)", - "event log_array (address[] val)", - "event log_named_address (string key, address val)", - "event log_named_decimal_int (string key, int val, uint decimals)", - "event log_named_bytes32 (string key, bytes32 val)", - "event log_named_decimal_uint (string key, uint val, uint decimals)", - "event log_named_int (string key, int val)", - "event log_named_uint (string key, uint val)", - "event log_named_bytes (string key, bytes val)", - "event log_named_string (string key, string val)", - "event log_named_array (string key, uint256[] val)", - "event log_named_array (string key, int256[] val)", - "event log_named_array (string key, address[] val)", - ]) - .expect("Could not parase console ABI") -}); - -/// This contains a map with all the `hardhat/console.log` log selectors that use `uint` or `int` -/// as key and the selector of the call with `uint256`, -/// -/// This is a bit terrible but a workaround for the differing selectors used by hardhat and the call -/// bindings which `abigen!` creates. `hardhat/console.log` logs its events in functions that accept -/// `uint` manually as `abi.encodeWithSignature("log(int)", p0)`, but `abigen!` uses `uint256` for -/// its call bindings (`HardhatConsoleCalls`) as generated by solc. -pub static HARDHAT_CONSOLE_SELECTOR_PATCHES: Lazy> = Lazy::new(|| { - HashMap::from( - [ - // log(bool,uint256,uint256,address) - ([241, 97, 178, 33], [0, 221, 135, 185]), - // log(uint256,address,address,string) - ([121, 67, 220, 102], [3, 28, 111, 115]), - // log(uint256,bool,address,uint256) - ([65, 181, 239, 59], [7, 130, 135, 245]), - // log(bool,address,bool,uint256) - ([76, 182, 15, 209], [7, 131, 21, 2]), - // log(bool,uint256,address) - ([196, 210, 53, 7], [8, 142, 249, 210]), - // log(uint256,address,address,bool) - ([1, 85, 11, 4], [9, 31, 250, 245]), - // log(address,bool,uint256,string) - ([155, 88, 142, 204], [10, 166, 207, 173]), - // log(bool,bool,uint256,uint256) - ([70, 103, 222, 142], [11, 176, 14, 171]), - // log(bool,address,address,uint256) - ([82, 132, 189, 108], [12, 102, 209, 190]), - // log(uint256,address,uint256,uint256) - ([202, 154, 62, 180], [12, 156, 217, 193]), - // log(string,address,uint256) - ([7, 200, 18, 23], [13, 38, 185, 37]), - // log(address,string,uint256,bool) - ([126, 37, 13, 91], [14, 247, 224, 80]), - // log(address,uint256,address,uint256) - ([165, 217, 135, 104], [16, 15, 101, 14]), - // log(string,string,uint256,address) - ([93, 79, 70, 128], [16, 35, 247, 178]), - // log(bool,string,uint256) - ([192, 56, 42, 172], [16, 147, 238, 17]), - // log(bool,bool,uint256) - ([176, 19, 101, 187], [18, 242, 22, 2]), - // log(bool,address,uint256,address) - ([104, 241, 88, 181], [19, 107, 5, 221]), - // log(bool,uint256,address,uint256) - ([202, 165, 35, 106], [21, 55, 220, 135]), - // log(bool,string,uint256,address) - ([91, 34, 185, 56], [21, 150, 161, 206]), - // log(address,string,string,uint256) - ([161, 79, 208, 57], [21, 159, 137, 39]), - // log(uint256,address,uint256,address) - ([253, 178, 236, 212], [21, 193, 39, 181]), - // log(uint256,uint256,address,bool) - ([168, 232, 32, 174], [21, 202, 196, 118]), - // log(bool,string,bool,uint256) - ([141, 111, 156, 165], [22, 6, 163, 147]), - // log(address,address,uint256) - ([108, 54, 109, 114], [23, 254, 97, 133]), - // log(uint256,uint256,uint256,uint256) - ([92, 160, 173, 62], [25, 63, 184, 0]), - // log(bool,string,uint256,string) - ([119, 161, 171, 237], [26, 217, 109, 230]), - // log(bool,uint256,address,string) - ([24, 9, 19, 65], [27, 179, 176, 154]), - // log(string,uint256,address) - ([227, 132, 159, 121], [28, 126, 196, 72]), - // log(uint256,bool) - ([30, 109, 212, 236], [28, 157, 126, 179]), - // log(address,uint256,address,string) - ([93, 113, 243, 158], [29, 169, 134, 234]), - // log(address,string,uint256,uint256) - ([164, 201, 42, 96], [29, 200, 225, 184]), - // log(uint256,bool,uint256) - ([90, 77, 153, 34], [32, 9, 128, 20]), - // log(uint256,bool,bool) - ([213, 206, 172, 224], [32, 113, 134, 80]), - // log(address,uint256,uint256,address) - ([30, 246, 52, 52], [32, 227, 152, 77]), - // log(uint256,string,string,string) - ([87, 221, 10, 17], [33, 173, 6, 131]), - // log(address,uint256,bool,uint256) - ([105, 143, 67, 146], [34, 246, 185, 153]), - // log(uint256,address,address,address) - ([85, 71, 69, 249], [36, 136, 180, 20]), - // log(string,bool,string,uint256) - ([52, 203, 48, 141], [36, 249, 20, 101]), - // log(bool,uint256,address,address) - ([138, 47, 144, 170], [38, 245, 96, 168]), - // log(uint256,uint256,string,string) - ([124, 3, 42, 50], [39, 216, 175, 210]), - // log(bool,string,uint256,uint256) - ([142, 74, 232, 110], [40, 134, 63, 203]), - // log(uint256,bool,string,uint256) - ([145, 95, 219, 40], [44, 29, 7, 70]), - // log(address,uint256,uint256,uint256) - ([61, 14, 157, 228], [52, 240, 230, 54]), - // log(uint256,bool,address) - ([66, 78, 255, 191], [53, 8, 95, 123]), - // log(string,uint256,bool,bool) - ([227, 127, 243, 208], [53, 76, 54, 214]), - // log(bool,uint256,uint256) - ([59, 92, 3, 224], [55, 16, 51, 103]), - // log(bool,uint256,uint256,uint256) - ([50, 223, 165, 36], [55, 75, 180, 178]), - // log(uint256,string,uint256) - ([91, 109, 232, 63], [55, 170, 125, 76]), - // log(address,bool,uint256,uint256) - ([194, 16, 160, 30], [56, 111, 245, 244]), - // log(address,address,bool,uint256) - ([149, 214, 95, 17], [57, 113, 231, 140]), - // log(bool,uint256) - ([54, 75, 106, 146], [57, 145, 116, 211]), - // log(uint256,string,uint256,address) - ([171, 123, 217, 253], [59, 34, 121, 180]), - // log(address,uint256,bool,bool) - ([254, 161, 213, 90], [59, 245, 229, 55]), - // log(uint256,address,string,string) - ([141, 119, 134, 36], [62, 18, 140, 163]), - // log(string,address,bool,uint256) - ([197, 209, 187, 139], [62, 159, 134, 106]), - // log(uint256,uint256,string,address) - ([67, 50, 133, 162], [66, 210, 29, 183]), - // log(address,string,uint256,string) - ([93, 19, 101, 201], [68, 136, 48, 168]), - // log(uint256,bool,address,bool) - ([145, 251, 18, 66], [69, 77, 84, 165]), - // log(address,string,address,uint256) - ([140, 25, 51, 169], [69, 127, 227, 207]), - // log(uint256,address,string,uint256) - ([160, 196, 20, 232], [70, 130, 107, 93]), - // log(uint256,uint256,bool) - ([103, 87, 15, 247], [71, 102, 218, 114]), - // log(address,uint256,address,address) - ([236, 36, 132, 111], [71, 141, 28, 98]), - // log(address,uint256,uint256,string) - ([137, 52, 13, 171], [74, 40, 192, 23]), - // log(bool,bool,address,uint256) - ([96, 147, 134, 231], [76, 18, 61, 87]), - // log(uint256,string,bool) - ([70, 167, 208, 206], [76, 237, 167, 90]), - // log(string,uint256,address,uint256) - ([88, 73, 122, 254], [79, 4, 253, 198]), - // log(address,string,bool,uint256) - ([231, 32, 82, 28], [81, 94, 56, 182]), - // log(bool,address,uint256,string) - ([160, 104, 88, 51], [81, 240, 159, 248]), - // log(bool,bool,uint256,address) - ([11, 255, 149, 13], [84, 167, 169, 160]), - // log(uint256,uint256,address,address) - ([202, 147, 155, 32], [86, 165, 209, 177]), - // log(string,string,uint256) - ([243, 98, 202, 89], [88, 33, 239, 161]), - // log(string,uint256,string) - ([163, 245, 199, 57], [89, 112, 224, 137]), - // log(uint256,uint256,uint256,string) - ([120, 173, 122, 12], [89, 207, 203, 227]), - // log(string,address,uint256,string) - ([76, 85, 242, 52], [90, 71, 118, 50]), - // log(uint256,address,uint256) - ([136, 67, 67, 170], [90, 155, 94, 213]), - // log(string,uint256,string,string) - ([108, 152, 218, 226], [90, 184, 78, 31]), - // log(uint256,address,bool,uint256) - ([123, 8, 232, 235], [90, 189, 153, 42]), - // log(address,uint256,string,address) - ([220, 121, 38, 4], [92, 67, 13, 71]), - // log(uint256,uint256,address) - ([190, 51, 73, 27], [92, 150, 179, 49]), - // log(string,bool,address,uint256) - ([40, 223, 78, 150], [93, 8, 187, 5]), - // log(string,string,uint256,string) - ([141, 20, 44, 221], [93, 26, 151, 26]), - // log(uint256,uint256,string,uint256) - ([56, 148, 22, 61], [93, 162, 151, 235]), - // log(string,uint256,address,address) - ([234, 200, 146, 129], [94, 162, 183, 174]), - // log(uint256,address,uint256,bool) - ([25, 246, 115, 105], [95, 116, 58, 124]), - // log(bool,address,uint256) - ([235, 112, 75, 175], [95, 123, 154, 251]), - // log(uint256,string,address,address) - ([127, 165, 69, 139], [97, 104, 237, 97]), - // log(bool,bool,uint256,bool) - ([171, 92, 193, 196], [97, 158, 77, 14]), - // log(address,string,uint256,address) - ([223, 215, 216, 11], [99, 24, 54, 120]), - // log(uint256,address,string) - ([206, 131, 4, 123], [99, 203, 65, 249]), - // log(string,address,uint256,address) - ([163, 102, 236, 128], [99, 251, 139, 197]), - // log(uint256,string) - ([15, 163, 243, 69], [100, 63, 208, 223]), - // log(string,bool,uint256,uint256) - ([93, 191, 240, 56], [100, 181, 187, 103]), - // log(address,uint256,uint256,bool) - ([236, 75, 168, 162], [102, 241, 188, 103]), - // log(address,uint256,bool) - ([229, 74, 225, 68], [103, 130, 9, 168]), - // log(address,string,uint256) - ([28, 218, 242, 138], [103, 221, 111, 241]), - // log(uint256,bool,string,string) - ([164, 51, 252, 253], [104, 200, 184, 189]), - // log(uint256,string,uint256,bool) - ([135, 90, 110, 46], [105, 26, 143, 116]), - // log(uint256,address) - ([88, 235, 134, 12], [105, 39, 108, 134]), - // log(uint256,bool,bool,address) - ([83, 6, 34, 93], [105, 100, 11, 89]), - // log(bool,uint256,string,uint256) - ([65, 128, 1, 27], [106, 17, 153, 226]), - // log(bool,string,uint256,bool) - ([32, 187, 201, 175], [107, 14, 93, 83]), - // log(uint256,uint256,address,string) - ([214, 162, 209, 222], [108, 222, 64, 184]), - // log(bool,bool,bool,uint256) - ([194, 72, 131, 77], [109, 112, 69, 193]), - // log(uint256,uint256,string) - ([125, 105, 14, 230], [113, 208, 74, 242]), - // log(uint256,address,address,uint256) - ([154, 60, 191, 150], [115, 110, 251, 182]), - // log(string,bool,uint256,string) - ([66, 185, 162, 39], [116, 45, 110, 231]), - // log(uint256,bool,bool,uint256) - ([189, 37, 173, 89], [116, 100, 206, 35]), - // log(string,uint256,uint256,bool) - ([247, 60, 126, 61], [118, 38, 219, 146]), - // log(uint256,uint256,string,bool) - ([178, 46, 175, 6], [122, 246, 171, 37]), - // log(uint256,string,address) - ([31, 144, 242, 74], [122, 250, 201, 89]), - // log(address,uint256,address) - ([151, 236, 163, 148], [123, 192, 216, 72]), - // log(bool,string,string,uint256) - ([93, 219, 37, 146], [123, 224, 195, 235]), - // log(bool,address,uint256,uint256) - ([155, 254, 114, 188], [123, 241, 129, 161]), - // log(string,uint256,string,address) - ([187, 114, 53, 233], [124, 70, 50, 164]), - // log(string,string,address,uint256) - ([74, 129, 165, 106], [124, 195, 198, 7]), - // log(string,uint256,string,bool) - ([233, 159, 130, 207], [125, 36, 73, 29]), - // log(bool,bool,uint256,string) - ([80, 97, 137, 55], [125, 212, 208, 224]), - // log(bool,uint256,bool,uint256) - ([211, 222, 85, 147], [127, 155, 188, 162]), - // log(address,bool,string,uint256) - ([158, 18, 123, 110], [128, 230, 162, 11]), - // log(string,uint256,address,bool) - ([17, 6, 168, 247], [130, 17, 42, 66]), - // log(uint256,string,uint256,uint256) - ([192, 4, 56, 7], [130, 194, 91, 116]), - // log(address,uint256) - ([34, 67, 207, 163], [131, 9, 232, 168]), - // log(string,uint256,uint256,string) - ([165, 78, 212, 189], [133, 75, 52, 150]), - // log(uint256,bool,string) - ([139, 14, 20, 254], [133, 119, 80, 33]), - // log(address,uint256,string,string) - ([126, 86, 198, 147], [136, 168, 196, 6]), - // log(uint256,bool,uint256,address) - ([79, 64, 5, 142], [136, 203, 96, 65]), - // log(uint256,uint256,address,uint256) - ([97, 11, 168, 192], [136, 246, 228, 178]), - // log(string,bool,uint256,bool) - ([60, 197, 181, 211], [138, 247, 207, 138]), - // log(address,bool,bool,uint256) - ([207, 181, 135, 86], [140, 78, 93, 230]), - // log(address,address,uint256,address) - ([214, 198, 82, 118], [141, 166, 222, 245]), - // log(string,bool,bool,uint256) - ([128, 117, 49, 232], [142, 63, 120, 169]), - // log(bool,uint256,uint256,string) - ([218, 6, 102, 200], [142, 105, 251, 93]), - // log(string,string,string,uint256) - ([159, 208, 9, 245], [142, 175, 176, 43]), - // log(string,address,address,uint256) - ([110, 183, 148, 61], [142, 243, 243, 153]), - // log(uint256,string,address,bool) - ([249, 63, 255, 55], [144, 195, 10, 86]), - // log(uint256,address,bool,string) - ([99, 240, 226, 66], [144, 251, 6, 170]), - // log(bool,uint256,bool,string) - ([182, 213, 105, 212], [145, 67, 219, 177]), - // log(uint256,bool,uint256,bool) - ([210, 171, 196, 253], [145, 160, 46, 42]), - // log(string,address,string,uint256) - ([143, 98, 75, 233], [145, 209, 17, 46]), - // log(string,bool,uint256,address) - ([113, 211, 133, 13], [147, 94, 9, 191]), - // log(address,address,address,uint256) - ([237, 94, 172, 135], [148, 37, 13, 119]), - // log(uint256,uint256,bool,address) - ([225, 23, 116, 79], [154, 129, 106, 131]), - // log(bool,uint256,bool,address) - ([66, 103, 199, 248], [154, 205, 54, 22]), - // log(address,address,uint256,bool) - ([194, 246, 136, 236], [155, 66, 84, 226]), - // log(uint256,address,bool) - ([122, 208, 18, 142], [155, 110, 192, 66]), - // log(uint256,string,address,string) - ([248, 152, 87, 127], [156, 58, 223, 161]), - // log(address,bool,uint256) - ([44, 70, 141, 21], [156, 79, 153, 251]), - // log(uint256,address,string,address) - ([203, 229, 142, 253], [156, 186, 143, 255]), - // log(string,uint256,address,string) - ([50, 84, 194, 232], [159, 251, 47, 147]), - // log(address,uint256,address,bool) - ([241, 129, 161, 233], [161, 188, 201, 179]), - // log(uint256,bool,address,address) - ([134, 237, 193, 12], [161, 239, 76, 187]), - // log(address,uint256,string) - ([186, 249, 104, 73], [161, 242, 232, 170]), - // log(address,uint256,bool,address) - ([35, 229, 73, 114], [163, 27, 253, 204]), - // log(uint256,uint256,bool,string) - ([239, 217, 203, 238], [165, 180, 252, 153]), - // log(bool,string,address,uint256) - ([27, 11, 149, 91], [165, 202, 218, 148]), - // log(address,bool,address,uint256) - ([220, 113, 22, 210], [167, 92, 89, 222]), - // log(string,uint256,uint256,uint256) - ([8, 238, 86, 102], [167, 168, 120, 83]), - // log(uint256,uint256,bool,bool) - ([148, 190, 59, 177], [171, 8, 90, 230]), - // log(string,uint256,bool,string) - ([118, 204, 96, 100], [171, 247, 58, 152]), - // log(uint256,bool,address,string) - ([162, 48, 118, 30], [173, 224, 82, 199]), - // log(uint256,string,bool,address) - ([121, 111, 40, 160], [174, 46, 197, 129]), - // log(uint256,string,string,uint256) - ([118, 236, 99, 94], [176, 40, 201, 189]), - // log(uint256,string,string) - ([63, 87, 194, 149], [177, 21, 97, 31]), - // log(uint256,string,string,bool) - ([18, 134, 43, 152], [179, 166, 182, 189]), - // log(bool,uint256,address,bool) - ([101, 173, 244, 8], [180, 195, 20, 255]), - // log(string,uint256) - ([151, 16, 169, 208], [182, 14, 114, 204]), - // log(address,uint256,uint256) - ([135, 134, 19, 94], [182, 155, 202, 246]), - // log(uint256,bool,bool,bool) - ([78, 108, 83, 21], [182, 245, 119, 161]), - // log(uint256,string,uint256,string) - ([162, 188, 12, 153], [183, 185, 20, 202]), - // log(uint256,string,bool,bool) - ([81, 188, 43, 193], [186, 83, 93, 156]), - // log(uint256,address,address) - ([125, 119, 166, 27], [188, 253, 155, 224]), - // log(address,address,uint256,uint256) - ([84, 253, 243, 228], [190, 85, 52, 129]), - // log(bool,uint256,uint256,bool) - ([164, 29, 129, 222], [190, 152, 67, 83]), - // log(address,uint256,string,uint256) - ([245, 18, 207, 155], [191, 1, 248, 145]), - // log(bool,address,string,uint256) - ([11, 153, 252, 34], [194, 31, 100, 199]), - // log(string,string,uint256,bool) - ([230, 86, 88, 202], [195, 168, 166, 84]), - // log(bool,uint256,string) - ([200, 57, 126, 176], [195, 252, 57, 112]), - // log(address,bool,uint256,bool) - ([133, 205, 197, 175], [196, 100, 62, 32]), - // log(uint256,uint256,uint256,bool) - ([100, 82, 185, 203], [197, 152, 209, 133]), - // log(address,uint256,bool,string) - ([142, 142, 78, 117], [197, 173, 133, 249]), - // log(string,uint256,string,uint256) - ([160, 196, 178, 37], [198, 126, 169, 209]), - // log(uint256,bool,uint256,uint256) - ([86, 130, 141, 164], [198, 172, 199, 168]), - // log(string,bool,uint256) - ([41, 27, 185, 208], [201, 89, 88, 214]), - // log(string,uint256,uint256) - ([150, 156, 221, 3], [202, 71, 196, 235]), - // log(string,uint256,bool) - ([241, 2, 238, 5], [202, 119, 51, 177]), - // log(uint256,address,string,bool) - ([34, 164, 121, 166], [204, 50, 171, 7]), - // log(address,bool,uint256,address) - ([13, 140, 230, 30], [204, 247, 144, 161]), - // log(bool,uint256,bool,bool) - ([158, 1, 247, 65], [206, 181, 244, 215]), - // log(uint256,string,bool,uint256) - ([164, 180, 138, 127], [207, 0, 152, 128]), - // log(address,uint256,string,bool) - ([164, 2, 79, 17], [207, 24, 16, 92]), - // log(uint256,uint256,uint256) - ([231, 130, 10, 116], [209, 237, 122, 60]), - // log(uint256,string,bool,string) - ([141, 72, 156, 160], [210, 212, 35, 205]), - // log(uint256,string,string,address) - ([204, 152, 138, 160], [213, 131, 198, 2]), - // log(bool,address,uint256,bool) - ([238, 141, 134, 114], [214, 1, 159, 28]), - // log(string,string,bool,uint256) - ([134, 129, 138, 122], [214, 174, 250, 210]), - // log(uint256,address,uint256,string) - ([62, 211, 189, 40], [221, 176, 101, 33]), - // log(uint256,bool,bool,string) - ([49, 138, 229, 155], [221, 219, 149, 97]), - // log(uint256,bool,uint256,string) - ([232, 221, 188, 86], [222, 3, 231, 116]), - // log(string,uint256,bool,address) - ([229, 84, 157, 145], [224, 233, 91, 152]), - // log(string,uint256,uint256,address) - ([190, 215, 40, 191], [226, 29, 226, 120]), - // log(uint256,address,bool,bool) - ([126, 39, 65, 13], [227, 81, 20, 15]), - // log(bool,bool,string,uint256) - ([23, 139, 70, 133], [227, 169, 202, 47]), - // log(string,uint256,bool,uint256) - ([85, 14, 110, 245], [228, 27, 111, 111]), - // log(bool,uint256,string,bool) - ([145, 210, 248, 19], [229, 231, 11, 43]), - // log(uint256,string,address,uint256) - ([152, 231, 243, 243], [232, 211, 1, 141]), - // log(bool,uint256,bool) - ([27, 173, 201, 235], [232, 222, 251, 169]), - // log(uint256,uint256,bool,uint256) - ([108, 100, 124, 140], [235, 127, 111, 210]), - // log(uint256,bool,string,bool) - ([52, 110, 184, 199], [235, 146, 141, 127]), - // log(address,address,string,uint256) - ([4, 40, 147, 0], [239, 28, 239, 231]), - // log(uint256,bool,string,address) - ([73, 110, 43, 180], [239, 82, 144, 24]), - // log(uint256,address,bool,address) - ([182, 49, 48, 148], [239, 114, 197, 19]), - // log(string,string,uint256,uint256) - ([213, 207, 23, 208], [244, 93, 125, 44]), - // log(bool,uint256,string,string) - ([211, 42, 101, 72], [245, 188, 34, 73]), - // log(uint256,uint256) - ([108, 15, 105, 128], [246, 102, 113, 90]), - // log(uint256) and logUint(uint256) - ([245, 177, 187, 169], [248, 44, 80, 241]), - // log(string,address,uint256,uint256) - ([218, 163, 148, 189], [248, 245, 27, 30]), - // log(uint256,uint256,uint256,address) - ([224, 133, 63, 105], [250, 129, 133, 175]), - // log(string,address,uint256,bool) - ([90, 193, 193, 60], [252, 72, 69, 240]), - // log(address,address,uint256,string) - ([157, 209, 46, 173], [253, 180, 249, 144]), - // log(bool,uint256,string,address) - ([165, 199, 13, 41], [254, 221, 31, 255]), - // logInt(int256) - ([78, 12, 29, 29], [101, 37, 181, 245]), - // logBytes(bytes) - ([11, 231, 127, 86], [225, 123, 249, 86]), - // logBytes1(bytes1) - ([110, 24, 161, 40], [111, 65, 113, 201]), - // logBytes2(bytes2) - ([233, 182, 34, 150], [155, 94, 148, 62]), - // logBytes3(bytes3) - ([45, 131, 73, 38], [119, 130, 250, 45]), - // logBytes4(bytes4) - ([224, 95, 72, 209], [251, 163, 173, 57]), - // logBytes5(bytes5) - ([166, 132, 128, 141], [85, 131, 190, 46]), - // logBytes6(bytes6) - ([174, 132, 165, 145], [73, 66, 173, 198]), - // logBytes7(bytes7) - ([78, 213, 126, 40], [69, 116, 175, 171]), - // logBytes8(bytes8) - ([79, 132, 37, 46], [153, 2, 228, 127]), - // logBytes9(bytes9) - ([144, 189, 140, 208], [80, 161, 56, 223]), - // logBytes10(bytes10) - ([1, 61, 23, 139], [157, 194, 168, 151]), - // logBytes11(bytes11) - ([4, 0, 74, 46], [220, 8, 182, 167]), - // logBytes12(bytes12) - ([134, 160, 106, 189], [118, 86, 214, 199]), - // logBytes13(bytes13) - ([148, 82, 158, 52], [52, 193, 216, 27]), - // logBytes14(bytes14) - ([146, 102, 240, 127], [60, 234, 186, 101]), - // logBytes15(bytes15) - ([218, 149, 116, 224], [89, 26, 61, 162]), - // logBytes16(bytes16) - ([102, 92, 97, 4], [31, 141, 115, 18]), - // logBytes17(bytes17) - ([51, 159, 103, 58], [248, 154, 83, 47]), - // logBytes18(bytes18) - ([196, 210, 61, 154], [216, 101, 38, 66]), - // logBytes19(bytes19) - ([94, 107, 90, 51], [0, 245, 107, 201]), - // logBytes20(bytes20) - ([81, 136, 227, 233], [236, 184, 86, 126]), - // logBytes21(bytes21) - ([233, 218, 53, 96], [48, 82, 192, 143]), - // logBytes22(bytes22) - ([213, 250, 232, 156], [128, 122, 180, 52]), - // logBytes23(bytes23) - ([171, 161, 207, 13], [73, 121, 176, 55]), - // logBytes24(bytes24) - ([241, 179, 91, 52], [9, 119, 174, 252]), - // logBytes25(bytes25) - ([11, 132, 188, 88], [174, 169, 150, 63]), - // logBytes26(bytes26) - ([248, 177, 73, 241], [211, 99, 86, 40]), - // logBytes27(bytes27) - ([58, 55, 87, 221], [252, 55, 47, 159]), - // logBytes28(bytes28) - ([200, 42, 234, 238], [56, 47, 154, 52]), - // logBytes29(bytes29) - ([75, 105, 195, 213], [122, 24, 118, 65]), - // logBytes30(bytes30) - ([238, 18, 196, 237], [196, 52, 14, 246]), - // logBytes31(bytes31) - ([194, 133, 77, 146], [129, 252, 134, 72]), - // logBytes32(bytes32) - ([39, 183, 207, 133], [45, 33, 214, 247]), - ] - .map(|s| (s.0, s.1)), - ) -}); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn hardhat_console_path_works() { - for (hh, abigen) in HARDHAT_CONSOLE_SELECTOR_PATCHES.iter() { - let mut hh = (*hh).to_vec(); - patch_hardhat_console_selector(&mut hh); - assert_eq!((*abigen).to_vec(), hh); - } - } -} diff --git a/crates/evm/src/executor/inspector/mod.rs b/crates/evm/src/executor/inspector/mod.rs deleted file mode 100644 index f25a6ab434b9b..0000000000000 --- a/crates/evm/src/executor/inspector/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -#[macro_use] -mod utils; - -mod access_list; -pub use access_list::AccessListTracer; - -pub mod cheatcodes; -pub use cheatcodes::{Cheatcodes, CheatsConfig, DEFAULT_CREATE2_DEPLOYER}; - -mod chisel_state; -pub use chisel_state::ChiselState; - -mod coverage; -pub use coverage::CoverageCollector; - -mod debugger; -pub use debugger::Debugger; - -mod fuzzer; -pub use fuzzer::Fuzzer; - -mod logs; -pub use logs::LogCollector; - -mod printer; -pub use printer::TracePrinter; - -mod stack; -pub use stack::{InspectorData, InspectorStack, InspectorStackBuilder}; - -mod tracer; -pub use tracer::Tracer; diff --git a/crates/evm/src/executor/inspector/utils.rs b/crates/evm/src/executor/inspector/utils.rs deleted file mode 100644 index 5c828e8ac9bd0..0000000000000 --- a/crates/evm/src/executor/inspector/utils.rs +++ /dev/null @@ -1,35 +0,0 @@ -use alloy_primitives::{Address, B256}; - -use revm::{ - interpreter::CreateInputs, - primitives::{CreateScheme, SpecId}, -}; - -/// Returns [InstructionResult::Continue] on an error, discarding the error. -/// -/// Useful for inspectors that read state that might be invalid, but do not want to emit -/// appropriate errors themselves, instead opting to continue. -macro_rules! try_or_continue { - ($e:expr) => { - match $e { - Ok(v) => v, - Err(_) => return InstructionResult::Continue, - } - }; -} - -/// Get the address of a contract creation -pub fn get_create_address(call: &CreateInputs, nonce: u64) -> Address { - match call.scheme { - CreateScheme::Create => call.caller.create(nonce), - CreateScheme::Create2 { salt } => { - call.caller.create2_from_code(B256::from(salt), &call.init_code) - } - } -} - -/// Get the gas used, accounting for refunds -pub fn gas_used(spec: SpecId, spent: u64, refunded: u64) -> u64 { - let refund_quotient = if SpecId::enabled(spec, SpecId::LONDON) { 5 } else { 2 }; - spent - (refunded).min(spent / refund_quotient) -} diff --git a/crates/evm/src/executor/builder.rs b/crates/evm/src/executors/builder.rs similarity index 94% rename from crates/evm/src/executor/builder.rs rename to crates/evm/src/executors/builder.rs index 5cbf6e453d650..2188629c29a83 100644 --- a/crates/evm/src/executor/builder.rs +++ b/crates/evm/src/executors/builder.rs @@ -1,6 +1,6 @@ -use super::{inspector::InspectorStackBuilder, Executor}; -use crate::executor::backend::Backend; +use crate::{executors::Executor, inspectors::InspectorStackBuilder}; use alloy_primitives::U256; +use foundry_evm_executors::backend::Backend; use revm::primitives::{Env, SpecId}; /// The builder that allows to configure an evm [`Executor`] which a stack of optional diff --git a/crates/evm/src/fuzz/mod.rs b/crates/evm/src/executors/fuzz/mod.rs similarity index 52% rename from crates/evm/src/fuzz/mod.rs rename to crates/evm/src/executors/fuzz/mod.rs index a2215da590f5a..4cb89a99e6f43 100644 --- a/crates/evm/src/fuzz/mod.rs +++ b/crates/evm/src/executors/fuzz/mod.rs @@ -1,38 +1,33 @@ -//! Executor wrapper which can be used for fuzzing, using [`proptest`](https://docs.rs/proptest/1.0.0/proptest/) -use crate::{ - coverage::HitMaps, - decode::{self, decode_console_logs}, - executor::{Executor, RawCallResult}, - trace::CallTraceArena, -}; -use alloy_dyn_abi::{DynSolValue, JsonAbiExt}; +use crate::executors::{Executor, RawCallResult}; +use alloy_dyn_abi::JsonAbiExt; use alloy_json_abi::{Function, JsonAbi as Abi}; use alloy_primitives::{Address, Bytes, U256}; -use error::{FuzzError, ASSUME_MAGIC_RETURN_CODE}; -use ethers::types::Log; use eyre::Result; -use foundry_common::{calc, contracts::ContractsByAddress}; use foundry_config::FuzzConfig; -pub use proptest::test_runner::Reason; -use proptest::test_runner::{TestCaseError, TestError, TestRunner}; -use serde::{Deserialize, Serialize}; -use std::{cell::RefCell, collections::BTreeMap, fmt}; -use strategies::{ - build_initial_state, collect_state_from_call, fuzz_calldata, fuzz_calldata_from_state, - EvmFuzzState, +use foundry_evm_coverage::HitMaps; +use foundry_evm_executors::{ + decode::{self, decode_console_logs}, + ASSUME_MAGIC_RETURN_CODE, +}; +use foundry_evm_fuzz::{ + strategies::{ + build_initial_state, collect_state_from_call, fuzz_calldata, fuzz_calldata_from_state, + EvmFuzzState, + }, + BaseCounterExample, CounterExample, FuzzCase, FuzzError, FuzzTestResult, }; -use types::{CaseOutcome, CounterExampleOutcome, FuzzCase, FuzzOutcome}; +use foundry_evm_traces::CallTraceArena; +use proptest::test_runner::{TestCaseError, TestError, TestRunner}; +use std::cell::RefCell; -pub mod error; -pub mod invariant; -pub mod strategies; -pub mod types; +mod types; +pub use types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}; -/// Wrapper around an [`Executor`] which provides fuzzing support using [`proptest`](https://docs.rs/proptest/1.0.0/proptest/). +/// Wrapper around an [`Executor`] which provides fuzzing support using [`proptest`]. /// /// After instantiation, calling `fuzz` will proceed to hammer the deployed smart contract with /// inputs, until it finds a counterexample. The provided [`TestRunner`] contains all the -/// configuration which can be overridden via [environment variables](https://docs.rs/proptest/1.0.0/proptest/test_runner/struct.Config.html) +/// configuration which can be overridden via [environment variables](proptest::test_runner::Config) pub struct FuzzedExecutor<'a> { /// The VM executor: &'a Executor, @@ -255,239 +250,3 @@ impl<'a> FuzzedExecutor<'a> { } } } - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum CounterExample { - /// Call used as a counter example for fuzz tests. - Single(BaseCounterExample), - /// Sequence of calls used as a counter example for invariant tests. - Sequence(Vec), -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct BaseCounterExample { - /// Address which makes the call - pub sender: Option
, - /// Address to which to call to - pub addr: Option
, - /// The data to provide - pub calldata: Bytes, - /// Function signature if it exists - pub signature: Option, - /// Contract name if it exists - pub contract_name: Option, - /// Traces - pub traces: Option, - #[serde(skip)] - pub args: Vec, -} - -impl BaseCounterExample { - pub fn create( - sender: Address, - addr: Address, - bytes: &Bytes, - contracts: &ContractsByAddress, - traces: Option, - ) -> Self { - if let Some((name, abi)) = &contracts.get(&addr) { - if let Some(func) = abi.functions().find(|f| f.selector() == bytes.0.as_ref()[0..4]) { - // skip the function selector when decoding - if let Ok(args) = func.abi_decode_input(&bytes.0.as_ref()[4..], false) { - return BaseCounterExample { - sender: Some(sender), - addr: Some(addr), - calldata: bytes.clone(), - signature: Some(func.signature()), - contract_name: Some(name.clone()), - traces, - args, - } - } - } - } - - BaseCounterExample { - sender: Some(sender), - addr: Some(addr), - calldata: bytes.clone(), - signature: None, - contract_name: None, - traces, - args: vec![], - } - } -} - -impl fmt::Display for BaseCounterExample { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let args = foundry_common::abi::format_tokens(&self.args).collect::>().join(", "); - - if let Some(sender) = self.sender { - write!(f, "sender={sender:?} addr=")? - } - - if let Some(name) = &self.contract_name { - write!(f, "[{name}]")? - } - - if let Some(addr) = &self.addr { - write!(f, "{addr:?} ")? - } - - if let Some(sig) = &self.signature { - write!(f, "calldata={}", &sig)? - } else { - write!(f, "calldata=0x{}", self.calldata)? - } - - write!(f, ", args=[{args}]") - } -} - -/// The outcome of a fuzz test -#[derive(Debug)] -pub struct FuzzTestResult { - /// we keep this for the debugger - pub first_case: FuzzCase, - /// Gas usage (gas_used, call_stipend) per cases - pub gas_by_case: Vec<(u64, u64)>, - /// Whether the test case was successful. This means that the transaction executed - /// properly, or that there was a revert and that the test was expected to fail - /// (prefixed with `testFail`) - pub success: bool, - - /// If there was a revert, this field will be populated. Note that the test can - /// still be successful (i.e self.success == true) when it's expected to fail. - pub reason: Option, - - /// Minimal reproduction test case for failing fuzz tests - pub counterexample: Option, - - /// Any captured & parsed as strings logs along the test's execution which should - /// be printed to the user. - pub logs: Vec, - - /// The decoded DSTest logging events and Hardhat's `console.log` from [logs](Self::logs). - pub decoded_logs: Vec, - - /// Labeled addresses - pub labeled_addresses: BTreeMap, - - /// Exemplary traces for a fuzz run of the test function - /// - /// **Note** We only store a single trace of a successful fuzz call, otherwise we would get - /// `num(fuzz_cases)` traces, one for each run, which is neither helpful nor performant. - pub traces: Option, - - /// Raw coverage info - pub coverage: Option, -} - -impl FuzzTestResult { - /// Returns the median gas of all test cases - pub fn median_gas(&self, with_stipend: bool) -> u64 { - let mut values = - self.gas_values(with_stipend).into_iter().map(U256::from).collect::>(); - values.sort_unstable(); - calc::median_sorted(&values).to::() - } - - /// Returns the average gas use of all test cases - pub fn mean_gas(&self, with_stipend: bool) -> u64 { - let mut values = - self.gas_values(with_stipend).into_iter().map(U256::from).collect::>(); - values.sort_unstable(); - calc::mean(&values).to::() - } - - fn gas_values(&self, with_stipend: bool) -> Vec { - self.gas_by_case - .iter() - .map(|gas| if with_stipend { gas.0 } else { gas.0.saturating_sub(gas.1) }) - .collect() - } -} - -/// Container type for all successful test cases -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(transparent)] -pub struct FuzzedCases { - cases: Vec, -} - -impl FuzzedCases { - #[inline] - pub fn new(mut cases: Vec) -> Self { - cases.sort_by_key(|c| c.gas); - Self { cases } - } - - #[inline] - pub fn cases(&self) -> &[FuzzCase] { - &self.cases - } - - #[inline] - pub fn into_cases(self) -> Vec { - self.cases - } - - /// Get the last [FuzzCase] - #[inline] - pub fn last(&self) -> Option<&FuzzCase> { - self.cases.last() - } - - /// Returns the median gas of all test cases - #[inline] - pub fn median_gas(&self, with_stipend: bool) -> u64 { - let mut values = - self.gas_values(with_stipend).into_iter().map(U256::from).collect::>(); - values.sort_unstable(); - calc::median_sorted(&values).to::() - } - - /// Returns the average gas use of all test cases - #[inline] - pub fn mean_gas(&self, with_stipend: bool) -> u64 { - let mut values = - self.gas_values(with_stipend).into_iter().map(U256::from).collect::>(); - values.sort_unstable(); - calc::mean(&values).to::() - } - - #[inline] - fn gas_values(&self, with_stipend: bool) -> Vec { - self.cases - .iter() - .map(|c| if with_stipend { c.gas } else { c.gas.saturating_sub(c.stipend) }) - .collect() - } - - /// Returns the case with the highest gas usage - #[inline] - pub fn highest(&self) -> Option<&FuzzCase> { - self.cases.last() - } - - /// Returns the case with the lowest gas usage - #[inline] - pub fn lowest(&self) -> Option<&FuzzCase> { - self.cases.first() - } - - /// Returns the highest amount of gas spent on a fuzz case - #[inline] - pub fn highest_gas(&self, with_stipend: bool) -> u64 { - self.highest() - .map(|c| if with_stipend { c.gas } else { c.gas - c.stipend }) - .unwrap_or_default() - } - - /// Returns the lowest amount of gas spent on a fuzz case - #[inline] - pub fn lowest_gas(&self) -> u64 { - self.lowest().map(|c| c.gas).unwrap_or_default() - } -} diff --git a/crates/evm/src/fuzz/types.rs b/crates/evm/src/executors/fuzz/types.rs similarity index 68% rename from crates/evm/src/fuzz/types.rs rename to crates/evm/src/executors/fuzz/types.rs index 3ab8c541e0d06..71d2c4bc2e9ca 100644 --- a/crates/evm/src/fuzz/types.rs +++ b/crates/evm/src/executors/fuzz/types.rs @@ -1,19 +1,11 @@ -use crate::{coverage::HitMaps, debug::DebugArena, executor::RawCallResult, trace::CallTraceArena}; +use crate::executors::RawCallResult; use alloy_primitives::Bytes; use foundry_common::evm::Breakpoints; +use foundry_evm_coverage::HitMaps; +use foundry_evm_executors::debug::DebugArena; +use foundry_evm_fuzz::FuzzCase; +use foundry_evm_traces::CallTraceArena; use revm::interpreter::InstructionResult; -use serde::{Deserialize, Serialize}; - -/// Data of a single fuzz test case -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct FuzzCase { - /// The calldata used for this fuzz test - pub calldata: Bytes, - /// Consumed gas - pub gas: u64, - /// The initial gas stipend for the transaction - pub stipend: u64, -} /// Returned by a single fuzz in the case of a successful run #[derive(Debug)] @@ -34,7 +26,7 @@ pub struct CaseOutcome { #[derive(Debug)] pub struct CounterExampleOutcome { /// Minimal reproduction test case for failing test - pub counterexample: (alloy_primitives::Bytes, RawCallResult), + pub counterexample: (Bytes, RawCallResult), /// The status of the call pub exit_reason: InstructionResult, /// The debug nodes of the call diff --git a/crates/evm/src/fuzz/invariant/error.rs b/crates/evm/src/executors/invariant/error.rs similarity index 80% rename from crates/evm/src/fuzz/invariant/error.rs rename to crates/evm/src/executors/invariant/error.rs index a51a58ccdf930..01e8e18a70fc1 100644 --- a/crates/evm/src/fuzz/invariant/error.rs +++ b/crates/evm/src/executors/invariant/error.rs @@ -1,16 +1,54 @@ use super::{BasicTxDetails, InvariantContract}; -use crate::{ - decode::decode_revert, - executor::{Executor, RawCallResult}, - fuzz::{invariant::set_up_inner_replay, *}, - trace::{load_contracts, TraceKind, Traces}, - CALLER, -}; +use crate::executors::{Executor, RawCallResult}; use alloy_json_abi::Function; +use alloy_primitives::Address; +use ethers::types::Log; use eyre::Result; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; +use foundry_evm_executors::{decode::decode_revert, CALLER}; +use foundry_evm_fuzz::{BaseCounterExample, CounterExample, FuzzedCases, Reason}; +use foundry_evm_traces::{load_contracts, CallTraceArena, TraceKind, Traces}; +use parking_lot::RwLock; use proptest::test_runner::TestError; use revm::primitives::U256; +use std::sync::Arc; + +#[derive(Clone, Default)] +/// Stores information about failures and reverts of the invariant tests. +pub struct InvariantFailures { + /// Total number of reverts. + pub reverts: usize, + /// How many different invariants have been broken. + pub broken_invariants_count: usize, + /// The latest revert reason of a run. + pub revert_reason: Option, + /// Maps a broken invariant to its specific error. + pub error: Option, +} + +impl InvariantFailures { + pub fn new() -> Self { + Self::default() + } + + pub fn into_inner(self) -> (usize, Option) { + (self.reverts, self.error) + } +} + +/// The outcome of an invariant fuzz test +#[derive(Debug)] +pub struct InvariantFuzzTestResult { + pub error: Option, + /// Every successful fuzz test case + pub cases: Vec, + /// Number of reverted fuzz calls + pub reverts: usize, + + /// The entire inputs of the last run of the invariant campaign, used for + /// replaying the run for collecting traces. + pub last_run_inputs: Vec, +} #[derive(Debug, Clone)] pub struct InvariantFuzzError { @@ -34,7 +72,7 @@ pub struct InvariantFuzzError { impl InvariantFuzzError { pub fn new( - invariant_contract: &InvariantContract, + invariant_contract: &InvariantContract<'_>, error_func: Option<&Function>, calldata: &[BasicTxDetails], call_result: RawCallResult, @@ -238,3 +276,13 @@ impl InvariantFuzzError { shrunk } } + +/// Sets up the calls generated by the internal fuzzer, if they exist. +fn set_up_inner_replay(executor: &mut Executor, inner_sequence: &[Option]) { + if let Some(fuzzer) = &mut executor.inspector.fuzzer { + if let Some(call_generator) = &mut fuzzer.call_generator { + call_generator.last_sequence = Arc::new(RwLock::new(inner_sequence.to_owned())); + call_generator.set_replay(true); + } + } +} diff --git a/crates/evm/src/fuzz/invariant/mod.rs b/crates/evm/src/executors/invariant/funcs.rs similarity index 65% rename from crates/evm/src/fuzz/invariant/mod.rs rename to crates/evm/src/executors/invariant/funcs.rs index 3d32401bc37b0..0b3ad792b2e33 100644 --- a/crates/evm/src/fuzz/invariant/mod.rs +++ b/crates/evm/src/executors/invariant/funcs.rs @@ -1,55 +1,19 @@ -//! Fuzzing support abstracted over the [`Evm`](crate::Evm) used - -use crate::{ - executor::Executor, - fuzz::*, - trace::{load_contracts, TraceKind, Traces}, - CALLER, -}; +use super::{InvariantFailures, InvariantFuzzError}; +use crate::executors::{Executor, RawCallResult}; use alloy_dyn_abi::JsonAbiExt; -use alloy_json_abi::{Function, JsonAbi as Abi}; -use alloy_primitives::{Address, Bytes}; -use foundry_common::ContractsByArtifact; -use parking_lot::Mutex; +use alloy_json_abi::Function; +use ethers::types::Log; +use foundry_common::{ContractsByAddress, ContractsByArtifact}; +use foundry_evm_executors::CALLER; +use foundry_evm_fuzz::invariant::{BasicTxDetails, InvariantContract}; +use foundry_evm_traces::{load_contracts, TraceKind, Traces}; use revm::primitives::U256; -use std::{collections::BTreeMap, sync::Arc}; - -pub use proptest::test_runner::Config as FuzzConfig; - -mod error; -pub use error::InvariantFuzzError; - -mod call_override; -pub use call_override::{set_up_inner_replay, RandomCallGenerator}; - -mod executor; -pub use executor::{InvariantExecutor, InvariantFailures}; - -mod filters; -pub use filters::{ArtifactFilters, SenderFilters}; - -pub type TargetedContracts = BTreeMap)>; -pub type FuzzRunIdentifiedContracts = Arc>; - -/// (Sender, (TargetContract, Calldata)) -pub type BasicTxDetails = (Address, (Address, Bytes)); - -/// Test contract which is testing its invariants. -#[derive(Debug, Clone)] -pub struct InvariantContract<'a> { - /// Address of the test contract. - pub address: Address, - /// Invariant functions present in the test contract. - pub invariant_function: &'a Function, - /// Abi of the test contract. - pub abi: &'a Abi, -} /// Given the executor state, asserts that no invariant has been broken. Otherwise, it fills the /// external `invariant_failures.failed_invariant` map and returns a generic error. /// Returns the mapping of (Invariant Function Name -> Call Result). pub fn assert_invariants( - invariant_contract: &InvariantContract, + invariant_contract: &InvariantContract<'_>, executor: &Executor, calldata: &[BasicTxDetails], invariant_failures: &mut InvariantFailures, @@ -102,7 +66,7 @@ pub fn assert_invariants( /// Replays the provided invariant run for collecting the logs and traces from all depths. #[allow(clippy::too_many_arguments)] pub fn replay_run( - invariant_contract: &InvariantContract, + invariant_contract: &InvariantContract<'_>, mut executor: Executor, known_contracts: Option<&ContractsByArtifact>, mut ided_contracts: ContractsByAddress, @@ -146,17 +110,3 @@ pub fn replay_run( logs.extend(error_call_result.logs); } } - -/// The outcome of an invariant fuzz test -#[derive(Debug)] -pub struct InvariantFuzzTestResult { - pub error: Option, - /// Every successful fuzz test case - pub cases: Vec, - /// Number of reverted fuzz calls - pub reverts: usize, - - /// The entire inputs of the last run of the invariant campaign, used for - /// replaying the run for collecting traces. - pub last_run_inputs: Vec, -} diff --git a/crates/evm/src/fuzz/invariant/executor.rs b/crates/evm/src/executors/invariant/mod.rs similarity index 94% rename from crates/evm/src/fuzz/invariant/executor.rs rename to crates/evm/src/executors/invariant/mod.rs index 940cfc025dbd4..2087bc8276ea5 100644 --- a/crates/evm/src/fuzz/invariant/executor.rs +++ b/crates/evm/src/executors/invariant/mod.rs @@ -1,23 +1,6 @@ -use super::{ - assert_invariants, - filters::{ArtifactFilters, SenderFilters}, - BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract, InvariantFuzzError, - InvariantFuzzTestResult, RandomCallGenerator, TargetedContracts, -}; use crate::{ - executor::{ - inspector::Fuzzer, Executor, RawCallResult, StateChangeset, CHEATCODE_ADDRESS, - HARDHAT_CONSOLE_ADDRESS, - }, - fuzz::{ - strategies::{ - build_initial_state, collect_created_contracts, collect_state_from_call, - invariant_strat, override_call_strat, EvmFuzzState, - }, - FuzzCase, FuzzedCases, - }, - utils::get_function, - CALLER, + executors::{Executor, RawCallResult}, + inspectors::Fuzzer, }; use alloy_dyn_abi::DynSolValue; use alloy_json_abi::JsonAbi as Abi; @@ -25,6 +8,20 @@ use alloy_primitives::{Address, FixedBytes}; use eyre::{eyre, ContextCompat, Result}; use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact}; use foundry_config::{FuzzDictionaryConfig, InvariantConfig}; +use foundry_evm_executors::{ + utils::get_function, StateChangeset, CALLER, CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, +}; +use foundry_evm_fuzz::{ + invariant::{ + ArtifactFilters, BasicTxDetails, FuzzRunIdentifiedContracts, InvariantContract, + RandomCallGenerator, SenderFilters, TargetedContracts, + }, + strategies::{ + build_initial_state, collect_created_contracts, collect_state_from_call, invariant_strat, + override_call_strat, EvmFuzzState, + }, + FuzzCase, FuzzedCases, +}; use parking_lot::{Mutex, RwLock}; use proptest::{ strategy::{BoxedStrategy, Strategy, ValueTree}, @@ -36,6 +33,12 @@ use revm::{ }; use std::{cell::RefCell, collections::BTreeMap, sync::Arc}; +mod error; +pub use error::{InvariantFailures, InvariantFuzzError, InvariantFuzzTestResult}; + +mod funcs; +pub use funcs::{assert_invariants, replay_run}; + /// Alias for (Dictionary for fuzzing, initial contracts to fuzz and an InvariantStrategy). type InvariantPreparation = (EvmFuzzState, FuzzRunIdentifiedContracts, BoxedStrategy>); @@ -54,11 +57,11 @@ impl RichInvariantResults { } } -/// Wrapper around any [`Executor`] implementor which provides fuzzing support using [`proptest`](https://docs.rs/proptest/1.0.0/proptest/). +/// Wrapper around any [`Executor`] implementor which provides fuzzing support using [`proptest`]. /// /// After instantiation, calling `fuzz` will proceed to hammer the deployed smart contracts with /// inputs, until it finds a counterexample sequence. The provided [`TestRunner`] contains all the -/// configuration which can be overridden via [environment variables](https://docs.rs/proptest/1.0.0/proptest/test_runner/struct.Config.html) +/// configuration which can be overridden via [environment variables](proptest::test_runner::Config) pub struct InvariantExecutor<'a> { pub executor: Executor, /// Proptest runner. @@ -96,7 +99,7 @@ impl<'a> InvariantExecutor<'a> { /// Fuzzes any deployed contract and checks any broken invariant at `invariant_address`. pub fn invariant_fuzz( &mut self, - invariant_contract: InvariantContract, + invariant_contract: InvariantContract<'_>, ) -> Result { // Throw an error to abort test run if the invariant function accepts input params if !invariant_contract.invariant_function.inputs.is_empty() { @@ -249,7 +252,7 @@ impl<'a> InvariantExecutor<'a> { /// * Invariant Strategy fn prepare_fuzzing( &mut self, - invariant_contract: &InvariantContract, + invariant_contract: &InvariantContract<'_>, ) -> eyre::Result { // Finds out the chosen deployed contracts and/or senders. self.select_contract_artifacts(invariant_contract.address, invariant_contract.abi)?; @@ -715,7 +718,7 @@ fn collect_data( /// asserted. #[allow(clippy::too_many_arguments)] fn can_continue( - invariant_contract: &InvariantContract, + invariant_contract: &InvariantContract<'_>, call_result: RawCallResult, executor: &Executor, calldata: &[BasicTxDetails], @@ -762,26 +765,3 @@ fn can_continue( } RichInvariantResults::new(true, call_results) } - -#[derive(Clone, Default)] -/// Stores information about failures and reverts of the invariant tests. -pub struct InvariantFailures { - /// Total number of reverts. - pub reverts: usize, - /// How many different invariants have been broken. - pub broken_invariants_count: usize, - /// The latest revert reason of a run. - pub revert_reason: Option, - /// Maps a broken invariant to its specific error. - pub error: Option, -} - -impl InvariantFailures { - fn new() -> Self { - Self::default() - } - - fn into_inner(self) -> (usize, Option) { - (self.reverts, self.error) - } -} diff --git a/crates/evm/src/executor/mod.rs b/crates/evm/src/executors/mod.rs similarity index 92% rename from crates/evm/src/executor/mod.rs rename to crates/evm/src/executors/mod.rs index f00b89d0f083c..2e0163283d134 100644 --- a/crates/evm/src/executor/mod.rs +++ b/crates/evm/src/executors/mod.rs @@ -1,79 +1,56 @@ -//! Main Foundry executor abstractions, which can execute calls. -//! Used for running tests, scripts, and interacting with the inner backend which holds the state. +//! EVM executors. -use self::inspector::{cheatcodes::util::BroadcastableTransactions, Cheatcodes, InspectorData}; -use crate::{ - debug::DebugArena, - decode, - trace::CallTraceArena, - utils::{eval_to_instruction_result, halt_to_instruction_result}, - CALLER, -}; -pub use abi::{ - patch_hardhat_console_selector, HardhatConsoleCalls, CHEATCODE_ADDRESS, CONSOLE_ABI, - HARDHAT_CONSOLE_ABI, HARDHAT_CONSOLE_ADDRESS, +// TODO: The individual executors in this module should be moved into the respective craits, and the +// `Executor` struct should be accessed using a trait defined in `foundry-evm-executors` instead of +// the concrete `Executor` type. + +use crate::inspectors::{ + cheatcodes::util::BroadcastableTransactions, Cheatcodes, InspectorData, InspectorStack, }; use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt}; use alloy_json_abi::{Function, JsonAbi as Abi}; -/// Reexport commonly used revm types -pub use alloy_primitives::{Address, Bytes, U256}; -use backend::FuzzBackendWrapper; +use alloy_primitives::{Address, Bytes, U256}; use ethers::{signers::LocalWallet, types::Log}; use foundry_common::{abi::IntoFunction, evm::Breakpoints}; -use revm::primitives::hex_literal::hex; -pub use revm::{ +use foundry_evm_coverage::HitMaps; +use foundry_evm_executors::{ + backend::{DatabaseError, DatabaseExt, DatabaseResult, FuzzBackendWrapper}, + debug::DebugArena, + utils::{eval_to_instruction_result, halt_to_instruction_result}, +}; +use foundry_evm_traces::CallTraceArena; +use revm::{ db::{DatabaseCommit, DatabaseRef}, interpreter::{return_ok, CreateScheme, InstructionResult, Memory, Stack}, primitives::{ - Account, BlockEnv, Bytecode, Env, ExecutionResult, HashMap, Output, ResultAndState, SpecId, - TransactTo, TxEnv, + BlockEnv, Bytecode, Env, ExecutionResult, Output, ResultAndState, SpecId, TransactTo, TxEnv, }, }; use std::collections::BTreeMap; -/// ABIs used internally in the executor -pub mod abi; -/// custom revm database implementations -pub mod backend; -pub use backend::Backend; -/// Executor builder -pub mod builder; -/// Forking provider -pub mod fork; -/// Executor inspectors -pub mod inspector; -/// Executor configuration -pub mod opts; -pub mod snapshot; - -use crate::{ - coverage::HitMaps, - executor::{ - backend::{ - error::{DatabaseError, DatabaseResult}, - DatabaseExt, - }, - inspector::{InspectorStack, DEFAULT_CREATE2_DEPLOYER}, - }, -}; +mod builder; pub use builder::ExecutorBuilder; -/// A mapping of addresses to their changed state. -pub type StateChangeset = HashMap; +pub mod fuzz; +pub use fuzz::FuzzedExecutor; -/// The initcode of the default create2 deployer. -pub const DEFAULT_CREATE2_DEPLOYER_CODE: &[u8] = &hex!("604580600e600039806000f350fe7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"); -/// The runtime code of the default create2 deployer. -pub const DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE: &[u8] = &hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"); +pub mod invariant; +pub use invariant::InvariantExecutor; + +mod tracing; +pub use tracing::TracingExecutor; + +#[doc(no_inline)] +pub use foundry_evm_executors::*; /// A type that can execute calls /// /// The executor can be configured with various `revm::Inspector`s, like `Cheatcodes`. /// /// There are two ways of executing calls: -/// - `committing`: any state changes made during the call are recorded and are persisting -/// - `raw`: state changes only exist for the duration of the call and are discarded afterwards, in -/// other words: the state of the underlying database remains unchanged. +/// - `committing`: any state changes made during the call are recorded and are persisting +/// - `raw`: state changes only exist for the duration of the call and are discarded afterwards, in +/// other words: the state of the underlying database remains unchanged. #[derive(Debug, Clone)] pub struct Executor { /// The underlying `revm::Database` that contains the EVM storage. @@ -92,8 +69,6 @@ pub struct Executor { gas_limit: U256, } -// === impl Executor === - impl Executor { #[inline] pub fn new(mut backend: Backend, env: Env, inspector: InspectorStack, gas_limit: U256) -> Self { diff --git a/crates/evm/src/trace/executor.rs b/crates/evm/src/executors/tracing.rs similarity index 93% rename from crates/evm/src/trace/executor.rs rename to crates/evm/src/executors/tracing.rs index f23a40a4bdd8a..3e95e50d1e2fc 100644 --- a/crates/evm/src/trace/executor.rs +++ b/crates/evm/src/executors/tracing.rs @@ -1,6 +1,7 @@ -use crate::executor::{fork::CreateFork, opts::EvmOpts, Backend, Executor, ExecutorBuilder}; +use crate::executors::{Executor, ExecutorBuilder}; use foundry_compilers::EvmVersion; use foundry_config::{utils::evm_spec_id, Config}; +use foundry_evm_executors::{fork::CreateFork, opts::EvmOpts, Backend}; use revm::primitives::Env; use std::ops::{Deref, DerefMut}; diff --git a/crates/evm/src/executor/inspector/access_list.rs b/crates/evm/src/inspectors/access_list.rs similarity index 100% rename from crates/evm/src/executor/inspector/access_list.rs rename to crates/evm/src/inspectors/access_list.rs diff --git a/crates/evm/src/executor/inspector/cheatcodes/config.rs b/crates/evm/src/inspectors/cheatcodes/config.rs similarity index 99% rename from crates/evm/src/executor/inspector/cheatcodes/config.rs rename to crates/evm/src/inspectors/cheatcodes/config.rs index 58438c238b9e2..988cbb44cddd8 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/config.rs +++ b/crates/evm/src/inspectors/cheatcodes/config.rs @@ -1,11 +1,11 @@ use super::{ensure, fmt_err, Result}; -use crate::executor::opts::EvmOpts; use foundry_common::fs::normalize_path; use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; use foundry_config::{ cache::StorageCachingConfig, fs_permissions::FsAccessKind, Config, FsPermissions, ResolvedRpcEndpoints, }; +use foundry_evm_executors::opts::EvmOpts; use std::path::{Path, PathBuf}; /// Additional, configurable context the `Cheatcodes` inspector has access to diff --git a/crates/evm/src/executor/inspector/cheatcodes/env.rs b/crates/evm/src/inspectors/cheatcodes/env.rs similarity index 98% rename from crates/evm/src/executor/inspector/cheatcodes/env.rs rename to crates/evm/src/inspectors/cheatcodes/env.rs index 6de3d2d2af947..7a65a3c164594 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/env.rs +++ b/crates/evm/src/inspectors/cheatcodes/env.rs @@ -1,19 +1,14 @@ -use super::{ensure, fmt_err, Cheatcodes, Result}; -use crate::{ - abi::HEVMCalls, - executor::{ - backend::DatabaseExt, - inspector::cheatcodes::{ - mapping::{get_mapping_key_and_parent, get_mapping_length, get_mapping_slot_at}, - util::{is_potential_precompile, with_journaled_account}, - DealRecord, - }, - }, +use super::{ + ensure, fmt_err, + mapping::{get_mapping_key_and_parent, get_mapping_length, get_mapping_slot_at}, + util::{is_potential_precompile, with_journaled_account}, + Cheatcodes, DealRecord, Result, }; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{Address, Bytes, Log, B256, U256}; use ethers::signers::{LocalWallet, Signer}; use foundry_config::Config; +use foundry_evm_executors::{abi::HEVMCalls, backend::DatabaseExt}; use foundry_utils::types::ToAlloy; use revm::{ primitives::{Bytecode, SpecId, KECCAK_EMPTY}, diff --git a/crates/evm/src/executor/inspector/cheatcodes/error.rs b/crates/evm/src/inspectors/cheatcodes/error.rs similarity index 80% rename from crates/evm/src/executor/inspector/cheatcodes/error.rs rename to crates/evm/src/inspectors/cheatcodes/error.rs index a67163a0d14e7..aad1d933fc833 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/error.rs +++ b/crates/evm/src/inspectors/cheatcodes/error.rs @@ -1,9 +1,9 @@ -use crate::executor::backend::{error::NoCheatcodeAccessError, DatabaseError}; use alloy_dyn_abi::DynSolValue; use alloy_primitives::Bytes; use ethers::prelude::k256::ecdsa::signature::Error as SignatureError; use foundry_common::errors::FsPathError; use foundry_config::UnresolvedEnvVarError; +use foundry_evm_executors::backend::{DatabaseError, NoCheatcodeAccessError}; use foundry_utils::error::{encode_error, SolError}; use std::{borrow::Cow, fmt::Arguments}; @@ -12,49 +12,49 @@ pub type Result = std::result::Result; macro_rules! fmt_err { ($msg:literal $(,)?) => { - $crate::executor::inspector::cheatcodes::Error::fmt(::std::format_args!($msg)) + $crate::inspectors::cheatcodes::Error::fmt(::std::format_args!($msg)) }; ($err:expr $(,)?) => { - <$crate::executor::inspector::cheatcodes::Error as ::std::convert::From<_>>::from($err) + <$crate::inspectors::cheatcodes::Error as ::std::convert::From<_>>::from($err) }; ($fmt:expr, $($arg:tt)*) => { - $crate::executor::inspector::cheatcodes::Error::fmt(::std::format_args!($fmt, $($arg)*)) + $crate::inspectors::cheatcodes::Error::fmt(::std::format_args!($fmt, $($arg)*)) }; } macro_rules! bail { ($msg:literal $(,)?) => { - return ::std::result::Result::Err($crate::executor::inspector::cheatcodes::fmt_err!($msg)) + return ::std::result::Result::Err($crate::inspectors::cheatcodes::fmt_err!($msg)) }; ($err:expr $(,)?) => { - return ::std::result::Result::Err($crate::executor::inspector::cheatcodes::fmt_err!($err)) + return ::std::result::Result::Err($crate::inspectors::cheatcodes::fmt_err!($err)) }; ($fmt:expr, $($arg:tt)*) => { - return ::std::result::Result::Err($crate::executor::inspector::cheatcodes::fmt_err!($fmt, $($arg)*)) + return ::std::result::Result::Err($crate::inspectors::cheatcodes::fmt_err!($fmt, $($arg)*)) }; } macro_rules! ensure { ($cond:expr $(,)?) => { if !$cond { - return ::std::result::Result::Err($crate::executor::inspector::cheatcodes::Error::custom( + return ::std::result::Result::Err($crate::inspectors::cheatcodes::Error::custom( ::std::concat!("Condition failed: `", ::std::stringify!($cond), "`") )); } }; ($cond:expr, $msg:literal $(,)?) => { if !$cond { - return ::std::result::Result::Err($crate::executor::inspector::cheatcodes::fmt_err!($msg)); + return ::std::result::Result::Err($crate::inspectors::cheatcodes::fmt_err!($msg)); } }; ($cond:expr, $err:expr $(,)?) => { if !$cond { - return ::std::result::Result::Err($crate::executor::inspector::cheatcodes::fmt_err!($err)); + return ::std::result::Result::Err($crate::inspectors::cheatcodes::fmt_err!($err)); } }; ($cond:expr, $fmt:expr, $($arg:tt)*) => { if !$cond { - return ::std::result::Result::Err($crate::executor::inspector::cheatcodes::fmt_err!($fmt, $($arg)*)); + return ::std::result::Result::Err($crate::inspectors::cheatcodes::fmt_err!($fmt, $($arg)*)); } }; } diff --git a/crates/evm/src/executor/inspector/cheatcodes/expect.rs b/crates/evm/src/inspectors/cheatcodes/expect.rs similarity index 98% rename from crates/evm/src/executor/inspector/cheatcodes/expect.rs rename to crates/evm/src/inspectors/cheatcodes/expect.rs index a14344a92e966..81a3d283c1d4c 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/expect.rs +++ b/crates/evm/src/inspectors/cheatcodes/expect.rs @@ -1,7 +1,7 @@ use super::{bail, ensure, fmt_err, Cheatcodes, Result}; -use crate::{abi::HEVMCalls, executor::backend::DatabaseExt}; use alloy_dyn_abi::DynSolType; use alloy_primitives::{Address, Bytes, Log as RawLog, U256}; +use foundry_evm_executors::{abi::HEVMCalls, backend::DatabaseExt}; use foundry_utils::{ error::{ERROR_PREFIX, REVERT_PREFIX}, types::ToAlloy, @@ -502,9 +502,7 @@ pub fn apply( .as_ref() .map_or(true, Bytecode::is_empty); if empty_bytecode { - let code = - Bytecode::new_raw(alloy_primitives::Bytes(bytes::Bytes::from_static(&[0u8]))) - .to_checked(); + let code = Bytecode::new_raw(Bytes::from_static(&[0u8])).to_checked(); data.journaled_state.set_code(inner.0.to_alloy(), code); } state.mocked_calls.entry(inner.0.to_alloy()).or_default().insert( diff --git a/crates/evm/src/executor/inspector/cheatcodes/ext.rs b/crates/evm/src/inspectors/cheatcodes/ext.rs similarity index 99% rename from crates/evm/src/executor/inspector/cheatcodes/ext.rs rename to crates/evm/src/inspectors/cheatcodes/ext.rs index 609463b36eedf..4ac78bdc8c0a3 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/ext.rs +++ b/crates/evm/src/inspectors/cheatcodes/ext.rs @@ -1,10 +1,10 @@ -use super::{bail, ensure, fmt_err, util::MAGIC_SKIP_BYTES, Cheatcodes, Error, Result}; -use crate::{abi::HEVMCalls, executor::inspector::cheatcodes::parse}; +use super::{bail, ensure, fmt_err, parse, Cheatcodes, Error, Result}; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::{Address, Bytes, B256, I256, U256}; use ethers::{abi::JsonAbi, prelude::artifacts::CompactContractBytecode}; use foundry_common::{fmt::*, fs, get_artifact_path}; use foundry_config::fs_permissions::FsAccessKind; +use foundry_evm_executors::{abi::HEVMCalls, MAGIC_SKIP_BYTES}; use foundry_utils::types::ToAlloy; use revm::{Database, EVMData}; use serde::Deserialize; @@ -754,7 +754,7 @@ pub fn apply( #[cfg(test)] mod tests { use super::*; - use crate::executor::inspector::CheatsConfig; + use crate::inspectors::CheatsConfig; use ethers::core::abi::AbiDecode; use std::{path::PathBuf, sync::Arc}; @@ -789,7 +789,7 @@ mod tests { #[test] fn test_artifact_parsing() { - let s = include_str!("../../../../test-data/solc-obj.json"); + let s = include_str!("../../../test-data/solc-obj.json"); let artifact: ArtifactBytecode = serde_json::from_str(s).unwrap(); assert!(artifact.into_bytecode().is_some()); diff --git a/crates/evm/src/executor/inspector/cheatcodes/fork.rs b/crates/evm/src/inspectors/cheatcodes/fork.rs similarity index 96% rename from crates/evm/src/executor/inspector/cheatcodes/fork.rs rename to crates/evm/src/inspectors/cheatcodes/fork.rs index b6c38cd0ec650..4a1c57d0bb6ca 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/fork.rs +++ b/crates/evm/src/inspectors/cheatcodes/fork.rs @@ -1,16 +1,12 @@ -use super::{fmt_err, Cheatcodes, Error, Result}; -use crate::{ - abi::HEVMCalls, - executor::{ - backend::DatabaseExt, fork::CreateFork, inspector::cheatcodes::ext::value_to_token, - }, - utils::RuntimeOrHandle, -}; +use super::{ext::value_to_token, fmt_err, Cheatcodes, Error, Result}; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{Bytes, B256, U256}; use ethers::{providers::Middleware, types::Filter}; use foundry_abi::hevm::{EthGetLogsCall, RpcCall}; use foundry_common::ProviderBuilder; +use foundry_evm_executors::{ + abi::HEVMCalls, backend::DatabaseExt, fork::CreateFork, utils::RuntimeOrHandle, +}; use foundry_utils::types::{ToAlloy, ToEthers}; use itertools::Itertools; use revm::EVMData; @@ -166,7 +162,7 @@ pub fn apply( } HEVMCalls::Transact0(inner) => data .db - .transact(None, inner.0.into(), data.env, &mut data.journaled_state, Some(state)) + .transact(None, inner.0.into(), data.env, &mut data.journaled_state, state) .map(empty) .map_err(Into::into), HEVMCalls::Transact1(inner) => data @@ -176,7 +172,7 @@ pub fn apply( inner.1.into(), data.env, &mut data.journaled_state, - Some(state), + state, ) .map(empty) .map_err(Into::into), @@ -190,7 +186,7 @@ pub fn apply( /// Selects the given fork id fn select_fork( state: &mut Cheatcodes, - data: &mut EVMData, + data: &mut EVMData<'_, DB>, fork_id: U256, ) -> Result { if state.broadcast.is_some() { @@ -275,7 +271,7 @@ fn create_fork_request( state: &Cheatcodes, url_or_alias: String, block: Option, - data: &EVMData, + data: &EVMData<'_, DB>, ) -> Result { let url = state.config.get_rpc_url(url_or_alias)?; let mut evm_opts = state.config.evm_opts.clone(); @@ -291,7 +287,7 @@ fn create_fork_request( /// Retrieve the logs specified for the current fork. /// Equivalent to eth_getLogs but on a cheatcode. -fn eth_getlogs(data: &EVMData, inner: &EthGetLogsCall) -> Result { +fn eth_getlogs(data: &EVMData<'_, DB>, inner: &EthGetLogsCall) -> Result { let url = data.db.active_fork_url().ok_or(fmt_err!("No active fork url found"))?; if inner.0.to_alloy() > U256::from(u64::MAX) || inner.1.to_alloy() > U256::from(u64::MAX) { return Err(fmt_err!("Blocks in block range must be less than 2^64 - 1")) @@ -392,7 +388,7 @@ fn eth_getlogs(data: &EVMData, inner: &EthGetLogsCall) -> R Ok(result) } -fn rpc(data: &EVMData, inner: &RpcCall) -> Result { +fn rpc(data: &EVMData<'_, DB>, inner: &RpcCall) -> Result { let url = data.db.active_fork_url().ok_or(fmt_err!("No active fork url found"))?; let provider = ProviderBuilder::new(url).build()?; diff --git a/crates/evm/src/executor/inspector/cheatcodes/fs.rs b/crates/evm/src/inspectors/cheatcodes/fs.rs similarity index 99% rename from crates/evm/src/executor/inspector/cheatcodes/fs.rs rename to crates/evm/src/inspectors/cheatcodes/fs.rs index e59d08a7344f8..e6e1659a41d4d 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/fs.rs +++ b/crates/evm/src/inspectors/cheatcodes/fs.rs @@ -1,9 +1,9 @@ use super::{Cheatcodes, Result}; -use crate::abi::hevm::{DirEntry, FsMetadata, HEVMCalls}; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{Bytes, U256}; use foundry_common::fs; use foundry_config::fs_permissions::FsAccessKind; +use foundry_evm_executors::abi::hevm::{DirEntry, FsMetadata, HEVMCalls}; use foundry_utils::types::ToAlloy; use std::{ io::{BufRead, BufReader, Write}, diff --git a/crates/evm/src/executor/inspector/cheatcodes/fuzz.rs b/crates/evm/src/inspectors/cheatcodes/fuzz.rs similarity index 87% rename from crates/evm/src/executor/inspector/cheatcodes/fuzz.rs rename to crates/evm/src/inspectors/cheatcodes/fuzz.rs index e833abdb402e2..c32d5a28aa773 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/fuzz.rs +++ b/crates/evm/src/inspectors/cheatcodes/fuzz.rs @@ -1,6 +1,6 @@ use super::{Error, Result}; -use crate::{abi::HEVMCalls, fuzz::error::ASSUME_MAGIC_RETURN_CODE}; use alloy_primitives::Bytes; +use foundry_evm_executors::{abi::HEVMCalls, ASSUME_MAGIC_RETURN_CODE}; #[instrument(level = "error", name = "fuzz", target = "evm::cheatcodes", skip_all)] pub fn apply(call: &HEVMCalls) -> Option { diff --git a/crates/evm/src/executor/inspector/cheatcodes/mapping.rs b/crates/evm/src/inspectors/cheatcodes/mapping.rs similarity index 100% rename from crates/evm/src/executor/inspector/cheatcodes/mapping.rs rename to crates/evm/src/inspectors/cheatcodes/mapping.rs diff --git a/crates/evm/src/executor/inspector/cheatcodes/mod.rs b/crates/evm/src/inspectors/cheatcodes/mod.rs similarity index 99% rename from crates/evm/src/executor/inspector/cheatcodes/mod.rs rename to crates/evm/src/inspectors/cheatcodes/mod.rs index 9dab9c7f0e737..fa5fbb9d86de8 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/mod.rs +++ b/crates/evm/src/inspectors/cheatcodes/mod.rs @@ -1,15 +1,8 @@ use self::{ - env::Broadcast, + env::{Broadcast, RecordedLogs}, expect::{handle_expect_emit, handle_expect_revert, ExpectedCallType}, mapping::MappingSlots, - util::{check_if_fixed_gas_limit, process_create, BroadcastableTransactions, MAGIC_SKIP_BYTES}, -}; -use crate::{ - abi::HEVMCalls, - executor::{ - backend::DatabaseExt, inspector::cheatcodes::env::RecordedLogs, CHEATCODE_ADDRESS, - HARDHAT_CONSOLE_ADDRESS, - }, + util::{check_if_fixed_gas_limit, process_create, BroadcastableTransactions}, }; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{Address, Bytes, Log as RawLog, B256, U256}; @@ -19,6 +12,12 @@ use ethers::{ types::{transaction::eip2718::TypedTransaction, NameOrAddress, TransactionRequest}, }; use foundry_common::evm::Breakpoints; +use foundry_evm_executors::{ + abi::HEVMCalls, + backend::{DatabaseExt, RevertDiagnostic}, + utils::get_create_address, + CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS, MAGIC_SKIP_BYTES, +}; use foundry_utils::{error::SolError, types::ToEthers}; use itertools::Itertools; use revm::{ @@ -64,10 +63,10 @@ mod snapshot; pub mod util; /// Wallet / key management related cheatcodes mod wallet; -pub use util::{BroadcastableTransaction, DEFAULT_CREATE2_DEPLOYER}; + +pub use util::BroadcastableTransaction; mod config; -use crate::executor::{backend::RevertDiagnostic, inspector::utils::get_create_address}; pub use config::CheatsConfig; mod error; @@ -926,7 +925,7 @@ impl Inspector for Cheatcodes { return ( status, remaining_gas, - DynSolValue::String(err.to_error_msg(self)).abi_encode().into(), + DynSolValue::String(err.to_error_msg(&self.labels)).abi_encode().into(), ) } } diff --git a/crates/evm/src/executor/inspector/cheatcodes/parse.rs b/crates/evm/src/inspectors/cheatcodes/parse.rs similarity index 99% rename from crates/evm/src/executor/inspector/cheatcodes/parse.rs rename to crates/evm/src/inspectors/cheatcodes/parse.rs index 322fd510b57c9..509bbd6f3c449 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/parse.rs +++ b/crates/evm/src/inspectors/cheatcodes/parse.rs @@ -1,7 +1,7 @@ use super::{fmt_err, Cheatcodes, Result}; -use crate::abi::HEVMCalls; use alloy_dyn_abi::{DynSolType, DynSolValue}; use alloy_primitives::{FixedBytes, I256, U256}; +use foundry_evm_executors::abi::HEVMCalls; use foundry_macros::UIfmt; use revm::{Database, EVMData}; diff --git a/crates/evm/src/executor/inspector/cheatcodes/snapshot.rs b/crates/evm/src/inspectors/cheatcodes/snapshot.rs similarity index 94% rename from crates/evm/src/executor/inspector/cheatcodes/snapshot.rs rename to crates/evm/src/inspectors/cheatcodes/snapshot.rs index c2daff5cdc24d..e9d597eb7fc80 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/snapshot.rs +++ b/crates/evm/src/inspectors/cheatcodes/snapshot.rs @@ -1,6 +1,6 @@ use super::Result; -use crate::{abi::HEVMCalls, executor::backend::DatabaseExt}; use alloy_dyn_abi::DynSolValue; +use foundry_evm_executors::{abi::HEVMCalls, backend::DatabaseExt}; use foundry_utils::types::ToAlloy; use revm::EVMData; diff --git a/crates/evm/src/executor/inspector/cheatcodes/util.rs b/crates/evm/src/inspectors/cheatcodes/util.rs similarity index 75% rename from crates/evm/src/executor/inspector/cheatcodes/util.rs rename to crates/evm/src/inspectors/cheatcodes/util.rs index b21af6b1dce7e..6cf2a7a1f001b 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/util.rs +++ b/crates/evm/src/inspectors/cheatcodes/util.rs @@ -1,37 +1,20 @@ use super::{ensure, Result}; -use crate::{ - executor::backend::{ - error::{DatabaseError, DatabaseResult}, - DatabaseExt, - }, - utils::h256_to_u256_be, -}; use alloy_primitives::{Address, Bytes, U256}; use bytes::{BufMut, BytesMut}; use ethers::{ core::k256::elliptic_curve::Curve, - prelude::{ - k256::{ecdsa::SigningKey, elliptic_curve::bigint::Encoding, Secp256k1}, - Transaction, - }, + prelude::k256::{ecdsa::SigningKey, elliptic_curve::bigint::Encoding, Secp256k1}, types::{transaction::eip2718::TypedTransaction, NameOrAddress}, }; use foundry_common::RpcUrl; -use foundry_utils::types::{ToAlloy, ToEthers}; -use revm::{ - interpreter::CreateInputs, - primitives::{Account, TransactTo}, - Database, EVMData, JournaledState, +use foundry_evm_executors::{ + backend::{DatabaseError, DatabaseExt, DatabaseResult}, + DEFAULT_CREATE2_DEPLOYER, }; +use foundry_utils::types::ToEthers; +use revm::{interpreter::CreateInputs, primitives::Account, Database, EVMData, JournaledState}; use std::collections::VecDeque; -pub const MAGIC_SKIP_BYTES: &[u8] = b"FOUNDRY::SKIP"; - -/// Address of the default CREATE2 deployer 0x4e59b44847b379578588920ca78fbf26c0b4956c -pub const DEFAULT_CREATE2_DEPLOYER: Address = Address::new([ - 78, 89, 180, 72, 71, 179, 121, 87, 133, 136, 146, 12, 167, 143, 191, 38, 192, 180, 149, 108, -]); - /// Helps collecting transactions from different forks. #[derive(Debug, Clone, Default)] pub struct BroadcastableTransaction { @@ -41,32 +24,6 @@ pub struct BroadcastableTransaction { pub type BroadcastableTransactions = VecDeque; -/// Configures the env for the transaction -pub fn configure_tx_env(env: &mut revm::primitives::Env, tx: &Transaction) { - env.tx.caller = tx.from.to_alloy(); - env.tx.gas_limit = tx.gas.as_u64(); - env.tx.gas_price = tx.gas_price.unwrap_or_default().to_alloy(); - env.tx.gas_priority_fee = tx.max_priority_fee_per_gas.map(|g| g.to_alloy()); - env.tx.nonce = Some(tx.nonce.as_u64()); - env.tx.access_list = tx - .access_list - .clone() - .unwrap_or_default() - .0 - .into_iter() - .map(|item| { - ( - item.address.to_alloy(), - item.storage_keys.into_iter().map(h256_to_u256_be).map(|g| g.to_alloy()).collect(), - ) - }) - .collect(); - env.tx.value = tx.value.to_alloy(); - env.tx.data = alloy_primitives::Bytes(tx.input.0.clone()); - env.tx.transact_to = - tx.to.map(|tx| tx.to_alloy()).map(TransactTo::Call).unwrap_or_else(TransactTo::create) -} - /// Applies the given function `f` to the `revm::Account` belonging to the `addr` /// /// This will ensure the `Account` is loaded and `touched`, see [`JournaledState::touch`] diff --git a/crates/evm/src/executor/inspector/cheatcodes/wallet.rs b/crates/evm/src/inspectors/cheatcodes/wallet.rs similarity index 99% rename from crates/evm/src/executor/inspector/cheatcodes/wallet.rs rename to crates/evm/src/inspectors/cheatcodes/wallet.rs index a3cebf34f1d2a..ba582ea127b51 100644 --- a/crates/evm/src/executor/inspector/cheatcodes/wallet.rs +++ b/crates/evm/src/inspectors/cheatcodes/wallet.rs @@ -1,5 +1,4 @@ use super::{ensure, Cheatcodes, Result}; -use crate::abi::HEVMCalls; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{keccak256, B256, U256}; use ethers::{ @@ -21,6 +20,7 @@ use ethers::{ }, utils, }; +use foundry_evm_executors::abi::HEVMCalls; use foundry_utils::types::{ToAlloy, ToEthers}; use revm::{Database, EVMData}; use std::str::FromStr; diff --git a/crates/evm/src/executor/inspector/chisel_state.rs b/crates/evm/src/inspectors/chisel_state.rs similarity index 100% rename from crates/evm/src/executor/inspector/chisel_state.rs rename to crates/evm/src/inspectors/chisel_state.rs diff --git a/crates/evm/src/executor/inspector/debugger.rs b/crates/evm/src/inspectors/debugger.rs similarity index 96% rename from crates/evm/src/executor/inspector/debugger.rs rename to crates/evm/src/inspectors/debugger.rs index 2e59b1f700298..e2544959a1d34 100644 --- a/crates/evm/src/executor/inspector/debugger.rs +++ b/crates/evm/src/inspectors/debugger.rs @@ -1,13 +1,10 @@ -use crate::{ +use alloy_primitives::{Address, Bytes}; +use foundry_evm_executors::{ + backend::DatabaseExt, debug::{DebugArena, DebugNode, DebugStep, Instruction}, - executor::{ - backend::DatabaseExt, - inspector::utils::{gas_used, get_create_address}, - CHEATCODE_ADDRESS, - }, - CallKind, + utils::{gas_used, get_create_address}, + CallKind, CHEATCODE_ADDRESS, }; -use alloy_primitives::{Address, Bytes}; use foundry_utils::error::SolError; use revm::{ interpreter::{ diff --git a/crates/evm/src/executor/inspector/logs.rs b/crates/evm/src/inspectors/logs.rs similarity index 99% rename from crates/evm/src/executor/inspector/logs.rs rename to crates/evm/src/inspectors/logs.rs index a0c1d8be2adf0..ddd6010de0809 100644 --- a/crates/evm/src/executor/inspector/logs.rs +++ b/crates/evm/src/inspectors/logs.rs @@ -1,11 +1,11 @@ -use crate::executor::{ - patch_hardhat_console_selector, HardhatConsoleCalls, HARDHAT_CONSOLE_ADDRESS, -}; use alloy_primitives::{Address, Bytes, B256}; use ethers::{ abi::{AbiDecode, Token}, types::{Bytes as ethersBytes, Log, H256}, }; +use foundry_evm_executors::{ + patch_hardhat_console_selector, HardhatConsoleCalls, HARDHAT_CONSOLE_ADDRESS, +}; use foundry_macros::ConsoleFmt; use foundry_utils::types::ToEthers; use revm::{ diff --git a/crates/evm/src/inspectors/mod.rs b/crates/evm/src/inspectors/mod.rs new file mode 100644 index 0000000000000..89ba94cfa243a --- /dev/null +++ b/crates/evm/src/inspectors/mod.rs @@ -0,0 +1,36 @@ +//! EVM inspectors. + +pub use foundry_evm_coverage::CoverageCollector; +pub use foundry_evm_fuzz::Fuzzer; +pub use foundry_evm_traces::Tracer; + +macro_rules! try_or_continue { + ($e:expr) => { + match $e { + Ok(v) => v, + Err(_) => return InstructionResult::Continue, + } + }; +} + +mod access_list; +pub use access_list::AccessListTracer; + +#[allow(unreachable_pub)] +pub mod cheatcodes; +pub use cheatcodes::{Cheatcodes, CheatsConfig}; + +mod chisel_state; +pub use chisel_state::ChiselState; + +mod debugger; +pub use debugger::Debugger; + +mod logs; +pub use logs::LogCollector; + +mod printer; +pub use printer::TracePrinter; + +mod stack; +pub use stack::{InspectorData, InspectorStack, InspectorStackBuilder}; diff --git a/crates/evm/src/executor/inspector/printer.rs b/crates/evm/src/inspectors/printer.rs similarity index 100% rename from crates/evm/src/executor/inspector/printer.rs rename to crates/evm/src/inspectors/printer.rs diff --git a/crates/evm/src/executor/inspector/stack.rs b/crates/evm/src/inspectors/stack.rs similarity index 98% rename from crates/evm/src/executor/inspector/stack.rs rename to crates/evm/src/inspectors/stack.rs index 71e95c81fae6a..e5ad113145312 100644 --- a/crates/evm/src/executor/inspector/stack.rs +++ b/crates/evm/src/inspectors/stack.rs @@ -1,15 +1,12 @@ use super::{ - Cheatcodes, CheatsConfig, ChiselState, Debugger, Fuzzer, LogCollector, TracePrinter, Tracer, -}; -use crate::{ - coverage::HitMaps, - debug::DebugArena, - executor::{backend::DatabaseExt, inspector::CoverageCollector}, - trace::CallTraceArena, + Cheatcodes, CheatsConfig, ChiselState, CoverageCollector, Debugger, Fuzzer, LogCollector, + TracePrinter, Tracer, }; use alloy_primitives::{Address, Bytes, B256, U256}; use ethers::{signers::LocalWallet, types::Log}; - +use foundry_evm_coverage::HitMaps; +use foundry_evm_executors::{backend::DatabaseExt, debug::DebugArena}; +use foundry_evm_traces::CallTraceArena; use revm::{ interpreter::{ return_revert, CallInputs, CreateInputs, Gas, InstructionResult, Interpreter, Memory, Stack, diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 8dbe9eaf973ea..0d17606b77c8f 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -1,113 +1,20 @@ -#![warn(unused_crate_dependencies)] +//! # foundry-evm +//! +//! EVM executor and inspector implementations. + +#![warn(unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] #[macro_use] extern crate tracing; -/// Decoding helpers -pub mod decode; - -/// Call tracing -/// Contains a call trace arena, decoding and formatting utilities -pub mod trace; - -/// Debugger data structures -pub mod debug; - -/// Coverage data structures -pub mod coverage; - -/// Forge test execution backends -pub mod executor; - -pub use executor::abi; - -use ethers::types::{ActionType, CallType}; - -/// Fuzzing wrapper for executors -pub mod fuzz; - -/// utils for working with revm -pub mod utils; - -// Re-exports -pub use hashbrown; -use revm::interpreter::{CallScheme, CreateScheme}; -pub use revm::{ - self, - primitives::{Address as aB160, HashMap}, -}; -use serde::{Deserialize, Serialize}; - -/// Stores the caller address to be used as _sender_ account for: -/// - deploying Test contracts -/// - deploying Script contracts -/// -/// The address was derived from `address(uint160(uint256(keccak256("foundry default caller"))))` -/// and is equal to 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38. -pub const CALLER: aB160 = aB160::new([ - 0x18, 0x04, 0xc8, 0xAB, 0x1F, 0x12, 0xE6, 0xbb, 0xF3, 0x89, 0x4D, 0x40, 0x83, 0xF3, 0x3E, 0x07, - 0x30, 0x9D, 0x1F, 0x38, -]); - -/// Stores the default test contract address: 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84 -pub const TEST_CONTRACT_ADDRESS: aB160 = aB160::new([ - 180, 199, 157, 171, 143, 37, 156, 122, 238, 110, 91, 42, 167, 41, 130, 24, 100, 34, 126, 132, -]); - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "UPPERCASE")] -#[derive(Default)] -pub enum CallKind { - #[default] - Call, - StaticCall, - CallCode, - DelegateCall, - Create, - Create2, -} - -impl From for CallKind { - fn from(scheme: CallScheme) -> Self { - match scheme { - CallScheme::Call => CallKind::Call, - CallScheme::StaticCall => CallKind::StaticCall, - CallScheme::CallCode => CallKind::CallCode, - CallScheme::DelegateCall => CallKind::DelegateCall, - } - } -} +pub mod executors; +pub use executors::{debug, decode, utils}; -impl From for CallKind { - fn from(create: CreateScheme) -> Self { - match create { - CreateScheme::Create => CallKind::Create, - CreateScheme::Create2 { .. } => CallKind::Create2, - } - } -} +pub mod inspectors; -impl From for ActionType { - fn from(kind: CallKind) -> Self { - match kind { - CallKind::Call | CallKind::StaticCall | CallKind::DelegateCall | CallKind::CallCode => { - ActionType::Call - } - CallKind::Create => ActionType::Create, - CallKind::Create2 => ActionType::Create, - } - } -} +pub use foundry_evm_coverage as coverage; +pub use foundry_evm_fuzz as fuzz; +pub use foundry_evm_traces as traces; -impl From for CallType { - fn from(ty: CallKind) -> Self { - match ty { - CallKind::Call => CallType::Call, - CallKind::StaticCall => CallType::StaticCall, - CallKind::CallCode => CallType::CallCode, - CallKind::DelegateCall => CallType::DelegateCall, - CallKind::Create => CallType::None, - CallKind::Create2 => CallType::None, - } - } -} +#[doc(hidden)] +pub use {hashbrown, revm}; diff --git a/crates/fmt/src/visit.rs b/crates/fmt/src/visit.rs index e48f912e4a96f..da7f3ca376d10 100644 --- a/crates/fmt/src/visit.rs +++ b/crates/fmt/src/visit.rs @@ -5,7 +5,7 @@ use crate::solang_ext::pt::*; /// A trait that is invoked while traversing the Solidity Parse Tree. /// Each method of the [Visitor] trait is a hook that can be potentially overridden. /// -/// Currently the main implementor of this trait is the [`Formatter`](crate::Formatter) struct. +/// Currently the main implementor of this trait is the [`Formatter`](crate::Formatter<'_>) struct. pub trait Visitor { type Error: std::error::Error; diff --git a/crates/fmt/tests/formatter.rs b/crates/fmt/tests/formatter.rs index 3078c2bc7bc1c..6d06fca366114 100644 --- a/crates/fmt/tests/formatter.rs +++ b/crates/fmt/tests/formatter.rs @@ -93,7 +93,7 @@ fn test_formatter(filename: &str, config: FormatterConfig, source: &str, expecte } impl std::fmt::Debug for PrettyString { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.0) } } diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index b8a1bae352fd7..c692b9a6051b1 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -26,8 +26,8 @@ foundry-evm.workspace = true comfy-table = "7" ethers = { workspace = true, features = ["solc-full"] } -foundry-compilers = { workspace = true, default-features = false, features = ["full"] } -foundry-block-explorers = { workspace = true, default-features = false, features = ["foundry-compilers"] } +foundry-compilers = { workspace = true, features = ["full"] } +foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } eyre.workspace = true proptest = "1" rayon = "1" diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 6e869bc0a2e53..c93ff3c3325d6 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -7,7 +7,8 @@ use forge::{ analysis::SourceAnalyzer, anchors::find_anchors, ContractId, CoverageReport, CoverageReporter, DebugReporter, ItemAnchor, LcovReporter, SummaryReporter, }, - executor::{inspector::CheatsConfig, opts::EvmOpts}, + executors::opts::EvmOpts, + inspectors::CheatsConfig, result::SuiteResult, revm::primitives::SpecId, utils::{build_ic_pc_map, ICPCMap}, diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index eea8eca9969c8..cdbaaca4fef72 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -195,7 +195,7 @@ enum Input { } impl fmt::Display for Line { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { None => f.write_str(" "), Some(idx) => write!(f, "{:<4}", idx + 1), diff --git a/crates/forge/bin/cmd/remappings.rs b/crates/forge/bin/cmd/remappings.rs index f9b45f6088195..a1a5f903f3ff2 100644 --- a/crates/forge/bin/cmd/remappings.rs +++ b/crates/forge/bin/cmd/remappings.rs @@ -3,7 +3,7 @@ use eyre::Result; use foundry_cli::utils::LoadConfig; use foundry_compilers::remappings::RelativeRemapping; use foundry_config::impl_figment_convert_basic; -use foundry_evm::HashMap; +use foundry_evm::hashbrown::HashMap; use std::path::PathBuf; /// CLI arguments for `forge remappings`. diff --git a/crates/forge/bin/cmd/script/executor.rs b/crates/forge/bin/cmd/script/executor.rs index 41166f545ad30..fd1d097ac3dc0 100644 --- a/crates/forge/bin/cmd/script/executor.rs +++ b/crates/forge/bin/cmd/script/executor.rs @@ -8,12 +8,10 @@ use alloy_primitives::{Address, Bytes, U256}; use ethers::types::transaction::eip2718::TypedTransaction; use eyre::Result; use forge::{ - executor::{ - inspector::{cheatcodes::util::BroadcastableTransactions, CheatsConfig}, - Backend, ExecutorBuilder, - }, - trace::{CallTraceDecoder, Traces}, - CallKind, + executors::{Backend, ExecutorBuilder}, + inspectors::{cheatcodes::util::BroadcastableTransactions, CheatsConfig}, + traces::{CallTraceDecoder, Traces}, + utils::CallKind, }; use foundry_cli::utils::{ensure_clean_constructor, needs_setup}; use foundry_common::{shell, RpcUrl}; diff --git a/crates/forge/bin/cmd/script/mod.rs b/crates/forge/bin/cmd/script/mod.rs index 0a8b83aa56fd3..d3366d1acaf00 100644 --- a/crates/forge/bin/cmd/script/mod.rs +++ b/crates/forge/bin/cmd/script/mod.rs @@ -16,13 +16,13 @@ use eyre::{ContextCompat, Result, WrapErr}; use forge::{ debug::DebugArena, decode::decode_console_logs, - executor::{opts::EvmOpts, Backend}, - trace::{ + executors::{opts::EvmOpts, Backend}, + traces::{ identifier::{EtherscanIdentifier, LocalTraceIdentifier, SignaturesIdentifier}, CallTraceDecoder, CallTraceDecoderBuilder, RawOrDecodedCall, RawOrDecodedReturnData, TraceKind, Traces, }, - CallKind, + utils::CallKind, }; use foundry_cli::opts::MultiWallet; use foundry_common::{ @@ -47,10 +47,8 @@ use foundry_config::{ }; use foundry_evm::{ decode, - executor::inspector::{ - cheatcodes::{util::BroadcastableTransactions, BroadcastableTransaction}, - DEFAULT_CREATE2_DEPLOYER, - }, + executors::DEFAULT_CREATE2_DEPLOYER, + inspectors::cheatcodes::{util::BroadcastableTransactions, BroadcastableTransaction}, }; use foundry_utils::types::{ToAlloy, ToEthers}; use futures::future; diff --git a/crates/forge/bin/cmd/script/runner.rs b/crates/forge/bin/cmd/script/runner.rs index 2db86e8f698a2..7e5d07604b0e6 100644 --- a/crates/forge/bin/cmd/script/runner.rs +++ b/crates/forge/bin/cmd/script/runner.rs @@ -3,10 +3,11 @@ use alloy_primitives::{Address, Bytes, U256}; use ethers::types::NameOrAddress; use eyre::Result; use forge::{ - executor::{CallResult, DeployResult, EvmError, ExecutionErr, Executor, RawCallResult}, + executors::{ + CallResult, DeployResult, EvmError, ExecutionErr, Executor, RawCallResult, CALLER, + }, revm::interpreter::{return_ok, InstructionResult}, - trace::{TraceKind, Traces}, - CALLER, + traces::{TraceKind, Traces}, }; use tracing::log::trace; diff --git a/crates/forge/bin/cmd/script/transaction.rs b/crates/forge/bin/cmd/script/transaction.rs index b94254b1fde7b..3f032f7ad69d0 100644 --- a/crates/forge/bin/cmd/script/transaction.rs +++ b/crates/forge/bin/cmd/script/transaction.rs @@ -5,9 +5,7 @@ use alloy_primitives::{Address, B256}; use ethers::{prelude::NameOrAddress, types::transaction::eip2718::TypedTransaction}; use eyre::{ContextCompat, Result, WrapErr}; use foundry_common::{abi::format_token_raw, RpcUrl, SELECTOR_LEN}; -use foundry_evm::{ - executor::inspector::DEFAULT_CREATE2_DEPLOYER, trace::CallTraceDecoder, CallKind, -}; +use foundry_evm::{executors::DEFAULT_CREATE2_DEPLOYER, traces::CallTraceDecoder, utils::CallKind}; use foundry_utils::types::{ToAlloy, ToEthers}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; diff --git a/crates/forge/bin/cmd/test/filter.rs b/crates/forge/bin/cmd/test/filter.rs index ccfcf6d423167..7af0e4042a8f7 100644 --- a/crates/forge/bin/cmd/test/filter.rs +++ b/crates/forge/bin/cmd/test/filter.rs @@ -139,7 +139,7 @@ impl TestFilter for FilterArgs { } impl fmt::Display for FilterArgs { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut patterns = Vec::new(); if let Some(ref p) = self.test_pattern { patterns.push(format!("\tmatch-test: `{}`", p.as_str())); @@ -211,7 +211,7 @@ impl TestFilter for ProjectPathsAwareFilter { } impl fmt::Display for ProjectPathsAwareFilter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.args_filter.fmt(f) } } diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 34e708c87c5cb..a85b5441cb910 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -4,10 +4,10 @@ use clap::Parser; use eyre::Result; use forge::{ decode::decode_console_logs, - executor::inspector::CheatsConfig, gas_report::GasReport, + inspectors::CheatsConfig, result::{SuiteResult, TestResult, TestStatus}, - trace::{ + traces::{ identifier::{EtherscanIdentifier, LocalTraceIdentifier, SignaturesIdentifier}, CallTraceDecoderBuilder, TraceKind, }, diff --git a/crates/forge/src/gas_report.rs b/crates/forge/src/gas_report.rs index b1e30029c6af2..cf16c668ed58f 100644 --- a/crates/forge/src/gas_report.rs +++ b/crates/forge/src/gas_report.rs @@ -1,6 +1,6 @@ use crate::{ - executor::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, - trace::{CallTraceArena, RawOrDecodedCall, TraceKind}, + executors::{CHEATCODE_ADDRESS, HARDHAT_CONSOLE_ADDRESS}, + traces::{CallTraceArena, RawOrDecodedCall, TraceKind}, }; use alloy_primitives::U256; use comfy_table::{presets::ASCII_MARKDOWN, *}; @@ -116,7 +116,7 @@ impl GasReport { } impl Display for GasReport { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { for (name, contract) in self.contracts.iter() { if contract.functions.is_empty() { continue diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 13e71ed6e1c1e..2ff22826fe7ff 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -8,10 +8,8 @@ use foundry_compilers::{ ArtifactOutput, ProjectCompileOutput, }; use foundry_evm::{ - executor::{ - backend::Backend, fork::CreateFork, inspector::CheatsConfig, opts::EvmOpts, Executor, - ExecutorBuilder, - }, + executors::{backend::Backend, fork::CreateFork, opts::EvmOpts, Executor, ExecutorBuilder}, + inspectors::CheatsConfig, revm, }; use foundry_utils::{PostLinkInput, ResolvedDependency}; diff --git a/crates/forge/src/result.rs b/crates/forge/src/result.rs index 02ff027a60ed1..d46be910b18eb 100644 --- a/crates/forge/src/result.rs +++ b/crates/forge/src/result.rs @@ -6,9 +6,9 @@ use foundry_common::evm::Breakpoints; use foundry_evm::{ coverage::HitMaps, debug::DebugArena, - executor::EvmError, - fuzz::{types::FuzzCase, CounterExample}, - trace::{TraceKind, Traces}, + executors::EvmError, + fuzz::{CounterExample, FuzzCase}, + traces::{TraceKind, Traces}, }; use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, fmt, time::Duration}; diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 55189b11b1e61..feeb7049a2734 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -12,17 +12,13 @@ use foundry_common::{ use foundry_config::{FuzzConfig, InvariantConfig}; use foundry_evm::{ decode::decode_console_logs, - executor::{CallResult, EvmError, ExecutionErr, Executor}, - fuzz::{ - invariant::{ - replay_run, InvariantContract, InvariantExecutor, InvariantFuzzError, - InvariantFuzzTestResult, - }, - types::{CaseOutcome, CounterExampleOutcome, FuzzOutcome}, - CounterExample, FuzzedExecutor, + executors::{ + fuzz::{CaseOutcome, CounterExampleOutcome, FuzzOutcome, FuzzedExecutor}, + invariant::{replay_run, InvariantExecutor, InvariantFuzzError, InvariantFuzzTestResult}, + CallResult, EvmError, ExecutionErr, Executor, CALLER, }, - trace::{load_contracts, TraceKind}, - CALLER, + fuzz::{invariant::InvariantContract, CounterExample}, + traces::{load_contracts, TraceKind}, }; use proptest::test_runner::{TestError, TestRunner}; use rayon::prelude::*; diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index c383eb779c250..89d04b31e5220 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -1,14 +1,13 @@ //! Contains various tests for checking forge commands related to config values use alloy_primitives::{Address, B256, U256}; -use foundry_compilers::artifacts::{RevertStrings, YulDetails}; - use foundry_cli::utils as forge_utils; +use foundry_compilers::artifacts::{RevertStrings, YulDetails}; use foundry_config::{ cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, Config, FuzzConfig, InvariantConfig, OptimizerDetails, SolcReq, }; -use foundry_evm::executor::opts::EvmOpts; +use foundry_evm::executors::opts::EvmOpts; use foundry_test_utils::{ forgetest, forgetest_init, foundry_compilers::{remappings::Remapping, EvmVersion}, diff --git a/crates/forge/tests/it/config.rs b/crates/forge/tests/it/config.rs index cccb9ea7e12a4..5ecbf4ea9a6a2 100644 --- a/crates/forge/tests/it/config.rs +++ b/crates/forge/tests/it/config.rs @@ -12,7 +12,7 @@ use foundry_config::{ InvariantConfig, RpcEndpoint, RpcEndpoints, }; use foundry_evm::{ - decode::decode_console_logs, executor::inspector::CheatsConfig, revm::primitives::SpecId, + decode::decode_console_logs, inspectors::CheatsConfig, revm::primitives::SpecId, }; use std::{ collections::BTreeMap, diff --git a/crates/forge/tests/it/core.rs b/crates/forge/tests/it/core.rs index fd2f22812cdcb..dabd7183c7223 100644 --- a/crates/forge/tests/it/core.rs +++ b/crates/forge/tests/it/core.rs @@ -2,7 +2,7 @@ use crate::{config::*, test_helpers::filter::Filter}; use forge::result::SuiteResult; -use foundry_evm::trace::TraceKind; +use foundry_evm::traces::TraceKind; use std::{collections::BTreeMap, env}; #[tokio::test(flavor = "multi_thread")] diff --git a/crates/forge/tests/it/test_helpers.rs b/crates/forge/tests/it/test_helpers.rs index 5408713a95c31..dea6943e18c8b 100644 --- a/crates/forge/tests/it/test_helpers.rs +++ b/crates/forge/tests/it/test_helpers.rs @@ -8,13 +8,12 @@ use foundry_compilers::{ }; use foundry_config::Config; use foundry_evm::{ - executor::{ + executors::{ backend::Backend, opts::{Env, EvmOpts}, - DatabaseRef, Executor, ExecutorBuilder, + Executor, ExecutorBuilder, FuzzedExecutor, CALLER, }, - fuzz::FuzzedExecutor, - CALLER, + revm::db::DatabaseRef, }; use foundry_utils::types::{ToAlloy, ToEthers}; use once_cell::sync::Lazy; diff --git a/crates/macros/src/fmt/token.rs b/crates/macros/src/fmt/token.rs index e206b389cdd50..9853ec7ed9d2b 100644 --- a/crates/macros/src/fmt/token.rs +++ b/crates/macros/src/fmt/token.rs @@ -8,7 +8,7 @@ use std::{fmt, fmt::Write}; pub struct TokenDisplay<'a>(pub &'a DynSolValue); impl fmt::Display for TokenDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt_token(f, self.0) } } diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 733aa0fc2ad11..bbf7deb0a8a28 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -19,7 +19,7 @@ foundry-utils.workspace = true ethers.workspace = true alloy-primitives.workspace = true -foundry-compilers = { workspace = true, features = ["project-util"]} +foundry-compilers = { workspace = true, features = ["project-util"] } eyre.workspace = true once_cell = "1" diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index dc8fd85ef657c..80d7c4c3fd88d 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -18,6 +18,7 @@ ethers-addressbook.workspace = true ethers-providers.workspace = true alloy-primitives.workspace = true +alloy-json-abi.workspace = true alloy-dyn-abi.workspace = true foundry-compilers = { workspace = true, default-features = false } diff --git a/crates/utils/src/types.rs b/crates/utils/src/types.rs index 0b650d50e12dc..712784ab2c9bf 100644 --- a/crates/utils/src/types.rs +++ b/crates/utils/src/types.rs @@ -1,13 +1,17 @@ //! Temporary utility conversion traits between ethers-rs and alloy types. +use alloy_json_abi::{Event, EventParam, Function, InternalType, Param, StateMutability}; use alloy_primitives::{Address, B256, U256 as AlloyU256, U64 as AlloyU64}; -use ethers_core::types::{H160, H256, U256, U64}; +use ethers_core::{ + abi as ethabi, + types::{H160, H256, U256, U64}, +}; -/// Conversion trait to easily convert from ethers-rs types to alloy primitive types. +/// Conversion trait to easily convert from Ethers types to Alloy types. pub trait ToAlloy { type To; - /// Converts the alloy type to the corresponding ethers-rs type. + /// Converts the Ethers type to the corresponding Alloy type. fn to_alloy(self) -> Self::To; } @@ -56,11 +60,106 @@ impl ToAlloy for u64 { } } -/// Conversion trait to easily convert from alloy primitive types to ethers-rs types. +impl ToAlloy for ethabi::Event { + type To = Event; + + fn to_alloy(self) -> Self::To { + Event { + name: self.name, + inputs: self.inputs.into_iter().map(ToAlloy::to_alloy).collect(), + anonymous: self.anonymous, + } + } +} + +impl ToAlloy for ethabi::Function { + type To = Function; + + fn to_alloy(self) -> Self::To { + Function { + name: self.name, + inputs: self.inputs.into_iter().map(ToAlloy::to_alloy).collect(), + outputs: self.outputs.into_iter().map(ToAlloy::to_alloy).collect(), + state_mutability: self.state_mutability.to_alloy(), + } + } +} + +impl ToAlloy for ethabi::Param { + type To = Param; + + fn to_alloy(self) -> Self::To { + let (ty, components) = self.kind.to_alloy(); + Param { + name: self.name, + ty, + internal_type: self.internal_type.as_deref().and_then(InternalType::parse), + components, + } + } +} + +impl ToAlloy for ethabi::EventParam { + type To = EventParam; + + fn to_alloy(self) -> Self::To { + let (ty, components) = self.kind.to_alloy(); + EventParam { name: self.name, ty, internal_type: None, components, indexed: self.indexed } + } +} + +impl ToAlloy for ethabi::ParamType { + type To = (String, Vec); + + fn to_alloy(self) -> Self::To { + let (s, t) = split_pt(self); + (s, t.into_iter().map(pt_to_param).collect()) + } +} + +fn split_pt(x: ethabi::ParamType) -> (String, Vec) { + let s = ethabi::ethabi::param_type::Writer::write_for_abi(&x, false); + let t = get_tuple(x); + (s, t) +} + +fn get_tuple(x: ethabi::ParamType) -> Vec { + match x { + ethabi::ParamType::FixedArray(x, _) | ethabi::ParamType::Array(x) => get_tuple(*x), + ethabi::ParamType::Tuple(t) => t, + _ => Default::default(), + } +} + +fn pt_to_param(x: ethabi::ParamType) -> Param { + let (ty, components) = split_pt(x); + Param { + name: String::new(), + ty, + internal_type: None, + components: components.into_iter().map(pt_to_param).collect(), + } +} + +impl ToAlloy for ethabi::StateMutability { + type To = StateMutability; + + #[inline(always)] + fn to_alloy(self) -> Self::To { + match self { + ethabi::StateMutability::Pure => StateMutability::Pure, + ethabi::StateMutability::View => StateMutability::View, + ethabi::StateMutability::NonPayable => StateMutability::NonPayable, + ethabi::StateMutability::Payable => StateMutability::Payable, + } + } +} + +/// Conversion trait to easily convert from Alloy types to Ethers types. pub trait ToEthers { type To; - /// Converts the alloy type to the corresponding ethers-rs type. + /// Converts the Alloy type to the corresponding Ethers type. fn to_ethers(self) -> Self::To; }