Skip to content

Commit ea771e9

Browse files
authored
Merge branch 'master' into no-fuzz-proptest
2 parents 06f713c + 5e92b6e commit ea771e9

File tree

11 files changed

+248
-108
lines changed

11 files changed

+248
-108
lines changed

crates/cast/src/cmd/access_list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub struct AccessListArgs {
2727
sig: Option<String>,
2828

2929
/// The arguments of the function to call.
30-
#[arg(value_name = "ARGS")]
30+
#[arg(value_name = "ARGS", allow_negative_numbers = true)]
3131
args: Vec<String>,
3232

3333
/// The block height to query at.

crates/cast/src/cmd/call.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ pub struct CallArgs {
7272
sig: Option<String>,
7373

7474
/// The arguments of the function to call.
75+
#[arg(allow_negative_numbers = true)]
7576
args: Vec<String>,
7677

7778
/// Raw hex-encoded data for the transaction. Used instead of \[SIG\] and \[ARGS\].
@@ -177,6 +178,7 @@ pub enum CallSubcommands {
177178
sig: Option<String>,
178179

179180
/// The arguments of the constructor.
181+
#[arg(allow_negative_numbers = true)]
180182
args: Vec<String>,
181183

182184
/// Ether to send in the transaction.
@@ -643,4 +645,21 @@ mod tests {
643645
Some(vec!["0x123:0x1:0x1234".to_string(), "0x456:0x2:0x5678".to_string()])
644646
);
645647
}
648+
649+
#[test]
650+
fn test_negative_args_with_flags() {
651+
// Test that negative args work with flags
652+
let args = CallArgs::parse_from([
653+
"foundry-cli",
654+
"--trace",
655+
"0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
656+
"process(int256)",
657+
"-999999",
658+
"--debug",
659+
]);
660+
661+
assert!(args.trace);
662+
assert!(args.debug);
663+
assert_eq!(args.args, vec!["-999999"]);
664+
}
646665
}

crates/cast/src/cmd/estimate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub struct EstimateArgs {
2222
sig: Option<String>,
2323

2424
/// The arguments of the function to call.
25+
#[arg(allow_negative_numbers = true)]
2526
args: Vec<String>,
2627

2728
/// The block height to query at.
@@ -58,6 +59,7 @@ pub enum EstimateSubcommands {
5859
sig: Option<String>,
5960

6061
/// Constructor arguments
62+
#[arg(allow_negative_numbers = true)]
6163
args: Vec<String>,
6264

6365
/// Ether to send in the transaction

crates/cast/src/cmd/mktx.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct MakeTxArgs {
2525
sig: Option<String>,
2626

2727
/// The arguments of the function to call.
28+
#[arg(allow_negative_numbers = true)]
2829
args: Vec<String>,
2930

3031
#[command(subcommand)]
@@ -69,6 +70,7 @@ pub enum MakeTxSubcommands {
6970
sig: Option<String>,
7071

7172
/// The constructor arguments.
73+
#[arg(allow_negative_numbers = true)]
7274
args: Vec<String>,
7375
},
7476
}

crates/cast/src/cmd/send.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub struct SendTxArgs {
3030
sig: Option<String>,
3131

3232
/// The arguments of the function to call.
33+
#[arg(allow_negative_numbers = true)]
3334
args: Vec<String>,
3435

3536
/// Only print the transaction hash and exit immediately.
@@ -80,6 +81,7 @@ pub enum SendTxSubcommands {
8081
sig: Option<String>,
8182

8283
/// The arguments of the function to call.
84+
#[arg(allow_negative_numbers = true)]
8385
args: Vec<String>,
8486
},
8587
}

crates/cast/tests/cli/main.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3741,3 +3741,119 @@ Transaction successfully executed.
37413741
37423742
"#]]);
37433743
});
3744+
3745+
// Tests for negative number argument parsing
3746+
// Ensures that negative numbers in function arguments are properly parsed
3747+
// instead of being treated as command flags
3748+
3749+
// Test that cast call accepts negative numbers as function arguments
3750+
casttest!(cast_call_negative_numbers, |_prj, cmd| {
3751+
let rpc = next_rpc_endpoint(NamedChain::Sepolia);
3752+
// Test with negative int parameter - should not treat -456789 as a flag
3753+
cmd.args([
3754+
"call",
3755+
"0xAbCdEf1234567890aBcDeF1234567890aBcDeF12",
3756+
"processValue(int128)",
3757+
"-456789",
3758+
"--rpc-url",
3759+
rpc.as_str(),
3760+
])
3761+
.assert_success();
3762+
});
3763+
3764+
// Test negative numbers with multiple parameters
3765+
casttest!(cast_call_multiple_negative_numbers, |_prj, cmd| {
3766+
let rpc = next_rpc_endpoint(NamedChain::Sepolia);
3767+
cmd.args([
3768+
"call",
3769+
"--rpc-url",
3770+
rpc.as_str(),
3771+
"0xDeaDBeeFcAfEbAbEfAcEfEeDcBaDbEeFcAfEbAbE",
3772+
"calculateDelta(int64,int32,uint16)",
3773+
"-987654321",
3774+
"-42",
3775+
"65535",
3776+
])
3777+
.assert_success();
3778+
});
3779+
3780+
// Test negative numbers mixed with flags
3781+
casttest!(cast_call_negative_with_flags, |_prj, cmd| {
3782+
let rpc = next_rpc_endpoint(NamedChain::Sepolia);
3783+
cmd.args([
3784+
"call",
3785+
"--trace", // flag before
3786+
"0x9876543210FeDcBa9876543210FeDcBa98765432",
3787+
"updateBalance(int256)",
3788+
"-777888",
3789+
"--rpc-url",
3790+
rpc.as_str(), // flag after
3791+
])
3792+
.assert_success();
3793+
});
3794+
3795+
// Test that actual invalid flags are still caught
3796+
casttest!(cast_call_invalid_flag_still_caught, |_prj, cmd| {
3797+
cmd.args([
3798+
"call",
3799+
"--invalid-flag", // This should be caught as invalid
3800+
"0x5555555555555555555555555555555555555555",
3801+
])
3802+
.assert_failure()
3803+
.stderr_eq(str![[r#"
3804+
error: unexpected argument '--invalid-flag' found
3805+
3806+
tip: to pass '--invalid-flag' as a value, use '-- --invalid-flag'
3807+
3808+
Usage: cast[..] call [OPTIONS] [TO] [SIG] [ARGS]... [COMMAND]
3809+
3810+
For more information, try '--help'.
3811+
3812+
"#]]);
3813+
});
3814+
3815+
// Test cast estimate with negative numbers
3816+
casttest!(cast_estimate_negative_numbers, |_prj, cmd| {
3817+
let rpc = next_rpc_endpoint(NamedChain::Sepolia);
3818+
cmd.args([
3819+
"estimate",
3820+
"0xBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBbBb",
3821+
"rebalance(int64)",
3822+
"-8888",
3823+
"--rpc-url",
3824+
rpc.as_str(),
3825+
])
3826+
.assert_success();
3827+
});
3828+
3829+
// Test cast mktx with negative numbers
3830+
casttest!(cast_mktx_negative_numbers, |_prj, cmd| {
3831+
let rpc = next_rpc_endpoint(NamedChain::Sepolia);
3832+
cmd.args([
3833+
"mktx",
3834+
"0x1111111111111111111111111111111111111111",
3835+
"settleDebt(int256)",
3836+
"-15000",
3837+
"--private-key",
3838+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", // anvil wallet #0
3839+
"--rpc-url",
3840+
rpc.as_str(),
3841+
"--gas-limit",
3842+
"100000",
3843+
])
3844+
.assert_success();
3845+
});
3846+
3847+
// Test cast access-list with negative numbers
3848+
casttest!(cast_access_list_negative_numbers, |_prj, cmd| {
3849+
let rpc = next_rpc_endpoint(NamedChain::Sepolia);
3850+
cmd.args([
3851+
"access-list",
3852+
"0x9999999999999999999999999999999999999999",
3853+
"adjustPosition(int128)",
3854+
"-33333",
3855+
"--rpc-url",
3856+
rpc.as_str(),
3857+
])
3858+
.assert_success();
3859+
});

crates/forge/src/cmd/build.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{install, watch::WatchArgs};
22
use clap::Parser;
3-
use eyre::Result;
3+
use eyre::{Result, eyre};
44
use forge_lint::{linter::Linter, sol::SolidityLinter};
55
use foundry_cli::{
66
opts::{BuildOpts, solar_pcx_from_build_opts},
@@ -112,17 +112,21 @@ impl BuildArgs {
112112
sh_println!("{}", serde_json::to_string_pretty(&output.output())?)?;
113113
}
114114

115+
if !config.lint.lint_on_build {
116+
return Ok(output);
117+
}
118+
115119
// Only run the `SolidityLinter` if there are no compilation errors
116-
if output.output().errors.iter().all(|e| !e.is_error()) {
117-
self.lint(&project, &config)?;
120+
if !output.output().errors.iter().any(|e| e.is_error()) {
121+
self.lint(&project, &config).map_err(|err| eyre!("Lint failed: {err}"))?;
118122
}
119123

120124
Ok(output)
121125
}
122126

123127
fn lint(&self, project: &Project, config: &Config) -> Result<()> {
124128
let format_json = shell::is_json();
125-
if project.compiler.solc.is_some() && config.lint.lint_on_build && !shell::is_quiet() {
129+
if project.compiler.solc.is_some() && !shell::is_quiet() {
126130
let linter = SolidityLinter::new(config.project_paths())
127131
.with_json_emitter(format_json)
128132
.with_description(!format_json)

crates/forge/tests/cli/test_cmd.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3941,6 +3941,7 @@ Ran 1 test suite [ELAPSED]: 1 tests passed, 0 failed, 0 skipped (1 total tests)
39413941

39423942
// tests proper reverts in fork mode for contracts with non-existent linked libraries.
39433943
// <https://github.com/foundry-rs/foundry/issues/11185>
3944+
#[cfg(not(feature = "isolate-by-default"))]
39443945
forgetest_init!(can_fork_test_with_non_existent_linked_library, |prj, cmd| {
39453946
prj.update_config(|config| {
39463947
config.libraries =

crates/lint/src/sol/codesize/unwrapped_modifier_logic.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,32 @@ impl UnwrappedModifierLogic {
6262

6363
/// Checks if a block of statements is complex and should be wrapped in a helper function.
6464
///
65-
/// This is true if the block contains:
65+
/// This always is 'false' the modifier contains assembly. We assume that if devs know how to
66+
/// use assembly, they will also know how to reduce the codesize of their contracts and they
67+
/// have a good reason to use it on their modifiers.
68+
///
69+
/// This is 'true' if the block contains:
6670
/// 1. Any statement that is not a placeholder or a valid expression.
6771
/// 2. More than one simple call expression.
6872
fn stmts_require_wrapping(&self, hir: &hir::Hir<'_>, stmts: &[hir::Stmt<'_>]) -> bool {
69-
let mut has_valid_stmt = false;
73+
let (mut res, mut has_valid_stmt) = (false, false);
7074
for stmt in stmts {
7175
match &stmt.kind {
7276
hir::StmtKind::Placeholder => continue,
7377
hir::StmtKind::Expr(expr) => {
7478
if !self.is_valid_expr(hir, expr) || has_valid_stmt {
75-
return true;
79+
res = true;
7680
}
7781
has_valid_stmt = true;
7882
}
79-
_ => return true,
83+
// HIR doesn't support assembly yet:
84+
// <https://github.com/paradigmxyz/solar/blob/d25bf38a5accd11409318e023f701313d98b9e1e/crates/sema/src/hir/mod.rs#L977-L982>
85+
hir::StmtKind::Err(_) => return false,
86+
_ => res = true,
8087
}
8188
}
8289

83-
false
90+
res
8491
}
8592

8693
fn get_snippet<'a>(

crates/lint/testdata/UnwrappedModifierLogic.sol

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,28 @@ contract UnwrappedModifierLogicTest {
2525
mapping(address => bool) isOwner;
2626
mapping(address => mapping(bytes32 => bool)) hasRole;
2727

28+
/// -----------------------------------------------------------------------
29+
/// Exceptions (assembly block)
30+
/// -----------------------------------------------------------------------
31+
32+
modifier freeTempMemory() {
33+
uint256 m;
34+
assembly ("memory-safe") {
35+
m := mload(0x40)
36+
}
37+
_;
38+
assembly ("memory-safe") {
39+
mstore(0x40, m)
40+
}
41+
}
42+
43+
modifier assemblyBlock(address sender) {
44+
assembly {
45+
let x := sender
46+
}
47+
_;
48+
}
49+
2850
/// -----------------------------------------------------------------------
2951
/// Good patterns (only 1 valid statement before or after placeholder)
3052
/// -----------------------------------------------------------------------
@@ -64,7 +86,7 @@ contract UnwrappedModifierLogicTest {
6486
/// -----------------------------------------------------------------------
6587

6688
// Bad because there are multiple valid function calls before the placeholder
67-
modifier multipleBeforePlaceholder() { //~NOTE: wrap modifier logic to reduce code size
89+
modifier multipleBeforePlaceholder() { //~NOTE: wrap modifier logic to reduce code size
6890
checkPublic(msg.sender); // These should become _multipleBeforePlaceholder()
6991
checkPrivate(msg.sender);
7092
checkInternal(msg.sender);
@@ -127,14 +149,6 @@ contract UnwrappedModifierLogicTest {
127149
_;
128150
}
129151

130-
// Only call expressions are allowed (public/private/internal functions).
131-
modifier assemblyBlock(address sender) { //~NOTE: wrap modifier logic to reduce code size
132-
assembly {
133-
let x := sender
134-
}
135-
_;
136-
}
137-
138152
// Only call expressions are allowed (public/private/internal functions).
139153
modifier uncheckedBlock(address sender) { //~NOTE: wrap modifier logic to reduce code size
140154
unchecked {
@@ -158,4 +172,4 @@ contract UnwrappedModifierLogicTest {
158172
c.onlyOwner(sender);
159173
_;
160174
}
161-
}
175+
}

0 commit comments

Comments
 (0)