diff --git a/cli/src/cmd/forge/fourbyte.rs b/cli/src/cmd/forge/fourbyte.rs index 37f48d4a0996..d11128038c9d 100644 --- a/cli/src/cmd/forge/fourbyte.rs +++ b/cli/src/cmd/forge/fourbyte.rs @@ -8,7 +8,9 @@ use ethers::prelude::artifacts::output_selection::ContractOutputSelection; use foundry_common::{ compile, selectors::{import_selectors, SelectorImportData}, + shell, }; +use yansi::Paint; /// CLI arguments for `forge upload-selectors`. #[derive(Debug, Clone, Parser)] @@ -28,6 +30,8 @@ pub struct UploadSelectorsArgs { impl UploadSelectorsArgs { /// Builds a contract and uploads the ABI to selector database pub async fn run(self) -> eyre::Result<()> { + shell::println(Paint::yellow("Warning! This command is deprecated and will be removed in v1, use `forge selectors upload` instead"))?; + let UploadSelectorsArgs { contract, all, project_paths } = self; let build_args = CoreBuildArgs { diff --git a/cli/src/cmd/forge/mod.rs b/cli/src/cmd/forge/mod.rs index 29fcadf9c73c..73e694d8bc33 100644 --- a/cli/src/cmd/forge/mod.rs +++ b/cli/src/cmd/forge/mod.rs @@ -57,6 +57,7 @@ pub mod install; pub mod remappings; pub mod remove; pub mod script; +pub mod selectors; pub mod snapshot; pub mod test; pub mod tree; diff --git a/cli/src/cmd/forge/selectors.rs b/cli/src/cmd/forge/selectors.rs new file mode 100644 index 000000000000..db6293d80bea --- /dev/null +++ b/cli/src/cmd/forge/selectors.rs @@ -0,0 +1,95 @@ +use crate::{ + cmd::forge::build::{CoreBuildArgs, ProjectPathsArgs}, + opts::forge::CompilerArgs, + utils::FoundryPathExt, +}; +use clap::Parser; +use ethers::prelude::artifacts::output_selection::ContractOutputSelection; +use foundry_common::{ + compile, + selectors::{import_selectors, SelectorImportData}, +}; + +/// CLI arguments for `forge selectors`. +#[derive(Debug, Clone, Parser)] +pub enum SelectorsSubcommands { + /// Upload selectors to registry + #[clap(visible_alias = "up")] + Upload { + /// The name of the contract to upload selectors for. + #[clap(required_unless_present = "all")] + contract: Option, + + /// Upload selectors for all contracts in the project. + #[clap(long, required_unless_present = "contract")] + all: bool, + + #[clap(flatten)] + project_paths: ProjectPathsArgs, + }, +} + +impl SelectorsSubcommands { + pub async fn run(self) -> eyre::Result<()> { + match self { + SelectorsSubcommands::Upload { contract, all, project_paths } => { + let build_args = CoreBuildArgs { + project_paths: project_paths.clone(), + compiler: CompilerArgs { + extra_output: vec![ContractOutputSelection::Abi], + ..Default::default() + }, + ..Default::default() + }; + + let project = build_args.project()?; + let outcome = compile::suppress_compile(&project)?; + let artifacts = if all { + outcome + .into_artifacts_with_files() + .filter(|(file, _, _)| { + let is_sources_path = file + .starts_with(&project.paths.sources.to_string_lossy().to_string()); + let is_test = file.is_sol_test(); + + is_sources_path && !is_test + }) + .map(|(_, contract, artifact)| (contract, artifact)) + .collect() + } else { + let contract = contract.unwrap(); + let found_artifact = outcome.find_first(&contract); + let artifact = found_artifact + .ok_or_else(|| { + eyre::eyre!( + "Could not find artifact `{contract}` in the compiled artifacts" + ) + })? + .clone(); + vec![(contract, artifact)] + }; + + let mut artifacts = artifacts.into_iter().peekable(); + while let Some((contract, artifact)) = artifacts.next() { + let abi = artifact.abi.ok_or(eyre::eyre!("Unable to fetch abi"))?; + if abi.abi.functions.is_empty() && + abi.abi.events.is_empty() && + abi.abi.errors.is_empty() + { + continue + } + + println!("Uploading selectors for {contract}..."); + + // upload abi to selector database + import_selectors(SelectorImportData::Abi(vec![abi])).await?.describe(); + + if artifacts.peek().is_some() { + println!() + } + } + } + } + Ok(()) + } +} diff --git a/cli/src/forge.rs b/cli/src/forge.rs index 815383cabffe..9d7a38e5f3c5 100644 --- a/cli/src/forge.rs +++ b/cli/src/forge.rs @@ -96,5 +96,6 @@ fn main() -> eyre::Result<()> { Ok(()) } Subcommands::Doc(cmd) => cmd.run(), + Subcommands::Selectors { command } => utils::block_on(command.run()), } } diff --git a/cli/src/opts/forge.rs b/cli/src/opts/forge.rs index 7db36a9fc5d8..f0ae4c7a5e9e 100644 --- a/cli/src/opts/forge.rs +++ b/cli/src/opts/forge.rs @@ -16,6 +16,7 @@ use crate::cmd::forge::{ remappings::RemappingArgs, remove::RemoveArgs, script::ScriptArgs, + selectors::SelectorsSubcommands, snapshot, test, tree, update, verify::{VerifyArgs, VerifyCheckArgs}, }; @@ -155,6 +156,13 @@ pub enum Subcommands { /// Generate documentation for the project. Doc(DocArgs), + + /// Function selector utilities + #[clap(visible_alias = "se")] + Selectors { + #[clap(subcommand)] + command: SelectorsSubcommands, + }, } // A set of solc compiler settings that can be set via command line arguments, which are intended