Skip to content

Commit 33e4b4b

Browse files
authored
feat(forge-selectors-list): add --no-group option (#11270)
1 parent bd3db5b commit 33e4b4b

File tree

2 files changed

+168
-16
lines changed

2 files changed

+168
-16
lines changed

crates/forge/src/cmd/selectors.rs

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use foundry_common::{
1111
selectors::{SelectorImportData, import_selectors},
1212
};
1313
use foundry_compilers::{artifacts::output_selection::ContractOutputSelection, info::ContractInfo};
14-
use std::fs::canonicalize;
14+
use std::{collections::BTreeMap, fs::canonicalize};
1515

1616
/// CLI arguments for `forge selectors`.
1717
#[derive(Clone, Debug, Parser)]
@@ -56,6 +56,9 @@ pub enum SelectorsSubcommands {
5656

5757
#[command(flatten)]
5858
project_paths: ProjectPathOpts,
59+
60+
#[arg(long, help = "Do not group the selectors by contract in separate tables.")]
61+
no_group: bool,
5962
},
6063

6164
/// Find if a selector is present in the project
@@ -225,7 +228,7 @@ impl SelectorsSubcommands {
225228
sh_println!("\n{table}\n")?;
226229
}
227230
}
228-
Self::List { contract, project_paths } => {
231+
Self::List { contract, project_paths, no_group } => {
229232
sh_println!("Listing selectors for contracts in the project...")?;
230233
let build_args = BuildOpts {
231234
project_paths,
@@ -273,41 +276,90 @@ impl SelectorsSubcommands {
273276

274277
let mut artifacts = artifacts.into_iter().peekable();
275278

276-
while let Some((contract, artifact)) = artifacts.next() {
277-
let abi = artifact.abi.ok_or_else(|| eyre::eyre!("Unable to fetch abi"))?;
278-
if abi.functions.is_empty() && abi.events.is_empty() && abi.errors.is_empty() {
279-
continue;
279+
#[derive(PartialEq, PartialOrd, Eq, Ord)]
280+
enum SelectorType {
281+
Function,
282+
Event,
283+
Error,
284+
}
285+
impl std::fmt::Display for SelectorType {
286+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287+
match self {
288+
Self::Function => write!(f, "Function"),
289+
Self::Event => write!(f, "Event"),
290+
Self::Error => write!(f, "Error"),
291+
}
280292
}
293+
}
281294

282-
sh_println!("{contract}")?;
295+
let mut selectors =
296+
BTreeMap::<String, BTreeMap<SelectorType, Vec<(String, String)>>>::new();
283297

284-
let mut table = Table::new();
285-
table.apply_modifier(UTF8_ROUND_CORNERS);
298+
for (contract, artifact) in artifacts.by_ref() {
299+
let abi = artifact.abi.ok_or_else(|| eyre::eyre!("Unable to fetch abi"))?;
286300

287-
table.set_header(["Type", "Signature", "Selector"]);
301+
let contract_selectors = selectors.entry(contract.clone()).or_default();
288302

289303
for func in abi.functions() {
290304
let sig = func.signature();
291305
let selector = func.selector();
292-
table.add_row(["Function", &sig, &hex::encode_prefixed(selector)]);
306+
contract_selectors
307+
.entry(SelectorType::Function)
308+
.or_default()
309+
.push((hex::encode_prefixed(selector), sig));
293310
}
294311

295312
for event in abi.events() {
296313
let sig = event.signature();
297314
let selector = event.selector();
298-
table.add_row(["Event", &sig, &hex::encode_prefixed(selector)]);
315+
contract_selectors
316+
.entry(SelectorType::Event)
317+
.or_default()
318+
.push((hex::encode_prefixed(selector), sig));
299319
}
300320

301321
for error in abi.errors() {
302322
let sig = error.signature();
303323
let selector = error.selector();
304-
table.add_row(["Error", &sig, &hex::encode_prefixed(selector)]);
324+
contract_selectors
325+
.entry(SelectorType::Error)
326+
.or_default()
327+
.push((hex::encode_prefixed(selector), sig));
305328
}
329+
}
306330

307-
sh_println!("\n{table}\n")?;
331+
if no_group {
332+
let mut table = Table::new();
333+
table.apply_modifier(UTF8_ROUND_CORNERS);
334+
table.set_header(["Type", "Signature", "Selector", "Contract"]);
335+
336+
for (contract, contract_selectors) in selectors {
337+
for (selector_type, selectors) in contract_selectors {
338+
for (selector, sig) in selectors {
339+
table.add_row([
340+
selector_type.to_string(),
341+
sig,
342+
selector,
343+
contract.to_string(),
344+
]);
345+
}
346+
}
347+
}
308348

309-
if artifacts.peek().is_some() {
310-
sh_println!()?
349+
sh_println!("\n{table}")?;
350+
} else {
351+
for (idx, (contract, contract_selectors)) in selectors.into_iter().enumerate() {
352+
sh_println!("{}{contract}", if idx == 0 { "" } else { "\n" })?;
353+
let mut table = Table::new();
354+
table.apply_modifier(UTF8_ROUND_CORNERS);
355+
table.set_header(["Type", "Signature", "Selector"]);
356+
357+
for (selector_type, selectors) in contract_selectors {
358+
for (selector, sig) in selectors {
359+
table.add_row([selector_type.to_string(), sig, selector]);
360+
}
361+
}
362+
sh_println!("\n{table}")?;
311363
}
312364
}
313365
}

crates/forge/tests/cli/test_cmd.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3411,6 +3411,106 @@ Selectors successfully uploaded to OpenChain
34113411
"#]]);
34123412
});
34133413

3414+
forgetest_init!(selectors_list_cmd, |prj, cmd| {
3415+
prj.add_source(
3416+
"Counter.sol",
3417+
r"
3418+
contract Counter {
3419+
uint256 public number;
3420+
event Incremented(uint256 newNumber);
3421+
error IncrementError();
3422+
3423+
function setNumber(uint256 newNumber) public {
3424+
number = newNumber;
3425+
}
3426+
3427+
function increment() public {
3428+
number++;
3429+
}
3430+
}
3431+
",
3432+
)
3433+
.unwrap();
3434+
3435+
prj.add_source(
3436+
"CounterV2.sol",
3437+
r"
3438+
contract CounterV2 {
3439+
uint256 public number;
3440+
3441+
function setNumberV2(uint256 newNumber) public {
3442+
number = newNumber;
3443+
}
3444+
3445+
function incrementV2() public {
3446+
number++;
3447+
}
3448+
}
3449+
",
3450+
)
3451+
.unwrap();
3452+
3453+
cmd.args(["selectors", "list"]).assert_success().stdout_eq(str![[r#"
3454+
Listing selectors for contracts in the project...
3455+
Counter
3456+
3457+
╭----------+----------------------+--------------------------------------------------------------------╮
3458+
| Type | Signature | Selector |
3459+
+======================================================================================================+
3460+
| Function | increment() | 0xd09de08a |
3461+
|----------+----------------------+--------------------------------------------------------------------|
3462+
| Function | number() | 0x8381f58a |
3463+
|----------+----------------------+--------------------------------------------------------------------|
3464+
| Function | setNumber(uint256) | 0x3fb5c1cb |
3465+
|----------+----------------------+--------------------------------------------------------------------|
3466+
| Event | Incremented(uint256) | 0x20d8a6f5a693f9d1d627a598e8820f7a55ee74c183aa8f1a30e8d4e8dd9a8d84 |
3467+
|----------+----------------------+--------------------------------------------------------------------|
3468+
| Error | IncrementError() | 0x46544c04 |
3469+
╰----------+----------------------+--------------------------------------------------------------------╯
3470+
3471+
CounterV2
3472+
3473+
╭----------+----------------------+------------╮
3474+
| Type | Signature | Selector |
3475+
+==============================================+
3476+
| Function | incrementV2() | 0x49365a69 |
3477+
|----------+----------------------+------------|
3478+
| Function | number() | 0x8381f58a |
3479+
|----------+----------------------+------------|
3480+
| Function | setNumberV2(uint256) | 0xb525b68c |
3481+
╰----------+----------------------+------------╯
3482+
3483+
"#]]);
3484+
3485+
cmd.forge_fuse()
3486+
.args(["selectors", "list", "--no-group"])
3487+
.assert_success()
3488+
.stdout_eq(str![[r#"
3489+
Listing selectors for contracts in the project...
3490+
3491+
╭----------+----------------------+--------------------------------------------------------------------+-----------╮
3492+
| Type | Signature | Selector | Contract |
3493+
+==================================================================================================================+
3494+
| Function | increment() | 0xd09de08a | Counter |
3495+
|----------+----------------------+--------------------------------------------------------------------+-----------|
3496+
| Function | number() | 0x8381f58a | Counter |
3497+
|----------+----------------------+--------------------------------------------------------------------+-----------|
3498+
| Function | setNumber(uint256) | 0x3fb5c1cb | Counter |
3499+
|----------+----------------------+--------------------------------------------------------------------+-----------|
3500+
| Event | Incremented(uint256) | 0x20d8a6f5a693f9d1d627a598e8820f7a55ee74c183aa8f1a30e8d4e8dd9a8d84 | Counter |
3501+
|----------+----------------------+--------------------------------------------------------------------+-----------|
3502+
| Error | IncrementError() | 0x46544c04 | Counter |
3503+
|----------+----------------------+--------------------------------------------------------------------+-----------|
3504+
| Function | incrementV2() | 0x49365a69 | CounterV2 |
3505+
|----------+----------------------+--------------------------------------------------------------------+-----------|
3506+
| Function | number() | 0x8381f58a | CounterV2 |
3507+
|----------+----------------------+--------------------------------------------------------------------+-----------|
3508+
| Function | setNumberV2(uint256) | 0xb525b68c | CounterV2 |
3509+
╰----------+----------------------+--------------------------------------------------------------------+-----------╯
3510+
3511+
"#]]);
3512+
});
3513+
34143514
// tests `interceptInitcode` function
34153515
forgetest_init!(intercept_initcode, |prj, cmd| {
34163516
prj.wipe_contracts();

0 commit comments

Comments
 (0)