Off-chain quotes: pass trader wallet for CL8Y fee-discount parity (frontend + indexer) #245

Closed
opened 2026-05-31 04:53:47 +00:00 by PlasticDigits · 33 comments
PlasticDigits commented 2026-05-31 04:53:47 +00:00 (Migrated from gitlab.com)

Reference

Follow-up to contract work in GitLab #238 (gap H1 — on-chain HybridSimulation + router sim now accept optional trader / sender). Off-chain callers still omit trader, so discounted CL8Y holders get full-fee quotes in the dapp and indexer while execution applies tier discounts.

Parent invariant: L8 (docs/contracts-security-audit.md).


Current codebase

Frontend

  • simulateHybridSwap in frontend-dapp/src/services/terraclassic/pair.ts already supports optional { trader, sender } on the LCD query (added in #238), but no call site passes the connected wallet.
  • Direct-pair quotes in TradeMarketOrderPanel.tsx call simulateHybridSwap(...) without trader (line ~247); pool-only path uses simulateSwap (same gap).
  • Multihop preflight in swapRoutePreflight.ts calls simulateHybridSwap per hop without trader.
  • SwapPage.tsx uses indexer getRouteSolve / postRouteSolve and preflightSwapRouteSpread — neither path forwards wallet address.
  • Execute path does use wallet: swap() accepts options.trader; router swaps set trader when executing via trusted router. Discount display already queries registry via feeDiscount.ts / useLimitOrderMakerFeeRates, but quote amounts ignore tier.

Indexer

  • Hybrid optimization in indexer/src/api/hybrid_route_opt.rs builds LCD hybrid_simulation JSON without trader / sender (query_hybrid_sim, ~line 65).
  • Final validation in route_solver.rs maybe_simulate calls router simulate_swap_operations without trader (~line 390).
  • Global best execution in best_execution.rs inherits the same gap through optimize_multihop_hybrid_joint + maybe_simulate.
  • SolveRouteParams / SolveRoutePostBody have no trader field; frontend client.ts does not send one.
  • LCD mock in indexer/tests/common/lcd_mock.rs ignores trader today.

Why this is needed

Registered CL8Y tier holders still see under-quoted estimated_amount_out / receive previews in Trade and Swap flows while on-chain execution (post-#238) charges discounted fees. Symptoms:

  • minimum_receive / slippage floor too high → avoidable reverts or conservative UX
  • Route comparison misleading — indexer “best execution” optimizes splits assuming full fee
  • Quote ≠ execution breaks ADR-0001 retail trust for tier holders

Both layers must pass the same wallet the user will sign with (EOA: trader == sender; router path: distinct sender when applicable).


Constraints / guardrails

  • Backward compatible: omitting trader must keep current full-fee quote behavior (anonymous / integrator callers).
  • Privacy / scope: trader is optional query metadata only — do not log raw addresses at info level; do not require wallet for route discovery (amount_in absent).
  • Router execute semantics: when quoting router multihop, trader should be the beneficiary wallet; sender only when future router-execute preflight distinguishes trusted-router trader forwarding (default sender = trader for EOA direct swaps).
  • Cache keys: indexer hybrid GET cache (hybrid_cache_key in route_solver.rs) must include trader when set, or use separate cache namespace — avoid serving full-fee cached quotes to discounted wallets.
  • No contract changes expected unless audit reveals a gap; this is wiring + API surface.
  • Indexer LCD budget: passing trader must not increase grid size; only add fields to existing queries.
  • Update agent skills: skills/AGENTS_HYBRID_QUOTING.md, skills/AGENTS_FEE_DISCOUNT_TIERS.md, skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md.

Relevant files

Path Role
frontend-dapp/src/services/terraclassic/pair.ts simulateHybridSwap API (trader param exists)
frontend-dapp/src/services/terraclassic/swapRoutePreflight.ts Per-hop LCD preflight
frontend-dapp/src/components/trade/TradeMarketOrderPanel.tsx Trade page market quotes
frontend-dapp/src/pages/SwapPage.tsx Advanced swap + indexer routes
frontend-dapp/src/services/indexer/client.ts Route solve HTTP client
indexer/src/api/route_solver.rs SolveRouteParams, maybe_simulate
indexer/src/api/hybrid_route_opt.rs Per-hop HybridSimulation grid
indexer/src/api/best_execution.rs Global best execution
indexer/tests/common/lcd_mock.rs Integration test LCD stub
indexer/tests/api_route_solve*.rs Route solve regression tests
docs/integrators.md, docs/adr/0001-hybrid-quoting-and-routing.md Integrator docs

A — Frontend preflight quotes

  1. Thread optional trader?: string (and sender? when needed) through:
    • preflightSwapRouteSpread(operations, offerAmount, maxSpread, { trader })
    • simulateSwap / direct simulateHybridSwap call sites in Trade + Swap
  2. When wallet connected, pass address as trader on every pair hybrid_simulation used for amount preview, spread preflight, and min-received calculation.
  3. Pass trader on indexer getRouteSolve / postRouteSolve once indexer accepts it (part B).
  4. Extend pair.test.ts / Trade/Swap unit tests: mocked LCD payload includes trader when wallet present; omitted when disconnected.
  5. Optional UX: disclose in quote line when tier-adjusted (“includes CL8Y fee discount”) — only if discount query confirms registration.

B — Indexer route-solve LCD

  1. Add optional trader: Option<String> and sender: Option<String> to SolveRouteParams and SolveRoutePostBody; document in OpenAPI / utoipa.
  2. Plumb through execute_hybrid_route_solve → best_execution::solve_global_best_execution → hybrid_route_opt::* → query_hybrid_sim.
  3. Plumb through maybe_simulate router JSON (simulate_swap_operations.trader).
  4. Extend hybrid_cache_key (and POST path if cached) with normalized trader (or "none").
  5. Update frontend getRouteSolve / postRouteSolve to send connected wallet as trader query/body field.
  6. LCD mock: when trader present and stub configured with discount tier, return higher return_amount (deterministic test fixture).

Acceptance criteria

  • Connected wallet on Trade market panel: direct hybrid_simulation quotes match executed swap output for registered tier holder (same snapshot), within existing L8 tolerance
  • Connected wallet: preflightSwapRouteSpread passes trader on each hop LCD call
  • Disconnected wallet: quote behavior unchanged (no trader in LCD JSON)
  • Indexer GET/POST /api/v1/route/solve with trader + amount_in: estimated_amount_out reflects discounted fees on all LCD hops
  • Indexer without trader: unchanged full-fee quotes (backward compatible)
  • Hybrid GET cache cannot return a full-fee quote for a discounted trader request
  • OpenAPI / integrator docs updated for new optional params
  • Agent skills cross-linked to this issue

Test plan — all paths

Path Test
Wallet connected, registered tier, direct pair hybrid quote LCD mock asserts trader in payload; receive ≥ undiscounted quote
Wallet connected, unregistered tier Same as no-trader (full fee)
Wallet disconnected No trader field in LCD requests
Multihop preflight (2–3 hops) Each hop query includes same trader
Trade panel pool-only quote trader passed on pool-only hybrid sim
Indexer GET route/solve + amount_in + trader estimated_amount_out uses discounted sim (LCD mock or localnet)
Indexer POST with hybrid_by_hop + trader Router sim JSON includes trader
Indexer GET best execution (/route/solve/best) + trader Same as GET default
Cache: same route, different trader Distinct cache entries / no cross-hit
Frontend → indexer E2E smoke getRouteSolve(..., { trader: address }) end-to-end

Run:

cd frontend-dapp && npm test -- src/services/terraclassic/__tests__/pair.test.ts
cd indexer && cargo test api_route_solve
bash scripts/with-node.sh --cwd frontend-dapp -- npx playwright test e2e/hybrid-swap.spec.ts --project=e2e-tx  # when localnet tier wallet available

Test plan — attack / abuse vectors

Vector Expected
Spoofed trader in indexer query Quote reflects that wallet's tier only (informational; no funds at risk)
Cache poisoning via trader param Normalized key prevents wrong quote reuse
Log injection / oversized trader string Reject non-terra/bech32 or length > chain max; 400 Bad Request
Missing trader on public integrator Full-fee quotes (no regression)
Trader set but registry LCD down Full-fee fallback matches on-chain execute fallback

Verification criteria

  • Unit tests green for frontend pair client + swapRoutePreflight + indexer route solve
  • Manual: connect tier-registered wallet on Trade → quoted receive matches post-swap balance delta (localnet)
  • Manual: indexer GET /api/v1/route/solve?...&trader=terra1... returns higher estimated_amount_out than same call without trader for tier holder
  • QA agents confirm checklist above before close
  • Closes the remaining #238 QA checklist items for frontend + indexer

Depends on

## Reference Follow-up to contract work in [GitLab **#238**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/238) (gap H1 — on-chain `HybridSimulation` + router sim now accept optional `trader` / `sender`). **Off-chain callers still omit `trader`**, so discounted CL8Y holders get full-fee quotes in the dapp and indexer while execution applies tier discounts. Parent invariant: **L8** ([`docs/contracts-security-audit.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/contracts-security-audit.md)). --- ## Current codebase ### Frontend - `simulateHybridSwap` in [`frontend-dapp/src/services/terraclassic/pair.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/services/terraclassic/pair.ts) **already supports** optional `{ trader, sender }` on the LCD query (added in #238), but **no call site passes the connected wallet**. - Direct-pair quotes in [`TradeMarketOrderPanel.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/components/trade/TradeMarketOrderPanel.tsx) call `simulateHybridSwap(...)` without `trader` (line ~247); pool-only path uses `simulateSwap` (same gap). - Multihop preflight in [`swapRoutePreflight.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/services/terraclassic/swapRoutePreflight.ts) calls `simulateHybridSwap` per hop **without `trader`**. - [`SwapPage.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/pages/SwapPage.tsx) uses indexer `getRouteSolve` / `postRouteSolve` and `preflightSwapRouteSpread` — neither path forwards wallet address. - Execute path **does** use wallet: `swap()` accepts `options.trader`; router swaps set `trader` when executing via trusted router. Discount **display** already queries registry via [`feeDiscount.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/services/terraclassic/feeDiscount.ts) / `useLimitOrderMakerFeeRates`, but **quote amounts ignore tier**. ### Indexer - Hybrid optimization in [`indexer/src/api/hybrid_route_opt.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/hybrid_route_opt.rs) builds LCD `hybrid_simulation` JSON **without `trader` / `sender`** (`query_hybrid_sim`, ~line 65). - Final validation in [`route_solver.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/route_solver.rs) `maybe_simulate` calls router `simulate_swap_operations` **without `trader`** (~line 390). - Global best execution in [`best_execution.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/best_execution.rs) inherits the same gap through `optimize_multihop_hybrid_joint` + `maybe_simulate`. - `SolveRouteParams` / `SolveRoutePostBody` have **no `trader` field**; frontend [`client.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/services/indexer/client.ts) does not send one. - LCD mock in [`indexer/tests/common/lcd_mock.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/tests/common/lcd_mock.rs) ignores `trader` today. --- ## Why this is needed Registered CL8Y tier holders still see **under-quoted `estimated_amount_out` / receive previews** in Trade and Swap flows while on-chain execution (post-#238) charges discounted fees. Symptoms: - **`minimum_receive` / slippage floor too high** → avoidable reverts or conservative UX - **Route comparison misleading** — indexer “best execution” optimizes splits assuming full fee - **Quote ≠ execution** breaks ADR-0001 retail trust for tier holders Both layers must pass the same wallet the user will sign with (EOA: `trader == sender`; router path: distinct `sender` when applicable). --- ## Constraints / guardrails - **Backward compatible:** omitting `trader` must keep current full-fee quote behavior (anonymous / integrator callers). - **Privacy / scope:** `trader` is optional query metadata only — do not log raw addresses at info level; do not require wallet for route discovery (`amount_in` absent). - **Router execute semantics:** when quoting router multihop, `trader` should be the **beneficiary wallet**; `sender` only when future router-execute preflight distinguishes trusted-router `trader` forwarding (default `sender = trader` for EOA direct swaps). - **Cache keys:** indexer hybrid GET cache (`hybrid_cache_key` in `route_solver.rs`) must include `trader` when set, or use separate cache namespace — avoid serving full-fee cached quotes to discounted wallets. - **No contract changes** expected unless audit reveals a gap; this is wiring + API surface. - **Indexer LCD budget:** passing `trader` must not increase grid size; only add fields to existing queries. - Update agent skills: [`skills/AGENTS_HYBRID_QUOTING.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_HYBRID_QUOTING.md), [`skills/AGENTS_FEE_DISCOUNT_TIERS.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_FEE_DISCOUNT_TIERS.md), [`skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_INDEXER_HYBRID_BEST_EXECUTION.md). --- ## Relevant files | Path | Role | |------|------| | `frontend-dapp/src/services/terraclassic/pair.ts` | `simulateHybridSwap` API (trader param exists) | | `frontend-dapp/src/services/terraclassic/swapRoutePreflight.ts` | Per-hop LCD preflight | | `frontend-dapp/src/components/trade/TradeMarketOrderPanel.tsx` | Trade page market quotes | | `frontend-dapp/src/pages/SwapPage.tsx` | Advanced swap + indexer routes | | `frontend-dapp/src/services/indexer/client.ts` | Route solve HTTP client | | `indexer/src/api/route_solver.rs` | `SolveRouteParams`, `maybe_simulate` | | `indexer/src/api/hybrid_route_opt.rs` | Per-hop `HybridSimulation` grid | | `indexer/src/api/best_execution.rs` | Global best execution | | `indexer/tests/common/lcd_mock.rs` | Integration test LCD stub | | `indexer/tests/api_route_solve*.rs` | Route solve regression tests | | `docs/integrators.md`, `docs/adr/0001-hybrid-quoting-and-routing.md` | Integrator docs | --- ## Recommended direction ### A — Frontend preflight quotes 1. Thread optional `trader?: string` (and `sender?` when needed) through: - `preflightSwapRouteSpread(operations, offerAmount, maxSpread, { trader })` - `simulateSwap` / direct `simulateHybridSwap` call sites in Trade + Swap 2. When wallet connected, pass `address` as `trader` on **every** pair `hybrid_simulation` used for amount preview, spread preflight, and min-received calculation. 3. Pass `trader` on indexer `getRouteSolve` / `postRouteSolve` once indexer accepts it (part B). 4. Extend `pair.test.ts` / Trade/Swap unit tests: mocked LCD payload includes `trader` when wallet present; omitted when disconnected. 5. Optional UX: disclose in quote line when tier-adjusted (“includes CL8Y fee discount”) — only if discount query confirms registration. ### B — Indexer route-solve LCD 1. Add optional `trader: Option<String>` and `sender: Option<String>` to `SolveRouteParams` and `SolveRoutePostBody`; document in OpenAPI / utoipa. 2. Plumb through `execute_hybrid_route_solve` → `best_execution::solve_global_best_execution` → `hybrid_route_opt::*` → `query_hybrid_sim`. 3. Plumb through `maybe_simulate` router JSON (`simulate_swap_operations.trader`). 4. Extend `hybrid_cache_key` (and POST path if cached) with normalized trader (or `"none"`). 5. Update frontend `getRouteSolve` / `postRouteSolve` to send connected wallet as `trader` query/body field. 6. LCD mock: when `trader` present and stub configured with discount tier, return higher `return_amount` (deterministic test fixture). --- ## Acceptance criteria - [ ] Connected wallet on Trade market panel: direct `hybrid_simulation` quotes match executed swap output for registered tier holder (same snapshot), within existing L8 tolerance - [ ] Connected wallet: `preflightSwapRouteSpread` passes `trader` on each hop LCD call - [ ] Disconnected wallet: quote behavior unchanged (no `trader` in LCD JSON) - [ ] Indexer `GET/POST /api/v1/route/solve` with `trader` + `amount_in`: `estimated_amount_out` reflects discounted fees on all LCD hops - [ ] Indexer without `trader`: unchanged full-fee quotes (backward compatible) - [ ] Hybrid GET cache cannot return a full-fee quote for a discounted `trader` request - [ ] OpenAPI / integrator docs updated for new optional params - [ ] Agent skills cross-linked to this issue --- ## Test plan — all paths | Path | Test | |------|------| | Wallet connected, registered tier, direct pair hybrid quote | LCD mock asserts `trader` in payload; receive ≥ undiscounted quote | | Wallet connected, unregistered tier | Same as no-trader (full fee) | | Wallet disconnected | No `trader` field in LCD requests | | Multihop preflight (2–3 hops) | Each hop query includes same `trader` | | Trade panel pool-only quote | `trader` passed on pool-only hybrid sim | | Indexer GET route/solve + `amount_in` + `trader` | `estimated_amount_out` uses discounted sim (LCD mock or localnet) | | Indexer POST with `hybrid_by_hop` + `trader` | Router sim JSON includes `trader` | | Indexer GET best execution (`/route/solve/best`) + `trader` | Same as GET default | | Cache: same route, different `trader` | Distinct cache entries / no cross-hit | | Frontend → indexer E2E smoke | `getRouteSolve(..., { trader: address })` end-to-end | Run: ```bash cd frontend-dapp && npm test -- src/services/terraclassic/__tests__/pair.test.ts cd indexer && cargo test api_route_solve bash scripts/with-node.sh --cwd frontend-dapp -- npx playwright test e2e/hybrid-swap.spec.ts --project=e2e-tx # when localnet tier wallet available ``` --- ## Test plan — attack / abuse vectors | Vector | Expected | |--------|----------| | Spoofed `trader` in indexer query | Quote reflects that wallet's tier only (informational; no funds at risk) | | Cache poisoning via trader param | Normalized key prevents wrong quote reuse | | Log injection / oversized `trader` string | Reject non-terra/bech32 or length > chain max; 400 Bad Request | | Missing `trader` on public integrator | Full-fee quotes (no regression) | | Trader set but registry LCD down | Full-fee fallback matches on-chain execute fallback | --- ## Verification criteria - [ ] Unit tests green for frontend pair client + swapRoutePreflight + indexer route solve - [ ] Manual: connect tier-registered wallet on Trade → quoted receive matches post-swap balance delta (localnet) - [ ] Manual: indexer `GET /api/v1/route/solve?...&trader=terra1...` returns higher `estimated_amount_out` than same call without `trader` for tier holder - [ ] QA agents confirm checklist above before close - [ ] Closes the remaining #238 QA checklist items for frontend + indexer --- ## Depends on - [GitLab **#238**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/238) (merged — on-chain sim support)
PlasticDigits commented 2026-05-31 05:48:34 +00:00 (Migrated from gitlab.com)

mentioned in commit 746eab9475

mentioned in commit 746eab947569188b644ef6265fe852699d046ead
PlasticDigits commented 2026-05-31 05:48:41 +00:00 (Migrated from gitlab.com)

Implementation summary (merged to main @ 746eab9)

Off-chain quote paths now forward the connected wallet as optional trader so CL8Y tier holders get execute-matching fee discounts in previews (closes the #238 frontend/indexer gap tracked in #245).

Changes

Frontend

  • simulateSwap / simulateHybridSwap / preflightSwapRouteSpread / simulateMultiHopSwap accept optional { trader, sender }.
  • Trade market panel + Swap page pass address as trader on every LCD preflight and indexer getRouteSolve / postRouteSolve when wallet connected.
  • Query cache keys include wallet address so quotes refresh on connect/disconnect.

Indexer

  • Optional trader / sender on GET/POST /api/v1/route/solve (SolveRouteParams, SolveRoutePostBody).
  • Plumbed through execute_hybrid_route_solve → best_execution → hybrid_route_opt → pair HybridSimulation and maybe_simulate router JSON.
  • Hybrid GET cache key includes normalized trader (none when omitted) — no cross-hit between discounted and full-fee quotes.
  • Invalid non-terra1 addresses → 400.

Docs / agent skills

  • L8 invariant row updated in docs/contracts-security-audit.md.
  • docs/integrators.md, docs/indexer-invariants.md, and skills/AGENTS_{HYBRID_QUOTING,FEE_DISCOUNT_TIERS,INDEXER_HYBRID_BEST_EXECUTION}.md cross-linked to #245.

Verification checklist

  • Connect tier-registered wallet on Trade → Market: quoted receive matches post-swap balance delta (localnet)
  • Same wallet on Swap (direct pair + multihop indexer route): receive preview ≥ undiscounted quote
  • Disconnect wallet: LCD payloads omit trader (unchanged full-fee behavior)
  • GET /api/v1/route/solve?...&amount_in=...&trader=terra1... returns higher estimated_amount_out than same call without trader (tier holder)
  • POST /api/v1/route/solve with trader in body: router sim JSON includes trader
  • Two GET requests same route, different trader: distinct cache entries (no full-fee quote served to discounted wallet)
  • Unit tests green:
    • cd frontend-dapp && npm test -- src/services/terraclassic/__tests__/pair.test.ts src/services/terraclassic/__tests__/swapRoutePreflight.test.ts src/services/indexer/__tests__/client.test.ts
    • cd indexer && cargo test --lib quote_trader_tests
    • cd indexer && cargo test --test api_route_solve -- --test-threads=1 (requires dex_indexer_test Postgres)

Follow-ups

  • Optional UX: disclose “includes CL8Y fee discount” on quote line when registration query confirms tier (not in scope for this wiring fix).
  • E2E: bash scripts/with-node.sh --cwd frontend-dapp -- npx playwright test e2e/hybrid-swap.spec.ts --project=e2e-tx when localnet tier wallet available.

@qa-agent-team — please run the checklist above on localnet/staging and confirm before closing #245. Issue left open pending QA sign-off.

## Implementation summary (merged to `main` @ 746eab9) Off-chain quote paths now forward the connected wallet as optional `trader` so CL8Y tier holders get execute-matching fee discounts in previews (closes the #238 frontend/indexer gap tracked in #245). ### Changes **Frontend** - `simulateSwap` / `simulateHybridSwap` / `preflightSwapRouteSpread` / `simulateMultiHopSwap` accept optional `{ trader, sender }`. - Trade market panel + Swap page pass `address` as `trader` on every LCD preflight and indexer `getRouteSolve` / `postRouteSolve` when wallet connected. - Query cache keys include wallet address so quotes refresh on connect/disconnect. **Indexer** - Optional `trader` / `sender` on `GET/POST /api/v1/route/solve` (`SolveRouteParams`, `SolveRoutePostBody`). - Plumbed through `execute_hybrid_route_solve` → `best_execution` → `hybrid_route_opt` → pair `HybridSimulation` and `maybe_simulate` router JSON. - Hybrid GET cache key includes normalized `trader` (`none` when omitted) — no cross-hit between discounted and full-fee quotes. - Invalid non-`terra1` addresses → **400**. **Docs / agent skills** - L8 invariant row updated in `docs/contracts-security-audit.md`. - `docs/integrators.md`, `docs/indexer-invariants.md`, and `skills/AGENTS_{HYBRID_QUOTING,FEE_DISCOUNT_TIERS,INDEXER_HYBRID_BEST_EXECUTION}.md` cross-linked to #245. ### Verification checklist - [ ] Connect tier-registered wallet on **Trade → Market**: quoted receive matches post-swap balance delta (localnet) - [ ] Same wallet on **Swap** (direct pair + multihop indexer route): receive preview ≥ undiscounted quote - [ ] Disconnect wallet: LCD payloads omit `trader` (unchanged full-fee behavior) - [ ] `GET /api/v1/route/solve?...&amount_in=...&trader=terra1...` returns higher `estimated_amount_out` than same call without `trader` (tier holder) - [ ] `POST /api/v1/route/solve` with `trader` in body: router sim JSON includes `trader` - [ ] Two GET requests same route, different `trader`: distinct cache entries (no full-fee quote served to discounted wallet) - [ ] Unit tests green: - `cd frontend-dapp && npm test -- src/services/terraclassic/__tests__/pair.test.ts src/services/terraclassic/__tests__/swapRoutePreflight.test.ts src/services/indexer/__tests__/client.test.ts` - `cd indexer && cargo test --lib quote_trader_tests` - `cd indexer && cargo test --test api_route_solve -- --test-threads=1` (requires `dex_indexer_test` Postgres) ### Follow-ups - Optional UX: disclose “includes CL8Y fee discount” on quote line when registration query confirms tier (not in scope for this wiring fix). - E2E: `bash scripts/with-node.sh --cwd frontend-dapp -- npx playwright test e2e/hybrid-swap.spec.ts --project=e2e-tx` when localnet tier wallet available. --- **@qa-agent-team** — please run the checklist above on localnet/staging and confirm before closing #245. Issue left open pending QA sign-off.
PlasticDigits commented 2026-05-31 06:57:02 +00:00 (Migrated from gitlab.com)

mentioned in issue #238

mentioned in issue #238
PlasticDigits commented 2026-05-31 07:35:23 +00:00 (Migrated from gitlab.com)

mentioned in commit 9704ba7a00

mentioned in commit 9704ba7a006e2667a8ad39bc3550d996cd3c1621
PlasticDigits commented 2026-05-31 07:35:31 +00:00 (Migrated from gitlab.com)

QA verification (@ verify/issue-245 worktree → main @ 9704ba7)

Verified GitLab #245 against main after merge. Implementation from 746eab9 is present; added regression test 9704ba7 for hybrid GET cache key isolation by trader.

Automated verification (pass)

Check Result
frontend-dapp: pair.test.ts, swapRoutePreflight.test.ts, client.test.ts 35/35 pass
indexer: cargo test --test api_route_solve 19/19 pass (incl. route_solve_get_with_trader_returns_higher_estimate, POST router sim trader, invalid trader → 400)
indexer: hybrid_cache_key_includes_trader_or_none unit test pass (new in 9704ba7)
Live indexer accepts trader validation trader=not-a-wallet → 400 trader must be a terra1 bech32 address
Docs / skills cross-links (#245, L8) Present in docs/integrators.md, docs/indexer-invariants.md, docs/contracts-security-audit.md, skills/AGENTS_{HYBRID_QUOTING,FEE_DISCOUNT_TIERS,INDEXER_HYBRID_BEST_EXECUTION}.md

Manual / localnet (blocked — infra not restarted per instructions)

Check Result
LCD hybrid_simulation with trader on deployed pair Fail — LocalTerra pair wasm rejects trader field (unknown field trader); chain image predates #238 on-chain sim support. Pool-only sim without trader succeeds (return_amount=982629).
GET /api/v1/route/solve?...&amount_in=... with vs without trader Blocked — same request returns 400 router simulation failed for the given route and hybrid parameters (no amount_in discovery path works; unrelated to trader wiring).
Trade UI: tier wallet quoted receive vs post-swap delta Not run — requires redeployed #238 contracts + browser pass; left for human QA.

Acceptance criteria status

  • Code + unit/integration tests for frontend preflight, indexer route solve, cache keys
  • Backward compatible (omit trader → full-fee; tests + LCD mock)
  • Invalid / spoofed trader → quote-only, 400 on bad bech32
  • Manual tier-holder Trade quote = execute (localnet)
  • Manual indexer estimated_amount_out higher with trader on live stack with #238 wasm + working router sim

Leaving open until manual checklist is confirmed on a stack with #238 contracts deployed. @brouie — please run Trade/Swap with a tier-registered dev wallet (terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v after scripts/e2e-provision-dev-wallet.sh) after scripts/deploy-dex-local.sh refreshes wasm, then compare indexer route solve with/without &trader=.

Re-verify commands

cd frontend-dapp && npm test -- src/services/terraclassic/__tests__/pair.test.ts src/services/terraclassic/__tests__/swapRoutePreflight.test.ts src/services/indexer/__tests__/client.test.ts
cd indexer && cargo test --test api_route_solve
cd indexer && cargo test hybrid_cache_key_includes_trader --lib
## QA verification (@ verify/issue-245 worktree → `main` @ 9704ba7) Verified GitLab **#245** against `main` after merge. Implementation from **746eab9** is present; added regression test **9704ba7** for hybrid GET cache key isolation by `trader`. ### Automated verification (pass) | Check | Result | |-------|--------| | `frontend-dapp`: `pair.test.ts`, `swapRoutePreflight.test.ts`, `client.test.ts` | **35/35** pass | | `indexer`: `cargo test --test api_route_solve` | **19/19** pass (incl. `route_solve_get_with_trader_returns_higher_estimate`, POST router sim `trader`, invalid `trader` → 400) | | `indexer`: `hybrid_cache_key_includes_trader_or_none` unit test | **pass** (new in 9704ba7) | | Live indexer accepts `trader` validation | `trader=not-a-wallet` → **400** `trader must be a terra1 bech32 address` | | Docs / skills cross-links (#245, L8) | Present in `docs/integrators.md`, `docs/indexer-invariants.md`, `docs/contracts-security-audit.md`, `skills/AGENTS_{HYBRID_QUOTING,FEE_DISCOUNT_TIERS,INDEXER_HYBRID_BEST_EXECUTION}.md` | ### Manual / localnet (blocked — infra not restarted per instructions) | Check | Result | |-------|--------| | LCD `hybrid_simulation` with `trader` on deployed pair | **Fail** — LocalTerra pair wasm rejects `trader` field (`unknown field trader`); chain image predates **#238** on-chain sim support. Pool-only sim **without** `trader` succeeds (`return_amount=982629`). | | `GET /api/v1/route/solve?...&amount_in=...` with vs without `trader` | **Blocked** — same request returns **400** `router simulation failed for the given route and hybrid parameters` (no `amount_in` discovery path works; unrelated to `trader` wiring). | | Trade UI: tier wallet quoted receive vs post-swap delta | **Not run** — requires redeployed #238 contracts + browser pass; left for human QA. | ### Acceptance criteria status - [x] Code + unit/integration tests for frontend preflight, indexer route solve, cache keys - [x] Backward compatible (omit `trader` → full-fee; tests + LCD mock) - [x] Invalid / spoofed `trader` → quote-only, **400** on bad bech32 - [ ] **Manual** tier-holder Trade quote = execute (localnet) - [ ] **Manual** indexer `estimated_amount_out` higher with `trader` on live stack with #238 wasm + working router sim **Leaving open** until manual checklist is confirmed on a stack with **#238** contracts deployed. @brouie — please run Trade/Swap with a tier-registered dev wallet (`terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v` after `scripts/e2e-provision-dev-wallet.sh`) after `scripts/deploy-dex-local.sh` refreshes wasm, then compare indexer route solve with/without `&trader=`. ### Re-verify commands ```bash cd frontend-dapp && npm test -- src/services/terraclassic/__tests__/pair.test.ts src/services/terraclassic/__tests__/swapRoutePreflight.test.ts src/services/indexer/__tests__/client.test.ts cd indexer && cargo test --test api_route_solve cd indexer && cargo test hybrid_cache_key_includes_trader --lib ```
PlasticDigits commented 2026-05-31 07:37:04 +00:00 (Migrated from gitlab.com)

Prerequisite for next manual QA pass: GitLab #238 must be closed (on-chain HybridSimulation / router sim trader support deployed and verified on the target stack) before re-running the open manual checklist items on #245 (Trade/Swap quote vs execute, indexer estimated_amount_out with &trader=). Off-chain wiring in #245 depends on #238 contracts; LocalTerra QA during the last pass still rejected trader on pair hybrid_simulation because wasm predated #238.

**Prerequisite for next manual QA pass:** GitLab **#238** must be **closed** (on-chain `HybridSimulation` / router sim `trader` support deployed and verified on the target stack) before re-running the open manual checklist items on **#245** (Trade/Swap quote vs execute, indexer `estimated_amount_out` with `&trader=`). Off-chain wiring in #245 depends on #238 contracts; LocalTerra QA during the last pass still rejected `trader` on pair `hybrid_simulation` because wasm predated #238.
PlasticDigits commented 2026-05-31 08:54:07 +00:00 (Migrated from gitlab.com)

mentioned in commit 649803920c

mentioned in commit 649803920c0a0b82d0f252141bc0efd0f6a440d5
PlasticDigits commented 2026-05-31 08:54:24 +00:00 (Migrated from gitlab.com)

Re-verification pass (main @ 6498039) — #238 prerequisite satisfied

Confirmed off-chain #245 wiring (merged @ 746eab9, cache-key test @ 9704ba7) on a LocalTerra stack with #238 contracts deployed (#238 closed). Added make verify-issue-245 to automate regression.

What was verified

Layer Result
Frontend unit tests (pair, swapRoutePreflight, indexer client) 35/35 pass
Indexer api_route_solve integration 19/19 pass
Indexer hybrid_cache_key isolates trader pass
Live LCD hybrid_simulation accepts trader; discounted > undiscounted pass (e.g. 945448 → 961912 pool-only)
Live execute return_amount == discounted sim pass (verify-issue-238)
Live indexer GET /route/solve?...&trader= pass (estimate higher with tier wallet)
Invalid trader → 400 pass

QA command

make verify-issue-245   # unit + integration + live stack when LocalTerra + deploy env present

Docs/skills: L8 in docs/contracts-security-audit.md, docs/integrators.md, docs/indexer-invariants.md, skills/AGENTS_{HYBRID_QUOTING,FEE_DISCOUNT_TIERS,INDEXER_HYBRID_BEST_EXECUTION,QA_DEPLOY_VERIFY}.md.

Checklist for sign-off (issue stays open)

  • Trade → Market (connected tier wallet): quoted receive ≈ post-swap balance delta on staging/localnet
  • Swap (direct pair + multihop indexer route): receive preview ≥ undiscounted quote for same inputs
  • Wallet disconnected: LCD/indexer omit trader (full-fee unchanged)
  • Indexer cache: same route, two trader values → distinct quotes (no cross-hit)
  • Run make verify-issue-245 on QA host after make deploy-local

Follow-up (optional, out of scope)

  • UX: show “includes CL8Y fee discount” when registry confirms tier
  • Playwright e2e/hybrid-swap.spec.ts --project=e2e-tx with tier dev wallet

@qa-agent-team — please run the checklist above on staging/localnet and confirm before closing #245.

## Re-verification pass (`main` @ 6498039) — #238 prerequisite satisfied Confirmed off-chain **#245** wiring (merged @ 746eab9, cache-key test @ 9704ba7) on a LocalTerra stack with **#238** contracts deployed (#238 **closed**). Added `make verify-issue-245` to automate regression. ### What was verified | Layer | Result | |-------|--------| | Frontend unit tests (`pair`, `swapRoutePreflight`, indexer `client`) | **35/35** pass | | Indexer `api_route_solve` integration | **19/19** pass | | Indexer `hybrid_cache_key` isolates `trader` | pass | | Live LCD `hybrid_simulation` accepts `trader`; discounted > undiscounted | pass (e.g. 945448 → 961912 pool-only) | | Live execute `return_amount` == discounted sim | pass (`verify-issue-238`) | | Live indexer `GET /route/solve?...&trader=` | pass (estimate higher with tier wallet) | | Invalid `trader` → **400** | pass | ### QA command ```bash make verify-issue-245 # unit + integration + live stack when LocalTerra + deploy env present ``` Docs/skills: L8 in `docs/contracts-security-audit.md`, `docs/integrators.md`, `docs/indexer-invariants.md`, `skills/AGENTS_{HYBRID_QUOTING,FEE_DISCOUNT_TIERS,INDEXER_HYBRID_BEST_EXECUTION,QA_DEPLOY_VERIFY}.md`. ### Checklist for sign-off (issue stays open) - [ ] **Trade → Market** (connected tier wallet): quoted receive ≈ post-swap balance delta on staging/localnet - [ ] **Swap** (direct pair + multihop indexer route): receive preview ≥ undiscounted quote for same inputs - [ ] Wallet **disconnected**: LCD/indexer omit `trader` (full-fee unchanged) - [ ] Indexer cache: same route, two `trader` values → distinct quotes (no cross-hit) - [ ] Run `make verify-issue-245` on QA host after `make deploy-local` ### Follow-up (optional, out of scope) - UX: show “includes CL8Y fee discount” when registry confirms tier - Playwright `e2e/hybrid-swap.spec.ts --project=e2e-tx` with tier dev wallet --- **@qa-agent-team** — please run the checklist above on staging/localnet and confirm before closing **#245**.
PlasticDigits commented 2026-05-31 09:57:13 +00:00 (Migrated from gitlab.com)

Agent QA — Playwright + browser (approved items)

Playwright e2e-tx — 4/4 pass (#245 + hybrid UI)

cd frontend-dapp && npx playwright test e2e/fee-discount-quote-245.spec.ts e2e/hybrid-swap.spec.ts --project=e2e-tx --grep "fee-discount|hybrid book disclosure|execution-aligned"
Test Result
Trade → Market — hybrid_simulation includes trader; executed return_amount == quoted sim pass
Swap — GET /route/solve?...&trader= + router simulate_swap_operations with trader; receive preview renders pass
hybrid-swap.spec.ts UI disclosure + route row (#158) pass

New files: frontend-dapp/e2e/fee-discount-quote-245.spec.ts, e2e/helpers/fee-discount-quote-e2e.ts.

Playwright — hybrid on-chain tx (separate)

hybrid-swap.spec.ts on-chain limit book fill still flakes on this host with “Transaction needed more gas than estimated” after repeated submits (LocalTerra gas estimator; unrelated to #245 trader wiring). UI + #245 tests are stable.

Browser (Cursor)

  • Trade pair page loads on LocalTerra (/trade/terra146…) with chart, tape, and market/limit ticket after risk ack.
  • Full wallet connect + market swap walkthrough is covered by Playwright (simulated dev wallet).

Re-run

make verify-issue-245   # unit + integration + live #238 script
# Playwright (UI + #245):
bash scripts/with-node.sh --cwd frontend-dapp -- npx playwright test e2e/fee-discount-quote-245.spec.ts e2e/hybrid-swap.spec.ts --project=e2e-tx --grep "fee-discount|hybrid book disclosure|execution-aligned"

@qa-agent-team — please confirm Trade market quote=execute and Swap trader on staging; optional: hybrid on-chain tx when gas estimator is stable. Issue remains open for your sign-off.

## Agent QA — Playwright + browser (approved items) ### Playwright `e2e-tx` — **4/4 pass** (#245 + hybrid UI) ```bash cd frontend-dapp && npx playwright test e2e/fee-discount-quote-245.spec.ts e2e/hybrid-swap.spec.ts --project=e2e-tx --grep "fee-discount|hybrid book disclosure|execution-aligned" ``` | Test | Result | |------|--------| | **Trade → Market** — `hybrid_simulation` includes `trader`; executed `return_amount` == quoted sim | **pass** | | **Swap** — `GET /route/solve?...&trader=` + router `simulate_swap_operations` with `trader`; receive preview renders | **pass** | | `hybrid-swap.spec.ts` UI disclosure + route row (#158) | **pass** | New files: `frontend-dapp/e2e/fee-discount-quote-245.spec.ts`, `e2e/helpers/fee-discount-quote-e2e.ts`. ### Playwright — hybrid on-chain tx (separate) `hybrid-swap.spec.ts` **on-chain limit book fill** still flakes on this host with **“Transaction needed more gas than estimated”** after repeated submits (LocalTerra gas estimator; unrelated to #245 `trader` wiring). UI + #245 tests are stable. ### Browser (Cursor) - Trade pair page loads on LocalTerra (`/trade/terra146…`) with chart, tape, and market/limit ticket after risk ack. - Full wallet connect + market swap walkthrough is covered by Playwright (simulated dev wallet). ### Re-run ```bash make verify-issue-245 # unit + integration + live #238 script # Playwright (UI + #245): bash scripts/with-node.sh --cwd frontend-dapp -- npx playwright test e2e/fee-discount-quote-245.spec.ts e2e/hybrid-swap.spec.ts --project=e2e-tx --grep "fee-discount|hybrid book disclosure|execution-aligned" ``` --- **@qa-agent-team** — please confirm Trade market quote=execute and Swap `trader` on staging; optional: hybrid on-chain tx when gas estimator is stable. Issue remains open for your sign-off.
PlasticDigits commented 2026-05-31 09:57:16 +00:00 (Migrated from gitlab.com)

mentioned in commit 6be0a135b1

mentioned in commit 6be0a135b133e9162f0bb87ed3a6abd41c8eebfa
PlasticDigits commented 2026-05-31 12:21:59 +00:00 (Migrated from gitlab.com)

mentioned in issue #251

mentioned in issue #251
Brouie commented 2026-05-31 12:47:30 +00:00 (Migrated from gitlab.com)

qa verified on the QA stack @PlasticDigits — closing path clear from my side.

pulled main, redeployed fresh wasm (deploy stamp git_sha 6be0a13, pair terra189zsa…kkj6q4), ran every QA-runnable layer:

  • frontend unit (pair / swapRoutePreflight / indexer client): 35/35
  • indexer hybrid_cache_key unit: pass
  • indexer api_route_solve integration: 19/19 (incl. route_solve_get_with_trader_returns_higher_estimate, post forwards trader to router sim, invalid trader → 400)
  • verify-issue-238 live stack: 7/7 — [6] indexer GET /route/solve with trader 98916710830 ≥ undiscounted 97190334993, plus execute == discounted sim parity (948594927 == 948594927)

off-chain trader wiring holds end to end on a stack with #238 wasm deployed. frontend e2e-tx I'm leaning on your agent's 4/4 clean-host run — didn't re-run here since this box is the public VPS the frontend guard is meant to block.

one infra thing: had to provision a cl8y_legal postgres role + dex_indexer_test DB on this box before the indexer + integration tests would connect — details in a separate note.

good to close.

qa verified on the QA stack @PlasticDigits — closing path clear from my side. pulled main, redeployed fresh wasm (deploy stamp git_sha 6be0a13, pair terra189zsa…kkj6q4), ran every QA-runnable layer: - frontend unit (pair / swapRoutePreflight / indexer client): 35/35 - indexer hybrid_cache_key unit: pass - indexer api_route_solve integration: 19/19 (incl. route_solve_get_with_trader_returns_higher_estimate, post forwards trader to router sim, invalid trader → 400) - verify-issue-238 live stack: 7/7 — [6] indexer GET /route/solve with trader 98916710830 ≥ undiscounted 97190334993, plus execute == discounted sim parity (948594927 == 948594927) off-chain trader wiring holds end to end on a stack with #238 wasm deployed. frontend e2e-tx I'm leaning on your agent's 4/4 clean-host run — didn't re-run here since this box is the public VPS the frontend guard is meant to block. one infra thing: had to provision a cl8y_legal postgres role + dex_indexer_test DB on this box before the indexer + integration tests would connect — details in a separate note. good to close.
Brouie commented 2026-05-31 12:51:01 +00:00 (Migrated from gitlab.com)

@PlasticDigits separate note on the QA-stack postgres provisioning referenced above.

this box's postgres container only ships the default postgres:postgres superuser — the cl8y_legal role the stack expects didn't exist, so setup-postgres hit a chicken-and-egg (it connects AS cl8y_legal to ensure the DB, but cl8y_legal has to pre-exist) and skipped with a warning. that left the running indexer on stale in-memory creds and failed all api_route_solve integration tests.

fixed on the box: created cl8y_legal (login + superuser, local QA only) and dex_indexer_test owned by it. indexer reconnects clean, integration is 19/19.

might be worth having setup-postgres bootstrap the role via the superuser, or documenting the one-time cl8y_legal creation as a stack prereq.

@PlasticDigits separate note on the QA-stack postgres provisioning referenced above. this box's postgres container only ships the default postgres:postgres superuser — the cl8y_legal role the stack expects didn't exist, so setup-postgres hit a chicken-and-egg (it connects AS cl8y_legal to ensure the DB, but cl8y_legal has to pre-exist) and skipped with a warning. that left the running indexer on stale in-memory creds and failed all api_route_solve integration tests. fixed on the box: created cl8y_legal (login + superuser, local QA only) and dex_indexer_test owned by it. indexer reconnects clean, integration is 19/19. might be worth having setup-postgres bootstrap the role via the superuser, or documenting the one-time cl8y_legal creation as a stack prereq.
PlasticDigits commented 2026-05-31 14:09:59 +00:00 (Migrated from gitlab.com)

Yes should have setup-psotgres bootstrap the role via superuser if needed/available, but also document the cl8y_legal creation as stack prereq

Yes should have setup-psotgres bootstrap the role via superuser if needed/available, but also document the cl8y_legal creation as stack prereq
PlasticDigits commented 2026-05-31 14:21:37 +00:00 (Migrated from gitlab.com)

mentioned in commit 0fd317be99

mentioned in commit 0fd317be99f151855374c2d32e4f027a62da27f2
PlasticDigits commented 2026-05-31 14:21:50 +00:00 (Migrated from gitlab.com)

Postgres bootstrap follow-up (Brouie QA note → merged @ 0fd317b)

Implemented the infra follow-up from the separate Postgres provisioning note on this issue: setup-postgres-dev-databases.sh now bootstraps cl8y_legal via superuser when the role is missing, and PG-1 stack prerequisite is documented for stacks that cannot auto-bootstrap.

What changed

Area Change
scripts/lib/postgres-bootstrap-role.sh New helper: connect as POSTGRES_USER; on failure, connect as POSTGRES_SUPERUSER (default postgres:postgres) and CREATE ROLE cl8y_legal WITH LOGIN CREATEDB (idempotent — never resets existing passwords)
scripts/setup-postgres-dev-databases.sh Calls bootstrap before DB ensure
scripts/lib/postgres-dev.env / .env.example POSTGRES_SUPERUSER + POSTGRES_SUPERUSER_PASSWORD overrides
Docs / skills Invariant PG-1 in docs/indexer-invariants.md; stack prereq in docs/testing.md, docs/local-development.md, scripts/qa/README.md; agent playbooks skills/AGENTS_LOCAL_POSTGRES_DEV.md, skills/AGENTS_QA_DEPLOY_VERIFY.md cross-linked to #245
Regression make test-setup-postgres — static checks + live Docker bootstrap via superuser

Automated verification (pass)

make test-setup-postgres   # static + live bootstrap
./scripts/setup-postgres-dev-databases.sh   # idempotent on compose stack with existing cl8y_legal

Checklist for @qa-agent-team

  • Fresh external Postgres (only postgres:postgres): run ./scripts/setup-postgres-dev-databases.sh — expect bootstrapping role cl8y_legal log, then dex_indexer + dex_indexer_test created
  • Compose stack (existing cl8y_legal): re-run setup — no bootstrap line; DBs unchanged
  • Indexer integration: cd indexer && cargo test --test api_route_solve connects after bootstrap on a host that previously lacked cl8y_legal
  • Locked-down host (no superuser for automation): manual SQL from skills/AGENTS_LOCAL_POSTGRES_DEV.md § Stack prerequisite still documented and required

Issue left open — fee-discount quote sign-off items from prior QA passes remain; this comment addresses only the Postgres infra note.

@qa-agent-team — please verify bootstrap on a QA box that still uses default postgres:postgres (or confirm make test-setup-postgres on CI/agent host).

## Postgres bootstrap follow-up (Brouie QA note → merged @ `0fd317b`) Implemented the infra follow-up from the separate Postgres provisioning note on this issue: **`setup-postgres-dev-databases.sh` now bootstraps `cl8y_legal` via superuser when the role is missing**, and **PG-1 stack prerequisite** is documented for stacks that cannot auto-bootstrap. ### What changed | Area | Change | |------|--------| | **`scripts/lib/postgres-bootstrap-role.sh`** | New helper: connect as `POSTGRES_USER`; on failure, connect as `POSTGRES_SUPERUSER` (default `postgres:postgres`) and `CREATE ROLE cl8y_legal WITH LOGIN CREATEDB` (idempotent — never resets existing passwords) | | **`scripts/setup-postgres-dev-databases.sh`** | Calls bootstrap before DB ensure | | **`scripts/lib/postgres-dev.env` / `.env.example`** | `POSTGRES_SUPERUSER` + `POSTGRES_SUPERUSER_PASSWORD` overrides | | **Docs / skills** | Invariant **PG-1** in `docs/indexer-invariants.md`; stack prereq in `docs/testing.md`, `docs/local-development.md`, `scripts/qa/README.md`; agent playbooks `skills/AGENTS_LOCAL_POSTGRES_DEV.md`, `skills/AGENTS_QA_DEPLOY_VERIFY.md` cross-linked to #245 | | **Regression** | `make test-setup-postgres` — static checks + live Docker bootstrap via superuser | ### Automated verification (pass) ```bash make test-setup-postgres # static + live bootstrap ./scripts/setup-postgres-dev-databases.sh # idempotent on compose stack with existing cl8y_legal ``` ### Checklist for @qa-agent-team - [ ] **Fresh external Postgres** (only `postgres:postgres`): run `./scripts/setup-postgres-dev-databases.sh` — expect `bootstrapping role cl8y_legal` log, then `dex_indexer` + `dex_indexer_test` created - [ ] **Compose stack** (existing `cl8y_legal`): re-run setup — no bootstrap line; DBs unchanged - [ ] **Indexer integration**: `cd indexer && cargo test --test api_route_solve` connects after bootstrap on a host that previously lacked `cl8y_legal` - [ ] **Locked-down host** (no superuser for automation): manual SQL from `skills/AGENTS_LOCAL_POSTGRES_DEV.md` § Stack prerequisite still documented and required Issue left **open** — fee-discount quote sign-off items from prior QA passes remain; this comment addresses only the Postgres infra note. **@qa-agent-team** — please verify bootstrap on a QA box that still uses default `postgres:postgres` (or confirm `make test-setup-postgres` on CI/agent host).
Brouie commented 2026-06-02 14:30:57 +00:00 (Migrated from gitlab.com)

Browser layer verified on the laptop (tunneled to the QA stack, deploy d6701c4). The off-chain trader wiring shows execute-aligned discounts in the dapp for a registered tier holder. This closes out my side of #245 together with the postgres-bootstrap follow-up.

Browser (laptop, frontend tunneled to VPS LCD/RPC/indexer):

  • Connected as a tier-9 (95%) wallet, Trade/Swap EMBER->CORAL, 1 EMBER:
    • FEE line renders 1.80% struck through -> 0.09% (-95%) — the tier discount applied in the quote (0.09% = 1.80% x 5%).
    • YOU RECEIVE 37.22K CORAL connected vs 36.26K disconnected (anonymous, FEE 1.80% (0)). Discounted preview > undiscounted, as expected.
  • Indexer route is exercised: solve?token_in=... (GET /route/solve) fires on the Swap page with the connected wallet; disconnected reverts to full-fee (no discount in quote -> trader not forwarded).
  • Distinct quotes per trader (37.22K vs 36.26K) — no full-fee number served to the discounted wallet (matches the hybrid_cache_key isolation already unit/integration-tested).

How the test wallet got its tier (QA-stack setup, for transparency): the browser pass used a fresh dev/simulated wallet (terra17ks3ncgx9q4q9d2rpfv0uafs732derhxvx0wnt), not test1. From governance (test1) I funded it: 5000 LUNC gas, minted EMBER above the tier-9 threshold, and RegisterWallet -> tier 9. get_discount then returns 9500 for it, and a live pair hybrid_simulation with that trader = 943490813 vs 927342587 undiscounted (+1.741%) — identical to the test1 numbers in verify-issue-238.

Execute==quote parity (the post-swap balance-delta half of the Trade check): I did NOT click-execute in the browser — the Swap page kept routing multihop EMBER->JADE->RUBY->CORAL which hits 100% price impact on thin localnet pools (a liquidity artifact, not a #245 issue). The execute==discounted-sim parity is instead proven at the contract/LCD layer: verify-issue-238 [4] shows executed return_amount 963093608 == discounted sim 963093608 (exact). So the frontend renders the discounted quote, and the chain pays exactly that quote — both layers confirmed, just at different layers.

VPS re-verify on current main (52a865b) alongside the above:

  • frontend unit (pair/swapRoutePreflight/indexer client) 38/38; indexer api_route_solve 19/19 (trader-higher-estimate, POST forwards trader, invalid->400); hybrid_cache_key unit pass.
  • live verify-issue-238 7/7.
  • postgres bootstrap (0fd317b): make test-setup-postgres static + live bootstrap pass (fresh postgres:16 -> cl8y_legal + DBs); idempotent path on the live stack leaves DBs/.env untouched and the indexer healthy. cl8y_legal prereq documented + #245 cross-linked.

From the QA/VPS/browser side this is fully verified — frontend quote wiring, indexer route/solve, cache isolation, execute parity, and the postgres infra follow-up. Good to close when you're satisfied. The only thing not done in-browser is the literal execute click (covered live instead). @PlasticDigits

Browser layer verified on the laptop (tunneled to the QA stack, deploy d6701c4). The off-chain trader wiring shows execute-aligned discounts in the dapp for a registered tier holder. This closes out my side of #245 together with the postgres-bootstrap follow-up. Browser (laptop, frontend tunneled to VPS LCD/RPC/indexer): - Connected as a tier-9 (95%) wallet, Trade/Swap EMBER->CORAL, 1 EMBER: - FEE line renders 1.80% struck through -> 0.09% (-95%) — the tier discount applied in the quote (0.09% = 1.80% x 5%). - YOU RECEIVE 37.22K CORAL connected vs 36.26K disconnected (anonymous, FEE 1.80% (0)). Discounted preview > undiscounted, as expected. - Indexer route is exercised: solve?token_in=... (GET /route/solve) fires on the Swap page with the connected wallet; disconnected reverts to full-fee (no discount in quote -> trader not forwarded). - Distinct quotes per trader (37.22K vs 36.26K) — no full-fee number served to the discounted wallet (matches the hybrid_cache_key isolation already unit/integration-tested). How the test wallet got its tier (QA-stack setup, for transparency): the browser pass used a fresh dev/simulated wallet (terra17ks3ncgx9q4q9d2rpfv0uafs732derhxvx0wnt), not test1. From governance (test1) I funded it: 5000 LUNC gas, minted EMBER above the tier-9 threshold, and RegisterWallet -> tier 9. get_discount then returns 9500 for it, and a live pair hybrid_simulation with that trader = 943490813 vs 927342587 undiscounted (+1.741%) — identical to the test1 numbers in verify-issue-238. Execute==quote parity (the post-swap balance-delta half of the Trade check): I did NOT click-execute in the browser — the Swap page kept routing multihop EMBER->JADE->RUBY->CORAL which hits 100% price impact on thin localnet pools (a liquidity artifact, not a #245 issue). The execute==discounted-sim parity is instead proven at the contract/LCD layer: verify-issue-238 [4] shows executed return_amount 963093608 == discounted sim 963093608 (exact). So the frontend renders the discounted quote, and the chain pays exactly that quote — both layers confirmed, just at different layers. VPS re-verify on current main (52a865b) alongside the above: - frontend unit (pair/swapRoutePreflight/indexer client) 38/38; indexer api_route_solve 19/19 (trader-higher-estimate, POST forwards trader, invalid->400); hybrid_cache_key unit pass. - live verify-issue-238 7/7. - postgres bootstrap (0fd317b): make test-setup-postgres static + live bootstrap pass (fresh postgres:16 -> cl8y_legal + DBs); idempotent path on the live stack leaves DBs/.env untouched and the indexer healthy. cl8y_legal prereq documented + #245 cross-linked. From the QA/VPS/browser side this is fully verified — frontend quote wiring, indexer route/solve, cache isolation, execute parity, and the postgres infra follow-up. Good to close when you're satisfied. The only thing not done in-browser is the literal execute click (covered live instead). @PlasticDigits
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-02 14:43:41 +00:00
Brouie commented 2026-06-04 06:29:26 +00:00 (Migrated from gitlab.com)

mentioned in issue #283

mentioned in issue #283
PlasticDigits commented 2026-06-05 04:08:16 +00:00 (Migrated from gitlab.com)

mentioned in issue #306

mentioned in issue #306
PlasticDigits commented 2026-06-05 11:37:09 +00:00 (Migrated from gitlab.com)

mentioned in merge request !798

mentioned in merge request !798
ghost1 commented 2026-06-05 13:47:55 +00:00 (Migrated from gitlab.com)

mentioned in merge request !809

mentioned in merge request !809
PlasticDigits commented 2026-06-05 13:56:15 +00:00 (Migrated from gitlab.com)

mentioned in issue #335

mentioned in issue #335
PlasticDigits commented 2026-06-05 16:04:17 +00:00 (Migrated from gitlab.com)

mentioned in merge request !821

mentioned in merge request !821
PlasticDigits commented 2026-06-12 04:46:03 +00:00 (Migrated from gitlab.com)

mentioned in issue #361

mentioned in issue #361
PlasticDigits commented 2026-06-12 05:05:46 +00:00 (Migrated from gitlab.com)

mentioned in issue #364

mentioned in issue #364
PlasticDigits commented 2026-06-12 05:26:36 +00:00 (Migrated from gitlab.com)

mentioned in merge request !876

mentioned in merge request !876
PlasticDigits commented 2026-06-26 06:50:19 +00:00 (Migrated from gitlab.com)

mentioned in merge request !947

mentioned in merge request !947
PlasticDigits commented 2026-07-12 07:14:20 +00:00 (Migrated from gitlab.com)

mentioned in issue #476

mentioned in issue #476
PlasticDigits commented 2026-07-13 09:16:07 +00:00 (Migrated from gitlab.com)

mentioned in issue #484

mentioned in issue #484
PlasticDigits commented 2026-08-18 00:44:25 +00:00 (Migrated from gitlab.com)

mentioned in issue #559

mentioned in issue #559
PlasticDigits commented 2026-08-22 11:02:34 +00:00 (Migrated from gitlab.com)

mentioned in issue #595

mentioned in issue #595
PlasticDigits commented 2026-08-22 12:26:36 +00:00 (Migrated from gitlab.com)

mentioned in issue #597

mentioned in issue #597
PlasticDigits commented 2026-08-24 00:35:17 +00:00 (Migrated from gitlab.com)

mentioned in issue #615

mentioned in issue #615
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#245
No description provided.