feat(bot): exact v2 quote math + Multicall3 reserve refresh (7906, 7904) #12

Merged
buddysan merged 1 commit from rev/quote-reserves into main 2026-09-16 14:10:20 +00:00
Owner

Phase 2, WPs 7906 + 7904 (revenue-director lane rev, PR 1 of 2).

quote.py (7906)

  • Exact integer UniswapV2 getAmountOut / getAmountIn with fee_bps taken from the pool entry; quote.py never defaults a fee (architecture §2.1). v3/curve are explicit UnsupportedKind stubs (no such pool discovered on Arc testnet).
  • Bit-exact against chain: fixture bot/tests/fixtures/quote/v2_swap_logs_62260442-62410442.json is the raw eth_getLogs (Swap+Sync) output for the 8 registry pairs from the LXC 140 node; tests reconstruct pre-swap reserves from the preceding Sync and assert event.amountOut == v2_get_amount_out(..., 30) for all 10 real swaps, and that the pair k-invariant admits no fee above 30 bps — pins fee_bps=30 for all 4 testnet factories from chain data.
  • The bps formula is proven identical to the 997/1000 router code over 5000 random cases; floats/bools rejected; router revert conditions surface as QuoteError.

reserves.py (7904)

  • One aggregate3 per chunk with allowFailure=true and an explicit block tag; sub-call 0 is Multicall3.getBlockNumber() and any mismatch raises BlockMismatch (§4.8 guard). Failed/short/zero-reserve sub-calls stale-mark the pool with a reason; RPC errors stale-mark the chunk; unsupported kinds stale-mark without aborting.
  • Chunk caps measured live 2026-09-16: local node 1024 sub-calls ≈ 54 ms (4096 = 425 ms, 16384 fails intrinsic gas too low); public 256 ≈ 195 ms (accepts ≥ 2048). Defaults {local: 1024, public: 256}, config-overridable.
  • Fixture aggregate3_62411099.json: exact calldata + byte-identical results from the local node and rpc.testnet.arc.io, cross-checked against 8 direct getReserves() calls at the same block. Live refresh() on the box matched all 8 direct calls; chunking at cap=3 gave identical states; a pruned block stale-marked all pools instead of crashing.

Tests: python3 -m unittest discover -s bot/tests -t . → all OK offline. PR 2 (cycles + search + config + docs/search.md + architecture §2.3/§4.2/§7 rows) follows.

🤖 Generated with Claude Code

Phase 2, WPs 7906 + 7904 (revenue-director lane `rev`, PR 1 of 2). ## quote.py (7906) - Exact integer UniswapV2 `getAmountOut` / `getAmountIn` with `fee_bps` taken from the pool entry; `quote.py` never defaults a fee (architecture §2.1). v3/curve are explicit `UnsupportedKind` stubs (no such pool discovered on Arc testnet). - **Bit-exact against chain**: fixture `bot/tests/fixtures/quote/v2_swap_logs_62260442-62410442.json` is the raw `eth_getLogs` (Swap+Sync) output for the 8 registry pairs from the LXC 140 node; tests reconstruct pre-swap reserves from the preceding `Sync` and assert `event.amountOut == v2_get_amount_out(..., 30)` for all 10 real swaps, and that the pair k-invariant admits no fee above 30 bps — pins `fee_bps=30` for all 4 testnet factories from chain data. - The bps formula is proven identical to the 997/1000 router code over 5000 random cases; floats/bools rejected; router revert conditions surface as `QuoteError`. ## reserves.py (7904) - One `aggregate3` per chunk with `allowFailure=true` and an **explicit block tag**; sub-call 0 is `Multicall3.getBlockNumber()` and any mismatch raises `BlockMismatch` (§4.8 guard). Failed/short/zero-reserve sub-calls stale-mark the pool with a reason; RPC errors stale-mark the chunk; unsupported kinds stale-mark without aborting. - Chunk caps measured live 2026-09-16: local node 1024 sub-calls ≈ 54 ms (4096 = 425 ms, 16384 fails `intrinsic gas too low`); public 256 ≈ 195 ms (accepts ≥ 2048). Defaults `{local: 1024, public: 256}`, config-overridable. - Fixture `aggregate3_62411099.json`: exact calldata + byte-identical results from the local node and `rpc.testnet.arc.io`, cross-checked against 8 direct `getReserves()` calls at the same block. Live `refresh()` on the box matched all 8 direct calls; chunking at cap=3 gave identical states; a pruned block stale-marked all pools instead of crashing. Tests: `python3 -m unittest discover -s bot/tests -t .` → all OK offline. PR 2 (cycles + search + config + docs/search.md + architecture §2.3/§4.2/§7 rows) follows. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(bot): exact v2 quote math + Multicall3 reserve refresh (WP 7906, 7904)
All checks were successful
ci / forge-tests (pull_request) Successful in 1s
ci / python-tests (pull_request) Successful in 9s
b1b5c3cbf1
quote.py: integer UniswapV2 getAmountOut/getAmountIn with per-pool fee_bps from the
registry (never defaulted here); bit-exact against 10 real Swap events recorded from
the LXC 140 testnet node (all 4 factories, fee pinned at exactly 30 bps by the pair
k-invariant). v3/curve raise UnsupportedKind — no such pool on Arc testnet yet.

