Forge/master#561
Conversation
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Bumps the cargo group with 1 update in the / directory: [alloy-dyn-abi](https://github.com/alloy-rs/core). Updates `alloy-dyn-abi` from 0.8.25 to 0.8.26 - [Release notes](https://github.com/alloy-rs/core/releases) - [Changelog](https://github.com/alloy-rs/core/blob/v0.8.26/CHANGELOG.md) - [Commits](alloy-rs/core@v0.8.25...v0.8.26) --- updated-dependencies: - dependency-name: alloy-dyn-abi dependency-version: 0.8.26 dependency-type: direct:production dependency-group: cargo ... Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
…in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
…in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
…in path expression Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
…gery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
…gery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com> --------- https://github.com/apps/gemini-code-assist Code Review This pull request updates the Rust version in the CI from 1.88.0 to 1.89.0. While this is a good maintenance step, I've identified a potential improvement for your CI configuration. The project's Cargo.toml specifies a Minimum Supported Rust Version (MSRV) of 1.86, but the CI doesn't test against it. I've added a comment suggesting the addition of an MSRV check to prevent compatibility issues.
https://github.com/apps/gemini-code-assist ------------------- Code Review This pull request downgrades the Rust version in the CI pipeline from 1.88.0 to 1.87.0. This is inconsistent with the project's declared Minimum Supported Rust Version (MSRV) of 1.89 in Cargo.toml. My review highlights this discrepancy and suggests aligning the CI's Rust version with the MSRV to ensure the project's compatibility guarantees are properly tested. --------------- Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
https://github.com/apps/gemini-code-assist Code Review This pull request introduces a CircleCI workflow to automate formatting checks and tests. My review has identified two main issues in the configuration: redundant steps that would unnecessarily increase job execution time, and a mismatch between the Rust version in the CI environment and the one specified in the project's Cargo.toml. I've provided suggestions to fix these issues for a more efficient and consistent CI process. Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
* Create jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Create docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 58: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update .github/workflows/docker-image.yml Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Update docker-image.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Revert "chore: fix isolate tests (foundry-rs#10344)" This reverts commit 70ded2b. * Delete .github/workflows/jekyll.yml Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> * Potential fix for code scanning alert no. 19: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> --------- Signed-off-by: AU_gdev_19 <64915515+Dargon789@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Streamline the Docker CI workflow by renaming the file and enhancing it with scheduled runs, Buildx multi-platform builds, metadata tagging, conditional pushes, and automated image signing with Cosign. CI: Rename and replace the legacy docker-image.yml workflow with docker.yml Add scheduled cron runs and triggers on pushes to master, semver tags, and PRs Configure Docker Buildx for multi-platform builds with cache Extract Docker metadata and conditionally push images to GHCR on non-PR events Install Cosign and sign published Docker images using ephemeral identity tokens Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Introduce a basic CircleCI pipeline for the web3 GameFi project, providing a custom Docker executor and a stub job within a workflow. CI: Add CircleCI config file ci-web3-gamefi.yml with version 2.1 pipeline Define a custom executor using the cimg/base:stable Docker image with Docker Hub credentials Create a web3-defi-game-project- job and integrate it into a my-custom-workflow Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
CI: Introduce docker-image.yml GitHub Actions workflow to checkout code and build Docker image on ubuntu-latest Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
There was a problem hiding this comment.
Sorry @Dargon789, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
There was a problem hiding this comment.
Code Review
This pull request introduces security hardening for file system operations to prevent directory traversal, adds a JSON schema generation tool for configurations, and implements a generic linting framework. It also includes a sample Foundry project and various infrastructure updates. Feedback focuses on improving the robustness of file system utilities by handling canonicalization errors and symlinks more effectively, adding bounds checks and fixing indentation in the new Solidity libraries, and correcting a duplicate field in the bug report template.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
…ed in path expression' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
* feat(cli): add JSON output envelope helpers (foundry-rs#14716) * feat(cli): add JSON output envelope helpers * fix(cli): remove failed JSON print helpers * fix(cli): address JSON envelope review * fix(cli): remove JSON serialization wrapper * docs(cli): clarify JSON envelope scope * docs(cli): tighten JSON success docs --------- Co-authored-by: Gustavo Figueiredo <me@figtracer.com> * feat(lint): add `costly-loop` (foundry-rs#14694) Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(lint): add `type-based-tautology` (foundry-rs#14697) * feat(lint): add `type-based-tautology` * fix: address zerosnacks' comment * feat: eq and ne --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(anvil): preserve network detected from chain id (foundry-rs#14773) * fix(traces): prefer contract ABI for decoding (foundry-rs#14788) * Update flake.lock (foundry-rs#14795) * docs(architecture): add missing punctuation between cheatcodes link and its description (foundry-rs#14776) * fix(cast): resolve tempo expiry for erc20 commands (foundry-rs#14797) * chore(workflows): remove in-repo developer convenience Nix flake (foundry-rs#14802) Remove in-repo Nix flake * feat(invariant): decouple handler-side assertions from invariant predicates (foundry-rs#14482) * feat(invariant): handler-side assertion bugs decoupled from invariant predicates Rebased on top of merged foundry-rs#12587 (assert_all). Adds handler-bug machinery: - Decoupled handler-side assertions from invariant predicates: dedup by (reverter, selector) site, shortest-reproducer wins on collision. - Dedicated 'Suite handlers:' report section with full counterexamples. - Continuous campaign under assert_all: preflight handler bugs are recorded and the campaign keeps running for the full budget. - Live pulse-stream metrics surface unique handler-bug counts alongside invariant unique_failures; record_new_invariant_failures emits per-tick events deterministically so multi-break ticks are all reported. - Persistence + replay under failures/<contract>/handlers/<site>.json, keyed by keccak256(reverter || selector); replays merge with shortest-wins, prune non-reproducing files, leave incompatible ones. - Post-campaign shrinking reuses the invariant shrink loop; intermediate calls that themselves assert are rejected to avoid promoting a different finding. - Cached failure counts on InvariantFailures keep invariant_count() and handler_count() O(1) on the per-call hot path. - E2E coverage in tests/cli/test_cmd/invariant/handler.rs. * fix(invariant): clear GLOBAL_FAIL_SLOT after recording handler bug A non-reverting `vm.assert*` (`assertions_revert = false`) writes GLOBAL_FAIL_SLOT = 1 on the cheatcode address and the call gets committed. Left in place, that committed slot poisons every subsequent `is_success` / `handlers_succeeded` check for the rest of the run, silently suppressing later `assert_invariants` and `afterInvariant` evaluations and undercounting bugs the campaign would otherwise find. Clear the slot via `Executor::clear_global_failure` right after `record_handler_assertion_bug` in both call sites (the per-call `can_continue` path and the `should_check_invariant == false` inline path). Simplified the helper signature: dropped the unused `Option<&mut StateChangeset>` argument and the always-discarded return type; backend-write failures (a fundamentally broken state) are traced. Adds a regression e2e test that asserts once on call #1, then bumps a counter on calls #2/#3 to trip a real predicate. Without the fix the predicate failure is silently dropped; with the fix both the handler bug and the predicate failure are reported. * feat(fuzz): generate msg.value for payable functions in invariant fuzzing (foundry-rs#14565) * feat(invariant): assert all invariants * Tests and Nits Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * fix: check all invariants in afterInvariant gate and preflight Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * fix: use per-invariant fail_on_revert when recording handler revert failures Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 * fix: commit state between txs in generate_counterexample Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * fix: preflight check all invariants, not just primary Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * fix: exclude secondary invariants from optimization mode runs Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * refactor: rename invariant_fn to primary_invariant_fn, deterministic preflight error, debug_assert on empty invariants Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * feat: show broken invariant count in progress bar during continuous runs Amp-Thread-ID: https://ampcode.com/threads/T-019dbf48-3fb0-7762-a01f-b5e966339e73 Co-authored-by: Amp <amp@ampcode.com> * feat(invariant): rename continuous_run to assert_all and default to true Renames the InvariantConfig field to better describe its semantics ("assert every invariant in the suite, don't stop on first failure") and flips the default to true so multi-invariant suites report all broken invariants by default, matching Echidna/Medusa behavior. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dcd68-66ac-76ed-ac5c-7ea722a9c9ae * feat(invariant): parameterize shrinker by target invariant + persisted failures footer - Generalize shrink_sequence, shrink_sequence_value, replay_run, replay_error to accept target_invariant: &Function (currently always primary; unblocks per-secondary shrinking). - Move reset_shrink_progress out of shrink fns; called once per invariant from replay_error. Progress label now 'Shrink: <invariant_name>'. - Add TestResult.invariant_failure_dir; Display appends 'N invariant failures persisted to <dir> — rerun to shrink' when secondary failures were written. Amp-Thread-ID: https://ampcode.com/threads/T-019dcd68-66ac-76ed-ac5c-7ea722a9c9ae Co-authored-by: Amp <amp@ampcode.com> * feat(invariant): structured InvariantOtherFailure for assert_all secondaries Promotes TestResult.other_failures from Vec<String> to Vec<InvariantOtherFailure> carrying name, reason, optional counterexample, and persisted path. Display renders each secondary symmetrically with [FAIL: reason] + [Sequence] block when a counterexample is available, falling back to the terse 'name: reason' one-liner otherwise. Amp-Thread-ID: https://ampcode.com/threads/T-019dcd68-66ac-76ed-ac5c-7ea722a9c9ae Co-authored-by: Amp <amp@ampcode.com> * feat(invariant): serial secondary shrinking + Ctrl-C persists un-shrunk secondaries PR-3 of the assert_all rollout. After the campaign finishes, every broken secondary invariant is shrunk in turn via replay_error so users get a ready-to-debug counterexample for each failure in a single run (matching how the primary is rendered: [FAIL: reason] <name> + [Sequence] block). On Ctrl-C, instead of dropping known secondaries (previous behavior was a 'break' before pushing them), the loop keeps recording every failure the campaign discovered. The shrink + replay step is skipped to honor the interrupt, but the un-shrunk sequence is persisted via BaseCounterExample::from_invariant_call (no execution required), so a re-run targeting that secondary picks up the saved counterexample and shrinks from there — same UX as re-running an interrupted primary. Output of an interrupted run now includes a terse '<invariant>: <reason>' line for each secondary the campaign saw, preserving visibility of all broken invariants while keeping the interrupt fast. Adds e2e coverage: - assert_all: extended to verify secondary failures render symmetrically with shrunk sequences and that re-running skips persisted secondaries. - assert_all_only_primary: new test confirming no secondary [FAIL] blocks or persisted-failures footer appear when only the primary breaks. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dcdd3-53f5-76b6-ac36-d59f06b58280 * feat(invariant): assert_all polish — [i/N] shrink counter, suite roll-up, opt-mode warning Three small UX wins for assert_all campaigns. No behavior change, no new dependencies. 1. Shrink progress bar gets an [i/N] queue counter when more than one invariant needs shrinking, so users see how many shrinkers are queued behind the current one (e.g. '[2/3] Shrink: invariant_X'). reset_shrink_progress and replay_error gain a position parameter; single-invariant call sites pass None. 2. Suite-level roll-up footer: when assert_all exercised >1 invariant and the test failed, render 'Suite assert_all: <broken>/<total> invariants broken' above the per-invariant blocks. Gives CI logs and Slack pastes a glanceable health line. New Option<usize> field on TestResult, populated only when meaningful. 3. Startup warning when assert_all + optimization-mode are combined. Optimization mode tracks one int256 return value, so any boolean secondary invariants in the same contract are filtered out before the campaign — previously silent. Now emits a once-per-suite warning naming the optimization invariant and every dropped boolean so users can move them to a separate contract. E2E tests: extend assert_all to assert the new 4/5 roll-up; assert_all_only_primary covers the 1/2 case; new assert_all_optimization_mode_warning verifies the warning fires with the dropped invariant names. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dcdd3-53f5-76b6-ac36-d59f06b58280 * feat(invariant): warn when assert_all skips invariants with persisted failures Symmetric with the primary's existing persisted-replay warning. Echidna and Medusa never silently drop properties between runs — properties are re-evaluated every campaign and a previous failure doesn't suppress them. Foundry's per-property failure file model meant secondaries with a stale persisted counterexample were filtered out of the campaign with no acknowledgment, so users coming from Echidna/Medusa would see fewer invariants in the report than their contract defines. Now emits one stderr line listing every skipped name and the cache dir to clean, e.g.: Warning: test/X.t.sol:Suite: 3 invariant(s) skipped due to persisted failures: invariant_a, invariant_b, invariant_c. Run `forge clean` or delete files in cache/invariant/failures/Suite to re-include. E2E: extends assert_all re-run case with stderr_eq snapshot asserting the warning fires with all 3 skipped names. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dcdd3-53f5-76b6-ac36-d59f06b58280 * fix(invariant): gate afterInvariant per-run under assert_all Previously afterInvariant was gated on failures.errors.is_empty() campaign-wide. Under assert_all that gate stayed closed for the rest of the campaign once any invariant broke, silently skipping the afterInvariant hook on every subsequent run. Any assertions or cleanup logic in afterInvariant therefore stopped running after the first unrelated invariant failure. Now snapshot failures.errors.len() at the start of each run and only skip afterInvariant when the current run produced a new failure. Preserves the legacy 'don't run afterInvariant on a run that already failed' semantics while letting it run on subsequent runs once an earlier invariant has broken. E2E: new assert_all_after_invariant_runs_after_earlier_failure case breaks invariant_first in run 1, keeps the campaign alive with a second never-breaking invariant, and asserts an always-reverting afterInvariant surfaces its marker in failure output. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * fix(invariant): re-evaluate secondary persisted failures on settings change The secondary persisted-failure skip used a bare `.exists()` check at two sites in runner.rs (the warning + InvariantContract::new filter, and the post-campaign shrink loop). Under the new `assert_all = true` default this meant any leftover failure file from a previous run was treated as still valid even after the user changed a tracked setting (target contracts/selectors, target/excluded senders, fail_on_revert), silently dropping the secondary from the campaign with a misleading 'skipped due to persisted failures' warning. Now both sites use the same settings-aware compatibility check the primary's replay path uses (persisted_call_sequence settings.diff). Stale caches fall back to a fresh evaluation; only secondaries whose persisted settings still match the current run are honored. Also hoists current_settings up so the new secondary_has_compatible_persisted closure can reuse it across all three call sites (warning, filter, shrink-loop skip). E2E: new assert_all_secondary_persisted_revalidates_on_settings_change runs once with fail_on_revert=false, flips it to true, re-runs and asserts the suite roll-up shows 2/2 invariants broken — proving the secondary was re-evaluated rather than silently filtered out. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * fix(invariant): drop hollow [FAIL] when only secondaries break under assert_all When the selected invariant test passes but a secondary breaks under assert_all, the report previously rendered a hollow '[FAIL]' header (no reason, no counterexample) for the primary and the suite roll-up overcounted broken invariants as '1 + other_failures.len()', attributing a non-existent primary failure. Now key the primary header on whether the primary actually broke (`reason.is_some() || counterexample.is_some()`) and skip the header when it didn't. Roll-up uses the same flag so the count reflects only invariants that actually broke (e.g., 1/2 instead of 2/2). JSON shape is unchanged: top-level reason/counterexample stay null when the selected primary didn't break, with full secondary detail in other_failures. E2E: new assert_all_secondary_only_failure_no_hollow_fail asserts a secondary-only break renders 'Suite assert_all: 1/2 invariants broken' followed by the secondary's '[FAIL: ...] <name>' block, with no hollow primary header. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * fix(invariant): scope assert_all hollow [FAIL] suppression to secondary-only case Previous commit suppressed the '[FAIL]' header whenever the primary had no top-level reason or counterexample, which also matches DS-style failures (they signal via the 'failed' flag and log events rather than through TestResult.reason). That regressed failure_assertions::ds_style_test_failing and test_cmd::core::legacy_assertions in CI. Now the suppression is scoped strictly to the assert_all secondary-only case: skip the primary header only when no primary failure AND assert_all is in play AND there is at least one secondary to render. DS-style, plain unit and single-invariant failures keep the original '[FAIL]'/'[FAIL: ...]' rendering. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * fix(invariant): attribute failure event to first broken invariant in declaration order The structured JSON 'failure' event emitted to stderr at campaign end (consumed by benchmark and CI tooling) used 'errors.values().next()' on a HashMap to pick its 'reason' field, while hardcoding the 'invariant' field to the primary's name. With HashMap RandomState, the same broken set of invariants produced a different reason string across runs, and the event was self-inconsistent (e.g., 'invariant: invariant_balance, reason: fee miscalculation'). Three sites used this pattern: in-run break path, afterInvariant break path, and the preflight check fallback. Now they walk 'invariant_contract.invariant_fns' in declaration order (a Vec, deterministic) and pick the first one with a recorded failure. Both 'invariant' and 'reason' fields refer to the same function, and the event is stable across runs. A new 'first_broken_event' helper centralizes the lookup. E2E: assert_all_failure_event_uses_declaration_order declares three invariants (a, b, c) that all break on the same call, runs with '--mt invariant_c' (primary is the last declared) and asserts the emitted event names invariant_a with reason 'a broken'. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * refactor(invariant): rename to InvariantSecondaryFailure / invariant_secondary_failures and serialize sparsely Renames TestResult.other_failures -> invariant_secondary_failures and the underlying InvariantOtherFailure struct -> InvariantSecondaryFailure. The previous names were generic ('other relative to what?'); the new names align with the existing 'primary/secondary' terminology used throughout the assert_all rollout and follow the Rust Vec<Foo>/foos plural-of-singular convention. Also marks the field with #[serde(default, skip_serializing_if = 'Vec::is_empty')] so it is omitted from JSON output for any test that has no secondary failure data — plain unit tests, fuzz tests, passing tests. Pre-PR JSON consumers continue to see the same shape on those results. invariant_failure_dir and assert_all_invariant_count already had Option::is_none guards. Updates the SimpleContractTest{NonVerbose,Verbose}.json fixtures to drop the now-skipped empty field. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019dce2d-57c7-734a-bbc6-6fa5e34b25de * test(invariant): assert_all + fail_on_revert=false attributes assert() to all live invariants Amp-Thread-ID: https://ampcode.com/threads/T-019dd262-ed81-723c-aaaa-8e1314bed45a Co-authored-by: Amp <amp@ampcode.com> * feat(invariant): decouple handler-side assertions from invariant predicates Handler-side assertion failures are now tracked in a dedicated broken_handlers map keyed by (reverter, selector) instead of being attributed to every live invariant. They surface in their own "Suite handlers:" report section, keeping invariant predicate breaks rendered separately. Under assert_all = true (the new default), the campaign continues for the full budget after a preflight invariant failure so handler-side bugs and still-live invariants can be discovered. The legacy abort-on-preflight behavior is preserved when assert_all = false. Live progress (progress bar + JSON pulse events) now surfaces unique handler bug counts alongside invariant failure counts so both classes are visible during the campaign. Tests: - New regression test assert_all_handler_assertion_routed_to_handler_section asserting the "handler bug != invariant break" semantics. - Existing handler-assert tests (invariant_fail_on_assert_panic, invariant_fail_on_vm_assert_*, etc.) updated to expect the new "Suite handlers:" rendering while keeping the failure-reason line. - should_exit_early_on_invariant_failure now sets assert_all = false explicitly so it continues to exercise the legacy abort-on-preflight path. Refs: foundry-rs#14437 * fix: avoid private intra-doc link in HandlerAssertionFailure Amp-Thread-ID: https://ampcode.com/threads/T-019dd3bb-9f77-7224-9d25-acdf2c0dd095 Co-authored-by: Amp <amp@ampcode.com> * refactor(invariant): fold broken_handlers into InvariantFailureMetrics Move the unique handler-bug counter into InvariantFailureMetrics alongside failures / unique_failures so all campaign-level failure counters live in one struct, and drop the separate parameter from build_invariant_progress_json. Snapshot is refreshed each iteration from failures.broken_handlers. Addresses review comment on foundry-rs#14482. * refactor(invariant): dedup handler-side assertion bugs by edge-coverage fingerprint - Handler-side assertion bugs are now deduped by edge-coverage fingerprint of the asserting call, not by `(reverter, selector)`. Distinct paths → distinct bugs (Medusa/Echidna semantics). - Smaller-reproducer rule: on fingerprint collision, the entry with the shortest `call_sequence` wins. - Graceful fallback: when edge coverage is disabled (no `corpus_dir`), fingerprint falls back to `keccak256(reverter || selector)` — preserves prior behavior. * feat(invariant): shrink handler-side assertion bug counterexamples - Each handler bug's call sequence is shrunk post-campaign to the minimal prefix that still triggers the asserting (anchor) call. Anchor is pinned and never dropped; intermediate calls that themselves assert are rejected so we don't promote a different finding. - DRY refactor in `shrink.rs`: extracted `run_shrink_loop` (shared by `shrink_sequence` and `shrink_handler_sequence`) and `replay_sequence` (shared by `check_sequence` and `handler_sequence_still_asserts`). `check_sequence_simple` + `check_sequence_with_accumulation` collapsed into one `check_sequence`. - Error policy: invariant shrink keeps legacy "keep removed on error" semantics; handler shrink restores on error so a replay failure can never silently produce a non-reproducible counterexample. - Belt-and-suspenders: shrunk handler sequences are replayed once more before being stored; on any failure we fall back to the original. - Renderer: `HandlerAssertionFailure` now carries `original_sequence_len` so the existing `(original: N, shrunk: M)` format works for handler bugs. * feat(invariant): persist and replay handler-side assertion bugs - Each broken handler is written to `<failure_dir>/handlers/<edge_fingerprint>.json` (reusing `InvariantPersistedFailure` with `assertion_failure: true`); `(reverter, selector)` is stripped off and recovered from the last call's `(target, calldata[..4])` at replay time. - At campaign start, every file in `handlers/` is replayed via `check_sequence` with `expect_assertion_failure: true`. Bugs that still reproduce are merged into the campaign's `handler_errors` (shortest-wins on collision); files that no longer reproduce are deleted in place; settings-incompatible files are left untouched. - DRY refactor in `runner.rs`: extracted `replay_persisted_call_sequence` (shared by primary-invariant and handler-side replay) and a small `ReplayContext<'_>` bundle so both call sites stay short. `HandlerAssertionFailure::from_replayed_sequence` constructor centralises the (reverter, selector) recovery. - E2E coverage in `handler_assertion_persisted_to_disk`: confirms the file is written on first run, that a `runs = 0` rerun fails purely from the persisted replay (asserting the new `Replayed handler-side assertion bug` warning), and that the file is deleted when the underlying contract is patched to no longer assert. * perf(invariant): only hash edge coverage for asserting calls - Narrow the `pre_merge_edges_hash` snapshot gate from `call_result.reverted` to `assertion_failure`. The hash is only consumed by the handler-bug dedup path; for every other reverted call (vm.assume, MAGIC_ASSUME, plain `require` failures, fail_on_revert-ignored reverts) we were keccak-ing the edge buffer for nothing. Net effect: a `keccak256` over the per-call edge buffer is now skipped on the dominant non-asserting revert path. - `did_fail_on_assert` is hoisted above the snapshot and reused by the non-discarded branch, so the predicate is computed once per call instead of twice. `!discarded` short-circuit also reclaims the wasted hash on MAGIC_ASSUME-discarded calls. - Drive-by `cargo fmt` on four unrelated lines touched by earlier commits. * feat(invariant): pre-seed campaign with persisted handler bugs for live telemetry * test(invariant): handler-bug coverage for multi-handler, post-shrink replay, settings invalidation - `multi_handler_bugs_each_persist_independently`: two distinct handler contracts in the same campaign, each producing its own assertion bug. Confirms both surface in `Suite handlers: 2 assertion bug(s) found` and that the `handlers/` directory holds one JSON per fingerprint. - `handler_bug_replay_is_idempotent_after_shrink`: noop-prefixed handler so the discovered sequence has to be shrunk. Reads the persisted JSON to assert it holds the post-shrink (anchor-only) sequence, then re-runs with `runs = 0` and asserts the report renders `(original: 1, shrunk: 1)` instead of growing back. - `handler_persisted_failure_skipped_on_settings_change`: flips a tracked `InvariantSettings` field (`fail_on_revert`) between runs. With `runs = 0` and the persisted file present, asserts the `settings have changed` warning fires, the test passes (no replay), and the file is left intact for a future run with the original settings to pick up. * fix(invariant): strict handler-bug replay rejects stale files and divergent shrinks - Persisted handler-side assertion bugs were replayed via `check_sequence(expect_assertion_failure=true)`, which accepted any sequence-level failure as proof the bug still reproduced. Stale files were silently kept whenever an earlier call now asserted, when only the invariant predicate now broke, or when the anchor still asserted on a different code path. - Shrinking checked only that the anchor still asserted; dropping a setup call could push the anchor through a different branch and produce a different bug under the original fingerprint, breaking dedup semantics and persisted identity. - Add `replay_handler_failure_sequence` as the single strict path used by both startup replay and post-shrink verification: requires no pre-anchor assertion, anchor must assert, and recomputes the normalized edge fingerprint. - Persisted-replay keeps a file only if the recomputed fingerprint matches the filename; the shrink predicate rejects candidates whose anchor fingerprint diverges from the originally recorded one. - Move the public `CheckSequenceOptions` and new `HandlerReplayOutcome` to the top of `shrink.rs` next to the other shared types, and split the handler-bug e2e tests into their own `invariant/handler.rs` module. * perf(invariant): drop per-call clone in campaign loop and restore O(k) replay fast path - Replace the per-call `BasicTxDetails::clone()` in the campaign hot loop with a one-shot extraction of `(handler_target, handler_selector)` (Address + 4-byte Selector). The clone existed solely so `&tx` could be passed to `can_continue` alongside `&mut current_run`; with `can_continue` now taking the two scalars directly, helpers (`execute_tx`, `record_metrics`, `collect_data`) re-borrow the input from `current_run.inputs.last()` via field-level split borrows. Net effect: the `Bytes` calldata buffer is no longer copied on every fuzzed call. - Also collapses the duplicate inline `(target, selector)` extraction in the `else` branch of `should_check_invariant` — both branches now reuse the snapshot. - Restore the O(k) fast path in `replay_sequence`: when `accumulate_warp_roll == false` we iterate only the kept indices and pass `&calls[idx]` directly, skipping both the full-length scan over `calls` and the `BasicTxDetails::clone()` per kept call. The accumulating arm still walks the full sequence so warp/roll from skipped txs lands as a concrete delta on the next kept tx. Shrinking calls this in a tight loop, so for a length-`n` candidate shrunk to `k` calls the per-iteration cost goes from O(n) + k clones back to O(k) + 0 clones. * feat(invariant): site-granular dedup for handler bugs + live pulse-metrics fix - Change `broken_handlers` dedup key from edge-coverage fingerprint to `(reverter, selector)`, matching Echidna/Medusa per-function semantics. Multiple paths through the same handler collapse to one bug; persisted reproducers stay minimal via shortest-`call_sequence`-wins on collision. - Persist handler bugs via stable site hash `keccak256(reverter || selector)` in `record_handler_failure` / `replay_persisted_handler_failures` so the on-disk artifact name is independent of edge coverage. - Pulse metrics: bridge any newly-recorded invariant breaks into `failure_metrics` regardless of the `can_continue` outcome via `record_new_invariant_failures`. Previously `unique_failures` stayed at 0 until *all* invariants in an `assert_all` run broke, since `can_continue` only returns false at that point. Now live pulses match the final report. Removed `first_broken_event` (no longer needed). - E2E tests updated to assert site-granular behavior (multiple paths through one selector collapse to one bug). - TODO noted for finer-grained dedup of multiple distinct `assert(...)` failures within the same handler. * fix(clippy): use values() instead of iter() with unused key Amp-Thread-ID: https://ampcode.com/threads/T-019df149-03af-722b-abda-d8ab6332ee3b Co-authored-by: Amp <amp@ampcode.com> * fix(clippy): drop redundant references in trimmed_hex format args Amp-Thread-ID: https://ampcode.com/threads/T-019df149-03af-722b-abda-d8ab6332ee3b Co-authored-by: Amp <amp@ampcode.com> * feat(fuzz): generate msg.value for payable functions in invariant fuzzing Adds automatic msg.value generation for payable functions during initial call generation and corpus mutation. Stacked on top of foundry-rs#14482. - Add value: Option<U256> field to CallDetails and BaseCounterExample (serde-default for corpus back-compat) - Add fuzz_msg_value() proptest strategy and generate_msg_value() runner helper, biased toward small values (85% none / 10% wei / 4% milli-eth / 1% eth for proptest; 60/30/9/1 for runner) - Detect payable mutability in fuzz_contract_with_calldata and compose the value strategy - Forward value through execute_tx with balance-aware fallback to 0 when sender has insufficient balance - Mutate value with 15% probability in abi_mutate for payable functions - Render value in counterexamples (regular: value=X; solidity: {value: X}) - Add invariant_msg_value cli test Excludes the all-call mutation strategy, sender-mutation, and max_deal config from the original PR foundry-rs#13177 — those are independent concerns. Based on foundry-rs#8644 Co-authored-by: QiuhaoLi <qiuhaoli@outlook.com> * fix(fuzz): bound msg.value by sender balance, use UintStrategy for value generation --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: George Niculae <george@gxn3ql7y5j.tail388b2e.ts.net> Co-authored-by: QiuhaoLi <qiuhaoli@outlook.com> * fix(invariant): gate handler success on pending changeset only Avoid mutating GLOBAL_FAIL_SLOT in committed backend state to clear the poisoned flag after recording a handler bug. Instead, route the invariant runner's view-call success checks through is_success_handler_gate, which only counts the slot as failed when this call's own changeset writes it, ignoring a stale committed 1 from a previously-recorded handler bug. Preserves deterministic fuzzing paths and stops the poisoned flag from suppressing later assert_invariants / afterInvariant evaluations. --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: George Niculae <george@gxn3ql7y5j.tail388b2e.ts.net> Co-authored-by: QiuhaoLi <qiuhaoli@outlook.com> * ci(tempo): make mainnet check manual-only (foundry-rs#14808) Drop the schedule trigger from the mainnet step so nightly only exercises testnet (and the shared fork-schedule cargo test). Mainnet can still be run on demand via workflow_dispatch with network=mainnet or network=all. Fixes foundry-rs#14705 Amp-Thread-ID: https://ampcode.com/threads/T-019e3a85-0219-746d-a104-018518dc5b86 Co-authored-by: Amp <amp@ampcode.com> * chore(bench): remove `pr_number` input, and unused steps (foundry-rs#14804) - Remove `pr_number` workflow input and Comment on PR step - Remove `pr_comment` job output (no longer consumed) - Remove Setup Node.js step (not used in run-benchmarks job) - Remove Read benchmark results step (only produced now-unused outputs) - Drop `--force-install` from benchmark steps 2-4 (versions already installed by step 1) * fix(cast): support tempo sponsors in erc20 commands (foundry-rs#14798) * Support network hardforks in vm.setEvmVersion (foundry-rs#14758) Support network hardforks in setEvmVersion Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * fix(script): handle null tx lookups as dropped (foundry-rs#14684) * feat(lint): add `uninitialized-local` (foundry-rs#14719) * feat(lint): add `uninitialized-local` * fix: according amp's review * fix: given fig's review --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * feat(lint): add `incorrect-strict-equality` (foundry-rs#14749) * feat(lint): add `incorrect-strict-equality` * fix: given fig's review * chore: deprecate npm publishing (foundry-rs#14811) Removes the npm/ directory, the npm publishing workflow, and all related configuration. - Delete npm/ (per-arch packages, meta packages, publish scripts, bun config, tsconfig) - Delete .github/workflows/npm.yml (publish-arch + publish-meta jobs that listened to the release workflow) - Drop the npm ecosystem entry from .github/dependabot.yml - Remove npm/** excludes from dprint.json - Remove NPM section (npm/dist, npm/bin) from .gitignore - Remove bun.lock entry from .gitattributes Amp-Thread-ID: https://ampcode.com/threads/T-019e3b2f-5d65-734e-8084-66be5b77e42f Co-authored-by: Amp <amp@ampcode.com> * chore(deps): bump the actions-weekly group with 3 updates (foundry-rs#14815) * chore: bump alloy and tempo (foundry-rs#14823) Co-authored-by: Amp <amp@ampcode.com> * chore(deps): bump the cargo-weekly group across 1 directory with 5 updates (foundry-rs#14816) Bumps the cargo-weekly group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [clap_complete](https://github.com/clap-rs/clap) | `4.6.3` | `4.6.5` | | [tokio](https://github.com/tokio-rs/tokio) | `1.52.2` | `1.52.3` | | [tower-http](https://github.com/tower-rs/tower-http) | `0.6.8` | `0.6.10` | | [pin-project](https://github.com/taiki-e/pin-project) | `1.1.11` | `1.1.12` | | [ui_test](https://github.com/oli-obk/ui_test) | `0.30.4` | `0.30.5` | Updates `clap_complete` from 4.6.3 to 4.6.5 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](clap-rs/clap@clap_complete-v4.6.3...clap_complete-v4.6.5) Updates `tokio` from 1.52.2 to 1.52.3 - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](tokio-rs/tokio@tokio-1.52.2...tokio-1.52.3) Updates `tower-http` from 0.6.8 to 0.6.10 - [Release notes](https://github.com/tower-rs/tower-http/releases) - [Commits](tower-rs/tower-http@tower-http-0.6.8...tower-http-0.6.10) Updates `pin-project` from 1.1.11 to 1.1.12 - [Release notes](https://github.com/taiki-e/pin-project/releases) - [Changelog](https://github.com/taiki-e/pin-project/blob/main/CHANGELOG.md) - [Commits](taiki-e/pin-project@v1.1.11...v1.1.12) Updates `ui_test` from 0.30.4 to 0.30.5 - [Release notes](https://github.com/oli-obk/ui_test/releases) - [Changelog](https://github.com/oli-obk/ui_test/blob/main/CHANGELOG.md) - [Commits](oli-obk/ui_test@0.30.4...0.30.5) --- updated-dependencies: - dependency-name: clap_complete dependency-version: 4.6.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly - dependency-name: pin-project dependency-version: 1.1.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly - dependency-name: tokio dependency-version: 1.52.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly - dependency-name: tower-http dependency-version: 0.6.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly - dependency-name: ui_test dependency-version: 0.30.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: cargo-weekly ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * ci: skip Tempo and MPP CI on PRs from forks (foundry-rs#14826) * ci: skip Tempo CI on PRs from forks Repository secrets (TEMPO_*_RPC_URL, VERIFIER_URL, ...) are not exposed to workflows triggered by pull_request from forks, so the tempo-check job can never succeed for external contributor PRs. Gate the job to only run on pull_request when the head repo is foundry-rs/foundry. Amp-Thread-ID: https://ampcode.com/threads/T-019e3fd9-b656-7373-b581-f2023d70ff4c Co-authored-by: Amp <amp@ampcode.com> * ci: skip MPP CI on PRs from forks Same rationale: secrets (TEMPO_PRIVATE_KEY, TEMPO_KEYS_TOML_B64, MPP_API_KEY) are not exposed to fork PRs. The job currently no-ops on fork PRs but still burns runner time building binaries. Gate it to only run for branches in foundry-rs/foundry. Amp-Thread-ID: https://ampcode.com/threads/T-019e3fd9-b656-7373-b581-f2023d70ff4c Co-authored-by: Amp <amp@ampcode.com> --------- Co-authored-by: Amp <amp@ampcode.com> * feat(lint): add could-be-constant lint (foundry-rs#14753) * feat(lint): add could-be-constant lint * fix(lint): track writes in state-variable initializers for could-be-constant * drop dead TypeCall arm in is_allowed_constant_call + add self-writing initializer test * comments * feat(lint): add `unused-return` (foundry-rs#14785) * fix(lint): detect named ERC20 transfer args (foundry-rs#14829) * feat(lint): add event-fields lint (foundry-rs#14751) Co-authored-by: zerosnacks Co-authored-by: figtracer <me@figtracer.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * feat(lint): add cache-array-length lint (foundry-rs#14741) Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> * feat(lint): add delegatecall-loop lint (foundry-rs#14752) Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de> * feat(lint): add return-bomb lint (foundry-rs#14793) Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de> * feat(lint): add external-function lint (foundry-rs#14737) * feat(lint): add external-function lint * scope external-fn super.<name> match to caller's inheritance chain * external fn, handle memory param aliasing * clippy --------- Co-authored-by: Gustavo Figueiredo <me@figtracer.com> Co-authored-by: Amp <amp@ampcode.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Emma Jamieson-Hoare <emmajam@users.noreply.github.com> Co-authored-by: Gustavo Figueiredo <me@figtracer.com> Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com> Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Perico Perica <pericc.periccca@gmail.com> Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Abhishek Krishna <invokerkrishna@gmail.com> Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: George Niculae <george@gxn3ql7y5j.tail388b2e.ts.net> Co-authored-by: QiuhaoLi <qiuhaoli@outlook.com> Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: stevencartavia <112043913+stevencartavia@users.noreply.github.com> Co-authored-by: Karl Yu <43113774+0xKarl98@users.noreply.github.com>
Motivation
Solution
PR Checklist