Skip to content

Commit c602db6

Browse files
schroyarmattsse
andauthored
feat(forge): add selectors list subcommand (#6075)
* add list (ls) cli arg for forge selectors * add type identifier for signature * fix CI * migrate to alloy --------- Co-authored-by: Matthias Seitz <[email protected]>
1 parent 871aba5 commit c602db6

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed

crates/forge/bin/cmd/selectors.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ pub enum SelectorsSubcommands {
5151
#[clap(flatten)]
5252
project_paths: ProjectPathsArgs,
5353
},
54+
55+
/// List selectors from current workspace
56+
#[clap(visible_alias = "ls")]
57+
List {
58+
/// The name of the contract to list selectors for.
59+
#[clap(help = "The name of the contract to list selectors for.")]
60+
contract: Option<String>,
61+
62+
#[clap(flatten)]
63+
project_paths: ProjectPathsArgs,
64+
},
5465
}
5566

5667
impl SelectorsSubcommands {
@@ -176,6 +187,95 @@ impl SelectorsSubcommands {
176187
println!("{table}");
177188
}
178189
}
190+
SelectorsSubcommands::List { contract, project_paths } => {
191+
println!("Listing selectors for contracts in the project...");
192+
let build_args = CoreBuildArgs {
193+
project_paths: project_paths.clone(),
194+
compiler: CompilerArgs {
195+
extra_output: vec![ContractOutputSelection::Abi],
196+
..Default::default()
197+
},
198+
..Default::default()
199+
};
200+
201+
// compile the project to get the artifacts/abis
202+
let project = build_args.project()?;
203+
let outcome = compile::suppress_compile(&project)?;
204+
let artifacts = if let Some(contract) = contract {
205+
let found_artifact = outcome.find_first(&contract);
206+
let artifact = found_artifact
207+
.ok_or_else(|| {
208+
let candidates = outcome
209+
.artifacts()
210+
.map(|(name, _,)| name)
211+
.collect::<Vec<_>>();
212+
let suggestion = if let Some(suggestion) = foundry_cli::utils::did_you_mean(&contract, candidates).pop() {
213+
format!("\nDid you mean `{suggestion}`?")
214+
} else {
215+
"".to_string()
216+
};
217+
eyre::eyre!(
218+
"Could not find artifact `{contract}` in the compiled artifacts{suggestion}",
219+
)
220+
})?
221+
.clone();
222+
vec![(contract, artifact)]
223+
} else {
224+
outcome
225+
.into_artifacts_with_files()
226+
.filter(|(file, _, _)| {
227+
let is_sources_path = file
228+
.starts_with(&project.paths.sources.to_string_lossy().to_string());
229+
let is_test = file.is_sol_test();
230+
231+
is_sources_path && !is_test
232+
})
233+
.map(|(_, contract, artifact)| (contract, artifact))
234+
.collect()
235+
};
236+
237+
let mut artifacts = artifacts.into_iter().peekable();
238+
239+
while let Some((contract, artifact)) = artifacts.next() {
240+
let abi = artifact.abi.ok_or(eyre::eyre!("Unable to fetch abi"))?;
241+
if abi.abi.functions.is_empty() &&
242+
abi.abi.events.is_empty() &&
243+
abi.abi.errors.is_empty()
244+
{
245+
continue
246+
}
247+
248+
println!("{contract}");
249+
250+
let mut table = Table::new();
251+
252+
table.set_header(vec!["Type", "Signature", "Selector"]);
253+
254+
for func in abi.abi.functions() {
255+
let sig = func.signature();
256+
let selector = func.selector();
257+
table.add_row(vec!["Function", &sig, &hex::encode_prefixed(selector)]);
258+
}
259+
260+
for event in abi.abi.events() {
261+
let sig = event.signature();
262+
let selector = event.selector();
263+
table.add_row(vec!["Event", &sig, &hex::encode_prefixed(selector)]);
264+
}
265+
266+
for error in abi.abi.errors() {
267+
let sig = error.signature();
268+
let selector = error.selector();
269+
table.add_row(vec!["Error", &sig, &hex::encode_prefixed(selector)]);
270+
}
271+
272+
println!("{table}");
273+
274+
if artifacts.peek().is_some() {
275+
println!()
276+
}
277+
}
278+
}
179279
}
180280
Ok(())
181281
}

0 commit comments

Comments
 (0)