feat(#508): UST1 secondary AMM Path A tooling and Path B waiver #1046

Merged
PlasticDigits merged 4 commits from feat/508-ust1-secondary-amm into main 2026-08-09 07:04:21 +00:00
PlasticDigits commented 2026-08-08 13:51:16 +00:00 (Migrated from gitlab.com)

Summary

  • Add Path A create/seed tooling for UST1/vFDUSD (or cUSTC) secondary AMM pairs (scripts/add-ust1-secondary-pair.sh, LocalTerra fixture, defaults lib) without folding UST1 into soft-launch gemstone catalogs (U6).
  • Document invariants U1–U7, agent playbook skills/AGENTS_UST1_SECONDARY_AMM.md, and an interim Path B product waiver because columbus-5 UST1 total_supply is 0 / deployer has no seed inventory.
  • Frontend helpers + CreatePair notice so AMM is never marketed as oracle mint/redeem (U1); make verify-issue-508 covers docs, DRY_RUN preflight, unit tests, and optional LocalTerra seed.

Test plan

  • make verify-issue-508 (twice)
  • VERIFY508_LOCAL=1 make verify-issue-508 (LocalTerra stand-in pair seeded, total_share > 0)
  • DRY_RUN=1 ./scripts/add-ust1-secondary-pair.sh (mainnet LCD preflight; reports inventory block)
  • After UST1 inventory exists: live ./scripts/add-ust1-secondary-pair.sh + smoke swap; set VERIFY508_MAINNET=1

Closes #508 via Path B interim waiver + Path A readiness (live mainnet seed remains ops follow-up when inventory exists).

## Summary - Add Path A create/seed tooling for UST1/vFDUSD (or cUSTC) secondary AMM pairs (`scripts/add-ust1-secondary-pair.sh`, LocalTerra fixture, defaults lib) without folding UST1 into soft-launch gemstone catalogs (**U6**). - Document invariants **U1–U7**, agent playbook `skills/AGENTS_UST1_SECONDARY_AMM.md`, and an interim **Path B product waiver** because columbus-5 UST1 `total_supply` is 0 / deployer has no seed inventory. - Frontend helpers + CreatePair notice so AMM is never marketed as oracle mint/redeem (**U1**); `make verify-issue-508` covers docs, DRY_RUN preflight, unit tests, and optional LocalTerra seed. ## Test plan - [x] `make verify-issue-508` (twice) - [x] `VERIFY508_LOCAL=1 make verify-issue-508` (LocalTerra stand-in pair seeded, `total_share > 0`) - [x] `DRY_RUN=1 ./scripts/add-ust1-secondary-pair.sh` (mainnet LCD preflight; reports inventory block) - [ ] After UST1 inventory exists: live `./scripts/add-ust1-secondary-pair.sh` + smoke swap; set `VERIFY508_MAINNET=1` Closes #508 via Path B interim waiver + Path A readiness (live mainnet seed remains ops follow-up when inventory exists).
PlasticDigits commented 2026-08-09 06:50:17 +00:00 (Migrated from gitlab.com)

RECOMMEND: FIX

Thorough review of !1046 (feat/508-ust1-secondary-amm → main, closes #508 via Path B + Path A tooling). Scope is ops/docs/frontend copy helpers — no smart-contract, indexer, or DB schema changes.

Verdict

Path B acceptance for #508/#502 is substantively met (waiver in-repo + comments on both issues), and Path A scripts are mostly well-shaped. Merge should wait on a short FIX pass: incorrect Trade/Swap deep-link helpers (tests lock in the wrong shape), mainnet ops footguns that can recreate the empty-market failure mode the waiver exists to prevent, and a missing on-chain symbol assertion before live create/seed.

Not BLOCK: no critical/high vulns, no prompt injection, no privilege escalation, no DB leak surface introduced.


Linked issue AC (#508 / parent #502)

Criterion Result Notes
(A) live seeded UST1/vFDUSD or UST1/cUSTC or (B) explicit waiver on #508 and #502 PASS (Path B) deployments/ust1-secondary-pair/PRODUCT_WAIVER.md + GitLab comments on #508 and #502 (2026-08-08) with rationale, deferred scope, revisit trigger
If (A): visible on dex + indexer N/A Correctly deferred until inventory
If (A): smoke swap + factory provenance N/A Same
Docs/issue list pair addr/txs or waiver text PASS Waiver + Path A fill-in template in deploy-trace.md
No copy implying AMM replaces /ust1 mint/redeem PASS (scoped) Create Pair notice + copyImpliesAmmIsMintRedeem tests; /ust1 CTA wiring correctly deferred to #506

U1–U7: U2–U7 covered by scripts/docs/verify. U1 partial — Create Pair only; UST1_SECONDARY_MARKET_BLURB unused until #506.

Local check: make verify-issue-508 → 9 PASS / 0 FAIL (mainnet DRY_RUN: code 10184, balances 0, no pair — inventory block expected).


Security audit

Attack surface triage (this MR)

Area Assessment
Smart contract design / new wasm N/A — no contract diffs
Rust indexer / API / DB leaks N/A — no indexer/DB changes; U5/P1 provenance unchanged
Access control / privileges OK for tooling — live path requires cl8ydeploy + address hard-check (add-ust1-secondary-pair.sh); create_pair remains permissionless on-chain (pre-existing)
Shell injection / secrets OK — jq --arg, quoted "$@", keyring via stdin; passphrase not written to addresses.env
Prompt injection (docs/skills) None — static operator guidance
Oracle manipulation N/A for Path B; Path A thin pool is discovery liquidity (U4), not oracle peg — copy correctly keeps /ust1 primary
Tokenomic / thin-pool econ Accepted under U4 if seeded; risk if unseeded (see FIX #2)
Common DeFi/SC attacks (reentrancy, flash-loan LP, etc.) Out of scope — uses existing factory/pair; no new AMMs

Findings (severity-ranked)

Sev Finding Location
Medium Preflight checks token_info non-empty + code_id==10184 but never asserts on-chain symbol matches expected leg. Env override to another whitelisted 10184 CW20 still labels pair UST1/vFDUSD in logs/trace. scripts/add-ust1-secondary-pair.sh ~126–136
Medium (ops) UST1_SEC_SKIP_LP=1 can create an unseeded factory pair on columbus-5 — the exact empty/misleading market Path B was written to avoid. Documented “discouraged” but still allowed live. scripts/add-ust1-secondary-pair.sh L7–8, L163–170, L216+
Low set_discount_registry failure swallowed (|| true) — post-governance deployer may silently leave pair without fee-discount registry. scripts/add-ust1-secondary-pair.sh ~211
Low Duplicate mainnet anchors (TS vs bash) — drift risk. ust1SecondaryMarket.ts vs ust1-secondary-pair-defaults.sh
Info CI test-frontend red on pipeline 2743658160 — failures are unrelated files (SwapPage, useTerraBroadcastMutation, formatAmount, lcdConnectivity, PriceChart, LcdQueryGate, WalletIndexerHistoryPanel). MR-added tests passed in that job. Still blocks green merge gate.

Gap analysis

Must-fix before merge (FIX)

  1. Deep-link helpers disagree with real routes — ust1SecondaryTradePath builds /trade?from=&to=&market=secondary, but app routes are /trade and /trade/:pairAddr (App.tsx, tradePairRoute.ts). market=secondary has no consumer. Swap helper /?from=&to= is also unused by SwapPage. Unit tests currently lock in the wrong shape. Align to /trade/${pairAddr} (via VITE_UST1_SECONDARY_PAIR_ADDRESS / indexer id) or mark helpers unfinished and stop asserting fake query params until #506 wires CTAs.
  2. Gate or remove live UST1_SEC_SKIP_LP=1 on mainnet (require explicit override like UST1_SEC_ALLOW_UNSEEDED=1 + loud abort), matching issue guardrail / U4 / Path B rationale.
  3. Assert on-chain symbols in mainnet preflight (case-insensitive match for UST1 / vFDUSD / cUSTC) before create/seed.

Should-fix / follow-ups (non-blocking for Path B intent, preferred same MR)

  1. Treat failed set_discount_registry as hard error or document that governance multisig must run it after handoff (do not || true silently).
  2. Local seed should also write VITE_UST1_SECONDARY_PAIR_ADDRESS when appending Vite hints.
  3. Add make verify-issue-508 to a lightweight CI job (docs/qa) so U1–U7 / U6 regression does not rely on local-only runs.
  4. Optional: broaden U1 forbidden-phrase list / wire blurb only from /ust1 (#506) — do not leave dead “ready” helpers that #506 will copy incorrectly.

Features users might expect (deferred / out of scope — OK if documented)

Expectation Status
Live UST1 secondary market on dex.cl8y.com Deferred (Path B) — correct
/ust1 secondary-market CTA #506
Indexer logos / CG-CMC listing After Path A
E2E Playwright swap on secondary pair Not in MR; LocalTerra fixture optional via VERIFY508_LOCAL=1
Gas optimizations N/A (no wasm)
DRY Good shared ust1-secondary-pair-defaults.sh; TS/bash anchor duplication acceptable with sync note

Testing matrix vs issue attack table

Vector Covered?
Market AMM as mint Unit + Create Pair notice
Non-whitelisted code ID DRY_RUN / live preflight die
Native uusd/uluna legs Script uses fixed CW20 only
Foreign indexer pair Doc-only (P1); no pair created
Dust/empty market as peg Path B waiver; undermined by SKIP_LP
Wrong addresses Partial (code id yes, symbol no)
Waiver without rationale PASS
Happy path LocalTerra seed Optional VERIFY508_LOCAL=1 (not default CI)
Bad path inventory block DRY_RUN WARN — good
E2E production smoke Explicitly unchecked (Path A ops)

Packages / readability / best practices

  • Bash: set -euo pipefail, shared defaults, idempotent LocalTerra fixture (fd91ae6) — solid.
  • Frontend: Small focused util + tests; Create Pair UST1 notice on all creates is slightly noisy but U1-safe.
  • Docs/skills: Clear U1–U7, Path A/B, soft-launch boundary (U6) — excellent.
  • No contract gas / Solidity-style issues.

Required FIX checklist

  • Fix or demote Trade/Swap deep-link helpers + tests to match /trade/:pairAddr (and real Swap deep-link behavior)
  • Harden against unseeded mainnet create (SKIP_LP)
  • Symbol assertion in add-ust1-secondary-pair.sh preflight
  • Prefer non-silent set_discount_registry failure handling
  • Re-run make verify-issue-508; note CI test-frontend unrelated failures separately if still red on main

After the above, Path B close of #508 looks merge-ready; Path A live seed remains ops follow-up when total_supply(UST1) > 0 and wallet inventory exists (VERIFY508_MAINNET=1 + smoke).

RECOMMEND: FIX Thorough review of !1046 (`feat/508-ust1-secondary-amm` → `main`, closes #508 via Path B + Path A tooling). Scope is ops/docs/frontend copy helpers — **no smart-contract, indexer, or DB schema changes**. ## Verdict Path B acceptance for #508/#502 is substantively met (waiver in-repo + comments on both issues), and Path A scripts are mostly well-shaped. Merge should wait on a short FIX pass: incorrect Trade/Swap deep-link helpers (tests lock in the wrong shape), mainnet ops footguns that can recreate the empty-market failure mode the waiver exists to prevent, and a missing on-chain symbol assertion before live create/seed. Not **BLOCK**: no critical/high vulns, no prompt injection, no privilege escalation, no DB leak surface introduced. --- ## Linked issue AC (#508 / parent #502) | Criterion | Result | Notes | |-----------|--------|-------| | **(A)** live seeded UST1/vFDUSD or UST1/cUSTC **or (B)** explicit waiver on #508 **and** #502 | **PASS (Path B)** | `deployments/ust1-secondary-pair/PRODUCT_WAIVER.md` + GitLab comments on #508 and #502 (2026-08-08) with rationale, deferred scope, revisit trigger | | If (A): visible on dex + indexer | **N/A** | Correctly deferred until inventory | | If (A): smoke swap + factory provenance | **N/A** | Same | | Docs/issue list pair addr/txs **or** waiver text | **PASS** | Waiver + Path A fill-in template in `deploy-trace.md` | | No copy implying AMM replaces `/ust1` mint/redeem | **PASS (scoped)** | Create Pair notice + `copyImpliesAmmIsMintRedeem` tests; `/ust1` CTA wiring correctly deferred to #506 | **U1–U7:** U2–U7 covered by scripts/docs/verify. **U1 partial** — Create Pair only; `UST1_SECONDARY_MARKET_BLURB` unused until #506. Local check: `make verify-issue-508` → **9 PASS / 0 FAIL** (mainnet DRY_RUN: code **10184**, balances **0**, no pair — inventory block expected). --- ## Security audit ### Attack surface triage (this MR) | Area | Assessment | |------|------------| | Smart contract design / new wasm | **N/A** — no contract diffs | | Rust indexer / API / DB leaks | **N/A** — no indexer/DB changes; U5/P1 provenance unchanged | | Access control / privileges | **OK for tooling** — live path requires `cl8ydeploy` + address hard-check (`add-ust1-secondary-pair.sh`); `create_pair` remains permissionless on-chain (pre-existing) | | Shell injection / secrets | **OK** — `jq --arg`, quoted `"$@"`, keyring via stdin; passphrase not written to `addresses.env` | | Prompt injection (docs/skills) | **None** — static operator guidance | | Oracle manipulation | **N/A for Path B**; Path A thin pool is **discovery liquidity (U4)**, not oracle peg — copy correctly keeps `/ust1` primary | | Tokenomic / thin-pool econ | **Accepted under U4** if seeded; **risk if unseeded** (see FIX #2) | | Common DeFi/SC attacks (reentrancy, flash-loan LP, etc.) | **Out of scope** — uses existing factory/pair; no new AMMs | ### Findings (severity-ranked) | Sev | Finding | Location | |-----|---------|----------| | **Medium** | Preflight checks `token_info` non-empty + `code_id==10184` but **never asserts on-chain `symbol`** matches expected leg. Env override to another whitelisted 10184 CW20 still labels pair `UST1/vFDUSD` in logs/trace. | `scripts/add-ust1-secondary-pair.sh` ~126–136 | | **Medium (ops)** | `UST1_SEC_SKIP_LP=1` can create an **unseeded** factory pair on columbus-5 — the exact empty/misleading market Path B was written to avoid. Documented “discouraged” but still allowed live. | `scripts/add-ust1-secondary-pair.sh` L7–8, L163–170, L216+ | | **Low** | `set_discount_registry` failure swallowed (`\|\| true`) — post-governance deployer may silently leave pair without fee-discount registry. | `scripts/add-ust1-secondary-pair.sh` ~211 | | **Low** | Duplicate mainnet anchors (TS vs bash) — drift risk. | `ust1SecondaryMarket.ts` vs `ust1-secondary-pair-defaults.sh` | | **Info** | CI `test-frontend` red on pipeline [2743658160](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/pipelines/2743658160) — failures are **unrelated** files (`SwapPage`, `useTerraBroadcastMutation`, `formatAmount`, `lcdConnectivity`, `PriceChart`, `LcdQueryGate`, `WalletIndexerHistoryPanel`). MR-added tests **passed** in that job. Still blocks green merge gate. | --- ## Gap analysis ### Must-fix before merge (FIX) 1. **Deep-link helpers disagree with real routes** — `ust1SecondaryTradePath` builds `/trade?from=&to=&market=secondary`, but app routes are `/trade` and `/trade/:pairAddr` (`App.tsx`, `tradePairRoute.ts`). `market=secondary` has **no consumer**. Swap helper `/?from=&to=` is also unused by SwapPage. Unit tests currently **lock in the wrong shape**. Align to `/trade/${pairAddr}` (via `VITE_UST1_SECONDARY_PAIR_ADDRESS` / indexer id) or mark helpers unfinished and stop asserting fake query params until #506 wires CTAs. 2. **Gate or remove live `UST1_SEC_SKIP_LP=1`** on mainnet (require explicit override like `UST1_SEC_ALLOW_UNSEEDED=1` + loud abort), matching issue guardrail / U4 / Path B rationale. 3. **Assert on-chain symbols** in mainnet preflight (case-insensitive match for `UST1` / `vFDUSD` / `cUSTC`) before create/seed. ### Should-fix / follow-ups (non-blocking for Path B intent, preferred same MR) 4. Treat failed `set_discount_registry` as hard error **or** document that governance multisig must run it after handoff (do not `|| true` silently). 5. Local seed should also write `VITE_UST1_SECONDARY_PAIR_ADDRESS` when appending Vite hints. 6. Add `make verify-issue-508` to a lightweight CI job (docs/qa) so U1–U7 / U6 regression does not rely on local-only runs. 7. Optional: broaden U1 forbidden-phrase list / wire blurb only from `/ust1` (#506) — do not leave dead “ready” helpers that #506 will copy incorrectly. ### Features users might expect (deferred / out of scope — OK if documented) | Expectation | Status | |-------------|--------| | Live UST1 secondary market on dex.cl8y.com | Deferred (Path B) — correct | | `/ust1` secondary-market CTA | #506 | | Indexer logos / CG-CMC listing | After Path A | | E2E Playwright swap on secondary pair | Not in MR; LocalTerra fixture optional via `VERIFY508_LOCAL=1` | | Gas optimizations | N/A (no wasm) | | DRY | Good shared `ust1-secondary-pair-defaults.sh`; TS/bash anchor duplication acceptable with sync note | ### Testing matrix vs issue attack table | Vector | Covered? | |--------|----------| | Market AMM as mint | Unit + Create Pair notice | | Non-whitelisted code ID | DRY_RUN / live preflight die | | Native `uusd`/`uluna` legs | Script uses fixed CW20 only | | Foreign indexer pair | Doc-only (P1); no pair created | | Dust/empty market as peg | Path B waiver; **undermined by SKIP_LP** | | Wrong addresses | Partial (code id yes, **symbol no**) | | Waiver without rationale | PASS | | Happy path LocalTerra seed | Optional `VERIFY508_LOCAL=1` (not default CI) | | Bad path inventory block | DRY_RUN WARN — good | | E2E production smoke | Explicitly unchecked (Path A ops) | --- ## Packages / readability / best practices - **Bash:** `set -euo pipefail`, shared defaults, idempotent LocalTerra fixture (`fd91ae6`) — solid. - **Frontend:** Small focused util + tests; Create Pair UST1 notice on *all* creates is slightly noisy but U1-safe. - **Docs/skills:** Clear U1–U7, Path A/B, soft-launch boundary (**U6**) — excellent. - **No contract gas / Solidity-style issues.** --- ## Required FIX checklist - [ ] Fix or demote Trade/Swap deep-link helpers + tests to match `/trade/:pairAddr` (and real Swap deep-link behavior) - [ ] Harden against unseeded mainnet create (`SKIP_LP`) - [ ] Symbol assertion in `add-ust1-secondary-pair.sh` preflight - [ ] Prefer non-silent `set_discount_registry` failure handling - [ ] Re-run `make verify-issue-508`; note CI `test-frontend` unrelated failures separately if still red on `main` After the above, Path B close of #508 looks merge-ready; Path A live seed remains ops follow-up when `total_supply(UST1) > 0` and wallet inventory exists (`VERIFY508_MAINNET=1` + smoke).
PlasticDigits commented 2026-08-09 06:52:19 +00:00 (Migrated from gitlab.com)

added 1 commit

  • a1e97c29 - fix(#508): harden Path A guards and Trade deep-link helpers

Compare with previous version

added 1 commit <ul><li>a1e97c29 - fix(#508): harden Path A guards and Trade deep-link helpers</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/328/diffs?diff_id=1950753429&start_sha=fd91ae6964cd4b9e3e06d06d1d74eb7651a3468d)
PlasticDigits commented 2026-08-09 07:02:22 +00:00 (Migrated from gitlab.com)

mentioned in issue #508

mentioned in issue #508
PlasticDigits commented 2026-08-09 07:04:03 +00:00 (Migrated from gitlab.com)

added 8 commits

  • a1e97c29...361cd808 - 7 commits from branch main
  • 7d43b493 - merge(main): integrate #506/#507 into #508 branch

Compare with previous version

added 8 commits <ul><li>a1e97c29...361cd808 - 7 commits from branch <code>main</code></li><li>7d43b493 - merge(main): integrate #506/#507 into #508 branch</li></ul> [Compare with previous version](/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/328/diffs?diff_id=1950756931&start_sha=a1e97c2980779df854e39dddd41e4c0c5c50a93e)
PlasticDigits commented 2026-08-09 07:04:22 +00:00 (Migrated from gitlab.com)

mentioned in commit 2bc31e4522

mentioned in commit 2bc31e4522c072d35953e1c50fa8f5079449362c
PlasticDigits (Migrated from gitlab.com) merged commit 2bc31e4522 into main 2026-08-09 07:04:26 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic!1046
No description provided.