reserves.py: aggregate3 with allowFailure=true and an explicit block tag; sub-call 0 is
Multicall3.getBlockNumber() and a mismatch raises BlockMismatch (§4.8 guard); failed or
malformed sub-calls stale-mark the pool instead of failing the refresh; chunk caps per
endpoint class measured live (local 1024 ≈ 54 ms, public 256 ≈ 195 ms). Fixture is the
exact calldata + byte-identical results from both endpoints at block 62411099, cross-
checked against direct getReserves() calls.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Signed-off-by: BuddySan <[email protected]>
fortiblox-dev approved these changes 2026-09-16 14:02:28 +00:00
Dismissed
fortiblox-dev left a comment

automerger v2 auto-approved: all 7 gates pass. Approver: fortiblox-dev (opposite-identity rule). Gates: mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker.

automerger v2 auto-approved: all 7 gates pass. Approver: fortiblox-dev (opposite-identity rule). Gates: mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker.
Merge branch 'main' into rev/quote-reserves
All checks were successful
ci / forge-tests (pull_request) Successful in 2s
ci / python-tests (pull_request) Successful in 6s
dd71ee8b86
buddysan force-pushed rev/quote-reserves from dd71ee8b86
All checks were successful
ci / forge-tests (pull_request) Successful in 2s
ci / python-tests (pull_request) Successful in 6s
to 444426cefc
All checks were successful
ci / forge-tests (pull_request) Successful in 2s
ci / python-tests (pull_request) Successful in 6s
ci / python-tests (push) Successful in 17s
ci / forge-tests (push) Successful in 23s
2026-09-16 14:08:44 +00:00
Compare
fortiblox-dev left a comment

automerger v2 auto-approved: all 7 gates pass. Approver: fortiblox-dev (opposite-identity rule). Gates: mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker.

automerger v2 auto-approved: all 7 gates pass. Approver: fortiblox-dev (opposite-identity rule). Gates: mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker.
buddysan deleted branch rev/quote-reserves 2026-09-16 14:10:21 +00:00
Author
Owner

Auto-merged by forgejo-automerger at 2026-09-16 14:10:21 UTC. Approver: fortiblox-dev (opposite-identity rule, Option C upgrade 2026-05-13). Merger: buddysan via fast-forward-only (NOT admin force_merge). Criteria passed: mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker. Branch 444426cefc merged onto main and feature branch deleted. See project_forgejo_automerger memory for rationale.

Auto-merged by **forgejo-automerger** at 2026-09-16 14:10:21 UTC. **Approver:** `fortiblox-dev` (opposite-identity rule, Option C upgrade 2026-05-13). **Merger:** `buddysan` via `fast-forward-only` (NOT admin `force_merge`). **Criteria passed:** `mergeable=true,label=auto-merge,author=buddysan,identity=ok,status_checks=not_required,signatures=upstream_BP_only,opt_in=repo_marker`. Branch `444426cefc` merged onto `main` and feature branch deleted. See `project_forgejo_automerger` memory for rationale.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
fortiblox/arc-mev-bot!12
No description provided.