From bc2b4fdcf3b893692b9c974ffd404d876f25e4b2 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 10 Jul 2024 18:22:00 +0400 Subject: [PATCH] config section --- crates/config/src/bind_json.rs | 29 ++++++++++++++++++++++ crates/config/src/lib.rs | 7 ++++++ crates/forge/bin/cmd/bind_json.rs | 40 +++++++++++++++++++++++++------ crates/forge/bin/opts.rs | 1 + crates/forge/tests/cli/config.rs | 1 + 5 files changed, 71 insertions(+), 7 deletions(-) create mode 100644 crates/config/src/bind_json.rs diff --git a/crates/config/src/bind_json.rs b/crates/config/src/bind_json.rs new file mode 100644 index 0000000000000..1eb3e54de5012 --- /dev/null +++ b/crates/config/src/bind_json.rs @@ -0,0 +1,29 @@ +use crate::from_vec_glob; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +/// Contains the config for `forge bind-json` +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BindJsonConfig { + /// Path for the generated bindings file. + pub out: PathBuf, + /// Globs to include. + /// + /// If provided, only the files matching the globs will be included. Otherwise, defaults to + /// including all project files. + #[serde(with = "from_vec_glob")] + pub include: Vec, + /// Globs to ignore + #[serde(with = "from_vec_glob")] + pub exclude: Vec, +} + +impl Default for BindJsonConfig { + fn default() -> Self { + Self { + out: PathBuf::from("utils/JsonBindings.sol"), + exclude: Vec::new(), + include: Vec::new(), + } + } +} diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 3efdc44cf9327..4bc23b0fbb9f6 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -110,6 +110,9 @@ use soldeer::SoldeerConfig; mod vyper; use vyper::VyperConfig; +mod bind_json; +use bind_json::BindJsonConfig; + /// Foundry configuration /// /// # Defaults @@ -383,6 +386,8 @@ pub struct Config { pub fmt: FormatterConfig, /// Configuration for `forge doc` pub doc: DocConfig, + /// Configuration for `forge bind-json` + pub bind_json: BindJsonConfig, /// Configures the permissions of cheat codes that touch the file system. /// /// This includes what operations can be executed (read, write) @@ -478,6 +483,7 @@ impl Config { "labels", "dependencies", "vyper", + "bind_json", ]; /// File name of config toml file @@ -2130,6 +2136,7 @@ impl Default for Config { build_info_path: None, fmt: Default::default(), doc: Default::default(), + bind_json: Default::default(), labels: Default::default(), unchecked_cheatcode_artifacts: false, create2_library_salt: Self::DEFAULT_CREATE2_LIBRARY_SALT, diff --git a/crates/forge/bin/cmd/bind_json.rs b/crates/forge/bin/cmd/bind_json.rs index b38a62627a41f..78419e16224f2 100644 --- a/crates/forge/bin/cmd/bind_json.rs +++ b/crates/forge/bin/cmd/bind_json.rs @@ -13,6 +13,7 @@ use foundry_compilers::{ solc::SolcLanguage, CompilerSettings, Graph, Project, }; +use foundry_config::{filter::GlobMatcher, Config}; use itertools::Itertools; use rayon::prelude::*; use solang_parser::pt as solang_ast; @@ -57,7 +58,7 @@ impl BindJsonArgs { let config = self.try_load_config_emit_warnings()?; let project = config.create_project(false, true)?; - let target_path = config.root.0.join("utils/JsonBindings.sol"); + let target_path = config.root.0.join(&config.bind_json.out); let sources = project.paths.read_input_files()?; let graph = Graph::::resolve_sources(&project.paths, sources)?; @@ -156,7 +157,7 @@ impl BindJsonArgs { .collect::>>()?, ); - Ok(PreprocessedState { sources, target_path, project }) + Ok(PreprocessedState { sources, target_path, project, config }) } } @@ -215,11 +216,12 @@ struct PreprocessedState { sources: Sources, target_path: PathBuf, project: Project, + config: Config, } impl PreprocessedState { fn compile(self) -> Result { - let Self { sources, target_path, mut project } = self; + let Self { sources, target_path, mut project, config } = self; project.settings.update_output_selection(|selection| { *selection = OutputSelection::ast_output_selection(); @@ -248,19 +250,21 @@ impl PreprocessedState { }) .collect::>>()?; - Ok(CompiledState { asts, target_path }) + Ok(CompiledState { asts, target_path, config, project }) } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] struct CompiledState { asts: BTreeMap, target_path: PathBuf, + config: Config, + project: Project, } impl CompiledState { fn find_structs(self) -> Result { - let Self { asts, target_path } = self; + let Self { asts, target_path, config, project } = self; // construct mapping (file, id) -> (struct definition, optional parent contract name) let structs = asts @@ -293,6 +297,11 @@ impl CompiledState { let mut structs_to_write = Vec::new(); + let include = + config.bind_json.include.into_iter().map(GlobMatcher::new).collect::>(); + let exclude = + config.bind_json.exclude.into_iter().map(GlobMatcher::new).collect::>(); + for ((path, id), (def, contract_name)) in structs { // For some structs there's no schema (e.g. if they contain a mapping), so we just skip // those. @@ -301,6 +310,21 @@ impl CompiledState { continue }; + if !include.is_empty() { + if !include.iter().any(|matcher| matcher.is_match(path)) { + continue; + } + } else { + // Exclude library files by default + if project.paths.has_library_ancestor(path) { + continue; + } + } + + if exclude.iter().any(|matcher| matcher.is_match(path)) { + continue; + } + structs_to_write.push(StructToWrite { name: def.name.clone(), contract_name, @@ -412,6 +436,8 @@ impl ResolvedState { fs::create_dir_all(self.target_path.parent().unwrap())?; fs::write(&self.target_path, &result)?; + println!("Bindings written to {}", self.target_path.display()); + Ok(result) } @@ -426,7 +452,7 @@ impl ResolvedState { .insert(item); } - result.push_str("pragma solidity >=0.6.2 <0.9.0;\npragma experimental ABIEncoderV2;\n\n"); + result.push_str("// Automatically generated by forge bind-json.\n\npragma solidity >=0.6.2 <0.9.0;\npragma experimental ABIEncoderV2;\n\n"); for (path, names) in grouped_imports { result.push_str(&format!( diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs index eae0b4935237c..b86d19c17728d 100644 --- a/crates/forge/bin/opts.rs +++ b/crates/forge/bin/opts.rs @@ -168,6 +168,7 @@ pub enum ForgeSubcommand { /// Generate EIP-712 struct encodings for structs from a given file. Eip712(eip712::Eip712Args), + /// Generate bindings for serialization/deserialization of project structs via JSON cheatcodes. BindJson(bind_json::BindJsonArgs), } diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index 63df640060c96..bf11e65e94da2 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -130,6 +130,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { build_info_path: None, fmt: Default::default(), doc: Default::default(), + bind_json: Default::default(), fs_permissions: Default::default(), labels: Default::default(), prague: true,