design(indexer): remaining GET /route/solve failure modes after wrap-enter and mixed-decimal quotes #1265

Closed
opened 2026-09-14 22:13:52 +00:00 by PlasticDigits · 2 comments

Summary

Research / decision only — do not implement indexer solver, Swap quote, wrap-mapper, or CosmWasm changes in this issue.

Question: after the known wrap-enter ranking work and the mixed-decimal CW20 quote tickets, which additional GET /api/v1/route/solve failure modes still exist on retail Swap/Trade (wrong path, implausible output, no path, timeout, client BFS fallback), and which of those are already owned vs still uncatalogued.

Bundle into one investigation (do not split indexer vs frontend vs wrap vs Trade):

  1. Inventory every retail quote path that can skip, timeout, or disagree with GET /api/v1/route/solve (quoteCw20ViaRouteSolve vs findRouteWithNativeSupport / simulateNativeSwap vs client BFS fallback).
  2. Probe listed hub pairs at a few human sizes (quote-only is enough): native wrap-enter, unwrap-exit, 6-dec hub CW20s, mixed 6/18 product CW20s, direct 1-hop, and a distant ≥3-hop pair. Record path (intermediate_tokens), estimated_amount_out vs wallet sim, worst-hop spread, quote_kind, hop count vs GET_DEFAULT_MAX_HOPS (4), and whether the client timed out.
  3. Decide whether remaining pain is (A) already filed (#1218 / #1257 / #1264), (B) a new ranking/scale/cache class that needs its own bug ticket, (C) latency/progress only (#484 / #485), or (D) no further implement spawn. Spawn separate implement issues only after that decision.

Related, not duplicates (keyword overlap on “route” / “solve” is not enough):

  • #1218 (open) — native wrap-enter never reaches GET /route/solve; hop-count / top-K ranking drops wrap-then-cUSTC on LUNC→USTR (and wrap-enter LUNC→listed CL8Y). Specific ranking + native I/O mapping bug. This census must not fold those constraints into a sibling implement ticket.
  • #1257 (open) — listed USTR→USDT You Receive is size-inconsistent through UST1/cLUNC (mixed 18/6 hops). CW20 path does hit /route/solve. Scale/identity/sim honesty, not wrap-enter.
  • #1264 (open) — USTC→USTR wrap+2hop execute gas. Not quote ranking.
  • #1256 (open) — typeable You Receive on wrap/native. Reverse-quote UX; not solver census.
  • #484 (closed) — Swap stuck on Calculating… (refetch pile-up vs slow distant solve). UI state machine; follow-up latency is #485.
  • #485 — distant-pair p95 <15s + GET /route/solve/progress. Latency/UX, not wrong-path ranking.
  • #209 / #191 / #323 — unified global best-execution, default hops, cap 3→4. Shipped solver; not a remaining-failure memo.
  • #493 / #369 — empty-book short-circuit; skip zero-reserve candidates.
  • #615 — tax-aware estimated_amount_out_net ranking.
  • #690 / #691 — other-DEX v2 LP + IBC USDC intermediate. Extra edges; still one winner path.
  • #1203 (open) — parallel split routing across paths. Feature spike; not a failure census.
  • #1222 (closed) — remaining gas envelope vs wallet auto-fee. Execute fees, not solve ranking.

Current codebase

docs/route-solver.md is the integrator contract. Optimized GET /api/v1/route/solve with amount_in (and pool_only not true):

  1. Load assets + pairs from Postgres; graph nodes are CW20 contract ids only. Native uluna / uusd without a wrap-mapper substitution are not indexer-routable.
  2. find_paths_top_k — ≤ MAX_PATH_CANDIDATES (5) simple paths, ≤ GET_DEFAULT_MAX_HOPS (4), shortest-first.
  3. Per path: optimize_multihop_hybrid_joint (17-point book grid + 2 coordinate-descent passes). Empty-book hops short-circuit (#493).
  4. Winner = max estimated_amount_out_net (#615). estimated_amount_out stays pre-tax raw_out.
  5. OPTIMALITY_SCOPE: optimal within top-5 shortest simple paths + per-hop hybrid grid — not a global optimum; paths outside top-5 are never scored.
  6. Cache: 12s direct / 90s distant; key includes solver version, tokens, AMOUNT_CACHE_BUCKET (1_000_000 raw), maker-fill bucket, discount/tax identity. Trader address is not keyed.

Retail Swap/Trade:

Path When Solve?
CW20 pay + CW20 receive quoteCw20ViaRouteSolve Yes — GET /api/v1/route/solve then wallet simulateMultiHopSwap + hop preflight
Native pay or receive findRouteWithNativeSupport → first BFS after wrap substitution → simulateNativeSwap No — #1218. Execute stays pool-only (H596-7)
Indexer timeout / 404 / 502 Client BFS + extra LCD Fallback can disagree with indexer top-K
Discovery GET (no amount_in) / POST First BFS only Not the retail amount quote

You Receive is wallet sim humanized with getDecimals / fromRawAmount. Slippage chrome: >5% banner, >30% Expert, ≥99% extreme (#678). A 100% hop spread or 10^9-scale receive that still paints is a quote honesty failure.

Why this work is needed

  1. Retail “routing is wrong / quote is insane / Calculating forever” is several mechanisms. #1218 and #1257 each own one. A remaining-failure census prevents a third vague ticket from hijacking those, and prevents a new implement issue that only restates hop-count bias.
  2. MAX_PATH_CANDIDATES = 5 + shortest-first is a documented non-goal of global optimality. Some “failures” are in-scope of that bound (need a product decision: raise K vs reject ~100% spread vs map natives into solve). Others are bugs (decimals, cache alias, identity).
  3. Native wrap, mixed 6/18, and CW20-only graph are easy to conflate. The memo must keep H596-7, #1218, and #1257 as separate columns.

Constraints / guardrails

  • Docs / memo only. No MAX_PATH_CANDIDATES / hop-cap / cache-bucket / wrap-mapping / Swap quote-path change in this MR. No CosmWasm migrate. No other-DEX edges (#690) as the “fix.”
  • Do not attach hybrid to native wrap+multihop execute (H596-7). Direct 1:1 wrap/unwrap stays mapper-only.
  • Do not treat ticker USDT / USTR as identity. Contract / denom only (#715). Gems stay hidden (#562).
  • Fail-closed recommendations only: drop ~100% hop-spread winners, reject ask-out > ask reserve, do not paint theater You Receive. Do not recommend skipping #678 / 5–30–99 / blacklist / pause / freeze.
  • AMOUNT_CACHE_BUCKET must not be “fixed” here; the memo may recommend a later ticket if 18-dec vs 6-dec keys alias.
  • Copy (#489): no lecture banner “we ignored a better route.” Research may recommend silent fail-closed vs route-chip honesty for a later issue.
  • No operational leakage: public indexer JSON fields and columbus-5 pair ids only. No hosts, deploy pipelines, keys, or queue ids.
  • Anonymize people. Cite issue numbers, not private chats.

Relevant files

Path Why
indexer/src/api/best_execution.rs Winner ranking, SOLVER_VERSION_*, OPTIMALITY_SCOPE, LCD budget
indexer/src/api/route_paths.rs find_paths_top_k, hop-first top-K
indexer/src/api/route_solver.rs GET/POST handlers, hop caps, cache TTL / AMOUNT_CACHE_BUCKET
indexer/src/api/hybrid_route_opt.rs Joint hybrid grid
indexer/src/api/route_graph.rs Graph snapshot cache
indexer/tests/api_route_solve.rs Winner-not-shortest and API fixtures
frontend-dapp/src/utils/cw20RouteSolveQuote.ts Indexer solve + wallet sim
frontend-dapp/src/pages/SwapPage.tsx Native vs CW20 quote branch, You Receive
frontend-dapp/src/services/indexer/client.ts getRouteSolve timeout / AbortSignal
frontend-dapp/src/utils/swapRouting.ts Client BFS / wrap substitution
frontend-dapp/src/utils/swapRouteSlippage.ts 5 / 30 / 99 vs spot_amount_out
frontend-dapp/src/services/terraclassic/swapRoutePreflight.ts Per-hop spread; submit disable
docs/route-solver.md Optimality scope, non-goals, cache, progress
docs/adr/0002-global-best-execution-route-solver.md Decision record
skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md Solver playbook
skills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.md Route chip vs ops
scripts/check_route_solver_docs.py Constant drift guard
  1. Write ADR docs/adr/NNNN-route-solve-remaining-failures.md (Status: Proposed) plus a one-line pointer from docs/README.md and a “remaining failures” subsection pointer in docs/route-solver.md (no constant edits).
  2. Table each retail quote family vs: hits /route/solve? wrap-mapped? decimals per hop; last observed failure class (or “honest within OPTIMALITY_SCOPE”).
  3. Score remaining gaps:
ID Gap class Default hypothesis (confirm or kill) Owner if already filed
F0 Native wrap-enter / unwrap-exit skip solve First BFS after wrap; indexer never sees wrap-then-cUSTC #1218
F1 Mixed 6/18 humanize / sim scale 18-dec raw into 6-dec pool or inverse #1257
F2 Hop-count top-K drops better longer CW20 path Same bias as #1218 but already on /route/solve Census — spawn only if a listed CW20 pair proves it
F3 ~100% hop spread still wins No reject on dust/empty ask reserve May be #1218/#1257; spawn if a third pair class
F4 AMOUNT_CACHE_BUCKET aliases 18-dec vs 6-dec Distinct human sizes share a cache key Unfiled unless F1 already covers it
F5 Client timeout → BFS disagrees with indexer 15–45s abort + fallback path #484/#485 if only hang; else new bug
F6 Discovery/POST first-BFS used by mistake Retail amount quote must be optimized GET Unfiled if a call site still does this
F7 Frozen / paused / zero-reserve hop still scored F6 freeze + #369 skip should already drop Confirm or spawn
F8 Trade market vs Swap quote drift Same solve helper vs a second path Unfiled if Trade diverges
F9 Identity / colliding ticker token_out not unique-symbol winner #715 / #1257 carve-out
F10 Execute gas on wrap+N-hop Quote OK, broadcast OOG #1264 / #1222
  1. Decision: Stay (document only) / Spawn implement issue(s) for confirmed unfiled F2–F9 with pair ids, sizes, and expected fail-closed behavior. Do not sneak a solver-constant bump into this MR. Do not reopen #1218/#1257 from this census unless the ADR proves they are the wrong owner (they are not).
  2. Verify script scripts/qa/verify-issue-NNN.sh: required ADR headings present; no production best_execution.rs / route_paths.rs / cw20RouteSolveQuote.ts / SwapPage.tsx quote-branch edits in the research MR.

What would make a later implement issue ready

A follow-up bug or feature issue may be opened only when the ADR names:

  • Exact token ids (contract / denom, not ticker), human sizes, and whether the path is native wrap vs CW20 solve.
  • Observed vs honest path / output (or “in-scope of OPTIMALITY_SCOPE — product decision to raise K”).
  • Files to change (indexer ranking or frontend wrap-mapping or decimals — not all three guessed).
  • Tests: Rust route-solve fixture and/or Vitest quote helper with pinned pair graph; no live-mainnet-only acceptance.
  • Explicit non-goals (no hybrid on native execute, no #690 foreign hops as the fix, no split routing #1203).

Until that exists, this issue stays research. No Given/When/Then. No ready. No /agent implement.

Research acceptance criteria

  • AC1. One ADR covering quote-path inventory + failure-class table F0–F10 — not split across wrap vs CW20 vs Trade tickets.
  • AC2. Dated probes or an explicit “unmeasured” row per family; do not recycle #1218 LUNC→USTR numbers as proof for unrelated CW20 pairs.
  • AC3. F0–F10 scored; one product decision (stay vs spawn). Filed tickets stay owners of F0/F1/F10.
  • AC4. #1203, #690, #1222, #1264 explicitly out of this census.
  • AC5. Docs-only MR: no production solver/quote-path diff.
  • AC6. Follow-up implement issues listed only for confirmed unfiled gaps; otherwise “no impl spawn.”

Test plan (research paths)

Document and process tests, not chain deploys or solver patches.

ID Path How to test
P1 Inventory Swap + Trade market quote branches Call-graph: native vs quoteCw20ViaRouteSolve vs BFS fallback
P2 Wrap-enter Native LUNC → hub CW20 Confirm still F0 / #1218; do not “fix” here
P3 Mixed 18/6 Listed USTR → listed USDT Confirm still F1 / #1257; do not fold
P4 CW20 4-hop Two listed 6-dec CW20s with a longer cheaper path If top-K drops it, that is F2 evidence
F5 Timeout Distant pair (JADE-family class) Time to JSON vs client abort; progress endpoint existence (#485)
P6 Cache 1 vs 100 human on an 18-dec offer Same cache key? Document F4
P7 Slippage chrome Any ~100% hop-spread winner Does ≥99% / submit-disable fire?
P8 Trade lockstep Same ids on / vs Trade market Same token_in / token_out / hops

Negative / skip:

  • N1. Memo that only says “routing is bad” with no F0–F10 table → fail.
  • N2. Implementing #1218 or #1257 inside this research MR → fail AC5.
  • N3. Recommending other-DEX hops (#690) or path-split (#1203) as the remaining-failure fix → fail.
  • N4. Adding GWT / ready / /agent implement to this issue → fail (research).

Test plan (attack, hack, and abuse)

Research must evaluate each vector. Do not write exploits, PoCs, or attack procedures.

ID Vector What the memo must show Fail if
A1 Theater quote Implausible You Receive as a screenshot “price” Fail-closed / 99% block Treats 10^9-scale out as “deep liquidity”
A2 Cache poison Bucket alias across decimals / tiers Cache key components from route-solver.md Shares quotes across tax/discount identity
A3 Path spoof Route chip ≠ submitted ops Display vs router_operations (#450 / SEC-I02) Recommends showing a path the tx will not take
A4 LCD / progress DoS Progress poll or solve fan-out Existing RATE_LIMIT_LCD_HEAVY_RPS New unmetered progress/solve endpoint
A5 Frozen hop Frozen code_id still routed F6 skip / 404 frozen-only Recommends quoting frozen-only markets
A6 Hybrid on wrap Attach book to native wrap+N-hop H596-7 Recommends hybrid native execute to “fix ranking”

Verification criteria

  • ADR merged (or posted as the sole deliverable) with AC1–AC6 checked in the MR description.
  • scripts/qa/verify-issue-NNN.sh asserts required headings and that production solver/quote files are untouched (python3 scripts/check_route_solver_docs.py still green).
  • Reviewers can see, for each F-row, either a public solve JSON summary (ids + hops + amounts) or “unmeasured.”
  • No follow-up implement issue is opened unless the ADR decision is not stay and the gap is not already #1218 / #1257 / #1264.

Out of scope

  • Implementing wrap-enter mapping or hop-count ranking (#1218).
  • Implementing USTR→USDT decimal/scale honesty (#1257).
  • Wrap+2hop execute gas (#1264) or the gas census (#1222).
  • Parallel split routing (#1203).
  • Other-DEX v2 hops (#690) / IBC USDC intermediate (#691).
  • Exact-out / indexer reverse solve (#713 out of scope).
  • Deploy, migrate, host, SKU, or runtime model choice.

First-pass model recommendation

Recommendation: grok-high

Rationale: This is a research census across the indexer global solver (best_execution.rs, route_paths.rs, route_solver.rs cache), Swap/Trade quote branches (cw20RouteSolveQuote.ts, native wrap BFS), mixed-decimal hops, and slippage fail-closed chrome. That is more than three production files, crosses routing/quote protocol (not a local known edit), and has no deterministic product patch in this ticket. Composer criteria fail on investigation-only scope, file/subsystem count, and uncertain remaining failure classes. Verification is an ADR plus a docs-only check script, not a unit-test-only ranking change. Policy: research stays grok-high; this is advisory planning text, not a runner argument or permission to execute.

## Summary Research / decision only — **do not implement** indexer solver, Swap quote, wrap-mapper, or CosmWasm changes in this issue. Question: after the known wrap-enter ranking work and the mixed-decimal CW20 quote tickets, **which additional `GET /api/v1/route/solve` failure modes still exist** on retail Swap/Trade (wrong path, implausible output, no path, timeout, client BFS fallback), and which of those are already owned vs still uncatalogued. Bundle into **one** investigation (do not split indexer vs frontend vs wrap vs Trade): 1. **Inventory** every retail quote path that can skip, timeout, or disagree with `GET /api/v1/route/solve` (`quoteCw20ViaRouteSolve` vs `findRouteWithNativeSupport` / `simulateNativeSwap` vs client BFS fallback). 2. **Probe** listed hub pairs at a few human sizes (quote-only is enough): native wrap-enter, unwrap-exit, 6-dec hub CW20s, mixed 6/18 product CW20s, direct 1-hop, and a distant ≥3-hop pair. Record path (`intermediate_tokens`), `estimated_amount_out` vs wallet sim, worst-hop spread, `quote_kind`, hop count vs `GET_DEFAULT_MAX_HOPS` (4), and whether the client timed out. 3. **Decide** whether remaining pain is (A) already filed (#1218 / #1257 / #1264), (B) a new ranking/scale/cache class that needs its own bug ticket, (C) latency/progress only (#484 / #485), or (D) no further implement spawn. Spawn **separate** implement issues only after that decision. Related, **not duplicates** (keyword overlap on “route” / “solve” is not enough): - [#1218](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1218) (open) — native wrap-enter never reaches `GET /route/solve`; hop-count / top-K ranking drops wrap-then-cUSTC on LUNC→USTR (and wrap-enter LUNC→listed CL8Y). Specific ranking + native I/O mapping bug. This census must **not** fold those constraints into a sibling implement ticket. - [#1257](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1257) (open) — listed USTR→USDT You Receive is size-inconsistent through UST1/cLUNC (mixed 18/6 hops). CW20 path **does** hit `/route/solve`. Scale/identity/sim honesty, not wrap-enter. - [#1264](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1264) (open) — USTC→USTR wrap+2hop **execute gas**. Not quote ranking. - [#1256](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1256) (open) — typeable You Receive on wrap/native. Reverse-quote UX; not solver census. - [#484](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/484) (closed) — Swap stuck on Calculating… (refetch pile-up vs slow distant solve). UI state machine; follow-up latency is #485. - [#485](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/485) — distant-pair p95 &lt;15s + `GET /route/solve/progress`. Latency/UX, not wrong-path ranking. - [#209](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/209) / [#191](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/191) / [#323](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/323) — unified global best-execution, default hops, cap 3→4. Shipped solver; not a remaining-failure memo. - [#493](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/493) / [#369](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/369) — empty-book short-circuit; skip zero-reserve candidates. - [#615](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/615) — tax-aware `estimated_amount_out_net` ranking. - [#690](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/690) / [#691](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/691) — other-DEX v2 LP + IBC USDC intermediate. Extra edges; still one winner path. - [#1203](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1203) (open) — parallel **split** routing across paths. Feature spike; not a failure census. - [#1222](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1222) (closed) — remaining **gas envelope** vs wallet auto-fee. Execute fees, not solve ranking. ## Current codebase `docs/route-solver.md` is the integrator contract. Optimized `GET /api/v1/route/solve` with `amount_in` (and `pool_only` not true): 1. Load assets + pairs from Postgres; graph nodes are **CW20 contract ids only**. Native `uluna` / `uusd` without a wrap-mapper substitution are not indexer-routable. 2. `find_paths_top_k` — ≤ `MAX_PATH_CANDIDATES` (5) simple paths, ≤ `GET_DEFAULT_MAX_HOPS` (4), shortest-first. 3. Per path: `optimize_multihop_hybrid_joint` (17-point book grid + 2 coordinate-descent passes). Empty-book hops short-circuit (#493). 4. Winner = max `estimated_amount_out_net` (#615). `estimated_amount_out` stays pre-tax `raw_out`. 5. `OPTIMALITY_SCOPE`: optimal **within** top-5 shortest simple paths + per-hop hybrid grid — not a global optimum; paths outside top-5 are never scored. 6. Cache: 12s direct / 90s distant; key includes solver version, tokens, `AMOUNT_CACHE_BUCKET` (1_000_000 raw), maker-fill bucket, discount/tax identity. Trader address is not keyed. Retail Swap/Trade: | Path | When | Solve? | | --- | --- | --- | | CW20 pay + CW20 receive | `quoteCw20ViaRouteSolve` | Yes — `GET /api/v1/route/solve` then wallet `simulateMultiHopSwap` + hop preflight | | Native pay or receive | `findRouteWithNativeSupport` → first BFS after wrap substitution → `simulateNativeSwap` | **No** — #1218. Execute stays pool-only (H596-7) | | Indexer timeout / 404 / 502 | Client BFS + extra LCD | Fallback can disagree with indexer top-K | | Discovery GET (no `amount_in`) / POST | First BFS only | Not the retail amount quote | You Receive is wallet sim humanized with `getDecimals` / `fromRawAmount`. Slippage chrome: &gt;5% banner, &gt;30% Expert, ≥99% extreme (#678). A 100% hop spread or 10^9-scale receive that still paints is a quote honesty failure. ## Why this work is needed 1. Retail “routing is wrong / quote is insane / Calculating forever” is several mechanisms. #1218 and #1257 each own one. A remaining-failure census prevents a third vague ticket from hijacking those, and prevents a new implement issue that only restates hop-count bias. 2. `MAX_PATH_CANDIDATES = 5` + shortest-first is a documented non-goal of global optimality. Some “failures” are in-scope of that bound (need a product decision: raise K vs reject ~100% spread vs map natives into solve). Others are bugs (decimals, cache alias, identity). 3. Native wrap, mixed 6/18, and CW20-only graph are easy to conflate. The memo must keep H596-7, #1218, and #1257 as separate columns. ## Constraints / guardrails - **Docs / memo only.** No `MAX_PATH_CANDIDATES` / hop-cap / cache-bucket / wrap-mapping / Swap quote-path change in this MR. No CosmWasm migrate. No other-DEX edges (#690) as the “fix.” - Do **not** attach hybrid to native wrap+multihop execute (H596-7). Direct 1:1 wrap/unwrap stays mapper-only. - Do not treat ticker `USDT` / `USTR` as identity. Contract / denom only (#715). Gems stay hidden (#562). - Fail-closed recommendations only: drop ~100% hop-spread winners, reject ask-out &gt; ask reserve, do not paint theater You Receive. Do not recommend skipping #678 / 5–30–99 / blacklist / pause / freeze. - `AMOUNT_CACHE_BUCKET` must not be “fixed” here; the memo may **recommend** a later ticket if 18-dec vs 6-dec keys alias. - Copy (#489): no lecture banner “we ignored a better route.” Research may recommend silent fail-closed vs route-chip honesty for a later issue. - No operational leakage: public indexer JSON fields and columbus-5 pair ids only. No hosts, deploy pipelines, keys, or queue ids. - Anonymize people. Cite issue numbers, not private chats. ## Relevant files | Path | Why | | --- | --- | | `indexer/src/api/best_execution.rs` | Winner ranking, `SOLVER_VERSION_*`, `OPTIMALITY_SCOPE`, LCD budget | | `indexer/src/api/route_paths.rs` | `find_paths_top_k`, hop-first top-K | | `indexer/src/api/route_solver.rs` | GET/POST handlers, hop caps, cache TTL / `AMOUNT_CACHE_BUCKET` | | `indexer/src/api/hybrid_route_opt.rs` | Joint hybrid grid | | `indexer/src/api/route_graph.rs` | Graph snapshot cache | | `indexer/tests/api_route_solve.rs` | Winner-not-shortest and API fixtures | | `frontend-dapp/src/utils/cw20RouteSolveQuote.ts` | Indexer solve + wallet sim | | `frontend-dapp/src/pages/SwapPage.tsx` | Native vs CW20 quote branch, You Receive | | `frontend-dapp/src/services/indexer/client.ts` | `getRouteSolve` timeout / AbortSignal | | `frontend-dapp/src/utils/swapRouting.ts` | Client BFS / wrap substitution | | `frontend-dapp/src/utils/swapRouteSlippage.ts` | 5 / 30 / 99 vs `spot_amount_out` | | `frontend-dapp/src/services/terraclassic/swapRoutePreflight.ts` | Per-hop spread; submit disable | | `docs/route-solver.md` | Optimality scope, non-goals, cache, progress | | `docs/adr/0002-global-best-execution-route-solver.md` | Decision record | | `skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md` | Solver playbook | | `skills/AGENTS_FRONTEND_SWAP_ROUTE_DISPLAY.md` | Route chip vs ops | | `scripts/check_route_solver_docs.py` | Constant drift guard | ## Recommended direction 1. Write ADR `docs/adr/NNNN-route-solve-remaining-failures.md` (Status: **Proposed**) plus a one-line pointer from `docs/README.md` and a “remaining failures” subsection pointer in `docs/route-solver.md` (no constant edits). 2. Table each retail quote family vs: hits `/route/solve`? wrap-mapped? decimals per hop; last observed failure class (or “honest within OPTIMALITY_SCOPE”). 3. Score remaining gaps: | ID | Gap class | Default hypothesis (confirm or kill) | Owner if already filed | | --- | --- | --- | --- | | F0 | Native wrap-enter / unwrap-exit skip solve | First BFS after wrap; indexer never sees wrap-then-cUSTC | #1218 | | F1 | Mixed 6/18 humanize / sim scale | 18-dec raw into 6-dec pool or inverse | #1257 | | F2 | Hop-count top-K drops better longer CW20 path | Same bias as #1218 but **already on** `/route/solve` | Census — spawn only if a listed CW20 pair proves it | | F3 | ~100% hop spread still wins | No reject on dust/empty ask reserve | May be #1218/#1257; spawn if a third pair class | | F4 | `AMOUNT_CACHE_BUCKET` aliases 18-dec vs 6-dec | Distinct human sizes share a cache key | Unfiled unless F1 already covers it | | F5 | Client timeout → BFS disagrees with indexer | 15–45s abort + fallback path | #484/#485 if only hang; else new bug | | F6 | Discovery/POST first-BFS used by mistake | Retail amount quote must be optimized GET | Unfiled if a call site still does this | | F7 | Frozen / paused / zero-reserve hop still scored | F6 freeze + #369 skip should already drop | Confirm or spawn | | F8 | Trade market vs Swap quote drift | Same solve helper vs a second path | Unfiled if Trade diverges | | F9 | Identity / colliding ticker | `token_out` not unique-symbol winner | #715 / #1257 carve-out | | F10 | Execute gas on wrap+N-hop | Quote OK, broadcast OOG | #1264 / #1222 | 4. Decision: **Stay** (document only) / **Spawn implement issue(s)** for confirmed unfiled F2–F9 with pair ids, sizes, and expected fail-closed behavior. Do not sneak a solver-constant bump into this MR. Do not reopen #1218/#1257 from this census unless the ADR proves they are the wrong owner (they are not). 5. Verify script `scripts/qa/verify-issue-NNN.sh`: required ADR headings present; no production `best_execution.rs` / `route_paths.rs` / `cw20RouteSolveQuote.ts` / `SwapPage.tsx` quote-branch edits in the research MR. ## What would make a later implement issue ready A follow-up **bug** or **feature** issue may be opened only when the ADR names: - Exact token ids (contract / denom, not ticker), human sizes, and whether the path is native wrap vs CW20 solve. - Observed vs honest path / output (or “in-scope of OPTIMALITY_SCOPE — product decision to raise K”). - Files to change (indexer ranking **or** frontend wrap-mapping **or** decimals — not all three guessed). - Tests: Rust route-solve fixture and/or Vitest quote helper with pinned pair graph; no live-mainnet-only acceptance. - Explicit non-goals (no hybrid on native execute, no #690 foreign hops as the fix, no split routing #1203). Until that exists, this issue stays research. **No Given/When/Then. No `ready`. No `/agent implement`.** ## Research acceptance criteria - AC1. One ADR covering quote-path inventory + failure-class table F0–F10 — not split across wrap vs CW20 vs Trade tickets. - AC2. Dated probes or an explicit “unmeasured” row per family; do not recycle #1218 LUNC→USTR numbers as proof for unrelated CW20 pairs. - AC3. F0–F10 scored; one product decision (stay vs spawn). Filed tickets stay owners of F0/F1/F10. - AC4. #1203, #690, #1222, #1264 explicitly out of this census. - AC5. Docs-only MR: no production solver/quote-path diff. - AC6. Follow-up implement issues listed only for confirmed **unfiled** gaps; otherwise “no impl spawn.” ## Test plan (research paths) Document and process tests, not chain deploys or solver patches. | ID | Path | How to test | | --- | --- | --- | | P1 Inventory | Swap + Trade market quote branches | Call-graph: native vs `quoteCw20ViaRouteSolve` vs BFS fallback | | P2 Wrap-enter | Native LUNC → hub CW20 | Confirm still F0 / #1218; do not “fix” here | | P3 Mixed 18/6 | Listed USTR → listed USDT | Confirm still F1 / #1257; do not fold | | P4 CW20 4-hop | Two listed 6-dec CW20s with a longer cheaper path | If top-K drops it, that is F2 evidence | | F5 Timeout | Distant pair (JADE-family class) | Time to JSON vs client abort; progress endpoint existence (#485) | | P6 Cache | 1 vs 100 human on an 18-dec offer | Same cache key? Document F4 | | P7 Slippage chrome | Any ~100% hop-spread winner | Does ≥99% / submit-disable fire? | | P8 Trade lockstep | Same ids on `/` vs Trade market | Same `token_in` / `token_out` / hops | Negative / skip: - N1. Memo that only says “routing is bad” with no F0–F10 table → fail. - N2. Implementing #1218 or #1257 inside this research MR → fail AC5. - N3. Recommending other-DEX hops (#690) or path-split (#1203) as the remaining-failure fix → fail. - N4. Adding GWT / `ready` / `/agent implement` to this issue → fail (research). ## Test plan (attack, hack, and abuse) Research must **evaluate** each vector. Do **not** write exploits, PoCs, or attack procedures. | ID | Vector | What the memo must show | Fail if | | --- | --- | --- | --- | | A1 Theater quote | Implausible You Receive as a screenshot “price” | Fail-closed / 99% block | Treats 10^9-scale out as “deep liquidity” | | A2 Cache poison | Bucket alias across decimals / tiers | Cache key components from `route-solver.md` | Shares quotes across tax/discount identity | | A3 Path spoof | Route chip ≠ submitted ops | Display vs `router_operations` (#450 / SEC-I02) | Recommends showing a path the tx will not take | | A4 LCD / progress DoS | Progress poll or solve fan-out | Existing `RATE_LIMIT_LCD_HEAVY_RPS` | New unmetered progress/solve endpoint | | A5 Frozen hop | Frozen `code_id` still routed | F6 skip / 404 frozen-only | Recommends quoting frozen-only markets | | A6 Hybrid on wrap | Attach book to native wrap+N-hop | H596-7 | Recommends hybrid native execute to “fix ranking” | ## Verification criteria - ADR merged (or posted as the sole deliverable) with AC1–AC6 checked in the MR description. - `scripts/qa/verify-issue-NNN.sh` asserts required headings and that production solver/quote files are untouched (`python3 scripts/check_route_solver_docs.py` still green). - Reviewers can see, for each F-row, either a public solve JSON summary (ids + hops + amounts) or “unmeasured.” - No follow-up implement issue is opened unless the ADR decision is not stay **and** the gap is not already #1218 / #1257 / #1264. ## Out of scope - Implementing wrap-enter mapping or hop-count ranking (#1218). - Implementing USTR→USDT decimal/scale honesty (#1257). - Wrap+2hop execute gas (#1264) or the gas census (#1222). - Parallel split routing (#1203). - Other-DEX v2 hops (#690) / IBC USDC intermediate (#691). - Exact-out / indexer reverse solve (#713 out of scope). - Deploy, migrate, host, SKU, or runtime model choice. ## First-pass model recommendation Recommendation: grok-high Rationale: This is a **research** census across the indexer global solver (`best_execution.rs`, `route_paths.rs`, `route_solver.rs` cache), Swap/Trade quote branches (`cw20RouteSolveQuote.ts`, native wrap BFS), mixed-decimal hops, and slippage fail-closed chrome. That is more than three production files, crosses routing/quote protocol (not a local known edit), and has no deterministic product patch in this ticket. Composer criteria fail on investigation-only scope, file/subsystem count, and uncertain remaining failure classes. Verification is an ADR plus a docs-only check script, not a unit-test-only ranking change. Policy: research stays grok-high; this is advisory planning text, not a runner argument or permission to execute.
Author
Owner

Merged as PR #1289. ADR 0007 Stay; no solver impl spawn. F0 stays #1218 (now also merged). Wallet sim vs estimated_amount_out still unmeasured as designed.

Leftover ops: #1300

Merged as PR #1289. ADR 0007 Stay; no solver impl spawn. F0 stays #1218 (now also merged). Wallet sim vs estimated_amount_out still unmeasured as designed. Leftover ops: https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/1300
Author
Owner

Verified 2026-09-24 on origin/main 54c4868e: make verify-issue-1265 passes 9/9, including R-CENSUS-1–8, constants/docs lockstep, cross-links, and no production solver or quote-path diff. ADR 0007 accepts the documented unmeasured wallet-simulation comparison for the docs-only Stay; no solver issue is warranted. Census implementation PR #1289 is merged and post-merge tracker #1300 is closed. Separate F0 follow-up remains under #1218 (PR #1297); #1257 and #1264 are closed. Newer adjacent Swap route-display verification is tracked separately in #1330.

Verified 2026-09-24 on origin/main 54c4868e: make verify-issue-1265 passes 9/9, including R-CENSUS-1–8, constants/docs lockstep, cross-links, and no production solver or quote-path diff. ADR 0007 accepts the documented unmeasured wallet-simulation comparison for the docs-only Stay; no solver issue is warranted. Census implementation PR #1289 is merged and post-merge tracker #1300 is closed. Separate F0 follow-up remains under #1218 (PR #1297); #1257 and #1264 are closed. Newer adjacent Swap route-display verification is tracked separately in #1330.
Sign in to join this conversation.
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#1265
No description provided.