feat: V3 Grid product — /v3 UI, indexer stats, Pool/Limits links (from #546) #619

Open
opened 2026-08-24 03:15:44 +00:00 by PlasticDigits · 6 comments
PlasticDigits commented 2026-08-24 03:15:44 +00:00 (Migrated from gitlab.com)

Summary

Ship the V3 Grid product surface: route /v3, copy “V3 Grid”, indexer APIs for per-grid revenue / APR estimates, and short links from /pool and /limits. Users deposit and withdraw; inventory growth is handled by vault Rebalance. Do not add Claim, Reinvest, or Rebalance buttons.

Parent: #546 option E, ship scope S4, economics E2, surface /v3.

Blocked by #617 (instantiate / deposit / withdraw / query shapes). The keeper package (#618 once opened) is not required for the UI to ship (keeper is ops); do not wait on tip→LUNC to land /v3.

Current codebase

Layer Behavior today
Routes App.tsx has /pool, /limits, /trade, /portfolio, /charts, /protocol, … — no /v3. Unknown paths Navigate to /.
Nav navItems.ts: primary Swap / Pool / Limits / Trade / Portfolio / Charts. More folds at < 1200px (#136, #483). No V3 Grid item.
/limits LimitOrdersPage.tsx Single vs Ladder. Ladder is one-sided, one-shot (LimitOrderLadderPanel.tsx). Crossing guard is client-only (#297, #385). Disconnect still shows create fields (#494).
/pool v2 provide/withdraw + catalog. Opt-in how-to PoolLpHowto / poolLpHowtoCopy.ts (H531-4 no incentive APR; H531-6 limits ≠ LP shares).
Indexer Pair/trader limit-fills, limit-book, hub prices, pair 24h volume, external oracle (#515). No grid/vault catalog, no per-grid PnL windows, no 95% APR preview.
Fees Pair place half from escrow; I13 tier table. Unregistered place = 90 bps on a 180 bps pair. Vault owner ≠ user wallet for GetDiscount.
Copy rules #489: no always-on essays on primary cards. Risk modal #138 / clickwrap #517 still apply.

There is no range+spread+both-sides recycle UX. Power users would bot; #546 chose a vault + /v3 instead.

Why this is needed

  1. Architecture item 5 without a dedicated page is an integrator-only vault. S4 is a retail product: pair, range, spread (1–2500 bps), inventory, estimated APR, and realized revenue windows.
  2. Users looking for “V3” / concentrated-range liquidity today land on v2 /pool. A named /v3 plus links from Pool and Limits steers them to the grid without pretending the vault mints LP shares.
  3. Claim / Reinvest buttons were rejected on #546: surplus is compounded by permissionless Rebalance. The UI must not re-introduce a harvest loop that fights the vault.

Constraints / guardrails

  1. Route /v3. Page title and primary heading V3 Grid. Do not name the page “LP”, “V3 LP”, or “concentrated liquidity shares.”
  2. Links, not lectures. /pool and /limits get a short same-origin link to /v3 (one line / button). Do not add always-on essays on Swap/Trade (#489). Pool how-to may add one sentence + link; keep H531-4 (no incentive program) and H531-6 (book escrow ≠ pool share).
  3. No Claim / Reinvest / Rebalance CTAs. Deposit (create / add inventory), update range/spread (beneficiary), Withdraw (full exit). Do not expose a retail crank button. Advanced users can still execute CosmWasm via a wallet; the official dApp must not present it.
  4. Spread 1–2500 bps, shown as a percent grid fee. E2 preview: buy at P → sell at P*(1+s); sell at P*(1+s) → buy back at P (no drift).
  5. Break-even vs place-fee. Gate or hard-block spreads that cannot clear the vault’s effective place-fee + estimated gas (unregistered 90 bps is the conservative default if vault GetDiscount is unknown). Do not let a 1 bps spread submit when place-fee is 90 bps (A12 on #546).
  6. APR chrome is an estimate, not a promise. 95% probability band from spread, pair volatility, volume, and the connected wallet’s CL8Y tier (display). Must disclose that on-chain place discount is the vault’s. NFA / risk modal unchanged. No farm/points.
  7. Revenue windows per grid: daily / weekly / monthly / all-time — realized indexer fills + vault fee attrs, not mark-to-market fantasy.
  8. Disconnect: create/preview still visible (#494 parity). Submit → Connect Wallet.
  9. Pause / blacklist / clickwrap banners same as /limits / /pool.
  10. Decimals L20 for range prices (UST1/USTR).
  11. Retail gems hidden (#562).
  12. Do not change pair wasm in this issue. Do not call this v2 LP in forbiddenPoolLpHowtoCopyHits.
  13. Nav: add V3 Grid → /v3 to MORE_NAV_ITEMS (header already tight at 1200px). Do not stuff a seventh primary tab unless a follow-up measures overflow. Pool/Limits links are the discovery path.
  14. Verify in the browser (or Playwright) for /v3, /pool link, /limits link — not screenshot-only.

Relevant files

File Role
frontend-dapp/src/App.tsx Add /v3 (+ optional /v3/:vaultAddr)
frontend-dapp/src/components/common/navItems.ts More menu item
frontend-dapp/src/pages/PoolPage.tsx Link to /v3
frontend-dapp/src/utils/poolLpHowtoCopy.ts Optional one-line V3 Grid pointer; forbidden-copy test
frontend-dapp/src/pages/LimitOrdersPage.tsx Link to /v3 from Single/Ladder chrome
frontend-dapp/src/components/trade/LimitOrderLadderPanel.tsx Do not turn Ladder into V3 Grid; link out
frontend-dapp/src/utils/limitOrderNonCrossing.ts Preview post-only / crossing
frontend-dapp/src/utils/limitOrderLadder.ts Equal-rung math; L20 scale at submit
indexer/src/api/mod.rs Register new routes
indexer/src/api/traders.rs / pairs.rs Existing fill APIs to extend or sibling
indexer/src/indexer/parser.rs Columnar wasm ingest for vault attrs
docs/frontend.md IA + copy invariants
docs/design-system.md Terminology glossary
skills/AGENTS_FRONTEND_COPY_COGNITIVE_LOAD.md #489
skills/AGENTS_FRONTEND_POOL_LP_HOWTO.md #531 — do not conflate LP and grid
skills/AGENTS_LIMIT_PRICE_DECIMALS.md L20
Vault crate (sibling) Queries: position, unclaimed fees, live order ids

Indexer (same issue — product is unusable without it)

  1. Ingest vault/factory events: instantiate, deposit, withdraw, grid_rebalance, tip_paid, fee pot. Columnar zip like limit placements — do not use wasm_attr_last.
  2. Attribute pair limit_fills where maker == vault to that grid.
  3. Persist per-vault realized PnL in quote/base and USD (hub prices) for 24h / 7d / 30d / all-time.
  4. Pair stats for the preview model: realized vol (existing 24h/30d), a documented volatility estimator (e.g. σ of log returns from candles already indexed — #568). No live CoinGecko requirement (#579).
  5. APIs (names indicative):
    • GET /api/v1/v3/preview?pair=&spread_bps=&range=…&tier= → estimated 95% APR band + break-even vs place-fee (stateless).
    • GET /api/v1/v3/vaults?beneficiary= → user’s grids.
    • GET /api/v1/v3/vaults/{addr} → live params, idle inventory, unclaimed fees, revenue windows, open order ids.
  6. Preview APR is heuristic + disclaimer, fail closed to null on missing candles — do not invent a number.

dApp

  1. New V3GridPage at /v3. Pair picker (catalog rank #534, gems hidden). Inputs: range, spread 1–2500 bps, rung count, deposit amounts. Preview rungs + E2 pairing + estimated APR band.
  2. Connected: list my vaults from indexer (LCD fallback for balances). Withdraw full exit. Add inventory if the vault supports top-up. Update spread/range only when the vault allows (may require idle / after withdraw — follow vault msgs; do not invent a silent in-place rebuild that crosses).
  3. Copy: V3 Grid — “Places a buy↔sell limit grid in your range. Spread profit stays in the vault and is put back to work automatically.” One line that this is not a pool share.
  4. /pool: text button/link V3 Grid → /v3 near provide chrome or how-to. /limits: same near Single/Ladder tabs (“Need a range that flips? V3 Grid”).
  5. Gas preflight for deposit/withdraw using new terraGas.ts keys (do not fall through to BASE_GAS_LIMIT — #475).
  6. Tests: Vitest page + copy forbidden-hits; Playwright smoke /v3 render + pool/limits links (5 workers). make verify-issue-<iid>.

Acceptance criteria

  • AC1 — /v3. Route renders V3 Grid; wildcard still does not 404-loop.
  • AC2 — Create preview. Pair + range + spread (1–2500) + rungs + inventory; E2 no-drift pairing visible; disconnect still shows fields.
  • AC3 — Deposit / withdraw. Beneficiary can open and fully exit. No Claim / Reinvest / Rebalance buttons (getByRole / testid absent).
  • AC4 — Spread vs fee gate. Sub-break-even spread blocked or confirmed with an explicit loss warning that still prefers block for unregistered 90 bps vs tiny spread.
  • AC5 — Revenue + APR. Indexer windows + preview band with NFA; missing data → omit number, do not show 0% as a fake APR.
  • AC6 — Links. /pool and /limits each have a same-origin /v3 link. More nav lists V3 Grid.
  • AC7 — Copy. Forbidden phrases in how-to / V3 page tests: incentive program, LP shares, “V3 LP”. Allowed: V3 Grid.
  • AC8 — Guards. Pause, blacklist, clickwrap, L20, gem hide, #489 (no Swap lecture).
  • AC9 — Verify. make verify-issue-<iid> (indexer tests + frontend unit + optional Playwright).
  • AC10 — Browser/Playwright. Exercise create-preview, link navigation from Pool and Limits, withdraw CTA presence, crank-button absence.

Test plan (all paths)

# Path Assert
T1 GET /v3 disconnected Form visible; submit opens wallet modal
T2 Spread 0 / 2501 Invalid; cannot submit
T3 Spread 1 bps, vault unregistered place 90 bps Blocked (or equivalent hard gate)
T4 E2 preview Bid 1.00 + 200 bps → ask 1.02; ask fill preview restores 1.00
T5 Deposit tx (LocalTerra / mock) Vault instantiate or deposit msg; no pair mixed-side batch from UI
T6 Withdraw Cancel+return path; no leftover Claim CTA
T7 Query data-testid / role No claim, reinvest, rebalance retail buttons
T8 /pool link Navigates to /v3
T9 /limits link Navigates to /v3; Ladder panel still one-sided
T10 More nav V3 Grid present; 768–1199 header More still usable
T11 Indexer preview missing candles APR field hidden / “Estimate unavailable”
T12 Indexer vault list Only beneficiary’s vaults
T13 Pause / blacklist Same banners as Limits; submit disabled
T14 UST1/USTR pair Human range; raw prices scaled (L20)
T15 Gem pair Hidden in production picker (#562)
T16 forbiddenPoolLpHowtoCopyHits Still empty after how-to edit
T17 Revenue 24h / 7d / 30d / all-time Render from API; empty grid → zeros or em dash, not NaN

Test plan — attack, hack, and abuse vectors

# Vector What to prove
A1 XSS / innerHTML Static React; vault addr in URL sanitized (same as /trade/:pairAddr).
A2 Fake APR Preview cannot show a number without indexer fields; no hard-coded 95% APY marketing.
A3 Wrong vault Withdraw/deposit msgs use the selected vault address; cannot drain another beneficiary.
A4 Indexer lie Balances / OrderStatus for withdraw gates prefer LCD; stale “active” does not block honest withdraw (parity #530).
A5 Crossing preview UI does not encourage a spread that crosses mid; vault still post-only.
A6 Gem / phishing pair Retail catalog filters; paste-in custom pair follows Create Pair allowlist rules if offered.
A7 Cognitive overload Pool/Limits links are not multi-paragraph lectures.
A8 Hidden crank No “Advanced Rebalance” that re-introduces the rejected buttons.
A9 Fee-drain UX Tiny spread + high place-fee cannot one-click destroy inventory.
A10 Open-redirect Links are same-origin /v3 only.
A11 Clickwrap / NFA bypass /v3 still behind existing legal gates on dex.cl8y.com.

Verification criteria

  1. Product review: /v3 is findable from Pool and Limits without reading wasm.
  2. No Claim / Reinvest / Rebalance in the shipped UI.
  3. Docs + playbook (AGENTS_FRONTEND_V3_GRID.md) + make verify-issue-<iid>.
  4. Playwright or browser pass on the three routes (Pool → V3, Limits → V3, V3 preview).
  5. Indexer schema migration is versioned; empty DB tests do not fail the whole suite.

Out of scope

  • Vault / factory wasm (sibling).
  • grid-keeper binary and tip→LUNC (sibling).
  • Turning /limits Ladder into an auto-flip bot.
  • Uniswap-v3 AMM, v2 LP math, farms.
  • In-swap flip.
  • Putting V3 Grid into the /pool provide card as if it minted LP tokens.
## Summary Ship the **V3 Grid product surface**: route **`/v3`**, copy **“V3 Grid”**, indexer APIs for per-grid revenue / APR estimates, and **short links** from `/pool` and `/limits`. Users deposit and withdraw; **inventory growth is handled by vault `Rebalance`**. **Do not** add Claim, Reinvest, or Rebalance buttons. Parent: [#546](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/546) option **E**, ship scope **S4**, economics **E2**, surface **`/v3`**. **Blocked by** [#617](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/617) (instantiate / deposit / withdraw / query shapes). The keeper package ([#618](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/618) once opened) is **not** required for the UI to ship (keeper is ops); do not wait on tip→LUNC to land `/v3`. ## Current codebase | Layer | Behavior today | |-------|----------------| | **Routes** | [`App.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/App.tsx) has `/pool`, `/limits`, `/trade`, `/portfolio`, `/charts`, `/protocol`, … — **no `/v3`**. Unknown paths `Navigate` to `/`. | | **Nav** | [`navItems.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/components/common/navItems.ts): primary Swap / Pool / Limits / Trade / Portfolio / Charts. More folds at `< 1200px` ([#136](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/136), [#483](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/483)). No V3 Grid item. | | **`/limits`** | [`LimitOrdersPage.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/pages/LimitOrdersPage.tsx) **Single** vs **Ladder**. Ladder is **one-sided**, one-shot ([`LimitOrderLadderPanel.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/components/trade/LimitOrderLadderPanel.tsx)). Crossing guard is **client-only** ([#297](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/297), [#385](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/385)). Disconnect still shows create fields ([#494](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/494)). | | **`/pool`** | v2 provide/withdraw + catalog. Opt-in how-to [`PoolLpHowto`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/components/pool/PoolLpHowto.tsx) / [`poolLpHowtoCopy.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/utils/poolLpHowtoCopy.ts) (**H531-4** no incentive APR; **H531-6** limits ≠ LP shares). | | **Indexer** | Pair/trader `limit-fills`, `limit-book`, hub prices, pair 24h volume, external oracle ([#515](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/515)). **No** grid/vault catalog, no per-grid PnL windows, no 95% APR preview. | | **Fees** | Pair place half from escrow; **I13** tier table. Unregistered place = 90 bps on a 180 bps pair. Vault owner ≠ user wallet for `GetDiscount`. | | **Copy rules** | [#489](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/489): no always-on essays on primary cards. Risk modal [#138](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/138) / clickwrap [#517](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/517) still apply. | There is **no** range+spread+both-sides recycle UX. Power users would bot; #546 chose a vault + `/v3` instead. ## Why this is needed 1. Architecture item 5 without a dedicated page is an integrator-only vault. S4 is a **retail product**: pair, range, spread (1–2500 bps), inventory, estimated APR, and realized revenue windows. 2. Users looking for “V3” / concentrated-range liquidity today land on **v2 `/pool`**. A named **`/v3`** plus links from Pool and Limits steers them to the grid **without** pretending the vault mints LP shares. 3. Claim / Reinvest buttons were rejected on #546: surplus is compounded by permissionless `Rebalance`. The UI must not re-introduce a harvest loop that fights the vault. ## Constraints / guardrails 1. **Route `/v3`.** Page title and primary heading **V3 Grid**. Do not name the page “LP”, “V3 LP”, or “concentrated liquidity shares.” 2. **Links, not lectures.** `/pool` and `/limits` get a **short** same-origin link to `/v3` (one line / button). Do **not** add always-on essays on Swap/Trade ([#489](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/489)). Pool how-to may add **one** sentence + link; keep **H531-4** (no incentive program) and **H531-6** (book escrow ≠ pool share). 3. **No Claim / Reinvest / Rebalance CTAs.** Deposit (create / add inventory), update range/spread (beneficiary), **Withdraw** (full exit). Do not expose a retail crank button. Advanced users can still execute CosmWasm via a wallet; the official dApp must not present it. 4. **Spread 1–2500 bps**, shown as a percent grid fee. E2 preview: buy at `P` → sell at `P*(1+s)`; sell at `P*(1+s)` → buy back at `P` (no drift). 5. **Break-even vs place-fee.** Gate or hard-block spreads that cannot clear the **vault’s** effective place-fee + estimated gas (unregistered 90 bps is the conservative default if vault `GetDiscount` is unknown). Do not let a 1 bps spread submit when place-fee is 90 bps (**A12** on #546). 6. **APR chrome is an estimate, not a promise.** 95% probability band from spread, pair volatility, volume, and the **connected wallet’s** CL8Y tier (display). Must disclose that **on-chain place discount is the vault’s**. NFA / risk modal unchanged. **No farm/points.** 7. **Revenue windows** per grid: daily / weekly / monthly / all-time — **realized** indexer fills + vault fee attrs, not mark-to-market fantasy. 8. **Disconnect:** create/preview still visible ([#494](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/494) parity). Submit → Connect Wallet. 9. **Pause / blacklist / clickwrap** banners same as `/limits` / `/pool`. 10. **Decimals L20** for range prices (UST1/USTR). 11. **Retail gems hidden** ([#562](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/562)). 12. **Do not change pair wasm** in this issue. Do not call this v2 LP in `forbiddenPoolLpHowtoCopyHits`. 13. **Nav:** add **V3 Grid** → `/v3` to `MORE_NAV_ITEMS` (header already tight at 1200px). Do **not** stuff a seventh primary tab unless a follow-up measures overflow. Pool/Limits links are the discovery path. 14. **Verify in the browser** (or Playwright) for `/v3`, `/pool` link, `/limits` link — not screenshot-only. ## Relevant files | File | Role | |------|------| | [`frontend-dapp/src/App.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/App.tsx) | Add `/v3` (+ optional `/v3/:vaultAddr`) | | [`frontend-dapp/src/components/common/navItems.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/components/common/navItems.ts) | More menu item | | [`frontend-dapp/src/pages/PoolPage.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/pages/PoolPage.tsx) | Link to `/v3` | | [`frontend-dapp/src/utils/poolLpHowtoCopy.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/utils/poolLpHowtoCopy.ts) | Optional one-line V3 Grid pointer; forbidden-copy test | | [`frontend-dapp/src/pages/LimitOrdersPage.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/pages/LimitOrdersPage.tsx) | Link to `/v3` from Single/Ladder chrome | | [`frontend-dapp/src/components/trade/LimitOrderLadderPanel.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/components/trade/LimitOrderLadderPanel.tsx) | Do **not** turn Ladder into V3 Grid; link out | | [`frontend-dapp/src/utils/limitOrderNonCrossing.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/utils/limitOrderNonCrossing.ts) | Preview post-only / crossing | | [`frontend-dapp/src/utils/limitOrderLadder.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/utils/limitOrderLadder.ts) | Equal-rung math; **L20** scale at submit | | [`indexer/src/api/mod.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/mod.rs) | Register new routes | | [`indexer/src/api/traders.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/traders.rs) / [`pairs.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/pairs.rs) | Existing fill APIs to extend or sibling | | [`indexer/src/indexer/parser.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/indexer/parser.rs) | Columnar wasm ingest for vault attrs | | [`docs/frontend.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/frontend.md) | IA + copy invariants | | [`docs/design-system.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/design-system.md) | Terminology glossary | | [`skills/AGENTS_FRONTEND_COPY_COGNITIVE_LOAD.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_FRONTEND_COPY_COGNITIVE_LOAD.md) | #489 | | [`skills/AGENTS_FRONTEND_POOL_LP_HOWTO.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_FRONTEND_POOL_LP_HOWTO.md) | #531 — do not conflate LP and grid | | [`skills/AGENTS_LIMIT_PRICE_DECIMALS.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_LIMIT_PRICE_DECIMALS.md) | **L20** | | Vault crate (sibling) | Queries: position, unclaimed fees, live order ids | ## Recommended direction ### Indexer (same issue — product is unusable without it) 1. Ingest vault/factory events: instantiate, deposit, withdraw, `grid_rebalance`, tip_paid, fee pot. Columnar zip like limit placements — do not use `wasm_attr_last`. 2. Attribute pair `limit_fills` where `maker == vault` to that grid. 3. Persist per-vault realized PnL in quote/base and USD (hub prices) for 24h / 7d / 30d / all-time. 4. Pair stats for the **preview** model: realized vol (existing 24h/30d), a documented volatility estimator (e.g. σ of log returns from candles already indexed — [#568](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/568)). No live CoinGecko requirement ([#579](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/579)). 5. APIs (names indicative): - `GET /api/v1/v3/preview?pair=&spread_bps=&range=…&tier=` → estimated 95% APR band + break-even vs place-fee (stateless). - `GET /api/v1/v3/vaults?beneficiary=` → user’s grids. - `GET /api/v1/v3/vaults/{addr}` → live params, idle inventory, unclaimed fees, revenue windows, open order ids. 6. Preview APR is **heuristic + disclaimer**, fail closed to `null` on missing candles — do not invent a number. ### dApp 1. New `V3GridPage` at `/v3`. Pair picker (catalog rank [#534](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/534), gems hidden). Inputs: range, spread 1–2500 bps, rung count, deposit amounts. Preview rungs + E2 pairing + estimated APR band. 2. Connected: list my vaults from indexer (LCD fallback for balances). **Withdraw** full exit. **Add inventory** if the vault supports top-up. **Update** spread/range only when the vault allows (may require idle / after withdraw — follow vault msgs; do not invent a silent in-place rebuild that crosses). 3. Copy: **V3 Grid** — “Places a buy↔sell limit grid in your range. Spread profit stays in the vault and is put back to work automatically.” One line that this is **not** a pool share. 4. `/pool`: text button/link **V3 Grid** → `/v3` near provide chrome or how-to. `/limits`: same near Single/Ladder tabs (“Need a range that flips? V3 Grid”). 5. Gas preflight for deposit/withdraw using new `terraGas.ts` keys (do not fall through to `BASE_GAS_LIMIT` — [#475](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/475)). 6. Tests: Vitest page + copy forbidden-hits; Playwright smoke `/v3` render + pool/limits links (5 workers). `make verify-issue-<iid>`. ## Acceptance criteria - [ ] **AC1 — `/v3`.** Route renders **V3 Grid**; wildcard still does not 404-loop. - [ ] **AC2 — Create preview.** Pair + range + spread (1–2500) + rungs + inventory; E2 no-drift pairing visible; disconnect still shows fields. - [ ] **AC3 — Deposit / withdraw.** Beneficiary can open and fully exit. **No** Claim / Reinvest / Rebalance buttons (`getByRole` / testid absent). - [ ] **AC4 — Spread vs fee gate.** Sub-break-even spread blocked or confirmed with an explicit loss warning that still prefers block for unregistered 90 bps vs tiny spread. - [ ] **AC5 — Revenue + APR.** Indexer windows + preview band with NFA; missing data → omit number, do not show 0% as a fake APR. - [ ] **AC6 — Links.** `/pool` and `/limits` each have a same-origin `/v3` link. More nav lists **V3 Grid**. - [ ] **AC7 — Copy.** Forbidden phrases in how-to / V3 page tests: incentive program, LP shares, “V3 LP”. Allowed: **V3 Grid**. - [ ] **AC8 — Guards.** Pause, blacklist, clickwrap, **L20**, gem hide, #489 (no Swap lecture). - [ ] **AC9 — Verify.** `make verify-issue-<iid>` (indexer tests + frontend unit + optional Playwright). - [ ] **AC10 — Browser/Playwright.** Exercise create-preview, link navigation from Pool and Limits, withdraw CTA presence, crank-button absence. ## Test plan (all paths) | # | Path | Assert | |---|------|--------| | T1 | `GET /v3` disconnected | Form visible; submit opens wallet modal | | T2 | Spread 0 / 2501 | Invalid; cannot submit | | T3 | Spread 1 bps, vault unregistered place 90 bps | Blocked (or equivalent hard gate) | | T4 | E2 preview | Bid `1.00` + 200 bps → ask `1.02`; ask fill preview restores `1.00` | | T5 | Deposit tx (LocalTerra / mock) | Vault instantiate or deposit msg; no pair mixed-side batch from UI | | T6 | Withdraw | Cancel+return path; no leftover Claim CTA | | T7 | Query `data-testid` / role | No `claim`, `reinvest`, `rebalance` retail buttons | | T8 | `/pool` link | Navigates to `/v3` | | T9 | `/limits` link | Navigates to `/v3`; Ladder panel still one-sided | | T10 | More nav | **V3 Grid** present; 768–1199 header More still usable | | T11 | Indexer preview missing candles | APR field hidden / “Estimate unavailable” | | T12 | Indexer vault list | Only beneficiary’s vaults | | T13 | Pause / blacklist | Same banners as Limits; submit disabled | | T14 | UST1/USTR pair | Human range; raw prices scaled (**L20**) | | T15 | Gem pair | Hidden in production picker ([#562](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/562)) | | T16 | `forbiddenPoolLpHowtoCopyHits` | Still empty after how-to edit | | T17 | Revenue 24h / 7d / 30d / all-time | Render from API; empty grid → zeros or em dash, not NaN | ## Test plan — attack, hack, and abuse vectors | # | Vector | What to prove | |---|--------|----------------| | A1 | **XSS / innerHTML** | Static React; vault addr in URL sanitized (same as `/trade/:pairAddr`). | | A2 | **Fake APR** | Preview cannot show a number without indexer fields; no hard-coded 95% APY marketing. | | A3 | **Wrong vault** | Withdraw/deposit msgs use the selected vault address; cannot drain another beneficiary. | | A4 | **Indexer lie** | Balances / `OrderStatus` for withdraw gates prefer LCD; stale “active” does not block honest withdraw (parity #530). | | A5 | **Crossing preview** | UI does not encourage a spread that crosses mid; vault still post-only. | | A6 | **Gem / phishing pair** | Retail catalog filters; paste-in custom pair follows Create Pair allowlist rules if offered. | | A7 | **Cognitive overload** | Pool/Limits links are not multi-paragraph lectures. | | A8 | **Hidden crank** | No “Advanced Rebalance” that re-introduces the rejected buttons. | | A9 | **Fee-drain UX** | Tiny spread + high place-fee cannot one-click destroy inventory. | | A10 | **Open-redirect** | Links are same-origin `/v3` only. | | A11 | **Clickwrap / NFA bypass** | `/v3` still behind existing legal gates on `dex.cl8y.com`. | ## Verification criteria 1. Product review: `/v3` is findable from Pool and Limits without reading wasm. 2. No Claim / Reinvest / Rebalance in the shipped UI. 3. Docs + playbook (`AGENTS_FRONTEND_V3_GRID.md`) + `make verify-issue-<iid>`. 4. Playwright or browser pass on the three routes (Pool → V3, Limits → V3, V3 preview). 5. Indexer schema migration is versioned; empty DB tests do not fail the whole suite. ## Out of scope - Vault / factory wasm (sibling). - `grid-keeper` binary and tip→LUNC (sibling). - Turning `/limits` Ladder into an auto-flip bot. - Uniswap-v3 AMM, v2 LP math, farms. - In-swap flip. - Putting V3 Grid **into** the `/pool` provide card as if it minted LP tokens.
PlasticDigits commented 2026-08-24 03:15:45 +00:00 (Migrated from gitlab.com)

marked as related to #546

marked as related to #546
PlasticDigits commented 2026-08-24 03:15:46 +00:00 (Migrated from gitlab.com)

marked as related to #617

marked as related to #617
PlasticDigits commented 2026-08-24 03:16:05 +00:00 (Migrated from gitlab.com)

mentioned in issue #546

mentioned in issue #546
PlasticDigits commented 2026-08-24 03:16:16 +00:00 (Migrated from gitlab.com)

mentioned in issue #617

mentioned in issue #617
PlasticDigits commented 2026-08-26 03:06:33 +00:00 (Migrated from gitlab.com)

mentioned in issue #655

mentioned in issue #655
PlasticDigits commented 2026-08-26 04:15:26 +00:00 (Migrated from gitlab.com)

mentioned in issue #664

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