diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index b3aff6b62ed8..85b2dcaab61f 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -1,5 +1,6 @@ use super::Result; use crate::Vm::Rpc; +use alloy_primitives::Address; use foundry_common::fs::normalize_path; use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; use foundry_config::{ @@ -7,7 +8,10 @@ use foundry_config::{ ResolvedRpcEndpoints, }; use foundry_evm_core::opts::EvmOpts; -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; /// Additional, configurable context the `Cheatcodes` inspector has access to /// @@ -30,6 +34,8 @@ pub struct CheatsConfig { pub allowed_paths: Vec, /// How the evm was configured by the user pub evm_opts: EvmOpts, + /// Address labels from config + pub labels: HashMap, } impl CheatsConfig { @@ -51,6 +57,7 @@ impl CheatsConfig { root: config.__root.0.clone(), allowed_paths, evm_opts, + labels: config.labels.clone(), } } @@ -164,6 +171,7 @@ impl Default for CheatsConfig { root: Default::default(), allowed_paths: vec![], evm_opts: Default::default(), + labels: Default::default(), } } } diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index f139e47cd8c4..ffe224855543 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -222,7 +222,8 @@ impl Cheatcodes { /// Creates a new `Cheatcodes` with the given settings. #[inline] pub fn new(config: Arc) -> Self { - Self { config, fs_commit: true, ..Default::default() } + let labels = config.labels.clone(); + Self { config, fs_commit: true, labels, ..Default::default() } } fn apply_cheatcode( @@ -262,7 +263,7 @@ impl Cheatcodes { if data.journaled_state.depth > 1 && !data.db.has_cheatcode_access(inputs.caller) { // we only grant cheat code access for new contracts if the caller also has // cheatcode access and the new contract is created in top most call - return created_address + return created_address; } data.db.allow_cheatcode_access(created_address); @@ -279,12 +280,12 @@ impl Cheatcodes { // Delay revert clean up until expected revert is handled, if set. if self.expected_revert.is_some() { - return + return; } // we only want to apply cleanup top level if data.journaled_state.depth() > 0 { - return + return; } // Roll back all previously applied deals @@ -722,11 +723,11 @@ impl Inspector for Cheatcodes { return match self.apply_cheatcode(data, call) { Ok(retdata) => (InstructionResult::Return, gas, retdata.into()), Err(err) => (InstructionResult::Revert, gas, err.abi_encode().into()), - } + }; } if call.contract == HARDHAT_CONSOLE_ADDRESS { - return (InstructionResult::Continue, gas, Bytes::new()) + return (InstructionResult::Continue, gas, Bytes::new()); } // Handle expected calls @@ -769,7 +770,7 @@ impl Inspector for Cheatcodes { }) .map(|(_, v)| v) }) { - return (return_data.ret_type, gas, return_data.data.clone()) + return (return_data.ret_type, gas, return_data.data.clone()); } } @@ -826,7 +827,7 @@ impl Inspector for Cheatcodes { if let Err(err) = data.journaled_state.load_account(broadcast.new_origin, data.db) { - return (InstructionResult::Revert, gas, Error::encode(err)) + return (InstructionResult::Revert, gas, Error::encode(err)); } let is_fixed_gas_limit = check_if_fixed_gas_limit(data, call.gas_limit); @@ -857,7 +858,7 @@ impl Inspector for Cheatcodes { debug!(target: "cheatcodes", address=%broadcast.new_origin, nonce=prev+1, prev, "incremented nonce"); } else if broadcast.single_call { let msg = "`staticcall`s are not allowed after `broadcast`; use `startBroadcast` instead"; - return (InstructionResult::Revert, Gas::new(0), Error::encode(msg)) + return (InstructionResult::Revert, Gas::new(0), Error::encode(msg)); } } } @@ -920,7 +921,7 @@ impl Inspector for Cheatcodes { retdata: Bytes, ) -> (InstructionResult, Gas, Bytes) { if call.contract == CHEATCODE_ADDRESS || call.contract == HARDHAT_CONSOLE_ADDRESS { - return (status, remaining_gas, retdata) + return (status, remaining_gas, retdata); } if data.journaled_state.depth() == 0 && self.skip { @@ -928,7 +929,7 @@ impl Inspector for Cheatcodes { InstructionResult::Revert, remaining_gas, super::Error::from(MAGIC_SKIP).abi_encode().into(), - ) + ); } // Clean up pranks @@ -970,7 +971,7 @@ impl Inspector for Cheatcodes { (InstructionResult::Revert, remaining_gas, error.abi_encode().into()) } Ok((_, retdata)) => (InstructionResult::Return, remaining_gas, retdata), - } + }; } } @@ -1039,7 +1040,7 @@ impl Inspector for Cheatcodes { InstructionResult::Revert, remaining_gas, "log != expected log".abi_encode().into(), - ) + ); } else { // All emits were found, we're good. // Clear the queue, as we expect the user to declare more events for the next call @@ -1056,7 +1057,7 @@ impl Inspector for Cheatcodes { // return a better error here if status == InstructionResult::Revert { if let Some(err) = diag { - return (status, remaining_gas, Error::encode(err.to_error_msg(&self.labels))) + return (status, remaining_gas, Error::encode(err.to_error_msg(&self.labels))); } } @@ -1080,7 +1081,7 @@ impl Inspector for Cheatcodes { // earlier error that happened first with unrelated information about // another error when using cheatcodes. if status == InstructionResult::Revert { - return (status, remaining_gas, retdata) + return (status, remaining_gas, retdata); } // If there's not a revert, we can continue on to run the last logic for expect* @@ -1125,7 +1126,7 @@ impl Inspector for Cheatcodes { "expected call to {address} with {expected_values} \ to be called {count} time{s}, but {but}" ); - return (InstructionResult::Revert, remaining_gas, Error::encode(msg)) + return (InstructionResult::Revert, remaining_gas, Error::encode(msg)); } } } @@ -1142,7 +1143,7 @@ impl Inspector for Cheatcodes { "expected an emit, but the call reverted instead. \ ensure you're testing the happy path when using `expectEmit`" }; - return (InstructionResult::Revert, remaining_gas, Error::encode(msg)) + return (InstructionResult::Revert, remaining_gas, Error::encode(msg)); } } @@ -1177,7 +1178,7 @@ impl Inspector for Cheatcodes { call.caller == broadcast.original_caller { if let Err(err) = data.journaled_state.load_account(broadcast.new_origin, data.db) { - return (InstructionResult::Revert, None, gas, Error::encode(err)) + return (InstructionResult::Revert, None, gas, Error::encode(err)); } data.env.tx.caller = broadcast.new_origin; @@ -1304,7 +1305,7 @@ impl Inspector for Cheatcodes { Err(err) => { (InstructionResult::Revert, None, remaining_gas, err.abi_encode().into()) } - } + }; } } diff --git a/crates/cli/src/utils/cmd.rs b/crates/cli/src/utils/cmd.rs index e0e11d21a51f..6d100cb15d08 100644 --- a/crates/cli/src/utils/cmd.rs +++ b/crates/cli/src/utils/cmd.rs @@ -390,8 +390,12 @@ pub async fn handle_traces( None }); + let labeled_addresses_in_config = config.labels.clone().into_iter(); + + let concatenated_addresses = labeled_addresses.chain(labeled_addresses_in_config); + let mut decoder = CallTraceDecoderBuilder::new() - .with_labels(labeled_addresses) + .with_labels(concatenated_addresses) .with_signature_identifier(SignaturesIdentifier::new( Config::foundry_cache_dir(), config.offline, diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index c5fccf3296ab..d20eae044de4 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -369,6 +369,9 @@ pub struct Config { /// Should be removed once EvmVersion Cancun is supported by solc pub cancun: bool, + /// Address labels + pub labels: HashMap, + /// The root path where the config detection started from, `Config::with_root` #[doc(hidden)] // We're skipping serialization here, so it won't be included in the [`Config::to_string()`] @@ -411,7 +414,7 @@ impl Config { /// Standalone sections in the config which get integrated into the selected profile pub const STANDALONE_SECTIONS: &'static [&'static str] = - &["rpc_endpoints", "etherscan", "fmt", "doc", "fuzz", "invariant"]; + &["rpc_endpoints", "etherscan", "fmt", "doc", "fuzz", "invariant", "labels"]; /// File name of config toml file pub const FILE_NAME: &'static str = "foundry.toml"; @@ -1831,6 +1834,7 @@ impl Default for Config { build_info_path: None, fmt: Default::default(), doc: Default::default(), + labels: Default::default(), __non_exhaustive: (), __warnings: vec![], } @@ -4432,7 +4436,7 @@ mod tests { "foundry.toml", r" [default] - [profile.default.optimizer_details] + [profile.default.optimizer_details] ", )?; @@ -4442,4 +4446,35 @@ mod tests { Ok(()) }); } + + #[test] + fn test_parse_labels() { + figment::Jail::expect_with(|jail| { + jail.create_file( + "foundry.toml", + r#" + [labels] + 0x1F98431c8aD98523631AE4a59f267346ea31F984 = "Uniswap V3: Factory" + 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 = "Uniswap V3: Positions NFT" + "#, + )?; + + let config = Config::load(); + assert_eq!( + config.labels, + HashMap::from_iter(vec![ + ( + Address::from_str("0x1F98431c8aD98523631AE4a59f267346ea31F984").unwrap(), + "Uniswap V3: Factory".to_string() + ), + ( + Address::from_str("0xC36442b4a4522E871399CD717aBDD847Ab11FE88").unwrap(), + "Uniswap V3: Positions NFT".to_string() + ), + ]) + ); + + Ok(()) + }); + } } diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 3cd5c1a2fea1..35e9e67736fd 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -344,7 +344,7 @@ impl InspectorStack { if new_status != status || (new_status == InstructionResult::Revert && new_retdata != retdata) { - return (new_status, new_gas, new_retdata) + return (new_status, new_gas, new_retdata); } } ); @@ -371,7 +371,7 @@ impl Inspector for InspectorStack { // Allow inspectors to exit early if interpreter.instruction_result != res { #[allow(clippy::needless_return)] - return + return; } } ); @@ -395,7 +395,7 @@ impl Inspector for InspectorStack { // Allow inspectors to exit early if interpreter.instruction_result != res { #[allow(clippy::needless_return)] - return + return; } } ); @@ -433,7 +433,7 @@ impl Inspector for InspectorStack { // Allow inspectors to exit early if interpreter.instruction_result != res { #[allow(clippy::needless_return)] - return + return; } } ); @@ -460,7 +460,7 @@ impl Inspector for InspectorStack { // Allow inspectors to exit early #[allow(clippy::needless_return)] if status != InstructionResult::Continue { - return (status, gas, retdata) + return (status, gas, retdata); } } ); @@ -509,7 +509,7 @@ impl Inspector for InspectorStack { // Allow inspectors to exit early if status != InstructionResult::Continue { - return (status, addr, gas, retdata) + return (status, addr, gas, retdata); } } ); @@ -546,7 +546,7 @@ impl Inspector for InspectorStack { ); if new_status != status { - return (new_status, new_address, new_gas, new_retdata) + return (new_status, new_address, new_gas, new_retdata); } } ); diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 27001b732172..f5c2387158bc 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -111,6 +111,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { fmt: Default::default(), doc: Default::default(), fs_permissions: Default::default(), + labels: Default::default(), cancun: true, __non_exhaustive: (), __warnings: vec![],