Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
a764891
feat(invariant): assert all invariants
grandizzy Nov 17, 2025
a398def
Tests and Nits
grandizzy Nov 18, 2025
98b7cda
Merge branch 'master' into gdzzy/issue-9727
grandizzy Nov 20, 2025
875abbd
fix(fmt): correct indentation for closing brace in empty contracts wi…
Himess Feb 4, 2026
919fd93
feat(anvil): add `trace_replayBlockTransactions` endpoint for block t…
mablr Feb 4, 2026
1962f8b
fix(eip712): write diagnostics to stderr instead of stdout (#13293)
thunggis Feb 4, 2026
19ab6be
foundryup: tempo now distributes all binaries (#13337)
gakonst Feb 5, 2026
3f3ac93
fix(config): handle vyper section with skip_serializing_if fields (#1…
gakonst Feb 5, 2026
fab7a32
fix: broken config test, currently blocking CI (#13340)
zerosnacks Feb 5, 2026
54f1f07
skip checksum hash in create2 mining when case-insensitive (#13331)
DanielGuupta Feb 5, 2026
f6d7ce8
fix: avoid setting FOUNDRY_PROFILE: ci in template workflows, profile…
zerosnacks Feb 5, 2026
0993900
perf(evm): wrap Executor.backend in Arc for copy-on-write cloning (#1…
gakonst Feb 5, 2026
1cdcce3
chore(ci): update time crate (#13348)
gakonst Feb 6, 2026
262517e
test(cast): ignore flaky_run_celo_with_precompiles (Celo RPC no longe…
gakonst Feb 6, 2026
d9f4664
fix(test-utils): create destination directory in copy_dir_filtered (#…
gakonst Feb 6, 2026
01c18be
chore(wallets): Remove `NetworkWallet<FoundryNetwork>` impl for `Wall…
mablr Feb 6, 2026
266ea1e
fix(anvil): return error when querying future block number in with_da…
thunggis Feb 6, 2026
598798a
chore: remove stale `tiny-keccak` references (#13358)
gakonst Feb 6, 2026
a6402fe
chore(script): typo (#13353)
cuiweixie Feb 6, 2026
761254f
perf(cheatcodes): loop invariant code motion by hand (#13357)
cuiweixie Feb 6, 2026
d25e3a8
chore(anvil): remove unnecessary clone operations (#13330)
MarkFizz77 Feb 6, 2026
6021466
perf(linking): replace double hash mpa lookup contains_key + [] with …
cuiweixie Feb 6, 2026
cbcf283
fix(verify): correct Sourcify API URL construction for custom chains …
marukai67 Feb 6, 2026
39ffb93
chore(common): remove dead `with_spinner_reporter` function (#13366)
sashass1315 Feb 6, 2026
c3027b4
resolve absolute and relative paths on Windows (#13364)
DanielGuupta Feb 6, 2026
e286608
fix: unittest failed (#13371)
cuiweixie Feb 7, 2026
b523fd2
perf(anvil): reuse storage root from prove_storage instead of recompu…
thunggis Feb 7, 2026
0847fed
chore(deps): weekly `cargo update` (#13384)
github-actions[bot] Feb 8, 2026
17df54a
Update flake.lock (#13383)
github-actions[bot] Feb 9, 2026
9a31dec
perf(verify): reuse transaction from earlier RPC call instead of fetc…
thunggis Feb 9, 2026
2804e38
fix(cast): --json support for erc20 cmds (#12727)
0xferrous Feb 9, 2026
93aeec0
refactor(anvil): using is_ok since it's more robust (#13377)
cuiweixie Feb 9, 2026
1ac22cb
fix: may div by zero (#13369)
cuiweixie Feb 9, 2026
6039457
refactor(primitives): turn `FoundryTransactionRequest` into an enum (…
mablr Feb 9, 2026
8a2f572
perf: avoid checksum (#13374)
cuiweixie Feb 9, 2026
ddff6f1
docs: slim readme (#13393)
onbjerg Feb 9, 2026
1d693f2
fix: correct trace message in dynamic linking preprocessor (#13394)
radik878 Feb 9, 2026
dbc7d1f
perf(invariant): avoid cloning state changeset in fuzz runs (#13398)
thunggis Feb 10, 2026
fed9c2a
chore(deps): bump depot/build-push-action from 1.16.2 to 1.17.0 (#13405)
dependabot[bot] Feb 10, 2026
7c2a568
chore(deps): bump taiki-e/install-action from 2.67.18 to 2.67.27 (#13…
dependabot[bot] Feb 10, 2026
e788798
chore(deps): bump crate-ci/typos from 1.43.0 to 1.43.4 (#13407)
dependabot[bot] Feb 10, 2026
3845b03
ci: use dedicated template for isolate flaky test failures (#13409)
gakonst Feb 10, 2026
811ce2a
chore(deps): bump depot/setup-action from 1.6.0 to 1.7.1 (#13408)
dependabot[bot] Feb 10, 2026
45d2d6c
fix(primitives): `FoundryTransactionRequest` conversion w/ tempo vari…
mablr Feb 10, 2026
271e208
return error instead of empty array when filter not found (#13415)
DanielGuupta Feb 10, 2026
e46258f
chore(config): remove unused enum accessor methods (#13414)
MozirDmitriy Feb 11, 2026
d1c4f38
fix(cast): clean up temp dir in `cast storage` when etherscan cache i…
thunggis Feb 11, 2026
c39c8b5
perf(primitives): avoid cloning receipts (#13396)
thunggis Feb 11, 2026
074d732
fix: constructor params and args check (#13375)
cuiweixie Feb 11, 2026
7838a07
fix: correct path format in get_paths doc comment (#13388)
gap-editor Feb 11, 2026
bdd9c29
ci: replace merge_group with push on master (#13419)
onbjerg Feb 11, 2026
5d5cf29
ci(release): pin action-gh-release to v2.4.2 (#13420)
gakonst Feb 11, 2026
c0689eb
fix(anvil): handle disk cache write failures in state eviction (#13332)
thunggis Feb 11, 2026
5c25342
feat(forge,chisel): realtime `console.log` (#13321)
quangloc99 Feb 11, 2026
6527a54
fix(cast): remove duplicate receipt handling in Tempo transactions (#…
dizer-ti Feb 11, 2026
057f6b5
perf(traces): deduplicate addresses before external fetching (#13320)
marukai67 Feb 11, 2026
9abaa53
fix: prevent panic on etherscan client creation failure in test comma…
CreeptoGengar Feb 11, 2026
017120d
perf(config): skip redundant remapping detection in _with_root (#13389)
0xMars42 Feb 12, 2026
9ef198e
fix(common): remove trailing space in `state_root` match pattern (#13…
strmfos Feb 12, 2026
2e3f9b5
chore(config): `curl` mode as config key (#13260)
mablr Feb 12, 2026
6612bd6
fix(config): normalize deny_warnings from env vars (#13434)
aso20455 Feb 13, 2026
61290cf
fix: correct dead condition in command error formatting (#13427)
letmehateu Feb 13, 2026
81678d6
add missing JSON output support for `erc20 decimals` (#13438)
DanielGuupta Feb 13, 2026
28769aa
fix(anvil): variable shadowing bug in ReadyTransactions::remove_with_…
letmehateu Feb 14, 2026
a8617e5
Update flake.lock (#13448)
github-actions[bot] Feb 15, 2026
0bcfafa
chore(deps): weekly `cargo update` (#13449)
github-actions[bot] Feb 15, 2026
f8e3dfa
feat(evm): `ForkDatabase`/`MultiFork` generic over `Network` (#13459)
mablr Feb 16, 2026
0d2a1d3
fix(cheatcodes): fix vm.expectRevert for direct precompile calls (#13…
gakonst Feb 16, 2026
4f79da7
fix(lint): remove unreachable macro arm in declare_forge_lint (#13452)
aso20455 Feb 16, 2026
fdd3439
chore(flake): use nightly rustfmt (#13441)
beeb Feb 16, 2026
1e03468
feat: add `executeTransaction` cheatcode (#13437)
onbjerg Feb 16, 2026
98c6d9b
fix(forge): don't reset snapshot diff result on missing file (#13442)
dizer-ti Feb 16, 2026
278ba19
fix(traces): check HTTP status before JSON parsing in Sourcify fetche…
marukai67 Feb 16, 2026
288b27d
feat(cheatcodes): add Ed25519 crypto cheatcodes (#13450)
howydev Feb 16, 2026
6823f47
feat(lint): add missing visit methods to EarlyLintVisitor (#13454)
hawkadrian Feb 16, 2026
0b56ab3
notify subscribers for txs promoted after block mining (#13464)
DanielGuupta Feb 16, 2026
a1787f2
chore(deps): bump taiki-e/install-action from 2.67.27 to 2.68.0 (#13465)
dependabot[bot] Feb 17, 2026
508d21c
chore(deps): bump taiki-e/cache-cargo-install-action from 3.0.1 to 3.…
dependabot[bot] Feb 17, 2026
ed4e892
chore(deps): bump crate-ci/typos from 1.43.4 to 1.43.5 (#13467)
dependabot[bot] Feb 17, 2026
bcc6eb2
chore(deps): bump DeterminateSystems/update-flake-lock from 5adeaaaf3…
dependabot[bot] Feb 17, 2026
50dd825
chore(deps): bump softprops/action-gh-release from 2.4.2 to 2.5.0 (#1…
dependabot[bot] Feb 17, 2026
662b35a
test: mark clone CLI tests as flaky (#13472)
mattsse Feb 17, 2026
7326b79
fix: bind TempDir guard in clone test to prevent premature cleanup (#…
mattsse Feb 17, 2026
94d1caa
prevent balance overflow in anvil_addBalance (#13457)
DanielGuupta Feb 17, 2026
5389cae
chore(tests): bump forge-std version (#13482)
github-actions[bot] Feb 18, 2026
d933f2e
move `sccache --show-stats` into build RUN to show actual stats (#13483)
DanielGuupta Feb 18, 2026
d20285a
Merge foundry-rs/foundry#12587 into aviggiano/master
aviggiano Feb 18, 2026
8090cb2
Merge origin/master into aviggiano-pr-12587
aviggiano Feb 18, 2026
e77032b
fix: resolve merge regressions after origin/master sync
aviggiano Feb 18, 2026
285106b
fix(invariant): keep failed metrics for failed invariants
aviggiano Feb 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 3 additions & 15 deletions crates/config/src/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,8 @@ pub struct InvariantConfig {
pub timeout: Option<u32>,
/// Display counterexample as solidity calls.
pub show_solidity: bool,
/// Maximum time (in seconds) between generated txs.
pub max_time_delay: Option<u32>,
/// Maximum number of blocks elapsed between generated txs.
pub max_block_delay: Option<u32>,
/// Number of calls to execute between invariant assertions.
///
/// - `0`: Only assert on the last call of each run (fastest, but may miss exact breaking call)
/// - `1` (default): Assert after every call (current behavior, most precise)
/// - `N`: Assert every N calls AND always on the last call
///
/// Example: `check_interval = 10` means assert after calls 10, 20, 30, ... and the last call.
pub check_interval: u32,
/// Continue invariant run until all invariants declared in current test suite breaks.
pub continuous_run: bool,
}

impl Default for InvariantConfig {
Expand All @@ -67,9 +57,7 @@ impl Default for InvariantConfig {
show_metrics: true,
timeout: None,
show_solidity: false,
max_time_delay: None,
max_block_delay: None,
check_interval: 1,
continuous_run: false,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/evm/evm/src/executors/corpus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ pub(crate) struct CorpusMetrics {
impl fmt::Display for CorpusMetrics {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f)?;
writeln!(f, " Edge coverage metrics:")?;
writeln!(f, " - cumulative edges seen: {}", self.cumulative_edges_seen)?;
writeln!(f, " - cumulative features seen: {}", self.cumulative_features_seen)?;
writeln!(f, " - corpus count: {}", self.corpus_count)?;
Expand Down
44 changes: 35 additions & 9 deletions crates/evm/evm/src/executors/invariant/error.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use super::InvariantContract;
use crate::executors::RawCallResult;
use alloy_json_abi::Function;
use alloy_primitives::{Address, Bytes};
use foundry_config::InvariantConfig;
use foundry_evm_core::decode::RevertDecoder;
use foundry_evm_fuzz::{BasicTxDetails, Reason, invariant::FuzzRunIdentifiedContracts};
use proptest::test_runner::TestError;
use std::{collections::HashMap, fmt};

/// Stores information about failures and reverts of the invariant tests.
#[derive(Clone, Default)]
Expand All @@ -14,16 +15,40 @@ pub struct InvariantFailures {
/// The latest revert reason of a run.
pub revert_reason: Option<String>,
/// Maps a broken invariant to its specific error.
pub error: Option<InvariantFuzzError>,
pub errors: HashMap<String, InvariantFuzzError>,
}

impl InvariantFailures {
pub fn new() -> Self {
Self::default()
}

pub fn into_inner(self) -> (usize, Option<InvariantFuzzError>) {
(self.reverts, self.error)
pub fn into_inner(self) -> (usize, HashMap<String, InvariantFuzzError>) {
(self.reverts, self.errors)
}

pub fn record_failure(&mut self, invariant: &Function, failure: InvariantFuzzError) {
self.errors.insert(invariant.name.clone(), failure);
}

pub fn has_failure(&self, invariant: &Function) -> bool {
self.errors.contains_key(&invariant.name)
}

pub fn get_failure(&self, invariant: &Function) -> Option<&InvariantFuzzError> {
self.errors.get(&invariant.name)
}

pub fn can_continue(&self, invariants: usize) -> bool {
self.errors.len() < invariants
}
}

impl fmt::Display for InvariantFailures {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f)?;
writeln!(f, " ❌ Failures: {}", self.errors.len())?;
Ok(())
}
}

Expand Down Expand Up @@ -70,10 +95,11 @@ pub struct FailedInvariantCaseData {
impl FailedInvariantCaseData {
pub fn new(
invariant_contract: &InvariantContract<'_>,
invariant_config: &InvariantConfig,
shrink_run_limit: u32,
fail_on_revert: bool,
targeted_contracts: &FuzzRunIdentifiedContracts,
calldata: &[BasicTxDetails],
call_result: RawCallResult,
call_result: &RawCallResult,
inner_sequence: &[Option<BasicTxDetails>],
) -> Self {
// Collect abis of fuzzed and invariant contracts to decode custom error.
Expand All @@ -82,7 +108,7 @@ impl FailedInvariantCaseData {
.with_abi(invariant_contract.abi)
.decode(call_result.result.as_ref(), call_result.exit_reason);

let func = invariant_contract.invariant_function;
let func = invariant_contract.invariant_fn;
debug_assert!(func.inputs.is_empty());
let origin = func.name.as_str();
Self {
Expand All @@ -95,8 +121,8 @@ impl FailedInvariantCaseData {
addr: invariant_contract.address,
calldata: func.selector().to_vec().into(),
inner_sequence: inner_sequence.to_vec(),
shrink_run_limit: invariant_config.shrink_run_limit,
fail_on_revert: invariant_config.fail_on_revert,
shrink_run_limit,
fail_on_revert,
}
}
}
Loading
Loading