fix(routing): hop-0-only hybrid on retail GET (#1280) #1281

Merged
PlasticDigits merged 7 commits from fix/1280-hybrid-hop-offer into main 2026-09-21 04:00:08 +00:00

Summary

  • #1280 Policy A: retail GET /route/solve emits declared hybrid on hop 0 only; hops 1+ are hybrid: null so execute cannot freeze quote-time interior splits against realized hop_output. Wasm keeps pool_input + book_input == hop offer. POST hybrid_by_hop still allows interior splits that partition that hop.
  • #1264 wrap path (partial): USTC→USTR wrap+2hop stays pool-only (H596-7) at the existing 2,710,000 envelope. Inventory fixture wrap_plus_send_2hop_ustc + wrap-swap E10. Combo gas is not raised (columbus-5 AC1 still unmeasured; no trading wallet in workstation inventory).

Playbook: skills/AGENTS_HYBRID_HOP_OFFER.md (invariants H1280-1–H1280-8).

Closes #1280

Test plan

  • make verify-issue-1280 (indexer hop0-only unit, wasm hop0 + interior mismatch reverts, frontend strip/preflight/humanize, docs; first pass + retest)
  • make verify-issue-1264 (wrap+2hop 2.71M fixture, wrap hops omit hybrid, docs; first pass + retest)
  • VERIFY_ISSUE_1280_CHAIN=1 make verify-issue-1280 (LocalTerra Playwright multihop-hybrid-tx)
  • VERIFY_ISSUE_1264_CHAIN=1 make verify-issue-1264 (LocalTerra wrap-swap E10)
  • Columbus-5 #1264 AC1: one USTC→USTR Swap; record hash / gas_wanted / gas_used / fee / wallet rewrite (A/B/C). Do not raise WRAP_ROUTER_COMBO_OVERHEAD_GAS without that measurement.
## Summary - **#1280 Policy A:** retail `GET /route/solve` emits declared `hybrid` on **hop 0 only**; hops 1+ are `hybrid: null` so execute cannot freeze quote-time interior splits against realized `hop_output`. Wasm keeps `pool_input + book_input == hop offer`. `POST hybrid_by_hop` still allows interior splits that partition that hop. - **#1264 wrap path (partial):** USTC→USTR wrap+2hop stays **pool-only** (**H596-7**) at the existing **2,710,000** envelope. Inventory fixture `wrap_plus_send_2hop_ustc` + wrap-swap **E10**. Combo gas is **not** raised (columbus-5 AC1 still unmeasured; no trading wallet in workstation inventory). Playbook: [`skills/AGENTS_HYBRID_HOP_OFFER.md`](skills/AGENTS_HYBRID_HOP_OFFER.md) (invariants **H1280-1–H1280-8**). Closes #1280 ## Test plan - [x] `make verify-issue-1280` (indexer hop0-only unit, wasm hop0 + interior mismatch reverts, frontend strip/preflight/humanize, docs; first pass + retest) - [x] `make verify-issue-1264` (wrap+2hop 2.71M fixture, wrap hops omit hybrid, docs; first pass + retest) - [ ] `VERIFY_ISSUE_1280_CHAIN=1 make verify-issue-1280` (LocalTerra Playwright `multihop-hybrid-tx`) - [ ] `VERIFY_ISSUE_1264_CHAIN=1 make verify-issue-1264` (LocalTerra wrap-swap E10) - [ ] Columbus-5 **#1264 AC1**: one USTC→USTR Swap; record hash / `gas_wanted` / `gas_used` / fee / wallet rewrite (A/B/C). Do not raise `WRAP_ROUTER_COMBO_OVERHEAD_GAS` without that measurement.
fix(routing): emit hop-0-only hybrid on retail GET (#1280)
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
b0dade6b7f
Strip interior declared splits so execute cannot freeze quote-time book legs against realized hop_output. Keep wrap+2hop USTC→USTR pool-only at the existing 2.71M envelope (#1264) without raising combo gas.
Author
Owner

drain skip: no occupying job for rebase/fix-pr/CI-wait; remaining continue

drain skip: no occupying job for rebase/fix-pr/CI-wait; remaining continue
Author
Owner

/agent review

/agent review
Author
Owner

/agent review

/agent review
Author
Owner

cl8y-agent-control: queued bug_review job 55e2dfad-bf66-4f3f-ba78-de861b055fb1 (not executed; no Hetzner VM).

cl8y-agent-control: queued `bug_review` job `55e2dfad-bf66-4f3f-ba78-de861b055fb1` (not executed; no Hetzner VM).
Author
Owner

RECOMMEND: FIX

Frozen SHA: b0dade6b7ff114501b110d27c456e6ac0ec7ab21 (PR 1281 HEAD fix/1280-hybrid-hop-offer; prompt frozen SHA was unset). Independent review in an isolated worktree vs origin/main cf1eff3252a29e07c42f1544dd9e500a53e5c4b9. Does not replace test-only verify or the Kimi security gate.

Policy A itself is the right product lock: wasm still fail-closes pool_input + book_input == hop offer; retail GET nulls hops 1+; POST hybrid_by_hop interiors remain; wrap/native BFS still omits hybrid (H596-7). estimated_amount_out after the strip is LCD-sim of emitted ops, and the dApp strips again plus hop-0 preflight before sign. That part matches #1280 AC1–AC6 / H1280-1–H1280-8.

One actionable regression blocks ACCEPT.

Findings

1. Medium — DB fidelity compares unstripped joint grid to hop-0-only LCD sim

  • Where: indexer/src/api/best_execution.rs:405-419 (strip + keep pre-strip grid_out in DB mode), indexer/src/api/best_execution.rs:209-251 (apply_fidelity_guard), indexer/src/api/best_execution.rs:945-952 (winner fidelity), UI frontend-dapp/src/utils/swapDisclosure.ts:152-161.
  • Impact: Under ROUTE_SOLVER_DB_HYBRID=1 (global_v4, default ROUTE_FIDELITY_DRIFT_BPS=100), joint optimize still scores interior books, then Policy A nulls hops 1+ before maybe_simulate. Fidelity still treats the unstripped mirror grid_out as db_out. If interior books beat pool-only interiors by >1%, a healthy quote is marked fidelity_check=drift, degraded=true, quote_kind=indexer_hybrid_db_degraded. The Swap/Trade chrome then says market data was partially unavailable. The poisoned-mirror guard becomes a Policy A false positive on common ≥2-hop paths (intermediate UST1/USTR books). Execute amount is still the stripped LCD sim (not a fund-theft path), but quote_kind / operator fidelity_drift telemetry are wrong, and quote_kind_for can show hybrid-degraded even after any_book_leg was recomputed false.
  • Reproduction:
    1. Indexer with ROUTE_SOLVER_DB_HYBRID=1, ROUTE_FIDELITY_DRIFT_BPS=100, router LCD mock.
    2. Two-hop seed where hop 1 has a live match-side book the joint grid allocates (book_input > 0).
    3. GET /api/v1/route/solve?token_in=A&token_out=C&amount_in=<hop0 offer>.
    4. Confirm hop 1 hybrid is null (Policy A) and hop 0 still partitions the offer.
    5. If (grid_out - lcd_sim) * 10000 / lcd_sim > 100, response is fidelity_check=drift / indexer_hybrid_db_degraded despite emitted ops matching execute.
    6. Gap: indexer/tests/api_route_solve_db_hybrid.rs has no 2-hop interior-book case asserting fidelity_check=passed after hop-0-only emit. LCD 2-hop tests hide this because grid_out is replaced with out_u when db_mode is false (best_execution.rs:419).
  • Fix: After retail_declared_hybrid_plan_hop0_only, recompute grid_out with propagate_offer_through_plan (or equivalent) on the stripped plan so fidelity compares DB-sim of emitted ops to LCD-sim of emitted ops. Add a DB 2-hop live-book test that stays passed when those two numbers agree.

Residuals (not merge-blocking by themselves)

  • SwapPage.tsx:753-756 swallows HybridHopOfferPartitionError from quote into pool-only fallback (noteIndexerFailure). executeMultiHopSwap still fail-closes before sign. Prefer surfacing HYBRID_HOP0_PARTITION_USER_MESSAGE instead of a silent worse quote.
  • executeMultiHopSwap asserts hop 0 but does not re-strip interiors. Retail GET + quote already strip; wasm still reverts interiors (H1280-8). Optional defense in depth.
  • LocalTerra Playwright (VERIFY_ISSUE_1280_CHAIN / VERIFY_ISSUE_1264_CHAIN) and columbus-5 #1264 AC1 are unchecked as disclosed. Not a code defect in this SHA.
  • No runtime incident under review; Loki was not queried. Coolify indexer fidelity_drift after land would be the live check for finding 1.

Subagent summary

Gate Result
Bugbot 1 medium: DB fidelity vs hop-0-only emit (confirmed)
Security review No medium/high/critical in the diff; wasm invariant unchanged; SWAP_STATE still atomic; wrap hops omit hybrid; POST interiors remain integrator self-grief

What looks correct on this SHA

  • Indexer retail_declared_hybrid_plan_hop0_only + GET tests assert_retail_get_hybrid_hop0_only; POST 3-hop interiors still merge (AC5 / H1280-4).
  • Frontend stripInteriorDeclaredHybrid + assertHop0DeclaredHybridPartitionsOffer on quote and executeMultiHopSwap; wrap path still serializes pool-only hops (router.ts:380-389).
  • Wasm: router_declared_split_mismatch_reverts_hop0, router_two_hop_interior_hybrid_mismatch_reverts; existing router_two_hop_first_leg_hybrid_matches_simulate is T5 (hop 1 hybrid: None executes).
  • Docs/skills H1280-1–H1280-8; wrap envelope not raised.

Repair: recompute stripped-plan grid_out before apply_fidelity_guard and add the DB 2-hop fidelity test. Then re-review this SHA’s successor.

RECOMMEND: FIX Frozen SHA: `b0dade6b7ff114501b110d27c456e6ac0ec7ab21` (PR 1281 HEAD `fix/1280-hybrid-hop-offer`; prompt frozen SHA was unset). Independent review in an isolated worktree vs `origin/main` `cf1eff3252a29e07c42f1544dd9e500a53e5c4b9`. Does not replace test-only verify or the Kimi security gate. Policy A itself is the right product lock: wasm still fail-closes `pool_input + book_input == hop offer`; retail GET nulls hops 1+; POST `hybrid_by_hop` interiors remain; wrap/native BFS still omits `hybrid` (**H596-7**). `estimated_amount_out` after the strip is LCD-sim of emitted ops, and the dApp strips again plus hop-0 preflight before sign. That part matches #1280 AC1–AC6 / **H1280-1–H1280-8**. One actionable regression blocks ACCEPT. ## Findings ### 1. Medium — DB fidelity compares unstripped joint grid to hop-0-only LCD sim - **Where:** `indexer/src/api/best_execution.rs:405-419` (strip + keep pre-strip `grid_out` in DB mode), `indexer/src/api/best_execution.rs:209-251` (`apply_fidelity_guard`), `indexer/src/api/best_execution.rs:945-952` (winner fidelity), UI `frontend-dapp/src/utils/swapDisclosure.ts:152-161`. - **Impact:** Under `ROUTE_SOLVER_DB_HYBRID=1` (`global_v4`, default `ROUTE_FIDELITY_DRIFT_BPS=100`), joint optimize still scores interior books, then Policy A nulls hops 1+ before `maybe_simulate`. Fidelity still treats the **unstripped** mirror `grid_out` as `db_out`. If interior books beat pool-only interiors by >1%, a healthy quote is marked `fidelity_check=drift`, `degraded=true`, `quote_kind=indexer_hybrid_db_degraded`. The Swap/Trade chrome then says market data was partially unavailable. The poisoned-mirror guard becomes a Policy A false positive on common ≥2-hop paths (intermediate UST1/USTR books). Execute amount is still the stripped LCD sim (not a fund-theft path), but quote_kind / operator `fidelity_drift` telemetry are wrong, and `quote_kind_for` can show hybrid-degraded even after `any_book_leg` was recomputed false. - **Reproduction:** 1. Indexer with `ROUTE_SOLVER_DB_HYBRID=1`, `ROUTE_FIDELITY_DRIFT_BPS=100`, router LCD mock. 2. Two-hop seed where hop 1 has a live match-side book the joint grid allocates (`book_input > 0`). 3. `GET /api/v1/route/solve?token_in=A&token_out=C&amount_in=<hop0 offer>`. 4. Confirm hop 1 `hybrid` is null (Policy A) and hop 0 still partitions the offer. 5. If `(grid_out - lcd_sim) * 10000 / lcd_sim > 100`, response is `fidelity_check=drift` / `indexer_hybrid_db_degraded` despite emitted ops matching execute. 6. Gap: `indexer/tests/api_route_solve_db_hybrid.rs` has no 2-hop interior-book case asserting `fidelity_check=passed` after hop-0-only emit. LCD 2-hop tests hide this because `grid_out` is replaced with `out_u` when `db_mode` is false (`best_execution.rs:419`). - **Fix:** After `retail_declared_hybrid_plan_hop0_only`, recompute `grid_out` with `propagate_offer_through_plan` (or equivalent) on the **stripped** plan so fidelity compares DB-sim of emitted ops to LCD-sim of emitted ops. Add a DB 2-hop live-book test that stays `passed` when those two numbers agree. ## Residuals (not merge-blocking by themselves) - `SwapPage.tsx:753-756` swallows `HybridHopOfferPartitionError` from quote into pool-only fallback (`noteIndexerFailure`). `executeMultiHopSwap` still fail-closes before sign. Prefer surfacing `HYBRID_HOP0_PARTITION_USER_MESSAGE` instead of a silent worse quote. - `executeMultiHopSwap` asserts hop 0 but does not re-strip interiors. Retail GET + quote already strip; wasm still reverts interiors (**H1280-8**). Optional defense in depth. - LocalTerra Playwright (`VERIFY_ISSUE_1280_CHAIN` / `VERIFY_ISSUE_1264_CHAIN`) and columbus-5 #1264 AC1 are unchecked as disclosed. Not a code defect in this SHA. - No runtime incident under review; Loki was not queried. Coolify indexer `fidelity_drift` after land would be the live check for finding 1. ## Subagent summary | Gate | Result | | --- | --- | | Bugbot | 1 medium: DB fidelity vs hop-0-only emit (confirmed) | | Security review | No medium/high/critical in the diff; wasm invariant unchanged; SWAP_STATE still atomic; wrap hops omit hybrid; POST interiors remain integrator self-grief | ## What looks correct on this SHA - Indexer `retail_declared_hybrid_plan_hop0_only` + GET tests `assert_retail_get_hybrid_hop0_only`; POST 3-hop interiors still merge (**AC5** / **H1280-4**). - Frontend `stripInteriorDeclaredHybrid` + `assertHop0DeclaredHybridPartitionsOffer` on quote and `executeMultiHopSwap`; wrap path still serializes pool-only hops (`router.ts:380-389`). - Wasm: `router_declared_split_mismatch_reverts_hop0`, `router_two_hop_interior_hybrid_mismatch_reverts`; existing `router_two_hop_first_leg_hybrid_matches_simulate` is T5 (hop 1 `hybrid: None` executes). - Docs/skills **H1280-1–H1280-8**; wrap envelope not raised. Repair: recompute stripped-plan `grid_out` before `apply_fidelity_guard` and add the DB 2-hop fidelity test. Then re-review this SHA’s successor.
fix(routing): recompute DB grid_out after hop-0 hybrid strip
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
f1f5c35ceb
Retail GET Policy A still ranked on joint interior books but fidelity
compared that unstripped grid_out to hop-0-only LCD sim. Recompute
propagate_offer_through_plan on the stripped plan so healthy quotes
stay passed under ROUTE_SOLVER_DB_HYBRID.
Merge branch 'main' into fix/1280-hybrid-hop-offer
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
60427f4975
fix(indexer): keep main USDT sqlx version after hop-0 merge
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline was canceled
8c805ea517
Rename the USDT backfill back to 20260916120000 so production checksum
matches #1282. Protocol fee hops stay 20260916120001.
chore: drop accidental node_modules symlink from hop-0 branch
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
1c815cdc30
Merge branch 'main' into fix/1280-hybrid-hop-offer
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
0f35d0dc8c
Merge branch 'main' into fix/1280-hybrid-hop-offer
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
747b72a7a6
PlasticDigits deleted branch fix/1280-hybrid-hop-offer 2026-09-21 04:00:08 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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
code/cl8y-dex-terraclassic!1281
No description provided.