From 0fb6375a38b36a0e80b000056c1bc45f5f72b482 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 3 Nov 2023 08:38:01 +0100 Subject: [PATCH 1/6] refactor: split foundry-cheatcodes defs into a separate crate --- .cargo/config.toml | 2 +- Cargo.lock | 15 +- Cargo.toml | 2 + crates/cheatcodes/Cargo.toml | 70 +++----- crates/cheatcodes/defs/Cargo.toml | 26 +++ crates/cheatcodes/defs/README.md | 3 + .../{src/defs => defs/src}/cheatcode.rs | 0 .../{src/defs => defs/src}/function.rs | 0 .../{src/defs => defs/src}/items.rs | 0 .../{src/defs/mod.rs => defs/src/lib.rs} | 83 +++++++++- .../cheatcodes/{src/defs => defs/src}/vm.rs | 0 crates/cheatcodes/src/{impls => }/config.rs | 2 +- crates/cheatcodes/src/{impls => }/env.rs | 3 +- crates/cheatcodes/src/{impls => }/error.rs | 10 +- crates/cheatcodes/src/{impls => }/evm.rs | 3 +- crates/cheatcodes/src/{impls => }/evm/fork.rs | 5 +- .../cheatcodes/src/{impls => }/evm/mapping.rs | 3 +- crates/cheatcodes/src/{impls => }/evm/mock.rs | 3 +- .../cheatcodes/src/{impls => }/evm/prank.rs | 3 +- crates/cheatcodes/src/{impls => }/fs.rs | 5 +- crates/cheatcodes/src/impls/mod.rs | 97 ----------- .../cheatcodes/src/{impls => }/inspector.rs | 13 +- crates/cheatcodes/src/{impls => }/json.rs | 3 +- crates/cheatcodes/src/lib.rs | 155 +++++++++--------- crates/cheatcodes/src/{impls => }/script.rs | 3 +- crates/cheatcodes/src/{impls => }/string.rs | 3 +- crates/cheatcodes/src/{impls => }/test.rs | 3 +- .../cheatcodes/src/{impls => }/test/expect.rs | 3 +- crates/cheatcodes/src/{impls => }/utils.rs | 3 +- crates/evm/evm/Cargo.toml | 2 +- crates/evm/evm/src/inspectors/mod.rs | 2 +- 31 files changed, 258 insertions(+), 267 deletions(-) create mode 100644 crates/cheatcodes/defs/Cargo.toml create mode 100644 crates/cheatcodes/defs/README.md rename crates/cheatcodes/{src/defs => defs/src}/cheatcode.rs (100%) rename crates/cheatcodes/{src/defs => defs/src}/function.rs (100%) rename crates/cheatcodes/{src/defs => defs/src}/items.rs (100%) rename crates/cheatcodes/{src/defs/mod.rs => defs/src/lib.rs} (50%) rename crates/cheatcodes/{src/defs => defs/src}/vm.rs (100%) rename crates/cheatcodes/src/{impls => }/config.rs (99%) rename crates/cheatcodes/src/{impls => }/env.rs (99%) rename crates/cheatcodes/src/{impls => }/error.rs (95%) rename crates/cheatcodes/src/{impls => }/evm.rs (99%) rename crates/cheatcodes/src/{impls => }/evm/fork.rs (98%) rename crates/cheatcodes/src/{impls => }/evm/mapping.rs (98%) rename crates/cheatcodes/src/{impls => }/evm/mock.rs (97%) rename crates/cheatcodes/src/{impls => }/evm/prank.rs (97%) rename crates/cheatcodes/src/{impls => }/fs.rs (99%) delete mode 100644 crates/cheatcodes/src/impls/mod.rs rename crates/cheatcodes/src/{impls => }/inspector.rs (99%) rename crates/cheatcodes/src/{impls => }/json.rs (99%) rename crates/cheatcodes/src/{impls => }/script.rs (98%) rename crates/cheatcodes/src/{impls => }/string.rs (98%) rename crates/cheatcodes/src/{impls => }/test.rs (96%) rename crates/cheatcodes/src/{impls => }/test/expect.rs (99%) rename crates/cheatcodes/src/{impls => }/utils.rs (98%) diff --git a/.cargo/config.toml b/.cargo/config.toml index a42c43c86c18..186e372f987e 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ [alias] -cheats = "test -p foundry-cheatcodes --features schema tests::" +cheats = "test -p foundry-cheatcodes-defs --features schema tests::" [target.x86_64-pc-windows-msvc] rustflags = [ diff --git a/Cargo.lock b/Cargo.lock index 2c2721a4d82e..779a1fbedf05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2626,22 +2626,31 @@ dependencies = [ "ethers-providers", "ethers-signers", "eyre", + "foundry-cheatcodes-defs", "foundry-common", "foundry-compilers", "foundry-config", "foundry-evm-core", - "foundry-macros", "foundry-utils", "itertools 0.11.0", "jsonpath_lib", "revm", - "schemars", - "serde", "serde_json", "tracing", "walkdir", ] +[[package]] +name = "foundry-cheatcodes-defs" +version = "0.2.0" +dependencies = [ + "alloy-sol-types", + "foundry-macros", + "schemars", + "serde", + "serde_json", +] + [[package]] name = "foundry-cli" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index ed8c856cea4d..351f61739fb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "crates/binder/", "crates/cast/", "crates/cheatcodes/", + "crates/cheatcodes/defs/", "crates/chisel/", "crates/cli/", "crates/common/", @@ -116,6 +117,7 @@ forge-fmt = { path = "crates/fmt" } foundry-abi = { path = "crates/abi" } foundry-binder = { path = "crates/binder" } foundry-cheatcodes = { path = "crates/cheatcodes" } +foundry-cheatcodes-defs = { path = "crates/cheatcodes/defs" } foundry-cli = { path = "crates/cli" } foundry-common = { path = "crates/common" } foundry-config = { path = "crates/config" } diff --git a/crates/cheatcodes/Cargo.toml b/crates/cheatcodes/Cargo.toml index 714d2822d232..e71529410118 100644 --- a/crates/cheatcodes/Cargo.toml +++ b/crates/cheatcodes/Cargo.toml @@ -12,56 +12,26 @@ repository.workspace = true exclude.workspace = true [dependencies] -foundry-macros.workspace = true - +foundry-cheatcodes-defs.workspace = true +foundry-common.workspace = true +foundry-compilers.workspace = true +foundry-config.workspace = true +foundry-evm-core.workspace = true +foundry-utils.workspace = true + +alloy-dyn-abi.workspace = true +alloy-json-abi.workspace = true alloy-primitives.workspace = true alloy-sol-types.workspace = true - -serde.workspace = true +ethers-core.workspace = true +ethers-providers.workspace = true +ethers-signers.workspace = true + +eyre.workspace = true +hex.workspace = true +itertools.workspace = true +jsonpath_lib.workspace = true +revm.workspace = true serde_json.workspace = true - -# schema -schemars = { version = "0.8.15", optional = true } - -# impls -foundry-common = { workspace = true, optional = true } -foundry-compilers = { workspace = true, optional = true } -foundry-config = { workspace = true, optional = true } -foundry-evm-core = { workspace = true, optional = true } -foundry-utils = { workspace = true, optional = true } - -alloy-dyn-abi = { workspace = true, optional = true } -alloy-json-abi = { workspace = true, optional = true } -ethers-core = { workspace = true, optional = true } -ethers-signers = { workspace = true, optional = true } -ethers-providers = { workspace = true, optional = true } - -eyre = { workspace = true, optional = true } -hex = { workspace = true, optional = true } -itertools = { workspace = true, optional = true } -jsonpath_lib = { workspace = true, optional = true } -revm = { workspace = true, optional = true } -tracing = { workspace = true, optional = true } -walkdir = { version = "2", optional = true } - -[features] -schema = ["dep:schemars"] -impls = [ - "dep:foundry-common", - "dep:foundry-compilers", - "dep:foundry-config", - "dep:foundry-evm-core", - "dep:foundry-utils", - "dep:alloy-dyn-abi", - "dep:alloy-json-abi", - "dep:ethers-core", - "dep:ethers-providers", - "dep:ethers-signers", - "dep:eyre", - "dep:hex", - "dep:itertools", - "dep:jsonpath_lib", - "dep:revm", - "dep:tracing", - "dep:walkdir", -] +tracing.workspace = true +walkdir = "2" diff --git a/crates/cheatcodes/defs/Cargo.toml b/crates/cheatcodes/defs/Cargo.toml new file mode 100644 index 000000000000..0185c6bf8009 --- /dev/null +++ b/crates/cheatcodes/defs/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "foundry-cheatcodes-defs" +description = "Foundry cheatcodes definitions" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +exclude.workspace = true + +[dependencies] +foundry-macros.workspace = true +alloy-sol-types.workspace = true +serde.workspace = true + +# schema +schemars = { version = "0.8.15", optional = true } + +[dev-dependencies] +serde_json.workspace = true + +[features] +schema = ["dep:schemars"] diff --git a/crates/cheatcodes/defs/README.md b/crates/cheatcodes/defs/README.md new file mode 100644 index 000000000000..136ce7ffbbae --- /dev/null +++ b/crates/cheatcodes/defs/README.md @@ -0,0 +1,3 @@ +# foundry-cheatcodes-defs + +Minimal crate to provide a common set of cheatcodes definitions. diff --git a/crates/cheatcodes/src/defs/cheatcode.rs b/crates/cheatcodes/defs/src/cheatcode.rs similarity index 100% rename from crates/cheatcodes/src/defs/cheatcode.rs rename to crates/cheatcodes/defs/src/cheatcode.rs diff --git a/crates/cheatcodes/src/defs/function.rs b/crates/cheatcodes/defs/src/function.rs similarity index 100% rename from crates/cheatcodes/src/defs/function.rs rename to crates/cheatcodes/defs/src/function.rs diff --git a/crates/cheatcodes/src/defs/items.rs b/crates/cheatcodes/defs/src/items.rs similarity index 100% rename from crates/cheatcodes/src/defs/items.rs rename to crates/cheatcodes/defs/src/items.rs diff --git a/crates/cheatcodes/src/defs/mod.rs b/crates/cheatcodes/defs/src/lib.rs similarity index 50% rename from crates/cheatcodes/src/defs/mod.rs rename to crates/cheatcodes/defs/src/lib.rs index 652f65f5a356..5e660302ad6e 100644 --- a/crates/cheatcodes/src/defs/mod.rs +++ b/crates/cheatcodes/defs/src/lib.rs @@ -1,4 +1,8 @@ -//! Cheatcode definitions. +//! # foundry-cheatcode-defs +//! +//! Foundry cheatcode definitions. + +#![warn(missing_docs, unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] use serde::{Deserialize, Serialize}; use std::{borrow::Cow, fmt}; @@ -88,3 +92,80 @@ impl Cheatcodes<'static> { } } } + +#[cfg(test)] +mod tests { + use super::*; + use std::{fs, path::Path}; + + const JSON_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/cheatcodes.json"); + #[cfg(feature = "schema")] + const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/cheatcodes.schema.json"); + const IFACE_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata/cheats/Vm.sol"); + + /// Generates the `cheatcodes.json` file contents. + fn json_cheatcodes() -> String { + serde_json::to_string_pretty(&Cheatcodes::new()).unwrap() + } + + /// Generates the [cheatcodes](json_cheatcodes) JSON schema. + #[cfg(feature = "schema")] + fn json_schema() -> String { + serde_json::to_string_pretty(&schemars::schema_for!(Cheatcodes<'_>)).unwrap() + } + + fn sol_iface() -> String { + let cheats = Cheatcodes::new().to_string().trim().replace('\n', "\n "); + format!( + "\ +// Automatically generated from `foundry-cheatcodes` Vm definitions. Do not modify manually. +// This interface is just for internal testing purposes. Use `forge-std` instead. + +interface Vm {{ + {cheats} +}} +" + ) + } + + #[test] + fn defs_up_to_date() { + ensure_file_contents(Path::new(JSON_PATH), &json_cheatcodes()); + } + + #[test] + #[cfg(feature = "schema")] + fn schema_up_to_date() { + ensure_file_contents(Path::new(SCHEMA_PATH), &json_schema()); + } + + #[test] + fn iface_up_to_date() { + ensure_file_contents(Path::new(IFACE_PATH), &sol_iface()); + } + + /// Checks that the `file` has the specified `contents`. If that is not the + /// case, updates the file and then fails the test. + fn ensure_file_contents(file: &Path, contents: &str) { + if let Ok(old_contents) = fs::read_to_string(file) { + if normalize_newlines(&old_contents) == normalize_newlines(contents) { + // File is already up to date. + return + } + } + + eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); + if std::env::var("CI").is_ok() { + eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n"); + } + if let Some(parent) = file.parent() { + let _ = fs::create_dir_all(parent); + } + fs::write(file, contents).unwrap(); + panic!("some file was not up to date and has been updated, simply re-run the tests"); + } + + fn normalize_newlines(s: &str) -> String { + s.replace("\r\n", "\n") + } +} diff --git a/crates/cheatcodes/src/defs/vm.rs b/crates/cheatcodes/defs/src/vm.rs similarity index 100% rename from crates/cheatcodes/src/defs/vm.rs rename to crates/cheatcodes/defs/src/vm.rs diff --git a/crates/cheatcodes/src/impls/config.rs b/crates/cheatcodes/src/config.rs similarity index 99% rename from crates/cheatcodes/src/impls/config.rs rename to crates/cheatcodes/src/config.rs index b3aff6b62ed8..4cfd537cfeaf 100644 --- a/crates/cheatcodes/src/impls/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -1,5 +1,5 @@ use super::Result; -use crate::Vm::Rpc; +use foundry_cheatcodes_defs::Vm::Rpc; use foundry_common::fs::normalize_path; use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; use foundry_config::{ diff --git a/crates/cheatcodes/src/impls/env.rs b/crates/cheatcodes/src/env.rs similarity index 99% rename from crates/cheatcodes/src/impls/env.rs rename to crates/cheatcodes/src/env.rs index 590a04c81421..d9022b16d4c1 100644 --- a/crates/cheatcodes/src/impls/env.rs +++ b/crates/cheatcodes/src/env.rs @@ -1,7 +1,6 @@ //! Implementations of [`Environment`](crate::Group::Environment) cheatcodes. -use super::{string, Cheatcode, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{string, Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_primitives::{Address, Bytes, B256, I256, U256}; use alloy_sol_types::SolValue; use std::env; diff --git a/crates/cheatcodes/src/impls/error.rs b/crates/cheatcodes/src/error.rs similarity index 95% rename from crates/cheatcodes/src/impls/error.rs rename to crates/cheatcodes/src/error.rs index 8c3cd2307d8b..494c43d083d3 100644 --- a/crates/cheatcodes/src/impls/error.rs +++ b/crates/cheatcodes/src/error.rs @@ -15,13 +15,13 @@ pub type Result, E = Error> = std::result::Result; macro_rules! fmt_err { ($msg:literal $(,)?) => { - $crate::impls::Error::fmt(::std::format_args!($msg)) + $crate::Error::fmt(::std::format_args!($msg)) }; ($err:expr $(,)?) => { - <$crate::impls::Error as ::std::convert::From<_>>::from($err) + <$crate::Error as ::std::convert::From<_>>::from($err) }; ($fmt:expr, $($arg:tt)*) => { - $crate::impls::Error::fmt(::std::format_args!($fmt, $($arg)*)) + $crate::Error::fmt(::std::format_args!($fmt, $($arg)*)) }; } @@ -40,7 +40,7 @@ macro_rules! bail { macro_rules! ensure { ($cond:expr $(,)?) => { if !$cond { - return ::std::result::Result::Err($crate::impls::Error::custom( + return ::std::result::Result::Err($crate::Error::custom( ::std::concat!("Condition failed: `", ::std::stringify!($cond), "`") )); } @@ -65,7 +65,7 @@ macro_rules! ensure { macro_rules! ensure_not_precompile { ($address:expr, $ctxt:expr) => { if $ctxt.is_precompile($address) { - return Err($crate::impls::error::precompile_error( + return Err($crate::error::precompile_error( ::CHEATCODE.func.id, $address, )) diff --git a/crates/cheatcodes/src/impls/evm.rs b/crates/cheatcodes/src/evm.rs similarity index 99% rename from crates/cheatcodes/src/impls/evm.rs rename to crates/cheatcodes/src/evm.rs index 1626ac2b38e2..3ac9861112d6 100644 --- a/crates/cheatcodes/src/impls/evm.rs +++ b/crates/cheatcodes/src/evm.rs @@ -1,7 +1,6 @@ //! Implementations of [`Evm`](crate::Group::Evm) cheatcodes. -use super::{Cheatcode, CheatsCtxt, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, Result, Vm::*}; use alloy_primitives::{Address, Bytes, U256}; use alloy_sol_types::SolValue; use ethers_signers::Signer; diff --git a/crates/cheatcodes/src/impls/evm/fork.rs b/crates/cheatcodes/src/evm/fork.rs similarity index 98% rename from crates/cheatcodes/src/impls/evm/fork.rs rename to crates/cheatcodes/src/evm/fork.rs index de722e7722bd..25b224182b1d 100644 --- a/crates/cheatcodes/src/impls/evm/fork.rs +++ b/crates/cheatcodes/src/evm/fork.rs @@ -1,5 +1,4 @@ -use super::{Cheatcode, CheatsCtxt, DatabaseExt, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::B256; use alloy_sol_types::SolValue; use ethers_core::types::Filter; @@ -229,7 +228,7 @@ impl Cheatcode for rpcCall { .block_on(provider.request(method, params_json)) .map_err(|err| fmt_err!("{method:?}: {err}"))?; - let result_as_tokens = crate::impls::json::value_to_token(&result) + let result_as_tokens = crate::json::value_to_token(&result) .map_err(|err| fmt_err!("failed to parse result: {err}"))?; Ok(result_as_tokens.abi_encode()) diff --git a/crates/cheatcodes/src/impls/evm/mapping.rs b/crates/cheatcodes/src/evm/mapping.rs similarity index 98% rename from crates/cheatcodes/src/impls/evm/mapping.rs rename to crates/cheatcodes/src/evm/mapping.rs index 2713807b9507..8ff789c8e419 100644 --- a/crates/cheatcodes/src/impls/evm/mapping.rs +++ b/crates/cheatcodes/src/evm/mapping.rs @@ -1,5 +1,4 @@ -use super::{Cheatcode, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_primitives::{keccak256, Address, B256, U256}; use alloy_sol_types::SolValue; use revm::interpreter::{opcode, Interpreter}; diff --git a/crates/cheatcodes/src/impls/evm/mock.rs b/crates/cheatcodes/src/evm/mock.rs similarity index 97% rename from crates/cheatcodes/src/impls/evm/mock.rs rename to crates/cheatcodes/src/evm/mock.rs index a3a61711dd32..1fa55539e8be 100644 --- a/crates/cheatcodes/src/impls/evm/mock.rs +++ b/crates/cheatcodes/src/evm/mock.rs @@ -1,5 +1,4 @@ -use super::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result}; -use crate::Vm::*; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, Bytes, U256}; use revm::{interpreter::InstructionResult, primitives::Bytecode}; use std::cmp::Ordering; diff --git a/crates/cheatcodes/src/impls/evm/prank.rs b/crates/cheatcodes/src/evm/prank.rs similarity index 97% rename from crates/cheatcodes/src/impls/evm/prank.rs rename to crates/cheatcodes/src/evm/prank.rs index e7764dfe94c1..73269b23d11b 100644 --- a/crates/cheatcodes/src/impls/evm/prank.rs +++ b/crates/cheatcodes/src/evm/prank.rs @@ -1,5 +1,4 @@ -use super::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result}; -use crate::Vm::*; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::Address; /// Prank information. diff --git a/crates/cheatcodes/src/impls/fs.rs b/crates/cheatcodes/src/fs.rs similarity index 99% rename from crates/cheatcodes/src/impls/fs.rs rename to crates/cheatcodes/src/fs.rs index 36b1912a9fcd..3e345db94a97 100644 --- a/crates/cheatcodes/src/impls/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -1,7 +1,6 @@ //! Implementations of [`Filesystem`](crate::Group::Filesystem) cheatcodes. -use super::{Cheatcode, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_json_abi::ContractObject; use alloy_primitives::U256; use alloy_sol_types::SolValue; @@ -406,7 +405,7 @@ mod tests { #[test] fn test_artifact_parsing() { - let s = include_str!("../../../evm/test-data/solc-obj.json"); + let s = include_str!("../../evm/test-data/solc-obj.json"); let artifact: ContractObject = serde_json::from_str(s).unwrap(); assert!(artifact.bytecode.is_some()); diff --git a/crates/cheatcodes/src/impls/mod.rs b/crates/cheatcodes/src/impls/mod.rs deleted file mode 100644 index 770016786c4a..000000000000 --- a/crates/cheatcodes/src/impls/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Cheatcode implementations. - -use crate::CheatcodeDef; -use alloy_primitives::Address; -use foundry_evm_core::backend::DatabaseExt; -use revm::EVMData; -use tracing::Level; - -#[macro_use] -mod error; -pub use error::{Error, ErrorKind, Result}; - -mod config; -pub use config::CheatsConfig; - -mod inspector; -pub use inspector::{BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, Context}; - -mod env; -mod evm; -mod fs; -mod json; -mod script; -mod string; -mod test; -mod utils; - -pub use test::expect::ExpectedCallTracker; - -/// Cheatcode implementation. -pub(crate) trait Cheatcode: CheatcodeDef { - /// Applies this cheatcode to the given state. - /// - /// Implement this function if you don't need access to the EVM data. - fn apply(&self, state: &mut Cheatcodes) -> Result { - let _ = state; - unimplemented!("{}", Self::CHEATCODE.func.id) - } - - /// Applies this cheatcode to the given context. - /// - /// Implement this function if you need access to the EVM data. - #[inline(always)] - fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { - self.apply(ccx.state) - } - - #[inline] - fn apply_traced(&self, ccx: &mut CheatsCtxt) -> Result { - let span = trace_span(self); - let _enter = span.enter(); - trace_call(); - let result = self.apply_full(ccx); - trace_return(&result); - result - } -} - -// Separate functions to avoid inline and monomorphization bloat. -fn trace_span(cheat: &T) -> tracing::Span { - if enabled!(Level::TRACE) { - trace_span!(target: "cheatcodes", "apply", cheat=?cheat) - } else { - debug_span!(target: "cheatcodes", "apply", id=%T::CHEATCODE.func.id) - } -} - -fn trace_call() { - trace!(target: "cheatcodes", "applying"); -} - -fn trace_return(result: &Result) { - trace!( - target: "cheatcodes", - return = match result { - Ok(b) => hex::encode(b), - Err(e) => e.to_string(), - } - ); -} - -/// The cheatcode context, used in [`Cheatcode`]. -pub(crate) struct CheatsCtxt<'a, 'b, 'c, DB: DatabaseExt> { - /// The cheatcodes inspector state. - pub(crate) state: &'a mut Cheatcodes, - /// The EVM data. - pub(crate) data: &'b mut EVMData<'c, DB>, - /// The original `msg.sender`. - pub(crate) caller: Address, -} - -impl CheatsCtxt<'_, '_, '_, DB> { - #[inline] - pub(crate) fn is_precompile(&self, address: &Address) -> bool { - self.data.precompiles.contains(address) - } -} diff --git a/crates/cheatcodes/src/impls/inspector.rs b/crates/cheatcodes/src/inspector.rs similarity index 99% rename from crates/cheatcodes/src/impls/inspector.rs rename to crates/cheatcodes/src/inspector.rs index 10ba204ff13d..14ac9f676739 100644 --- a/crates/cheatcodes/src/impls/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -1,6 +1,6 @@ //! Cheatcode EVM [Inspector]. -use super::{ +use crate::{ evm::{ mapping::{self, MappingSlots}, mock::{MockCallDataContext, MockCallReturnData}, @@ -11,9 +11,8 @@ use super::{ test::expect::{ self, ExpectedCallData, ExpectedCallTracker, ExpectedCallType, ExpectedEmit, ExpectedRevert, }, - CheatsCtxt, Error, Result, + CheatsConfig, CheatsCtxt, Error, Result, Vm, }; -use crate::{CheatsConfig, Vm}; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_sol_types::{SolInterface, SolValue}; use ethers_core::types::{ @@ -223,8 +222,12 @@ impl Cheatcodes { // but only if the backend is in forking mode data.db.ensure_cheatcode_access_forking_mode(caller)?; - // apply the cheatcode to the current state - decoded.apply(&mut CheatsCtxt { state: self, data, caller }) + // TODO + let _ = decoded; + let _ = CheatsCtxt { state: self, data, caller }; + // // apply the cheatcode to the current state + // decoded.apply(&mut CheatsCtxt { state: self, data, caller }) + todo!() } /// Determines the address of the contract and marks it as allowed diff --git a/crates/cheatcodes/src/impls/json.rs b/crates/cheatcodes/src/json.rs similarity index 99% rename from crates/cheatcodes/src/impls/json.rs rename to crates/cheatcodes/src/json.rs index 41c8e695991a..a52a37e1bf48 100644 --- a/crates/cheatcodes/src/impls/json.rs +++ b/crates/cheatcodes/src/json.rs @@ -1,7 +1,6 @@ //! Implementations of [`Json`](crate::Group::Json) cheatcodes. -use super::{string, Cheatcode, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{string, Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_dyn_abi::DynSolValue; use alloy_primitives::{Address, Bytes, B256, I256, U256}; use alloy_sol_types::SolValue; diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index e11640124f45..cd111f6be842 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -1,99 +1,106 @@ //! # foundry-cheatcodes //! -//! Foundry cheatcodes definitions and implementations. +//! Foundry cheatcodes implementations. #![warn(missing_docs, unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] #![allow(elided_lifetimes_in_paths)] // Cheats context uses 3 lifetimes -#[cfg(feature = "impls")] #[macro_use] extern crate tracing; -// Silence the "unused crate" warning. -#[cfg(not(feature = "impls"))] -use alloy_primitives as _; +use alloy_primitives::Address; +use foundry_evm_core::backend::DatabaseExt; +use revm::EVMData; +use tracing::Level; -pub mod defs; -pub use defs::{Cheatcode, CheatcodeDef, Vm}; +pub use foundry_cheatcodes_defs::{self as defs, CheatcodeDef, Vm}; -#[cfg(feature = "impls")] -pub mod impls; -#[cfg(feature = "impls")] -pub use impls::{Cheatcodes, CheatsConfig}; - -#[cfg(test)] -mod tests { - use super::*; - use std::{fs, path::Path}; - - const JSON_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/cheatcodes.json"); - #[cfg(feature = "schema")] - const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/cheatcodes.schema.json"); - const IFACE_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata/cheats/Vm.sol"); - - /// Generates the `cheatcodes.json` file contents. - fn json_cheatcodes() -> String { - serde_json::to_string_pretty(&defs::Cheatcodes::new()).unwrap() - } - - /// Generates the [cheatcodes](json_cheatcodes) JSON schema. - #[cfg(feature = "schema")] - fn json_schema() -> String { - serde_json::to_string_pretty(&schemars::schema_for!(defs::Cheatcodes)).unwrap() +#[macro_use] +mod error; +pub use error::{Error, ErrorKind, Result}; + +mod config; +pub use config::CheatsConfig; + +mod inspector; +pub use inspector::{BroadcastableTransaction, BroadcastableTransactions, Cheatcodes, Context}; + +mod env; +mod evm; +mod fs; +mod json; +mod script; +mod string; +mod test; +mod utils; + +pub use test::expect::ExpectedCallTracker; + +/// Cheatcode implementation. +pub(crate) trait Cheatcode: CheatcodeDef { + /// Applies this cheatcode to the given state. + /// + /// Implement this function if you don't need access to the EVM data. + fn apply(&self, state: &mut Cheatcodes) -> Result { + let _ = state; + unimplemented!("{}", Self::CHEATCODE.func.id) } - fn sol_iface() -> String { - let cheats = defs::Cheatcodes::new().to_string().trim().replace('\n', "\n "); - format!( - "\ -// Automatically generated from `foundry-cheatcodes` Vm definitions. Do not modify manually. -// This interface is just for internal testing purposes. Use `forge-std` instead. - -interface Vm {{ - {cheats} -}} -" - ) + /// Applies this cheatcode to the given context. + /// + /// Implement this function if you need access to the EVM data. + #[inline(always)] + fn apply_full(&self, ccx: &mut CheatsCtxt) -> Result { + self.apply(ccx.state) } - #[test] - fn defs_up_to_date() { - ensure_file_contents(Path::new(JSON_PATH), &json_cheatcodes()); + #[inline] + fn apply_traced(&self, ccx: &mut CheatsCtxt) -> Result { + let span = trace_span(self); + let _enter = span.enter(); + trace_call(); + let result = self.apply_full(ccx); + trace_return(&result); + result } +} - #[test] - #[cfg(feature = "schema")] - fn schema_up_to_date() { - ensure_file_contents(Path::new(SCHEMA_PATH), &json_schema()); +// Separate functions to avoid inline and monomorphization bloat. +fn trace_span(cheat: &T) -> tracing::Span { + if enabled!(Level::TRACE) { + trace_span!(target: "cheatcodes", "apply", cheat=?cheat) + } else { + debug_span!(target: "cheatcodes", "apply", id=%T::CHEATCODE.func.id) } +} - #[test] - fn iface_up_to_date() { - ensure_file_contents(Path::new(IFACE_PATH), &sol_iface()); - } +fn trace_call() { + trace!(target: "cheatcodes", "applying"); +} - /// Checks that the `file` has the specified `contents`. If that is not the - /// case, updates the file and then fails the test. - fn ensure_file_contents(file: &Path, contents: &str) { - if let Ok(old_contents) = fs::read_to_string(file) { - if normalize_newlines(&old_contents) == normalize_newlines(contents) { - // File is already up to date. - return - } +fn trace_return(result: &Result) { + trace!( + target: "cheatcodes", + return = match result { + Ok(b) => hex::encode(b), + Err(e) => e.to_string(), } + ); +} - eprintln!("\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", file.display()); - if std::env::var("CI").is_ok() { - eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n"); - } - if let Some(parent) = file.parent() { - let _ = fs::create_dir_all(parent); - } - fs::write(file, contents).unwrap(); - panic!("some file was not up to date and has been updated, simply re-run the tests"); - } +/// The cheatcode context, used in [`Cheatcode`]. +pub(crate) struct CheatsCtxt<'a, 'b, 'c, DB: DatabaseExt> { + /// The cheatcodes inspector state. + pub(crate) state: &'a mut Cheatcodes, + /// The EVM data. + pub(crate) data: &'b mut EVMData<'c, DB>, + /// The original `msg.sender`. + pub(crate) caller: Address, +} - fn normalize_newlines(s: &str) -> String { - s.replace("\r\n", "\n") +impl CheatsCtxt<'_, '_, '_, DB> { + #[inline] + pub(crate) fn is_precompile(&self, address: &Address) -> bool { + self.data.precompiles.contains(address) } } diff --git a/crates/cheatcodes/src/impls/script.rs b/crates/cheatcodes/src/script.rs similarity index 98% rename from crates/cheatcodes/src/impls/script.rs rename to crates/cheatcodes/src/script.rs index ab1d9347634f..8e7df685c9ed 100644 --- a/crates/cheatcodes/src/impls/script.rs +++ b/crates/cheatcodes/src/script.rs @@ -1,7 +1,6 @@ //! Implementations of [`Scripting`](crate::Group::Scripting) cheatcodes. -use super::{Cheatcode, CheatsCtxt, DatabaseExt, Result}; -use crate::Vm::*; +use crate::{Cheatcode, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{Address, U256}; use ethers_signers::Signer; use foundry_config::Config; diff --git a/crates/cheatcodes/src/impls/string.rs b/crates/cheatcodes/src/string.rs similarity index 98% rename from crates/cheatcodes/src/impls/string.rs rename to crates/cheatcodes/src/string.rs index 43fc2b9f0446..9a3db966d29a 100644 --- a/crates/cheatcodes/src/impls/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -1,7 +1,6 @@ //! Implementations of [`String`](crate::Group::String) cheatcodes. -use super::{Cheatcode, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{Cheatcode, Cheatcodes, Result, Vm::*}; use alloy_primitives::{Address, Bytes, B256, I256, U256}; use alloy_sol_types::{SolType, SolValue}; diff --git a/crates/cheatcodes/src/impls/test.rs b/crates/cheatcodes/src/test.rs similarity index 96% rename from crates/cheatcodes/src/impls/test.rs rename to crates/cheatcodes/src/test.rs index 6e372e86a01b..0294b23b73d6 100644 --- a/crates/cheatcodes/src/impls/test.rs +++ b/crates/cheatcodes/src/test.rs @@ -1,7 +1,6 @@ //! Implementations of [`Testing`](crate::Group::Testing) cheatcodes. -use super::{Cheatcode, CheatsCtxt, DatabaseExt, Error, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Error, Result, Vm::*}; use alloy_primitives::Address; use alloy_sol_types::SolValue; use foundry_evm_core::constants::{MAGIC_ASSUME, MAGIC_SKIP}; diff --git a/crates/cheatcodes/src/impls/test/expect.rs b/crates/cheatcodes/src/test/expect.rs similarity index 99% rename from crates/cheatcodes/src/impls/test/expect.rs rename to crates/cheatcodes/src/test/expect.rs index 4d6cd50a6c2d..3d5d95dff09f 100644 --- a/crates/cheatcodes/src/impls/test/expect.rs +++ b/crates/cheatcodes/src/test/expect.rs @@ -1,5 +1,4 @@ -use super::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result}; -use crate::Vm::*; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_dyn_abi::DynSolType; use alloy_primitives::{Address, Bytes, Log as RawLog, B256, U256}; use foundry_utils::error::{ERROR_PREFIX, REVERT_PREFIX}; diff --git a/crates/cheatcodes/src/impls/utils.rs b/crates/cheatcodes/src/utils.rs similarity index 98% rename from crates/cheatcodes/src/impls/utils.rs rename to crates/cheatcodes/src/utils.rs index d8a4c5a474c7..c40f4cac7104 100644 --- a/crates/cheatcodes/src/impls/utils.rs +++ b/crates/cheatcodes/src/utils.rs @@ -1,7 +1,6 @@ //! Implementations of [`Utils`](crate::Group::Utils) cheatcodes. -use super::{Cheatcode, CheatsCtxt, DatabaseExt, Result}; -use crate::{Cheatcodes, Vm::*}; +use crate::{Cheatcode, Cheatcodes, CheatsCtxt, DatabaseExt, Result, Vm::*}; use alloy_primitives::{keccak256, B256, U256}; use alloy_sol_types::SolValue; use ethers_core::k256::{ diff --git a/crates/evm/evm/Cargo.toml b/crates/evm/evm/Cargo.toml index 4bcf679476d7..68727729a8ae 100644 --- a/crates/evm/evm/Cargo.toml +++ b/crates/evm/evm/Cargo.toml @@ -11,7 +11,7 @@ homepage.workspace = true repository.workspace = true [dependencies] -foundry-cheatcodes = { workspace = true, features = ["impls"] } +foundry-cheatcodes.workspace = true foundry-common.workspace = true foundry-compilers.workspace = true foundry-config.workspace = true diff --git a/crates/evm/evm/src/inspectors/mod.rs b/crates/evm/evm/src/inspectors/mod.rs index 888f80bd6e9d..b781ce96c8dd 100644 --- a/crates/evm/evm/src/inspectors/mod.rs +++ b/crates/evm/evm/src/inspectors/mod.rs @@ -1,6 +1,6 @@ //! EVM inspectors. -pub use foundry_cheatcodes::{impls as cheatcodes, Cheatcodes, CheatsConfig}; +pub use foundry_cheatcodes::{self as cheatcodes, Cheatcodes, CheatsConfig}; pub use foundry_evm_coverage::CoverageCollector; pub use foundry_evm_fuzz::Fuzzer; pub use foundry_evm_traces::Tracer; From 2ff8a99a3135792b3541aed26d5d488d6662e6dd Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 3 Nov 2023 08:43:09 +0100 Subject: [PATCH 2/6] docs: update links --- crates/cheatcodes/README.md | 8 ++++---- docs/dev/cheatcodes.md | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/cheatcodes/README.md b/crates/cheatcodes/README.md index 3bf38171e196..a968c51d6d58 100644 --- a/crates/cheatcodes/README.md +++ b/crates/cheatcodes/README.md @@ -5,12 +5,12 @@ Foundry cheatcodes definitions and implementations. ## Structure - [`assets/`](./assets/): JSON interface and specification -- [`src/defs`](./src/defs/mod.rs): Defines traits and structs -- [`src/impls`](./src/impls/mod.rs): Rust implementations of the cheatcodes. This is gated to the `impl` feature, since these are not needed when only using the definitions. +- [`defs/`](./defs/src/lib.rs): Defines common traits and structs +- [`src/`](./src/lib.rs): Rust implementations of the cheatcodes. ## Overview -All cheatcodes are defined in a single [`sol!`] macro call in [`src/defs/vm.rs`]. +All cheatcodes are defined in a single [`sol!`] macro call in [`defs/src/vm.rs`]. This, combined with the use of an internal [`Cheatcode`](../macros/impl/src/cheatcodes.rs) derive macro, allows us to generate both the Rust definitions and the JSON specification of the cheatcodes. @@ -38,4 +38,4 @@ If you are making use of the JSON interface, please don't hesitate to open a PR Please see the [cheatcodes dev documentation](../../docs/dev/cheatcodes.md#adding-a-new-cheatcode) on how to add new cheatcodes. [`sol!`]: https://docs.rs/alloy-sol-macro/latest/alloy_sol_macro/macro.sol.html -[`src/defs/vm.rs`]: ./src/defs/vm.rs +[`defs/src/vm.rs`]: ./defs/src/vm.rs diff --git a/docs/dev/cheatcodes.md b/docs/dev/cheatcodes.md index 26f399a698a8..c00d6082f5b4 100644 --- a/docs/dev/cheatcodes.md +++ b/docs/dev/cheatcodes.md @@ -31,7 +31,7 @@ The [`evm`](../../crates/evm/evm/) crate has a variety of inspectors for differe - debugger - logging -## [Cheatcode inspector](../../crates/cheatcodes/src/impls/inspector.rs) +## [Cheatcode inspector](../../crates/cheatcodes/src/inspector.rs) The concept of cheatcodes and cheatcode inspector is very simple. @@ -70,7 +70,7 @@ through the use of a custom internal derive procedural macro. ## Cheatcodes implementation -All the cheatcodes are defined in a large [`sol!`] macro call in [`cheatcodes/src/defs/mod.rs`](../../crates/cheatcodes/src/defs/mod.rs): +All the cheatcodes are defined in a large [`sol!`] macro call in [`cheatcodes/defs/src/vm.rs`](../../crates/cheatcodes/defs/src/vm.rs): ```rust sol! { @@ -144,8 +144,8 @@ There are two methods that can be implemented: Only one of these methods can be implemented. -This trait is implemented manually for each cheatcode in the [`impls`](../../crates/cheatcodes/src/impls/) -module on the `sol!`-generated function call structs. +This trait is implemented manually for each cheatcode in the [`foundry-cheatcodes`](../../crates/cheatcodes/) +crate on the `sol!`-generated function call structs. ### [JSON interface](../../crates/cheatcodes/assets/cheatcodes.json) @@ -159,11 +159,11 @@ update of the files. ### Adding a new cheatcode -1. Add its Solidity definition(s) in [`src/defs/vm.rs`]. Ensure that all structs and functions are documented, and that all function parameters are named. This will initially fail to compile because of the automatically generated `match { ... }` expression. This is expected, and will be fixed in the next step -2. Implement the cheatcode in [`src/impls/`](src/impls/) in its category's respective module. Follow the existing implementations as a guide. +1. Add its Solidity definition(s) in [`defs/src/vm.rs`]. Ensure that all structs and functions are documented, and that all function parameters are named. This will initially fail to compile because of the automatically generated `match { ... }` expression. This is expected, and will be fixed in the next step +2. Implement the cheatcode in [`cheatcodes`](cheatcodes) in its category's respective module. Follow the existing implementations as a guide. 3. Update the JSON interface by running `cargo cheats` twice. This is expected to fail the first time that this is run after adding a new cheatcode; see [JSON interface](#json-interface) 4. Write an integration test for the cheatcode in [`testdata/cheats/`](../../testdata/cheats/) 5. Submit a PR to [`forge-std`](https://github.com/foundry-rs/forge-std) updating the Solidity interfaces as necessary. Note that this step won't be necessary once the Solidity interfaces are generated using the JSON interface [`sol!`]: https://docs.rs/alloy-sol-macro/latest/alloy_sol_macro/macro.sol.html -[`src/defs/vm.rs`]: ./src/defs/vm.rs +[`defs/src/vm.rs`]: ./defs/src/vm.rs From c34b61d3ca91ed469b551b41a6460f050c97ecc7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 3 Nov 2023 09:02:46 +0100 Subject: [PATCH 3/6] fix: separate dispatch impl from defs --- crates/cheatcodes/src/inspector.rs | 19 +++++++---- crates/cheatcodes/src/lib.rs | 48 +++++++++++++++------------- crates/macros/impl/src/cheatcodes.rs | 19 +++++------ 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/crates/cheatcodes/src/inspector.rs b/crates/cheatcodes/src/inspector.rs index 14ac9f676739..5c95cfc2fa2f 100644 --- a/crates/cheatcodes/src/inspector.rs +++ b/crates/cheatcodes/src/inspector.rs @@ -222,12 +222,7 @@ impl Cheatcodes { // but only if the backend is in forking mode data.db.ensure_cheatcode_access_forking_mode(caller)?; - // TODO - let _ = decoded; - let _ = CheatsCtxt { state: self, data, caller }; - // // apply the cheatcode to the current state - // decoded.apply(&mut CheatsCtxt { state: self, data, caller }) - todo!() + apply_dispatch(&decoded, &mut CheatsCtxt { state: self, data, caller }) } /// Determines the address of the contract and marks it as allowed @@ -1110,3 +1105,15 @@ fn check_if_fixed_gas_limit(data: &EVMData<'_, DB>, call_gas_li // gas too low" failure when simulated on chain && call_gas_limit > 2300 } + +/// Dispatches the cheatcode call to the appropriate function. +fn apply_dispatch(calls: &Vm::VmCalls, ccx: &mut CheatsCtxt) -> Result { + macro_rules! match_ { + ($($variant:ident),*) => { + match calls { + $(Vm::VmCalls::$variant(cheat) => crate::Cheatcode::apply_traced(cheat, ccx),)* + } + }; + } + vm_calls!(match_) +} diff --git a/crates/cheatcodes/src/lib.rs b/crates/cheatcodes/src/lib.rs index cd111f6be842..e39f98f5a962 100644 --- a/crates/cheatcodes/src/lib.rs +++ b/crates/cheatcodes/src/lib.rs @@ -5,6 +5,8 @@ #![warn(missing_docs, unreachable_pub, unused_crate_dependencies, rust_2018_idioms)] #![allow(elided_lifetimes_in_paths)] // Cheats context uses 3 lifetimes +#[macro_use] +pub extern crate foundry_cheatcodes_defs as defs; #[macro_use] extern crate tracing; @@ -13,7 +15,7 @@ use foundry_evm_core::backend::DatabaseExt; use revm::EVMData; use tracing::Level; -pub use foundry_cheatcodes_defs::{self as defs, CheatcodeDef, Vm}; +pub use defs::{CheatcodeDef, Vm}; #[macro_use] mod error; @@ -61,31 +63,31 @@ pub(crate) trait Cheatcode: CheatcodeDef { trace_call(); let result = self.apply_full(ccx); trace_return(&result); - result - } -} - -// Separate functions to avoid inline and monomorphization bloat. -fn trace_span(cheat: &T) -> tracing::Span { - if enabled!(Level::TRACE) { - trace_span!(target: "cheatcodes", "apply", cheat=?cheat) - } else { - debug_span!(target: "cheatcodes", "apply", id=%T::CHEATCODE.func.id) - } -} + return result; + + // Separate functions to avoid inline and monomorphization bloat. + fn trace_span(cheat: &T) -> tracing::Span { + if enabled!(Level::TRACE) { + trace_span!(target: "cheatcodes", "apply", cheat=?cheat) + } else { + debug_span!(target: "cheatcodes", "apply", id=%T::CHEATCODE.func.id) + } + } -fn trace_call() { - trace!(target: "cheatcodes", "applying"); -} + fn trace_call() { + trace!(target: "cheatcodes", "applying"); + } -fn trace_return(result: &Result) { - trace!( - target: "cheatcodes", - return = match result { - Ok(b) => hex::encode(b), - Err(e) => e.to_string(), + fn trace_return(result: &Result) { + trace!( + target: "cheatcodes", + return = match result { + Ok(b) => hex::encode(b), + Err(e) => e.to_string(), + } + ); } - ); + } } /// The cheatcode context, used in [`Cheatcode`]. diff --git a/crates/macros/impl/src/cheatcodes.rs b/crates/macros/impl/src/cheatcodes.rs index ad2b99642ea9..b38186e57732 100644 --- a/crates/macros/impl/src/cheatcodes.rs +++ b/crates/macros/impl/src/cheatcodes.rs @@ -98,7 +98,7 @@ fn derive_calls_enum(name: &Ident, input: &syn::DataEnum) -> Result } // keep original order for matching - let variants_names = input.variants.iter().map(|v| &v.ident); + let variant_names = input.variants.iter().map(|v| &v.ident); let mut variants = input.variants.iter().collect::>(); variants.sort_by(|a, b| a.ident.cmp(&b.ident)); @@ -110,16 +110,13 @@ fn derive_calls_enum(name: &Ident, input: &syn::DataEnum) -> Result /// All the cheatcodes in [this contract](self). pub const CHEATCODES: &'static [&'static Cheatcode<'static>] = &[#(<#variant_tys as CheatcodeDef>::CHEATCODE,)*]; - #[cfg(feature = "impls")] - impl #name { - pub(crate) fn apply( - &self, - ccx: &mut crate::impls::CheatsCtxt - ) -> crate::impls::Result { - match self { - #(Self::#variants_names(c) => crate::impls::Cheatcode::apply_traced(c, ccx),)* - } - } + /// Internal macro to implement the `Cheatcode` trait for the Vm calls enum. + #[doc(hidden)] + #[macro_export] + macro_rules! vm_calls { + ($mac:ident) => { + $mac!(#(#variant_names),*) + }; } }) } From 6acc76b168ad18363550b04833ae8c054b0457de Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 3 Nov 2023 09:09:17 +0100 Subject: [PATCH 4/6] docs: update again --- crates/cheatcodes/defs/src/lib.rs | 11 ++++++++--- docs/dev/cheatcodes.md | 9 +++++---- testdata/cheats/Vm.sol | 5 ++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/crates/cheatcodes/defs/src/lib.rs b/crates/cheatcodes/defs/src/lib.rs index 5e660302ad6e..53bd161a8f12 100644 --- a/crates/cheatcodes/defs/src/lib.rs +++ b/crates/cheatcodes/defs/src/lib.rs @@ -98,10 +98,12 @@ mod tests { use super::*; use std::{fs, path::Path}; - const JSON_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/cheatcodes.json"); + const JSON_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/cheatcodes.json"); #[cfg(feature = "schema")] - const SCHEMA_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/cheatcodes.schema.json"); - const IFACE_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../testdata/cheats/Vm.sol"); + const SCHEMA_PATH: &str = + concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/cheatcodes.schema.json"); + const IFACE_PATH: &str = + concat!(env!("CARGO_MANIFEST_DIR"), "/../../../testdata/cheats/Vm.sol"); /// Generates the `cheatcodes.json` file contents. fn json_cheatcodes() -> String { @@ -121,6 +123,9 @@ mod tests { // Automatically generated from `foundry-cheatcodes` Vm definitions. Do not modify manually. // This interface is just for internal testing purposes. Use `forge-std` instead. +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.4; + interface Vm {{ {cheats} }} diff --git a/docs/dev/cheatcodes.md b/docs/dev/cheatcodes.md index c00d6082f5b4..5ff4105d8e94 100644 --- a/docs/dev/cheatcodes.md +++ b/docs/dev/cheatcodes.md @@ -65,8 +65,8 @@ Rust bindings for the cheatcode interface are generated via the [Alloy](https:// If a call was successfully decoded into the `VmCalls` enum that the `sol!` macro generates, the last step is a large `match` over the decoded function call structs, which serves as the -implementation handler for the cheatcode. This is also automatically generated by the `sol!` macro, -through the use of a custom internal derive procedural macro. +implementation handler for the cheatcode. This is also automatically generated, in part, by the +`sol!` macro, through the use of a custom internal derive procedural macro. ## Cheatcodes implementation @@ -119,8 +119,9 @@ This is derived once on the `Vm` interface declaration, which recursively applie interface's items, as well as the `sol!`-generated items, such as the `VmCalls` enum. This macro performs extra checks on functions and structs at compile time to make sure they are -documented and have named parameters, and generates a `match { ... }` function that is be used to -dispatch the cheatcode implementations after a call is decoded. +documented and have named parameters, and generates a macro which is later used to implement the +`match { ... }` function that is be used to dispatch the cheatcode implementations after a call is +decoded. The latter is what fails compilation when adding a new cheatcode, and is fixed by implementing the [`Cheatcode` trait](#cheatcode-trait) to the newly-generated function call struct(s). diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 8ab2a14f5e84..0865a9c779eb 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -1,6 +1,9 @@ // Automatically generated from `foundry-cheatcodes` Vm definitions. Do not modify manually. // This interface is just for internal testing purposes. Use `forge-std` instead. +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.4; + interface Vm { error CheatcodeError(string message); enum CallerMode { None, Broadcast, RecurrentBroadcast, Prank, RecurrentPrank } @@ -223,4 +226,4 @@ interface Vm { function writeJson(string calldata json, string calldata path) external; function writeJson(string calldata json, string calldata path, string calldata valueKey) external; function writeLine(string calldata path, string calldata data) external; -} \ No newline at end of file +} From 16fba59d8f30a02878a05eb1057f5c5f0c57fb8c Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 3 Nov 2023 09:11:36 +0100 Subject: [PATCH 5/6] doc --- crates/cheatcodes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/README.md b/crates/cheatcodes/README.md index a968c51d6d58..ce1e8f32b9f0 100644 --- a/crates/cheatcodes/README.md +++ b/crates/cheatcodes/README.md @@ -6,7 +6,7 @@ Foundry cheatcodes definitions and implementations. - [`assets/`](./assets/): JSON interface and specification - [`defs/`](./defs/src/lib.rs): Defines common traits and structs -- [`src/`](./src/lib.rs): Rust implementations of the cheatcodes. +- [`src/`](./src/lib.rs): Rust implementations of the cheatcodes ## Overview From 866fd93f60298776f166bc41bf8f975405452474 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Fri, 3 Nov 2023 09:12:09 +0100 Subject: [PATCH 6/6] stuff --- crates/cheatcodes/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cheatcodes/src/config.rs b/crates/cheatcodes/src/config.rs index 4cfd537cfeaf..b3aff6b62ed8 100644 --- a/crates/cheatcodes/src/config.rs +++ b/crates/cheatcodes/src/config.rs @@ -1,5 +1,5 @@ use super::Result; -use foundry_cheatcodes_defs::Vm::Rpc; +use crate::Vm::Rpc; use foundry_common::fs::normalize_path; use foundry_compilers::{utils::canonicalize, ProjectPathsConfig}; use foundry_config::{