Skip to content

Commit d94e3c6

Browse files
authored
feat: allow supplying function name via forge script --sig (#7518)
* feat: allow supplying fn name via forge script --sig * fmt * clippy * add test
1 parent a16714e commit d94e3c6

File tree

2 files changed

+48
-22
lines changed

2 files changed

+48
-22
lines changed

crates/forge/tests/cli/script.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,3 +1179,21 @@ forgetest_async!(can_sign_with_script_wallet_multiple, |prj, cmd| {
11791179
.await
11801180
.simulate(ScriptOutcome::OkRun);
11811181
});
1182+
1183+
forgetest_async!(fails_with_function_name_and_overloads, |prj, cmd| {
1184+
let script = prj
1185+
.add_script(
1186+
"Sctipt.s.sol",
1187+
r#"
1188+
contract Script {
1189+
function run() external {}
1190+
1191+
function run(address,uint256) external {}
1192+
}
1193+
"#,
1194+
)
1195+
.unwrap();
1196+
1197+
cmd.arg("script").args([&script.to_string_lossy(), "--sig", "run"]);
1198+
assert!(cmd.stderr_lossy().contains("Multiple functions with the same name"));
1199+
});

crates/script/src/lib.rs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use build::PreprocessedState;
1212
use clap::{Parser, ValueHint};
1313
use dialoguer::Confirm;
1414
use ethers_signers::Signer;
15-
use eyre::{ContextCompat, Result, WrapErr};
15+
use eyre::{ContextCompat, Result};
1616
use forge_verify::RetryArgs;
1717
use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig};
1818
use foundry_common::{
@@ -311,30 +311,38 @@ impl ScriptArgs {
311311
///
312312
/// Note: We assume that the `sig` is already stripped of its prefix, See [`ScriptArgs`]
313313
fn get_method_and_calldata(&self, abi: &JsonAbi) -> Result<(Function, Bytes)> {
314-
let (func, data) = if let Ok(func) = get_func(&self.sig) {
315-
(
316-
abi.functions().find(|&abi_func| abi_func.selector() == func.selector()).wrap_err(
317-
format!("Function `{}` is not implemented in your script.", self.sig),
318-
)?,
319-
encode_function_args(&func, &self.args)?.into(),
320-
)
321-
} else {
322-
let decoded = hex::decode(&self.sig).wrap_err("Invalid hex calldata")?;
314+
if let Ok(decoded) = hex::decode(&self.sig) {
323315
let selector = &decoded[..SELECTOR_LEN];
324-
(
325-
abi.functions().find(|&func| selector == &func.selector()[..]).ok_or_else(
326-
|| {
327-
eyre::eyre!(
328-
"Function selector `{}` not found in the ABI",
329-
hex::encode(selector)
330-
)
331-
},
332-
)?,
333-
decoded.into(),
334-
)
316+
let func =
317+
abi.functions().find(|func| selector == &func.selector()[..]).ok_or_else(|| {
318+
eyre::eyre!(
319+
"Function selector `{}` not found in the ABI",
320+
hex::encode(selector)
321+
)
322+
})?;
323+
return Ok((func.clone(), decoded.into()));
324+
}
325+
326+
let func = if self.sig.contains('(') {
327+
let func = get_func(&self.sig)?;
328+
abi.functions()
329+
.find(|&abi_func| abi_func.selector() == func.selector())
330+
.wrap_err(format!("Function `{}` is not implemented in your script.", self.sig))?
331+
} else {
332+
let matching_functions =
333+
abi.functions().filter(|func| func.name == self.sig).collect::<Vec<_>>();
334+
match matching_functions.len() {
335+
0 => eyre::bail!("Function `{}` not found in the ABI", self.sig),
336+
1 => matching_functions[0],
337+
2.. => eyre::bail!(
338+
"Multiple functions with the same name `{}` found in the ABI",
339+
self.sig
340+
),
341+
}
335342
};
343+
let data = encode_function_args(func, &self.args)?;
336344

337-
Ok((func.clone(), data))
345+
Ok((func.clone(), data.into()))
338346
}
339347

340348
/// Checks if the transaction is a deployment with either a size above the `CONTRACT_MAX_SIZE`

0 commit comments

Comments
 (0)