Skip to content

Vercel wagmi#587

Closed
Dargon789 wants to merge 79 commits into
mainfrom
vercel-wagmi
Closed

Vercel wagmi#587
Dargon789 wants to merge 79 commits into
mainfrom
vercel-wagmi

Conversation

@Dargon789
Copy link
Copy Markdown
Owner

Motivation

Solution

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

googleworkspace-bot and others added 30 commits April 10, 2026 12:05
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>
Updated benchmark results for Foundry versions and system information. foundry-rs@1c4d334

Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Bumps [strum](https://github.com/Peternator7/strum) from 0.27.2 to 0.28.0.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](Peternator7/strum@v0.27.2...v0.28.0)

---
updated-dependencies:
- dependency-name: strum
  dependency-version: 0.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Delete CircleCI deployment config (.circleci/ci_deploy.yml). Update GitHub workflow files (.github/workflows/Docker.yml and docker.yml) to trigger on pushes to master and pin docker/metadata-action to v5 (was v6). Adjust Rust output in crates/cast/src/args.rs to print decoded_tx via its Display implementation instead of serializing with serde_json::to_string_pretty. Bump npm dev deps in npm/package.json: @types/node to ^25.5.2 and typescript to ^6.0.2.

Co-Authored-By: Copilot <198982749+Copilot@users.noreply.github.com>
Co-Authored-By: Dargon789 <64915515+dargon789@users.noreply.github.com>
Signed-off-by: googleworkspace-bot <googleworkspace-bot@google.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>
…) (#498)

* chore(deps): bump rui314/setup-mold from 725a8794d15fc7563f59595bd9556495c0564878 to 9c9c13bf4c3f1adef0cc596abc155580bcb04444 (foundry-rs#14442)

chore(deps): bump rui314/setup-mold

Bumps [rui314/setup-mold](https://github.com/rui314/setup-mold) from 725a8794d15fc7563f59595bd9556495c0564878 to 9c9c13bf4c3f1adef0cc596abc155580bcb04444.
- [Commits](rui314/setup-mold@725a879...9c9c13b)

---
updated-dependencies:
- dependency-name: rui314/setup-mold
  dependency-version: 9c9c13bf4c3f1adef0cc596abc155580bcb04444
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update flake.lock (foundry-rs#14458)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* fix(forge): adjust gas assertion `CounterWithFallback` (foundry-rs#14465)

* chore: update latest benchmarks (foundry-rs#14467)

* ci: split MPP e2e into its own workflow (foundry-rs#14468)

* ci: split MPP e2e into its own workflow

Move the MPP e2e step from ci-tempo.yml into a standalone ci-mpp.yml
workflow so transient HTTP 402 failures from the MPP RPC do not block
the Tempo CI workflow.

Amp-Thread-ID: https://ampcode.com/threads/T-019dceb8-61e5-734f-b047-17665b4ea7d3
Co-authored-by: Amp <amp@ampcode.com>

* ci: rename sanity-check job to tempo-check

Amp-Thread-ID: https://ampcode.com/threads/T-019dceb8-61e5-734f-b047-17665b4ea7d3
Co-authored-by: Amp <amp@ampcode.com>

* ci: rename mpp-e2e job to mpp-check

Amp-Thread-ID: https://ampcode.com/threads/T-019dceb8-61e5-734f-b047-17665b4ea7d3
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>

* Improve GH actions (foundry-rs#14473)

* fix(benches): add repos + extra args support to prevent blocking errors (foundry-rs#14470)

* fix(benches): add repos + extra args support to prevent blocking errors

* fix(ci): set `inputs.repos` default to empty

* fix: remove `--verbose` flags

* fix: exclude `uniswap/v4-core` `TickMathTestTest`

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
googleworkspace-bot and others added 23 commits May 7, 2026 08:48
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>
* Update .github/scripts/compare-nightly.sh

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>

* Update .github/scripts/compare-nightly.sh

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>

* Update .github/scripts/compare-nightly.sh

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>

---------

Signed-off-by: Dargon789 <64915515+Dargon789@users.noreply.github.com>
Co-authored-by: googleworkspace-bot <googleworkspace-bot@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@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>
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>
* 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>

* ci: add stale PR workflow (foundry-rs#14832)

* ci: add stale PR workflow

* Update .github/workflows/stale-prs.yml

Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>

* ci: remove stale label creation

* ci: use stale action for PRs

* Apply suggestion from @zerosnacks

* Apply suggestion from @zerosnacks

---------

Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>

* fix(cheatcodes): fix expectRevert with internal reverts for non-contract calls (foundry-rs#14828)

* fix(cheatcodes): fix expectRevert with internal reverts for non-contract calls

Fixes foundry-rs#11559

When `allow_internal_expect_revert = true`, calling a non-contract address
(e.g., `IERC20(address(0)).totalSupply()`) with `expectRevert` would fail
with "next call did not revert as expected".

The root cause was that calls to addresses with no code return immediately
with success (Stop), and the expectRevert was being consumed by this
successful call. The actual revert happens afterward when Solidity's
generated code checks the return data (or extcodesize) and reverts.

The fix modifies the `call_end` hook to only consume the expectRevert when:
1. The call actually reverted, OR
2. An external call was made (max_depth > depth), OR
3. internal_expect_revert is disabled (maintaining backward compatibility)

When internal_expect_revert is enabled and a call succeeds without going
deeper, the expectation is preserved, allowing the subsequent internal
revert (from Solidity's code validation) to satisfy it.

* style: fix rustfmt formatting

* fix: handle dangling expectRevert at root call

When the test function (root call at depth 0) ends, we must always
process any pending expectRevert to properly catch dangling expectations
that were never satisfied.

* fix: allow expectRevert to catch cheatcode reverts when internal_expect_revert is enabled

When allow_internal_expect_revert = true, expectRevert should also be able
to catch cheatcode reverts (like vm.createSelectFork failing due to invalid
RPC URL). This is consistent with the purpose of the flag - to catch reverts
that happen at the same depth, including cheatcode calls.

* fix: preserve internal expectRevert after successful calls

* fix: handle precompile errors for internal expectRevert

* fix: handle createSelectFork expectRevert

* Revert "fix: handle createSelectFork expectRevert"

This reverts commit 1fbf62a.

* refactor(cheatcodes): simplify expectRevert call_end logic + deterministic Issue7481 repro

---------

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>

* feat(lint): detect external calls in loops (foundry-rs#14778)

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>

* fix(cheatcodes): innermost as reverter for nested CREATE chain revert (foundry-rs#14625)

* fix(cheatcodes): innermost as reverter for nested CREATE chain revert

`create_end` mirrors `call_end`

* fix: comments consistency after merge conflict

* fix: fig's remark on CREATE2

* fix: forge fmt

* docs(cheatcodes): clarify reverter semantics for nested CREATE chains

- vm.rs: add a shared note on expectRevert(address) explaining that the
  matched reverter is the address of the innermost reverting frame
  (called address for CALL, would-be deployed address for CREATE/CREATE2),
  with the same rule per iteration when count > 1. Cross-reference from
  the other reverter-bearing overloads (incl. expectPartialRevert).
- inspector.rs: tighten the create_end comment to spell out that the
  divergence from call_end is intentional for count > 1
  (innermost wins per iteration) and document why outcome.address can
  be None.
- regenerated cheatcodes.json.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019e0187-eea7-711f-a192-20ff5d36c4e0

* fix: modify `call_end` to match `create_end` logic

* Revert "fix: modify `call_end` to match `create_end` logic"

This reverts commit 65039fa.

* docs(cheatcodes): clarify nested reverter count semantics

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>

* feat(edge-cov): log comparison operands (foundry-rs#14801)

* feat(edge-cov): log comparison operands

* feat(edge-cov): gate expanded cmp operands

* feat(fuzz): carry evm cmp logs through results

* feat(fuzz): feed evm cmp operands into dictionary

* feat(fuzz): classify evm cmp sites

* feat(fuzz): bias abi mutations to dictionary values

* test(fuzz): cover evm cmp dictionary candidates

* fix(fuzz): satisfy cmp log clippy checks

* fix(fuzz): refine evm cmp dictionary typing

* perf(fuzz): reduce evm cmp log overhead

* fix(fuzz): derive evm cmp logging from corpus mode

* feat(fuzz): replace evm cmp dictionary with input-to-state corpus mutation

Drops the global EVM cmp operand dictionary path and instead records
per-call cmp operand pairs on each corpus entry, then patches matching
bytes in calldata (LibAFL-style I2S) as a new mutation strategy.

Amp-Thread-ID: https://ampcode.com/threads/T-019e25fc-1c2e-756a-ad61-4ed5acfc3319
Co-authored-by: Amp <amp@ampcode.com>

* chore(fuzz): drop dictionary-era leftovers around evm cmp log

Removes the cmp-site classification (CmpKind enum, signed/width fields),
reverts collect_typed_cmp_values back to its sancov-only (u8, B256)
signature, drops the unused replace_param_value_from_state helper, and
removes the now-unused shared_state arg on single_fuzz.

Amp-Thread-ID: https://ampcode.com/threads/T-019e25fc-1c2e-756a-ad61-4ed5acfc3319
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: grandizzy <38490174+grandizzy@users.noreply.github.com>
Co-authored-by: Amp <amp@ampcode.com>

---------

Signed-off-by: dependabot[bot] <support@github.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: zerosnacks <95942363+zerosnacks@users.noreply.github.com>
Co-authored-by: Mablr <59505383+mablr@users.noreply.github.com>
Co-authored-by: Haythem Sellami <17862704+haythemsellami@users.noreply.github.com>
Co-authored-by: figtracer <me@figtracer.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: Emma Jamieson-Hoare <emmajam@users.noreply.github.com>
Co-authored-by: Karl Yu <43113774+0xKarl98@users.noreply.github.com>
Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
#[test]
fn distinct_salts_across_simulations_at_same_nonce() {
// Two "simulations" with different per-inspector seeds but identical on-chain state.
let a = compute_batch_create_salt(0xabcd_ef01_2345_6789, 1, 5, 0);
fn distinct_salts_across_simulations_at_same_nonce() {
// Two "simulations" with different per-inspector seeds but identical on-chain state.
let a = compute_batch_create_salt(0xabcd_ef01_2345_6789, 1, 5, 0);
let b = compute_batch_create_salt(0x1122_3344_5566_7788, 1, 5, 0);

#[test]
fn counter_changes_salt() {
let a = compute_batch_create_salt(1, 1, 5, 0);
#[test]
fn counter_changes_salt() {
let a = compute_batch_create_salt(1, 1, 5, 0);
let b = compute_batch_create_salt(1, 1, 5, 1);

#[test]
fn chain_id_changes_salt() {
let a = compute_batch_create_salt(1, 1, 5, 0);
#[test]
fn chain_id_changes_salt() {
let a = compute_batch_create_salt(1, 1, 5, 0);
let b = compute_batch_create_salt(1, 2, 5, 0);

#[test]
fn deterministic_for_same_inputs() {
let a = compute_batch_create_salt(42, 1, 5, 7);
#[test]
fn deterministic_for_same_inputs() {
let a = compute_batch_create_salt(42, 1, 5, 7);
let b = compute_batch_create_salt(42, 1, 5, 7);
@codesandbox
Copy link
Copy Markdown

codesandbox Bot commented Jun 7, 2026

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, we are unable to review this pull request

The GitHub API does not allow us to fetch diffs exceeding 300 files, and this pull request has 608

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 7, 2026

Deployment failed with the following error:

Resource is limited - try again in 24 hours (more than 100, code: "api-deployments-free-per-day").

Learn More: https://vercel.com/dargon789-forge?upgradeToPro=build-rate-limit

@snyk-io
Copy link
Copy Markdown

snyk-io Bot commented Jun 7, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@Dargon789 Dargon789 closed this Jun 7, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant updates, including support for the Tempo T5 hardfork, temporary wallet sessions, improved output channel separation (stdout vs. stderr), and the --machine agent contract mode for structured CLI outputs. It also adds new cheatcodes and updates several dependencies. The review feedback identifies multiple critical compilation errors due to missing imports or incorrect type usages (such as the sh_status macro, std::io::Write trait, and invalid abi_encode calls). Additionally, there are logic bugs like an inverted gas limit cap condition, invalid YAML indentation and duplicated steps in the CircleCI configurations, and opportunities to improve local IPv6 loopback support and fractional ETH parsing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +213 to +215
sh_status!("Successfully found contract address in {:?}", timer.elapsed())?;
sh_status!("Address: {address}")?;
sh_status!("Salt: {salt} ({})", U256::from_be_bytes(salt.0))?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The sh_status macro is used here but is not imported in this file, which will cause a compilation error. Please use the fully qualified path foundry_common::sh_status! or import it.

        foundry_common::sh_status!("Successfully found contract address in {:?}", timer.elapsed())?;
        foundry_common::sh_status!("Address: {address}")?;
        foundry_common::sh_status!("Salt: {salt} ({})", U256::from_be_bytes(salt.0))?;

}

sh_println!("Building batch transaction with {} call(s)...", tempo_calls.len())?;
sh_status!("Building batch transaction with {} call(s)...", tempo_calls.len())?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The sh_status macro is used here but is not imported in this file, which will cause a compilation error. Please use the fully qualified path foundry_common::sh_status! or import it.

Suggested change
sh_status!("Building batch transaction with {} call(s)...", tempo_calls.len())?;
foundry_common::sh_status!("Building batch transaction with {} call(s)...", tempo_calls.len())?;

existing_files.len()
)?;
std::io::stdout().flush()?;
std::io::stderr().flush()?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Calling flush() on std::io::stderr() requires the std::io::Write trait to be in scope. Since it is not imported in this file, this will cause a compilation error. Please import std::io::Write locally.

Suggested change
std::io::stderr().flush()?;
use std::io::Write;
std::io::stderr().flush()?;

Comment on lines +1766 to +1770
let previous = ecx
.journal_mut()
.sload(target, base_slot)
.map_err(|e| fmt_err!("failed to load previous TIP-20 logo URI: {:?}", e))?
.data;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The sload method on the journal returns SLoadOutcome, which has a value field, not data. Using .data will cause a compilation error. Please change it to .value.

Suggested change
let previous = ecx
.journal_mut()
.sload(target, base_slot)
.map_err(|e| fmt_err!("failed to load previous TIP-20 logo URI: {:?}", e))?
.data;
let previous = ecx
.journal_mut()
.sload(target, base_slot)
.map_err(|e| fmt_err!("failed to load previous TIP-20 logo URI: {:?}", e))?
.value;

depth: ccx.ecx.journal().depth(),
log: Some(RawLog::new_unchecked(
vec![keccak256("LogoURIUpdated(address,string)"), updater.into_word()],
new_logo_uri.abi_encode().into(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The abi_encode method is not implemented for &str because SolValue is only implemented for owned String (and &String). Calling abi_encode() on &str will cause a compilation error. Please convert it to a String first.

            new_logo_uri.to_string().abi_encode().into(),

Comment thread .circleci/dev_stage.yml
Comment on lines +23 to +32
jobs:
my-job:
steps:
- run: echo "Hello, world!"
- run:
command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts"
max_auto_reruns: 3
auto_rerun_delay: 10s

workflows:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The jobs and workflows blocks starting on line 23 are incorrectly indented under the workflows block on line 18. This is invalid CircleCI YAML syntax and will cause the pipeline to fail to parse. Please fix the indentation of these blocks so they are top-level keys.

Comment on lines +540 to +542
let host = url.host_str().unwrap_or("");
if host == "localhost" || host == "127.0.0.1" {
return Ok(());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The standard IPv6 loopback address [::1] (and ::1) is commonly used for local development but is currently rejected by the sponsor URL validation. Please allow [::1] and ::1 as valid local hosts.

Suggested change
let host = url.host_str().unwrap_or("");
if host == "localhost" || host == "127.0.0.1" {
return Ok(());
let host = url.host_str().unwrap_or("");
if host == "localhost" || host == "127.0.0.1" || host == "[::1]" || host == "::1" {
return Ok(());
}

Comment thread crates/anvil/src/cmd.rs
Comment on lines +347 to +350
let amount: u64 = parts[1]
.parse()
.map_err(|e| eyre::eyre!("Invalid amount '{}': {}", parts[1], e))?;
let balance = Unit::ETHER.wei().saturating_mul(U256::from(amount));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Parsing the funded account amount as u64 prevents users from specifying fractional ETH amounts (e.g., 0.5 or 1.5). Please parse the amount as f64 or use foundry_cli::utils::parse_ether_value to support fractional ETH values.

            let amount = parts[1]
                .parse::<f64>()
                .map_err(|e| eyre::eyre!("Invalid amount '{}': {}", parts[1], e))?;
            let balance = U256::from((amount * 1e18) as u128);

Comment thread .circleci/cargo.yml
Comment on lines +27 to +32
- run:
name: "Check formatting"
command: cargo fmt -- --check
- run:
name: "Run tests"
command: cargo test
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The formatting check and test run steps are duplicated at the end of the job. Please remove these redundant steps.

Comment thread .circleci/ci.yml
Comment on lines +26 to +31
- run:
name: "Check formatting"
command: cargo fmt -- --check
- run:
name: "Run tests"
command: cargo test
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The formatting check and test run steps are duplicated at the end of the job. Please remove these redundant steps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants