Design: team maker+taker limit MM bot (Tier 0 waiver, monthly subscription) #597

Open
opened 2026-08-22 12:26:34 +00:00 by PlasticDigits · 16 comments
PlasticDigits commented 2026-08-22 12:26:34 +00:00 (Migrated from gitlab.com)

Parent

Design / investigation only — do not implement a production bot in this issue.

Follow-up to community tax-token SKUs (#592, #593, #594) and the reusable pay-with-any-token invoice module (#595).

This is not AutoV2Lp (on-chain tax skim → provide_liquidity). This is an off-chain, team-operated maker + taker that posts resting limit orders to deepen the on-chain book, with Tier 0 (0 protocol fee) and a monthly subscription instead of per-trade DEX fees.

Problem statement

Retail /trade books on new and thin pairs are empty or one-tick deep. The hybrid AMM + FIFO book only looks like a market when makers rest size. Protocol swap/place fees at the default 180 bps pair (maker half at place + taker half on fill) make a tight two-sided book uneconomic for the team to run.

There is no production market-making bot. Existing “bots” are LocalTerra QA noise (Poisson swaps + random off-mid limits). There is no inventory policy, no requote/UpdateLimitOrderPrice loop, no taker that skips its own restings, no mainnet key/custody story, and no way to charge for a fee waiver except holding CL8Y (which Tier 0 already bypasses).

Product intent to investigate:

  1. Team-run bot (not a customer-signed SDK in v1).
  2. Maker + taker on the limit book (and AMM when the book vs pool is crossed or inventory is skewed).
  3. 0 protocol fee via existing governance Tier 0 (RegisterWallet), not a new fee-discount ladder.
  4. Monthly subscription (CMM revenue) instead of per-trade fees. Pay UX must reuse #595 — do not fork Swap.

Current codebase

Piece What it does Gap for production MM
Fee-discount Tier 0 RegisterWallet (governance) assigns any addr, including EOAs/contracts. GetDiscount skips CL8Y balance for governance_only tiers. discount_bps and limit_discount_bps are 10000 → 0 place + 0 take/swap fee. Docs: docs/reference/fee-discount-tiers.md, docs/security-model.md § Fee Discount Waiver is wallet-global on every pair that has DISCOUNT_REGISTRY. No pair-scope, no expiry, no subscription hook. Key leak = 0-fee trading everywhere.
Pair limit book Place batch/ladder, UpdateLimitOrderPrice (no second place fee), batch cancel/claim, hybrid Swap with book_start_hint, CleanLimitBook (#263 / #274). Fees: maker floor(eff/2) at place, taker half on fill (#514 I13). No self-match skip (audit LOB-13): a taker can fill its own resting orders. Wash volume is cheap once T0 is on (gas/LUNC only). Client-only post-only guard (#152); on-chain allows crossing limits.
scripts/bots/swarm.py --worker limit + @cl8y-dex/localnet-trading-swarm (#119) LocalTerra-only Poisson random bids/asks off pool mid; swap workers hit AMM only. Hard chain-id / VITE_NETWORK=local guards. Not a MM. No cancel/requote, no inventory, no fair-price oracle, no taker-vs-own-book, must never point at columbus-5.
Indexer limit-book, insert-hints, limit-placements, route/solve Book walk, ladder hints (#267), hybrid quotes with trader (#245) Prod LCD-heavy 10 RPS / IP (#363). A chatty MM needs an operator origin (or private indexer), not the public 10 RPS bucket.
Keepers CleanLimitBook is permissionless; no in-repo watcher (#263). Claim parked (parked_expired, L22 / #504). Pause blocks cancel/claim/clean (L6). MM must own claim/cancel/clean for its orders; do not rely on random keepers for inventory return.
AutoV2Lp (#592 SKU) Deferred SkimToLp → pool LP Complements book MM; does not rest limits. Do not fold MM into the tax CW20.
#595 Exact-out invoice in any routable token → canonical CW20 (UST1) to a payee One-shot pay. No recurring allowance. Monthly sub is either prepaid 30d on-chain, or ops invoice + widget.
scripts/rebalance-mint-ust1-lp.sh One-shot hub pool LP to CMM Inventory for AMM, not a live book. Different operator script (cl8ydeploy hot wallet).
Pair discount cache I9 (300s) Cached GetDiscount per (trader, sender) Fine for a hot MM; T0 does not need CL8Y refresh.

Why needed

  1. Book depth is the product differentiator vs pool-only DEXs. Empty books make /trade, CG/CMC hybrid orderbook (#220), and hybrid “best execution” look broken even when the pool has LP.
  2. Fee waiver is already in the contracts for this exact role (“market maker contracts”, Tier 0). Using it without a paid, time-bounded policy is a hidden subsidy and a key-leak blast radius.
  3. Community listings from #592 will otherwise launch with AutoLP (pool) and a blank book. A subscribe-to-MM offer is the book-side counterpart — billed through #595.
  4. Maker-only (rest forever) skews inventory; taker-only (cross the book / dump pool) does not rest depth. Production MM is both, with an inventory band.

Constraints / guardrails

  1. This issue ships a design, not mainnet keys or a bot binary. Implementation issues are spawned after sign-off.
  2. Do not reuse or “just env-flip” the LocalTerra swarm / scripts/bots/swarm.py onto columbus-5.
  3. Do not RegisterWallet Tier 0 on a customer-controlled key. Team signs; customer pays the invoice. Customer-loaned inventory (if any) sits in the team wallet or a team-admin MM contract.
  4. Tier 0 is not pair-scoped. Ops must use dedicated MM addresses and an off-chain pair allowlist. Investigate whether a later contract change (pair-scoped waiver) is worth it — default no for v1.
  5. Self-trade: bot must not take its own restings (filter owner, or book_start_hint past own prefix). On-chain self-match remains possible (LOB-13); do not treat “fees = 0” as a reason to wash for leaderboard/volume (#553 / #576).
  6. LUNC gas is not waived. Only pair protocol commission is 0. Size envelopes with skills/AGENTS_TERRACLASSIC_GAS.md (batch/ladder, hybrid 15M cap, max_maker_fills / MAX_SCAN_STEPS).
  7. Not a #592 SKU. AutoV2Lp stays the on-chain LP helper. MM-as-a-service is a separate invoice (monthly), not EnableFeature on the tax token.
  8. Tax-token listings stay 1:1 inbound to pair/router/escrow (H-01 / P2). MM must not special-case FoT; if a token is not 1:1, it must not be listed.
  9. Pause / blacklist / F6 freeze: bot stops placing/taking on that pair; does not fight SetPairPaused; does not route through frozen hops (#585).
  10. Quote = execute: taker clips use hybrid sim with trader = MM wallet (#418 / #245).
  11. Secrets: MM mnemonic/HSM is an operator secret (docs/operator-secrets.md). Never commit. Distinct from governance 2-of-3 and CMM wasm admin (skills/AGENTS_KEY_CUSTODY.md).
  12. Retail gems (#562): team MM on faucet tokens is QA-only, not a paid mainnet SKU.

Concept to investigate (working hypothesis)

flowchart LR
  sub[Subscriber pays monthly via 595] --> cmm[CMM treasury UST1]
  ops[Ops: RegisterWallet T0 / Deregister on expiry]
  bot[Team MM process]
  idx[Indexer limit-book + oracle]
  pair[Pair book + AMM]
  sub --> ops
  ops --> t0[MM wallet Tier 0]
  t0 --> bot
  idx --> bot
  bot -->|place / UpdateLimitOrderPrice / cancel / claim| pair
  bot -->|hybrid take skip own owner| pair

Maker

  • Two-sided grid or ladder around a fair price: pool mid, plus hub/oracle when the pair is a USD-ish quote (#556 / #515 / #522).
  • Post-only in the bot (do not cross). Prefer UpdateLimitOrderPrice when mid moves (same order_id, no extra place fee — already 0 at T0, still saves gas vs cancel+place).
  • Inventory skew: thin the heavy side, thicken the light side; hard band → taker rebalance.
  • Use batch/ladder + insert-hints (#206 / #267) so deep books do not burn max_adjust_steps.
  • TTL: short expires_at vs perpetual + cancel. Investigate gas vs park/claim load (L1 / L6).
  • Own-order lifecycle: poll limit-placements?status= and OrderStatus (#505); claim DustFilled / expiry; optional CleanLimitBook only for expired prefix, not live size.

Taker

  • Arb / toxic: when best bid/ask vs pool (or oracle) exceeds a threshold after gas, take with hybrid book_input and book_start_hint skipping own ids.
  • Inventory: if base (or quote) is outside the band, clip pool and/or opposite book — still skip owner == self.
  • Small clips vs blowing through retail: investigate max clip, max_maker_fills, and whether MM should improve retail fills or avoid competing with them at the inside.

Fee waiver + subscription

  • Waiver: RegisterWallet { wallet: mm, tier_id: 0 } after invoice; DeregisterWallet when prepaid window ends or mandate is revoked. Governance-only; MM cannot self-deregister from T0.
  • Price (open): monthly UST1 (or USD-quoted, settled in UST1) TBD. Do not assume the 50 UST1 SKU. MM is a capital + ops product; the sub pays for waiver + running the bot, not for inventory (inventory is team or separately escrowed).
  • Pay path: #595 exact-out to CMM. Recurrence: prepaid N days (simplest, on-chain receipt optional) vs monthly dApp invoice. Terra Classic has no pull-debit without allowance + keeper.
  • v1 ops ledger (spreadsheet / private DB + governance txs) is acceptable if the public design says so; an on-chain mm-subscription contract is optional later (pay UST1 → emit subscribed_until → keeper aligns T0). Do not block book MM on a new CosmWasm product.

Who is the first book

Priority order to decide:

  1. Protocol hub pairs the team already seeds (UST1/cUSTC, UST1/USTR, wrap markets) — internal mandate, sub may be $0 / treasury-funded.
  2. Community tax-token pairs after #592 listing — paid monthly.
  3. Other listed CW20s by application.

Research questions (must answer here)

ID Question Default if undecided
Q1 EOA hot wallet vs small MM contract (gov sets params; contract is T0; keys only Execute strategy)? Docs mention “MM contracts”. EOA + HSM/file keyring for v1 spike; contract only if custody review requires it.
Q2 One MM wallet for all pairs vs one wallet per mandate (blast radius)? Per mandate (hub vs each community pair).
Q3 Subscription: ops prepaid vs on-chain expiry contract? Ops + #595 prepaid; contract is a child issue.
Q4 Monthly price and whether hub MM is complimentary? Product; record the number on this issue.
Q5 Fair price: pool-only vs hub USD vs external oracle? Stale oracle behavior? Pool + optional hub; pause quoting if marks disagree beyond X bps.
Q6 Grid params: tick, levels, size vs TVL, skew formula, max inventory? Document a v1 numeric policy per pair class.
Q7 On-chain self-match prevent (owner != taker) vs client-only? Client-only for v1; document wash risk; optional later pair flag.
Q8 Pair-scoped T0 (contract change) vs ops allowlist? Ops allowlist for v1.
Q9 Indexer: shared prod API vs private instance / allowlisted IP? Private or operator IP; do not blow public 10 RPS.
Q10 Hosting: tmux/Coolify worker vs local operator laptop? Same class as indexer worker; runbook required.
Q11 Customer inventory: never / custodial loan / LP-like vault? Team inventory only for v1.
Q12 Should MM volume be excluded from public 24h/leaderboard stats? Investigate; default exclude T0 wallets from retail leaderboards if easy; do not hide from tape.
  1. Write a short ADR in-repo (docs/adr/ or a docs/runbooks/ draft linked from this issue) covering Q1–Q12. Sign-off = this issue’s Done.
  2. LocalTerra spike (child issue): new package or scripts/bots/mm/ — not swarm. Two-sided grid on one pair, requote, hybrid take with owner skip, T0 register in deploy script. Proof: book stays two-sided under the existing swap swarm.
  3. Mainnet hub (child): one dedicated wallet, T0, Coolify/tmux, inventory from CMM/treasury policy, no public subscribe UI yet.
  4. Subscribe UI (child, after #595): community manager pays monthly → ops enable mandate. #593 may link a “Book MM” card; it must not live inside the tax CW20.

Out of scope (this issue and v1 unless a Q flips)

  • Public third-party MM SDK / open T0 application form.
  • Pair wasm change for self-match or pair-scoped discount (unless Q7/Q8 explicitly require it).
  • AutoLP, wrap-mapper, UST1 window, faucet gems as paid MM.
  • Pointing QA swarm at mainnet.
  • MEV private relays (public mempool + slippage remains the model — #299).

Attack / abuse tests (design must address; implement later)

  • T0 key leak: attacker 0-fee wash on all registry-wired pairs. Mitigation: per-mandate keys, rapid DeregisterWallet, no customer keys.
  • Self-fill loop: bot takes itself → fake volume, inventory unchanged aside from gas. Mitigation: owner skip; optional indexer heuristic.
  • Inventory drain: toxic flow / stale oracle / crossed grid. Mitigation: bands, pause on mark disagreement, max clip.
  • Crossed post: on-chain accepts marketable limits; bot must not. Test: bid ≥ best ask never broadcast.
  • Pause / freeze: bot must idle, not retry-spam.
  • Subscription expiry: T0 removed; next place/take pays full fee (or bot stops). No silent forever-waiver.
  • Invoice spoof: #595 payee is CMM only; ops must not RegisterWallet on unpaid memos.

Acceptance criteria (design phase)

  • Q1–Q12 answered in the issue (or linked ADR) with a named default.
  • Explicit split vs AutoV2Lp (#592) and vs LocalTerra swarm (#119).
  • Subscription billed as a #595 invoice (canonical UST1 to CMM); amount and prepaid window recorded.
  • Custody note: MM hot wallet ≠ governance multisig ≠ customer wallet; T0 never on customer keys.
  • Self-trade policy written (client skip; on-chain status).
  • Indexer load policy written (RPS, private vs public).
  • Child issues filed only after the above: (a) LocalTerra spike, (b) hub mainnet ops, (c) optional subscribe UI, (d) optional on-chain sub contract.

Test plan (for children, not this issue)

Full path (LocalTerra spike)

  1. make setup-cloud-localterra (or make deploy-local).
  2. Governance RegisterWallet spike addr → Tier 0; GetDiscount → 10000 / 10000 with 0 TCL8Y.
  3. Bot posts N bids + N asks around mid; /trade book shows both sides.
  4. Swap swarm runs; MM requotes; inventory stays inside band.
  5. Forced cross (script): taker fills other owners, not owner == mm (inspect limit_order_fill maker).
  6. Pause pair → bot stops; unpause → resumes.
  7. DeregisterWallet → next place charges maker half of full fee.

Attack (children)

  1. Bot taker with empty skip list fills own bid (must fail the test / be blocked in code).
  2. Point spike config at non-localterra chain id → hard exit (copy swarm invariant).
  3. LCD-heavy hammer → document 429 / private indexer.

Verification

Design Done when ADR/answers are on this issue and children exist (or are explicitly deferred with dates). No make verify-issue-* until a child lands code.

Likely files (children)

  • New: scripts/bots/mm/ or packages/mm-bot/ (LocalTerra guard cloned from swarm README invariants).
  • Docs: docs/runbooks/market-maker.md, optional ADR.
  • Deploy: optional LocalTerra RegisterWallet for the spike addr in scripts/deploy-dex-local.sh (test-only).
  • dApp: subscribe card only in a later child; reuse #595 widget.
  • Contracts: none unless Q7/Q8/Q3 demand it.
  • #119 — QA swarm (do not extend to mainnet).
  • #206 — batch/ladder.
  • #263 — no in-repo clean watcher.
  • #514 — place vs take discount (I13).
  • #592 — AutoLP ≠ book MM.
  • #595 — monthly invoice pay path.
## Parent **Design / investigation only** — do not implement a production bot in this issue. Follow-up to community tax-token SKUs ([#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592), [#593](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/593), [#594](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/594)) and the reusable **pay-with-any-token** invoice module ([#595](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/595)). This is **not** AutoV2Lp (on-chain tax skim → `provide_liquidity`). This is an **off-chain, team-operated maker + taker** that posts **resting limit orders** to deepen the on-chain book, with **Tier 0 (0 protocol fee)** and a **monthly subscription** instead of per-trade DEX fees. ## Problem statement Retail `/trade` books on new and thin pairs are empty or one-tick deep. The hybrid AMM + FIFO book only looks like a market when **makers rest size**. Protocol swap/place fees at the default **180 bps** pair (maker half at place + taker half on fill) make a tight two-sided book uneconomic for the team to run. There is **no** production market-making bot. Existing “bots” are **LocalTerra QA noise** (Poisson swaps + random off-mid limits). There is **no** inventory policy, **no** requote/`UpdateLimitOrderPrice` loop, **no** taker that skips its own restings, **no** mainnet key/custody story, and **no** way to charge for a fee waiver except holding CL8Y (which Tier 0 already bypasses). Product intent to investigate: 1. **Team-run** bot (not a customer-signed SDK in v1). 2. **Maker + taker** on the **limit book** (and AMM when the book vs pool is crossed or inventory is skewed). 3. **0 protocol fee** via existing governance **Tier 0** (`RegisterWallet`), not a new fee-discount ladder. 4. **Monthly subscription** (CMM revenue) **instead of** per-trade fees. Pay UX must reuse [#595](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/595) — do not fork Swap. ## Current codebase | Piece | What it does | Gap for production MM | |-------|----------------|------------------------| | Fee-discount **Tier 0** | `RegisterWallet` (governance) assigns any addr, including EOAs/contracts. `GetDiscount` **skips CL8Y balance** for `governance_only` tiers. `discount_bps` **and** `limit_discount_bps` are **10000** → 0 place + 0 take/swap fee. Docs: [`docs/reference/fee-discount-tiers.md`](docs/reference/fee-discount-tiers.md), [`docs/security-model.md`](docs/security-model.md) § Fee Discount | Waiver is **wallet-global** on every pair that has `DISCOUNT_REGISTRY`. No pair-scope, no expiry, no subscription hook. Key leak = 0-fee trading **everywhere**. | | Pair limit book | Place batch/ladder, `UpdateLimitOrderPrice` (no second place fee), batch cancel/claim, hybrid `Swap` with `book_start_hint`, `CleanLimitBook` ([#263](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/263) / [#274](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/274)). Fees: maker `floor(eff/2)` at place, taker half on fill ([#514](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/514) **I13**). | **No self-match skip** (audit **LOB-13**): a taker can fill its own resting orders. Wash volume is cheap once T0 is on (gas/LUNC only). Client-only post-only guard ([#152](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/152)); on-chain **allows** crossing limits. | | `scripts/bots/swarm.py` `--worker limit` + `@cl8y-dex/localnet-trading-swarm` ([#119](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/119)) | LocalTerra-only Poisson **random** bids/asks off pool mid; swap workers hit **AMM** only. Hard chain-id / `VITE_NETWORK=local` guards. | **Not a MM.** No cancel/requote, no inventory, no fair-price oracle, no taker-vs-own-book, **must never** point at columbus-5. | | Indexer `limit-book`, `insert-hints`, `limit-placements`, `route/solve` | Book walk, ladder hints ([#267](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/267)), hybrid quotes with `trader` ([#245](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/245)) | Prod **LCD-heavy 10 RPS / IP** ([#363](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/363)). A chatty MM needs an **operator origin** (or private indexer), not the public 10 RPS bucket. | | Keepers | `CleanLimitBook` is permissionless; **no in-repo watcher** ([#263](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/263)). Claim parked (`parked_expired`, **L22** / [#504](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/504)). Pause blocks cancel/claim/clean (**L6**). | MM must **own** claim/cancel/clean for **its** orders; do not rely on random keepers for inventory return. | | AutoV2Lp ([#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592) SKU) | Deferred `SkimToLp` → pool LP | Complements book MM; **does not** rest limits. Do not fold MM into the tax CW20. | | [#595](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/595) | Exact-out invoice in any routable token → canonical CW20 (UST1) to a payee | One-shot pay. **No** recurring allowance. Monthly sub is either prepaid 30d on-chain, or ops invoice + widget. | | `scripts/rebalance-mint-ust1-lp.sh` | One-shot hub **pool** LP to CMM | Inventory for **AMM**, not a live book. Different operator script (`cl8ydeploy` hot wallet). | | Pair discount cache **I9** (300s) | Cached `GetDiscount` per `(trader, sender)` | Fine for a hot MM; T0 does not need CL8Y refresh. | ## Why needed 1. **Book depth** is the product differentiator vs pool-only DEXs. Empty books make `/trade`, CG/CMC hybrid orderbook ([#220](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/220)), and hybrid “best execution” look broken even when the pool has LP. 2. **Fee waiver is already in the contracts** for this exact role (“market maker contracts”, Tier 0). Using it without a **paid, time-bounded** policy is a hidden subsidy and a key-leak blast radius. 3. Community listings from [#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592) will otherwise launch with AutoLP (pool) and a **blank book**. A subscribe-to-MM offer is the book-side counterpart — billed through [#595](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/595). 4. Maker-only (rest forever) **skews inventory**; taker-only (cross the book / dump pool) **does not rest depth**. Production MM is both, with an inventory band. ## Constraints / guardrails 1. **This issue ships a design**, not mainnet keys or a bot binary. Implementation issues are spawned after sign-off. 2. **Do not** reuse or “just env-flip” the LocalTerra swarm / `scripts/bots/swarm.py` onto columbus-5. 3. **Do not** `RegisterWallet` Tier 0 on a **customer-controlled** key. Team signs; customer pays the invoice. Customer-loaned inventory (if any) sits in the **team** wallet or a team-admin MM contract. 4. **Tier 0 is not pair-scoped.** Ops must use **dedicated** MM addresses and an **off-chain pair allowlist**. Investigate whether a later contract change (pair-scoped waiver) is worth it — default **no** for v1. 5. **Self-trade:** bot **must not** take its own restings (filter `owner`, or `book_start_hint` past own prefix). On-chain self-match remains possible (**LOB-13**); do not treat “fees = 0” as a reason to wash for leaderboard/volume ([#553](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/553) / [#576](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/576)). 6. **LUNC gas is not waived.** Only pair protocol commission is 0. Size envelopes with [`skills/AGENTS_TERRACLASSIC_GAS.md`](skills/AGENTS_TERRACLASSIC_GAS.md) (batch/ladder, hybrid 15M cap, `max_maker_fills` / `MAX_SCAN_STEPS`). 7. **Not a #592 SKU.** AutoV2Lp stays the on-chain LP helper. MM-as-a-service is a **separate invoice** (monthly), not `EnableFeature` on the tax token. 8. **Tax-token listings** stay **1:1 inbound** to pair/router/escrow (**H-01** / **P2**). MM must not special-case FoT; if a token is not 1:1, it must not be listed. 9. **Pause / blacklist / F6 freeze:** bot stops placing/taking on that pair; does not fight `SetPairPaused`; does not route through frozen hops ([#585](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/585)). 10. **Quote = execute:** taker clips use hybrid sim with `trader` = MM wallet ([#418](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/418) / [#245](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/245)). 11. **Secrets:** MM mnemonic/HSM is an operator secret ([`docs/operator-secrets.md`](docs/operator-secrets.md)). Never commit. Distinct from governance 2-of-3 and CMM wasm admin ([`skills/AGENTS_KEY_CUSTODY.md`](skills/AGENTS_KEY_CUSTODY.md)). 12. **Retail gems** ([#562](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/562)): team MM on faucet tokens is QA-only, not a paid mainnet SKU. ## Concept to investigate (working hypothesis) ```mermaid flowchart LR sub[Subscriber pays monthly via 595] --> cmm[CMM treasury UST1] ops[Ops: RegisterWallet T0 / Deregister on expiry] bot[Team MM process] idx[Indexer limit-book + oracle] pair[Pair book + AMM] sub --> ops ops --> t0[MM wallet Tier 0] t0 --> bot idx --> bot bot -->|place / UpdateLimitOrderPrice / cancel / claim| pair bot -->|hybrid take skip own owner| pair ``` ### Maker - Two-sided **grid or ladder** around a **fair price**: pool mid, plus hub/oracle when the pair is a USD-ish quote ([#556](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/556) / [#515](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/515) / [#522](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/522)). - **Post-only** in the bot (do not cross). Prefer `UpdateLimitOrderPrice` when mid moves (same `order_id`, no extra place fee — already 0 at T0, still saves gas vs cancel+place). - **Inventory skew:** thin the heavy side, thicken the light side; hard band → taker rebalance. - Use **batch/ladder + insert-hints** ([#206](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/206) / [#267](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/267)) so deep books do not burn `max_adjust_steps`. - TTL: short `expires_at` vs perpetual + cancel. Investigate gas vs park/claim load (**L1** / **L6**). - Own-order lifecycle: poll `limit-placements?status=` and `OrderStatus` ([#505](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/505)); claim `DustFilled` / expiry; optional `CleanLimitBook` only for **expired prefix**, not live size. ### Taker - **Arb / toxic:** when best bid/ask vs pool (or oracle) exceeds a threshold after gas, take with hybrid `book_input` and `book_start_hint` **skipping own ids**. - **Inventory:** if base (or quote) is outside the band, clip pool and/or opposite book — still skip `owner == self`. - Small clips vs blowing through retail: investigate max clip, `max_maker_fills`, and whether MM should **improve** retail fills or **avoid** competing with them at the inside. ### Fee waiver + subscription - **Waiver:** `RegisterWallet { wallet: mm, tier_id: 0 }` after invoice; `DeregisterWallet` when prepaid window ends or mandate is revoked. Governance-only; MM cannot self-deregister from T0. - **Price (open):** monthly UST1 (or USD-quoted, settled in UST1) **TBD**. Do **not** assume the 50 UST1 SKU. MM is a **capital + ops** product; the sub pays for **waiver + running the bot**, not for inventory (inventory is team or separately escrowed). - **Pay path:** [#595](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/595) exact-out to CMM. Recurrence: **prepaid N days** (simplest, on-chain receipt optional) vs monthly dApp invoice. Terra Classic has **no** pull-debit without allowance + keeper. - **v1 ops ledger** (spreadsheet / private DB + governance txs) is acceptable if the public design says so; an on-chain `mm-subscription` contract is **optional later** (pay UST1 → emit `subscribed_until` → keeper aligns T0). Do not block book MM on a new CosmWasm product. ### Who is the first book Priority order to decide: 1. Protocol hub pairs the team already seeds (UST1/cUSTC, UST1/USTR, wrap markets) — internal mandate, sub may be **$0 / treasury-funded**. 2. Community tax-token pairs after [#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592) listing — **paid** monthly. 3. Other listed CW20s by application. ## Research questions (must answer here) | ID | Question | Default if undecided | |----|----------|----------------------| | **Q1** | EOA hot wallet vs small MM **contract** (gov sets params; contract is T0; keys only `Execute` strategy)? Docs mention “MM contracts”. | EOA + HSM/file keyring for v1 spike; contract only if custody review requires it. | | **Q2** | One MM wallet for all pairs vs **one wallet per mandate** (blast radius)? | **Per mandate** (hub vs each community pair). | | **Q3** | Subscription: ops prepaid vs on-chain expiry contract? | Ops + #595 prepaid; contract is a child issue. | | **Q4** | Monthly price and whether hub MM is complimentary? | Product; record the number on this issue. | | **Q5** | Fair price: pool-only vs hub USD vs external oracle? Stale oracle behavior? | Pool + optional hub; **pause quoting** if marks disagree beyond X bps. | | **Q6** | Grid params: tick, levels, size vs TVL, skew formula, max inventory? | Document a v1 numeric policy per pair class. | | **Q7** | On-chain **self-match prevent** (`owner != taker`) vs client-only? | **Client-only** for v1; document wash risk; optional later pair flag. | | **Q8** | Pair-scoped T0 (contract change) vs ops allowlist? | **Ops allowlist** for v1. | | **Q9** | Indexer: shared prod API vs private instance / allowlisted IP? | Private or operator IP; do not blow public 10 RPS. | | **Q10** | Hosting: tmux/Coolify worker vs local operator laptop? | Same class as indexer worker; runbook required. | | **Q11** | Customer inventory: never / custodial loan / LP-like vault? | **Team inventory only** for v1. | | **Q12** | Should MM volume be **excluded** from public 24h/leaderboard stats? | Investigate; default **exclude T0 wallets** from retail leaderboards if easy; do not hide from tape. | ## Recommended direction 1. **Write a short ADR** in-repo (`docs/adr/` or a `docs/runbooks/` draft linked from this issue) covering Q1–Q12. Sign-off = this issue’s Done. 2. **LocalTerra spike** (child issue): new package or `scripts/bots/mm/` — **not** swarm. Two-sided grid on one pair, requote, hybrid take with owner skip, T0 register in deploy script. Proof: book stays two-sided under the existing swap swarm. 3. **Mainnet hub** (child): one dedicated wallet, T0, Coolify/tmux, inventory from CMM/treasury policy, no public subscribe UI yet. 4. **Subscribe UI** (child, after #595): community manager pays monthly → ops enable mandate. [#593](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/593) may link a “Book MM” card; it must not live inside the tax CW20. ## Out of scope (this issue and v1 unless a Q flips) - Public third-party MM SDK / open T0 application form. - Pair wasm change for self-match or pair-scoped discount (unless Q7/Q8 explicitly require it). - AutoLP, wrap-mapper, UST1 window, faucet gems as paid MM. - Pointing QA swarm at mainnet. - MEV private relays (public mempool + slippage remains the model — [#299](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/299)). ## Attack / abuse tests (design must address; implement later) - **T0 key leak:** attacker 0-fee wash on all registry-wired pairs. Mitigation: per-mandate keys, rapid `DeregisterWallet`, no customer keys. - **Self-fill loop:** bot takes itself → fake volume, inventory unchanged aside from gas. Mitigation: owner skip; optional indexer heuristic. - **Inventory drain:** toxic flow / stale oracle / crossed grid. Mitigation: bands, pause on mark disagreement, max clip. - **Crossed post:** on-chain accepts marketable limits; bot must not. Test: bid ≥ best ask never broadcast. - **Pause / freeze:** bot must idle, not retry-spam. - **Subscription expiry:** T0 removed; next place/take pays full fee (or bot stops). No silent forever-waiver. - **Invoice spoof:** #595 payee is CMM only; ops must not RegisterWallet on unpaid memos. ## Acceptance criteria (design phase) - [ ] Q1–Q12 answered in the issue (or linked ADR) with a named default. - [ ] Explicit split vs AutoV2Lp ([#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592)) and vs LocalTerra swarm ([#119](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/119)). - [ ] Subscription billed as a **#595 invoice** (canonical UST1 to CMM); amount and prepaid window recorded. - [ ] Custody note: MM hot wallet ≠ governance multisig ≠ customer wallet; T0 never on customer keys. - [ ] Self-trade policy written (client skip; on-chain status). - [ ] Indexer load policy written (RPS, private vs public). - [ ] Child issues filed only after the above: (a) LocalTerra spike, (b) hub mainnet ops, (c) optional subscribe UI, (d) optional on-chain sub contract. ## Test plan (for children, not this issue) ### Full path (LocalTerra spike) 1. `make setup-cloud-localterra` (or `make deploy-local`). 2. Governance `RegisterWallet` spike addr → Tier 0; `GetDiscount` → 10000 / 10000 with **0** TCL8Y. 3. Bot posts N bids + N asks around mid; `/trade` book shows both sides. 4. Swap swarm runs; MM **requotes**; inventory stays inside band. 5. Forced cross (script): taker fills **other** owners, **not** `owner == mm` (inspect `limit_order_fill` maker). 6. Pause pair → bot stops; unpause → resumes. 7. `DeregisterWallet` → next place charges maker half of full fee. ### Attack (children) 1. Bot taker with empty skip list fills own bid (must fail the test / be blocked in code). 2. Point spike config at non-`localterra` chain id → hard exit (copy swarm invariant). 3. LCD-heavy hammer → document 429 / private indexer. ## Verification Design Done when ADR/answers are on this issue and children exist (or are explicitly deferred with dates). No `make verify-issue-*` until a child lands code. ## Likely files (children) - New: `scripts/bots/mm/` or `packages/mm-bot/` (LocalTerra guard cloned from swarm README invariants). - Docs: `docs/runbooks/market-maker.md`, optional ADR. - Deploy: optional LocalTerra `RegisterWallet` for the spike addr in `scripts/deploy-dex-local.sh` (test-only). - dApp: subscribe card only in a later child; reuse #595 widget. - Contracts: **none** unless Q7/Q8/Q3 demand it. ## Related - [#119](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/119) — QA swarm (do not extend to mainnet). - [#206](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/206) — batch/ladder. - [#263](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/263) — no in-repo clean watcher. - [#514](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/514) — place vs take discount (**I13**). - [#592](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/592) — AutoLP ≠ book MM. - [#595](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/595) — monthly invoice pay path.
PlasticDigits commented 2026-08-22 12:26:36 +00:00 (Migrated from gitlab.com)

marked as related to #592

marked as related to #592
PlasticDigits commented 2026-08-22 12:26:36 +00:00 (Migrated from gitlab.com)

marked as related to #593

marked as related to #593
PlasticDigits commented 2026-08-22 12:26:37 +00:00 (Migrated from gitlab.com)

marked as related to #595

marked as related to #595
PlasticDigits commented 2026-08-22 12:26:46 +00:00 (Migrated from gitlab.com)

marked as related to #119

marked as related to #119
PlasticDigits commented 2026-08-22 12:26:47 +00:00 (Migrated from gitlab.com)

marked as related to #263

marked as related to #263
PlasticDigits commented 2026-08-22 12:26:47 +00:00 (Migrated from gitlab.com)

marked as related to #514

marked as related to #514
PlasticDigits commented 2026-08-22 12:26:48 +00:00 (Migrated from gitlab.com)

marked as related to #594

marked as related to #594
PlasticDigits commented 2026-08-22 12:26:49 +00:00 (Migrated from gitlab.com)

mentioned in issue #592

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

mentioned in issue #595

mentioned in issue #595
PlasticDigits commented 2026-08-23 03:10:03 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1117

mentioned in merge request !1117
PlasticDigits commented 2026-08-23 03:50:24 +00:00 (Migrated from gitlab.com)

mentioned in merge request !1119

mentioned in merge request !1119
PlasticDigits commented 2026-08-23 03:53:49 +00:00 (Migrated from gitlab.com)

mentioned in issue #601

mentioned in issue #601
PlasticDigits commented 2026-08-25 13:10:20 +00:00 (Migrated from gitlab.com)

mentioned in issue #650

mentioned in issue #650
PlasticDigits commented 2026-08-25 13:10:22 +00:00 (Migrated from gitlab.com)

marked as related to #650

marked as related to #650
PlasticDigits commented 2026-08-27 12:00:25 +00:00 (Migrated from gitlab.com)

Fee changes:
50 ust1/mo
8 BPS/mo of inventory in the market maker
0 fee taker/maker orders

Fee changes: 50 ust1/mo 8 BPS/mo of inventory in the market maker 0 fee taker/maker orders
PlasticDigits commented 2026-08-27 12:01:30 +00:00 (Migrated from gitlab.com)

anaylze the 8 BPS per month, that should be max tier, should have higher rates for lower cl8y tiers

anaylze the 8 BPS per month, that should be max tier, should have higher rates for lower cl8y tiers
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#597
No description provided.