design: investigate automated range ladder LP (buy↔sell grid) — off-chain vs on-chain gas #546

Open
opened 2026-08-17 10:26:05 +00:00 by PlasticDigits · 18 comments
PlasticDigits commented 2026-08-17 10:26:05 +00:00 (Migrated from gitlab.com)

Summary

Investigate an automated range ladder for liquidity provisioning: the user picks a pair, a price range, and inventory; the system places a grid of resting limits that flip from buy to sell and back as rungs fill, so the maker stays in-range without babysitting /limits.

This issue is a design / gas / threat-model spike. Do not ship a new CosmWasm message, vault, or retail UI in this work item. The deliverable is a written recommendation: off-chain service vs on-chain, with gas numbers, security constraints, and a go/no-go for a follow-up implementation issue.

Related: #206 (batch / one-sided ladder place), #247 (batch storage collapse), #246 (batch cancel/claim), #266 / #267 / #268 (deep-book hints), #152 (pair accepts crossing limits; dApp is post-only), #297 / #385 (ladder crossing guard), #514 (maker placement discount I13), #529 (human vs raw limit prices L20), #504 / #505 (park reason / OrderStatus), #489 (no always-on essays if a later UI exists).

Current codebase

The DEX already has a one-shot, one-sided limit ladder. It does not keep a maker in a range after fills. Filled makers receive the other token in their wallet; nothing re-escrows it on the opposite side.

Layer Behavior today
Place Cw20HookMsg::PlaceLimitOrderBatch / PlaceLimitOrderLadder. Ladder expands on-chain (equal distribution only) to the same batch rules. One side per tx — bid escrows token1, ask escrows token0. Mixed buy+sell grids require two placements.
Rung caps Pair max_batch_rungs (factory default / SetPairLimitBatchMax). Hard ceiling MAX_LIMIT_BATCH_RUNGS_HARD_CAP = 100 (dex-common limit_placement.rs; LocalTerra gas #263). Retail ladder UI is typically far below that.
Fill Hybrid execute_swap walks the book under max_maker_fills (hard cap 100) and MAX_SCAN_STEPS (500) — invariant L5. Maker payouts are deferred CW20 transfers to the order owner at the end of the swap (#248). The resting row is reduced / removed. No opposite-side place.
Fees Maker pays half of effective pair fee at placement (from escrow; I13 can zero the place half at tier 9). Taker half is charged on fill. A flip cycle (bid fill → ask place → ask fill → bid place) pays placement fee again on each new order. UpdateLimitOrderPrice does not re-charge place fee, but it cannot change side or size.
Gas (place) dApp model: batch/ladder = 400_000 + 180_000 × N vs N separate places at ~950_000 each (docs/limit-orders.md § Batch / ladder gas savings). Example: 5 rungs ≈ 1.3M vs 4.75M. Two-sided initial grid ≈ two of those txs plus two allowances.
Gas (cancel/claim) Batch cancel/claim: 400_000 + 80_000 × N (#246).
dApp /limits Ladder panel + /trade single limit. Crossing guard is client-only. Deep-book path probes indexer limit-book + insert-hints. Disconnect still renders create fields (#494).
Indexer One limit_order_placements row per action=place_limit_order. Fills go to limit_fills / trader limit-fills. Placement lifecycle_status=active is not proof the row is still in ORDERS (#530) — bots must use LCD OrderStatus (L21).
Not present No grid/range strategy object, no vault that owns orders, no CosmWasm authz helper, no “on fill, place opposite” hook, no permissionless crank, no Uniswap-v3 tick AMM.

Product confusion to avoid: this is maker inventory on the FIFO limit book, not v2 AMM LP shares (provide_liquidity) and not /ust1 mint. Support already tells users those are different (#531).

Why this is needed

  1. Manual ladders die after the first wave of fills. A maker who wants to provide liquidity in a band (buy below, sell above, recycle inventory) must watch fills and re-place the opposite side. That is the actual MM loop; the current ladder is only the opening shot.
  2. Retail cannot run that loop. /limits has no range + both-sides + recycle UX. Power users would use a bot anyway; we should know whether the protocol should host that bot on-chain or document an off-chain path.
  3. On-chain auto-flip is a gas and taker-safety decision, not a UI tweak. Doing work inside execute_swap would charge takers for makers’ re-placement and can break L5 bounded-work if unbounded. That must be priced and rejected or designed before anyone writes wasm.
  4. Wrong abstraction is expensive. A “range LP” that mints AMM shares in a tick band does not exist here. Building Uniswap-v3 on this pair would be a different protocol. The investigation must say so explicitly so a later agent does not “just add concentrated liquidity.”

Constraints / guardrails

  1. Spike only. No new ExecuteMsg, no vault deploy, no dApp Grid tab, no indexer schema in this issue. Follow-up implementation issues after the write-up is accepted.
  2. Do not put maker re-place on the taker hot path. Any on-chain design that runs extra insert_bid / insert_ask inside execute_swap (or inside max_maker_fills walk) is disallowed unless the write-up proves taker gas stays within existing envelopes and cannot be griefed. Default assumption: forbidden.
  3. Keep L5 / L6 / L14 / L17 / L20 / L21 / I13. Pause still blocks place/cancel/claim. Hints stay advisory. Crossing stays allowed on-chain; client post-only is UX-only. Human vs raw prices unchanged. OrderStatus remains the custody oracle.
  4. One side per existing batch. Do not “fix” mixed-side batch as a silent side effect. A two-sided grid is two batches or a new message that is explicitly specified.
  5. Inventory and fees are real. Flip cycles consume place-fee on every new order (unless a future message is UpdateLimitOrderPrice-like and same-side, which cannot flip). Partial fills, dust parks (L22 / #504), blacklist, and expiry must be in the model.
  6. Keys. Off-chain “the dApp signs for you” is not a product. Simulated Wallet is LocalTerra-only. Any bot needs user-held keys, authz/grant, or a vault the user deposits into. Document which Terra Classic modules actually exist on columbus-5 / LocalTerra before recommending authz.
  7. Cognitive load (#489). If a later UI exists, it is a dedicated flow (likely /limits or /trade Advanced), not a lecture on /pool. Do not call this “LP” in retail copy if it is book escrow.
  8. Do not change pool math, wrap fees, or treasury in this spike.
  9. LocalTerra for any gas measurement. Do not report SKIP (no LocalTerra) without provisioning (make setup-cloud-localterra).
  10. No farm/APR chrome and no implication that range-grid is an incentive program.

Relevant files

File Role
smartcontracts/packages/dex-common/src/pair.rs PlaceLimitOrderBatch / PlaceLimitOrderLadder, hybrid params, max_maker_fills
smartcontracts/packages/dex-common/src/limit_placement.rs Ladder expand, MAX_LIMIT_BATCH_RUNGS_HARD_CAP
smartcontracts/contracts/pair/src/limit_placement.rs Batch execute, refunds, attrs
smartcontracts/contracts/pair/src/orderbook.rs Insert/match/payouts; deferred maker sends
smartcontracts/contracts/pair/src/contract.rs execute_swap
docs/limit-orders.md Messages, gas tables, fill/park
docs/contracts-security-audit.md L4–L6, L14, L17, L21, L22
frontend-dapp/src/services/terraclassic/terraGas.ts Batch/ladder gas envelopes
frontend-dapp/src/components/trade/LimitOrderLadderPanel.tsx Current one-sided ladder UI
frontend-dapp/src/utils/limitOrderLadder.ts Client expand; sumLadderAmountsRaw
indexer/src/indexer/parser.rs Placement/fill attrs
skills/AGENTS_LIMIT_ORDER_BATCH_LADDER.md Invariants + test commands

Evaluate three architectures against the same user story (pair + range [P_low, P_high] + inventory + rung count + optional spacing). Recommend one for a later implementation issue, or none (document as integrator-only).

A — Off-chain keeper (default candidate)

  1. User places two one-sided ladders (bids below mid, asks above mid) with existing batch/ladder msgs — or a bot does it.
  2. Keeper watches indexer limit-fills (or LCD OrderStatus + fills) and, on fill, places the opposite rung at fill_price ± grid_step inside the range, using the received token as new escrow.
  3. Signing: user bot (hot wallet), or Cosmos authz if available on this chain, or a documented “run this script” path. The hosted dApp must not custody keys.
  4. Measure: median time-to-rearm vs block time; missed flips during indexer lag; behavior when OrderStatus is Unknown after a full fill (#530).
  5. Pros: no taker-gas tax, no new wasm, uses #206 gas savings. Cons: latency, keeper liveness, user must keep LUNC for gas + CW20 allowance, centralization if CL8Y hosts the keeper.

B — On-chain vault / strategy contract (only if A is insufficient)

  1. User deposits both tokens into a pair-scoped vault that is the owner of resting orders.
  2. Flip is a permissionless crank (Rebalance / OnFill) paid by the caller (user, keeper, or searcher) — never by the unrelated taker’s swap.
  3. Vault must cap work per crank (rungs, inserts, CW20 sends) the same way L5 caps swaps.
  4. Pros: non-custodial vs a hosted hot wallet; atomic re-arm. Cons: new contract surface (reentrancy, accounting, pause, blacklist, fee-on-transfer), extra hop gas, governance/code-id, factory allowlist questions.

C — In-swap auto-flip (discouraged)

  1. Pair, on fill, immediately inserts the opposite order for the same owner.
  2. Almost certainly reject: taker pays maker gas; max_maker_fills × insert cost; chicken-and-egg with deferred payouts needing the received asset as escrow; griefing by filling many tiny rungs.

Gas work the spike must produce (LocalTerra)

Use current terraGas.ts envelopes as the dApp baseline, then measure actual gas_used on LocalTerra for:

Scenario What to measure
G1 One-sided ladder place, N = 2, 5, 20, 50 (if pair cap allows), 100 (hard cap)
G2 Two-sided initial grid = G1×2 (bid tx + ask tx)
G3 Single opposite re-place after a 1-rung fill (allowance + send)
G4 Hybrid swap that fills K makers without any re-place (control)
G5 Do not prototype in-swap re-place on main; if modeled, estimate gas_used delta per extra insert and show it vs current swap envelope
G6 Cancel remaining grid (batch cancel) when range is pulled

Report LUNC fee at the LocalTerra gas price used by make deploy-local, and say whether a 20+20 grid is retail-viable vs keeper-only.

Product recommendation section (required)

The write-up must answer:

  1. Ship later as off-chain docs + optional keeper, vault, or do not build?
  2. If UI: where (/limits Advanced vs not on /pool) and copy (“range maker grid”, not “LP”).
  3. What is explicitly out of scope (Uniswap-v3 ticks, inventory in the AMM curve, yield farming).

Acceptance criteria

  • AC1 — Written recommendation. A design note (GitLab issue comment or docs/ draft linked from this issue) chooses A / B / C / none, with reasons.
  • AC2 — Current-state accuracy. Note correctly describes one-sided ladder, fill→wallet payout, no auto-flip, rung caps, and dApp gas model (cite files above).
  • AC3 — Gas table. LocalTerra gas_used (or failed attempt with logs) for G1–G4 and G6. G5 only as a paper estimate unless a throwaway branch is clearly labeled and not merged.
  • AC4 — Taker-safety. Explicit reject or tightly constrained design for in-swap re-place; show why L5 still holds.
  • AC5 — Custody. States who signs re-places (user, authz, vault). No dApp key custody. Authz claim is verified against this chain, not copied from Cosmos Hub docs.
  • AC6 — Economic loop. Accounts for place-fee on each flip, partial fills, dust park, pause, blacklist, expiry, mixed decimals (L20).
  • AC7 — Threat model. Completes the attack table below (even if the recommendation is “don’t build”).
  • AC8 — Follow-ups. If build: open or outline a separate implementation issue. This spike stays closed as investigation.
  • AC9 — Tests for the note. Any scripts/benches used for G1–G6 are in-repo or attached; make verify-issue-<iid> exists if code/docs landed.
  • AC10 — Copy boundary. Recommendation does not propose always-on /pool essays or calling book escrow “LP shares.”

Test plan (all paths)

This spike does not ship product paths; exercise the existing ladder/fill paths that the recommendation depends on, plus any measurement scripts.

Unit / contract (existing)

# Path Assert
T1 cargo test -p cl8y-dex-tests limit_batch place_limit_order_ladder One-sided ladder still places; mixed-side still impossible on one batch
T2 Fill tests in limit_order_tests.rs Maker payout to owner; remaining/cancel/park unchanged
T3 Frontend limitOrderLadder / sumLadderAmountsRaw No string-concat of amounts (#233)
T4 Crossing tests LimitOrderLadderPanel.crossing Client still blocks crossing; contract still accepts it

Measurement / LocalTerra

# Path Assert
M1 Deployed pair, unpaused G1 ladder place gas_used recorded per N
M2 Bid ladder + ask ladder G2 two-tx grid; both sides rest on limit-book
M3 Taker hybrid swap fills ≥1 bid Maker OrderStatus → not Active; token1→token0 (or vice versa) in wallet
M4 Manual opposite place using received inventory G3 gas; new order on the other side inside range
M5 Indexer lag Fill visible on LCD before indexer limit-fills (documents keeper race)
M6 Pause Place/cancel/claim blocked (L6); any proposed crank must pause too
M7 Expiry / dust park Flip must not treat parked dust as live inventory (L22)
M8 UST1/USTR-style decimals Range prices use L20 human band, not raw-as-human

If a later UI is sketched only (not shipped)

# Path Assert
U1 IA Not on /pool chrome; progressive disclosure
U2 Disconnect Create/preview still visible (#494 parity)

Test plan — attack, hack, and abuse vectors

# Vector What to prove
A1 Taker griefing Filling many tiny rungs must not unbounded-increase swap gas. In-swap flip is rejected or hard-capped independently of max_maker_fills.
A2 Cross-side batch Cannot smuggle asks into a bid PlaceLimitOrderBatch (escrow token mismatch / contract error).
A3 Crossing grid On-chain still allows marketable limits; a keeper that places the opposite rung through the spread can self-trade or take itself. Spec must use post-only / mid+tick rules like the dApp guard.
A4 Hint poisoning Malicious hint_after_order_id on re-place cannot reorder the book (L14); worst case = bounded walk / skip.
A5 Keeper key theft Hot-wallet keeper can drain remaining grid via CancelLimitOrders. Document blast radius; prefer vault with strategy-only withdraw or authz spend limits.
A6 Authz over-grant Generic MsgExecuteContract grant on the pair = cancel+place+sweep risk. If authz is recommended, the grant msg allowlist must be named.
A7 Vault insolvency Crank places with vault inventory the vault does not have (fee, wrap tax, partial fill). Must fail closed; no minting unbacked limits.
A8 Reentrancy / callback Any vault that sends CW20 then places must follow existing pair hook patterns; no untrusted sender as owner.
A9 Indexer lie Keeper that trusts only lifecycle_status=active will cancel already-filled ids or double-place. Must LCD OrderStatus (L21).
A10 MEV / sandwich on re-arm Public mempool; delayed opposite place can be picked off. Document; do not invent private-relay product in this spike (#299).
A11 Blacklist / pause Mid-grid blacklist parks orders; crank must not loop place/fail burning user gas.
A12 Fee drain Tight grid + high place-fee (unregistered) can grind inventory to treasury. Show break-even vs pair fee_bps / I13.
A13 Range breakout Price leaves [P_low, P_high]; leftover one-sided inventory. Spec stop / cancel-remainder behavior (no silent conversion to AMM LP).
A14 Dust / round-trip precision 6 vs 18 decimals; leftover too small to place (min_remaining_* force-clean).
A15 DoS crank Permissionless Rebalance spam: must be no-op cheap when nothing to do, or gated, so attackers cannot grief vault gas (if they cannot pay the vault’s fee — attacker pays crank gas; still must not lock book).

Verification criteria

  1. Human review: product + contracts can accept or reject the recommendation without reading wasm diffs (there should be none on main).
  2. Gas appendix: table G1–G4, G6 with gas_used, tx hashes on LocalTerra, and comparison to terraGas.ts envelopes (over/under).
  3. Invariant checklist: L5, L6, L14, L17, L20, L21, L22, I13 explicitly marked “unchanged” or “requires follow-up issue.”
  4. Go/no-go sentence in the first paragraph of the write-up.
  5. If docs landed: make verify-issue-<iid> greps the note for AC1–AC7 headings and the gas table.
  6. No merge of prototype vault/wasm on this issue.

Out of scope

  • Implementing the keeper, vault, or UI.
  • Uniswap-v3 / concentrated AMM ticks.
  • Changing max_maker_fills, batch mixed-side, or taker swap envelopes “to make grid work.”
  • Incentive programs, APR, or /pool marketing that this is LP.
## Summary Investigate an **automated range ladder** for liquidity provisioning: the user picks a pair, a **price range**, and inventory; the system places a **grid of resting limits** that **flip from buy to sell and back** as rungs fill, so the maker stays in-range without babysitting `/limits`. This issue is a **design / gas / threat-model spike**. Do **not** ship a new CosmWasm message, vault, or retail UI in this work item. The deliverable is a written recommendation: **off-chain service vs on-chain**, with **gas numbers**, **security constraints**, and a go/no-go for a follow-up implementation issue. Related: [#206](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/206) (batch / one-sided ladder place), [#247](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/247) (batch storage collapse), [#246](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/246) (batch cancel/claim), [#266](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/266) / [#267](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/267) / [#268](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/268) (deep-book hints), [#152](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/152) (pair accepts crossing limits; dApp is post-only), [#297](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/297) / [#385](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/385) (ladder crossing guard), [#514](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/514) (maker placement discount **I13**), [#529](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/529) (human vs raw limit prices **L20**), [#504](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/504) / [#505](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/505) (park reason / `OrderStatus`), [#489](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/489) (no always-on essays if a later UI exists). ## Current codebase The DEX already has a **one-shot, one-sided limit ladder**. It does **not** keep a maker in a range after fills. Filled makers receive the other token in their wallet; nothing re-escrows it on the opposite side. | Layer | Behavior today | |-------|----------------| | **Place** | `Cw20HookMsg::PlaceLimitOrderBatch` / `PlaceLimitOrderLadder`. Ladder expands on-chain (`equal` distribution only) to the same batch rules. **One side per tx** — bid escrows token1, ask escrows token0. Mixed buy+sell grids require **two** placements. | | **Rung caps** | Pair `max_batch_rungs` (factory default / `SetPairLimitBatchMax`). Hard ceiling `MAX_LIMIT_BATCH_RUNGS_HARD_CAP` = **100** (`dex-common` `limit_placement.rs`; LocalTerra gas [#263](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/263)). Retail ladder UI is typically far below that. | | **Fill** | Hybrid `execute_swap` walks the book under `max_maker_fills` (hard cap **100**) and `MAX_SCAN_STEPS` (500) — invariant **L5**. Maker payouts are **deferred CW20 transfers to the order owner** at the end of the swap ([#248](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/248)). The resting row is reduced / removed. **No opposite-side place.** | | **Fees** | Maker pays **half** of effective pair fee at **placement** (from escrow; **I13** can zero the place half at tier 9). Taker half is charged on fill. A flip cycle (bid fill → ask place → ask fill → bid place) pays **placement fee again** on each new order. `UpdateLimitOrderPrice` does **not** re-charge place fee, but it cannot change side or size. | | **Gas (place)** | dApp model: batch/ladder = `400_000 + 180_000 × N` vs N separate places at ~`950_000` each ([`docs/limit-orders.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/limit-orders.md#batch-ladder-gas-savings) § Batch / ladder gas savings). Example: 5 rungs ≈ **1.3M** vs **4.75M**. Two-sided initial grid ≈ **two** of those txs plus two allowances. | | **Gas (cancel/claim)** | Batch cancel/claim: `400_000 + 80_000 × N` ([#246](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/246)). | | **dApp** | `/limits` **Ladder** panel + `/trade` single limit. Crossing guard is **client-only**. Deep-book path probes indexer `limit-book` + `insert-hints`. Disconnect still renders create fields ([#494](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/494)). | | **Indexer** | One `limit_order_placements` row per `action=place_limit_order`. Fills go to `limit_fills` / trader `limit-fills`. Placement `lifecycle_status=active` is **not** proof the row is still in `ORDERS` ([#530](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/530)) — bots must use LCD `OrderStatus` (**L21**). | | **Not present** | No grid/range strategy object, no vault that owns orders, no CosmWasm authz helper, no “on fill, place opposite” hook, no permissionless crank, no Uniswap-v3 tick AMM. | **Product confusion to avoid:** this is **maker inventory on the FIFO limit book**, not v2 AMM LP shares (`provide_liquidity`) and not `/ust1` mint. Support already tells users those are different ([#531](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/531)). ## Why this is needed 1. **Manual ladders die after the first wave of fills.** A maker who wants to provide liquidity *in a band* (buy below, sell above, recycle inventory) must watch fills and re-place the opposite side. That is the actual MM loop; the current ladder is only the opening shot. 2. **Retail cannot run that loop.** `/limits` has no range + both-sides + recycle UX. Power users would use a bot anyway; we should know whether the protocol should host that bot on-chain or document an off-chain path. 3. **On-chain auto-flip is a gas and taker-safety decision**, not a UI tweak. Doing work *inside* `execute_swap` would charge **takers** for **makers’** re-placement and can break **L5** bounded-work if unbounded. That must be priced and rejected or designed *before* anyone writes wasm. 4. **Wrong abstraction is expensive.** A “range LP” that mints AMM shares in a tick band does not exist here. Building Uniswap-v3 on this pair would be a different protocol. The investigation must say so explicitly so a later agent does not “just add concentrated liquidity.” ## Constraints / guardrails 1. **Spike only.** No new `ExecuteMsg`, no vault deploy, no dApp Grid tab, no indexer schema in this issue. Follow-up implementation issues after the write-up is accepted. 2. **Do not put maker re-place on the taker hot path.** Any on-chain design that runs extra `insert_bid` / `insert_ask` inside `execute_swap` (or inside `max_maker_fills` walk) is **disallowed** unless the write-up proves taker gas stays within existing envelopes and cannot be griefed. Default assumption: **forbidden**. 3. **Keep L5 / L6 / L14 / L17 / L20 / L21 / I13.** Pause still blocks place/cancel/claim. Hints stay advisory. Crossing stays allowed on-chain; client post-only is UX-only. Human vs raw prices unchanged. `OrderStatus` remains the custody oracle. 4. **One side per existing batch.** Do not “fix” mixed-side batch as a silent side effect. A two-sided grid is two batches or a new message that is explicitly specified. 5. **Inventory and fees are real.** Flip cycles consume place-fee on every new order (unless a future message is `UpdateLimitOrderPrice`-like and same-side, which cannot flip). Partial fills, dust parks (**L22** / [#504](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/504)), blacklist, and expiry must be in the model. 6. **Keys.** Off-chain “the dApp signs for you” is **not** a product. Simulated Wallet is LocalTerra-only. Any bot needs **user-held keys**, **authz/grant**, or a **vault the user deposits into**. Document which Terra Classic modules actually exist on columbus-5 / LocalTerra before recommending authz. 7. **Cognitive load ([#489](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/489)).** If a later UI exists, it is a dedicated flow (likely `/limits` or `/trade` Advanced), not a lecture on `/pool`. Do not call this “LP” in retail copy if it is book escrow. 8. **Do not change pool math, wrap fees, or treasury** in this spike. 9. **LocalTerra** for any gas measurement. Do not report `SKIP (no LocalTerra)` without provisioning (`make setup-cloud-localterra`). 10. **No farm/APR chrome** and no implication that range-grid is an incentive program. ## Relevant files | File | Role | |------|------| | [`smartcontracts/packages/dex-common/src/pair.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/smartcontracts/packages/dex-common/src/pair.rs) | `PlaceLimitOrderBatch` / `PlaceLimitOrderLadder`, hybrid params, `max_maker_fills` | | [`smartcontracts/packages/dex-common/src/limit_placement.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/smartcontracts/packages/dex-common/src/limit_placement.rs) | Ladder expand, `MAX_LIMIT_BATCH_RUNGS_HARD_CAP` | | [`smartcontracts/contracts/pair/src/limit_placement.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/smartcontracts/contracts/pair/src/limit_placement.rs) | Batch execute, refunds, attrs | | [`smartcontracts/contracts/pair/src/orderbook.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/smartcontracts/contracts/pair/src/orderbook.rs) | Insert/match/payouts; deferred maker sends | | [`smartcontracts/contracts/pair/src/contract.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/smartcontracts/contracts/pair/src/contract.rs) | `execute_swap` | | [`docs/limit-orders.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/limit-orders.md) | Messages, gas tables, fill/park | | [`docs/contracts-security-audit.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/contracts-security-audit.md) | **L4–L6**, **L14**, **L17**, **L21**, **L22** | | [`frontend-dapp/src/services/terraclassic/terraGas.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/services/terraclassic/terraGas.ts) | Batch/ladder gas envelopes | | [`frontend-dapp/src/components/trade/LimitOrderLadderPanel.tsx`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/components/trade/LimitOrderLadderPanel.tsx) | Current one-sided ladder UI | | [`frontend-dapp/src/utils/limitOrderLadder.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/utils/limitOrderLadder.ts) | Client expand; `sumLadderAmountsRaw` | | [`indexer/src/indexer/parser.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/indexer/parser.rs) | Placement/fill attrs | | [`skills/AGENTS_LIMIT_ORDER_BATCH_LADDER.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_LIMIT_ORDER_BATCH_LADDER.md) | Invariants + test commands | ## Recommended direction Evaluate **three** architectures against the same user story (pair + range `[P_low, P_high]` + inventory + rung count + optional spacing). Recommend **one** for a later implementation issue, or **none** (document as integrator-only). ### A — Off-chain keeper (default candidate) 1. User places **two** one-sided ladders (bids below mid, asks above mid) with existing batch/ladder msgs — or a bot does it. 2. Keeper watches indexer `limit-fills` (or LCD `OrderStatus` + fills) and, on fill, places the **opposite** rung at `fill_price ± grid_step` **inside the range**, using the received token as new escrow. 3. Signing: **user bot** (hot wallet), or **Cosmos `authz`** if available on this chain, or a **documented** “run this script” path. The hosted dApp must **not** custody keys. 4. Measure: median time-to-rearm vs block time; missed flips during indexer lag; behavior when `OrderStatus` is `Unknown` after a full fill ([#530](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/530)). 5. **Pros:** no taker-gas tax, no new wasm, uses #206 gas savings. **Cons:** latency, keeper liveness, user must keep LUNC for gas + CW20 allowance, centralization if CL8Y hosts the keeper. ### B — On-chain vault / strategy contract (only if A is insufficient) 1. User deposits both tokens into a **pair-scoped vault** that is the `owner` of resting orders. 2. Flip is a **permissionless crank** (`Rebalance` / `OnFill`) paid by the caller (user, keeper, or searcher) — **never** by the unrelated taker’s swap. 3. Vault must cap work per crank (rungs, inserts, CW20 sends) the same way **L5** caps swaps. 4. **Pros:** non-custodial vs a hosted hot wallet; atomic re-arm. **Cons:** new contract surface (reentrancy, accounting, pause, blacklist, fee-on-transfer), extra hop gas, governance/code-id, factory allowlist questions. ### C — In-swap auto-flip (discouraged) 1. Pair, on fill, immediately inserts the opposite order for the same owner. 2. Almost certainly **reject**: taker pays maker gas; `max_maker_fills` × insert cost; chicken-and-egg with deferred payouts needing the received asset as escrow; griefing by filling many tiny rungs. ### Gas work the spike must produce (LocalTerra) Use current `terraGas.ts` envelopes as the **dApp** baseline, then measure **actual** `gas_used` on LocalTerra for: | Scenario | What to measure | |----------|-----------------| | G1 | One-sided ladder place, N = 2, 5, 20, 50 (if pair cap allows), 100 (hard cap) | | G2 | Two-sided initial grid = G1×2 (bid tx + ask tx) | | G3 | Single opposite re-place after a 1-rung fill (allowance + send) | | G4 | Hybrid swap that fills K makers **without** any re-place (control) | | G5 | **Do not** prototype in-swap re-place on main; if modeled, estimate `gas_used` delta per extra insert and show it vs current swap envelope | | G6 | Cancel remaining grid (batch cancel) when range is pulled | Report LUNC fee at the LocalTerra gas price used by `make deploy-local`, and say whether a 20+20 grid is retail-viable vs keeper-only. ### Product recommendation section (required) The write-up must answer: 1. Ship later as **off-chain docs + optional keeper**, **vault**, or **do not build**? 2. If UI: where (`/limits` Advanced vs not on `/pool`) and copy (“range maker grid”, not “LP”). 3. What is explicitly **out of scope** (Uniswap-v3 ticks, inventory in the AMM curve, yield farming). ## Acceptance criteria - [ ] **AC1 — Written recommendation.** A design note (GitLab issue comment or `docs/` draft linked from this issue) chooses A / B / C / none, with reasons. - [ ] **AC2 — Current-state accuracy.** Note correctly describes one-sided ladder, fill→wallet payout, no auto-flip, rung caps, and dApp gas model (cite files above). - [ ] **AC3 — Gas table.** LocalTerra `gas_used` (or failed attempt with logs) for G1–G4 and G6. G5 only as a paper estimate unless a throwaway branch is clearly labeled and not merged. - [ ] **AC4 — Taker-safety.** Explicit reject or tightly constrained design for in-swap re-place; show why **L5** still holds. - [ ] **AC5 — Custody.** States who signs re-places (user, authz, vault). No dApp key custody. Authz claim is verified against this chain, not copied from Cosmos Hub docs. - [ ] **AC6 — Economic loop.** Accounts for place-fee on each flip, partial fills, dust park, pause, blacklist, expiry, mixed decimals (**L20**). - [ ] **AC7 — Threat model.** Completes the attack table below (even if the recommendation is “don’t build”). - [ ] **AC8 — Follow-ups.** If build: open or outline a **separate** implementation issue. This spike stays closed as investigation. - [ ] **AC9 — Tests for the note.** Any scripts/benches used for G1–G6 are in-repo or attached; `make verify-issue-<iid>` exists if code/docs landed. - [ ] **AC10 — Copy boundary.** Recommendation does not propose always-on `/pool` essays or calling book escrow “LP shares.” ## Test plan (all paths) This spike does not ship product paths; **exercise the existing ladder/fill paths** that the recommendation depends on, plus any measurement scripts. ### Unit / contract (existing) | # | Path | Assert | |---|------|--------| | T1 | `cargo test -p cl8y-dex-tests limit_batch place_limit_order_ladder` | One-sided ladder still places; mixed-side still impossible on one batch | | T2 | Fill tests in `limit_order_tests.rs` | Maker payout to owner; remaining/cancel/park unchanged | | T3 | Frontend `limitOrderLadder` / `sumLadderAmountsRaw` | No string-concat of amounts ([#233](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/233)) | | T4 | Crossing tests `LimitOrderLadderPanel.crossing` | Client still blocks crossing; contract still accepts it | ### Measurement / LocalTerra | # | Path | Assert | |---|------|--------| | M1 | Deployed pair, unpaused | G1 ladder place `gas_used` recorded per N | | M2 | Bid ladder + ask ladder | G2 two-tx grid; both sides rest on `limit-book` | | M3 | Taker hybrid swap fills ≥1 bid | Maker `OrderStatus` → not `Active`; token1→token0 (or vice versa) in wallet | | M4 | Manual opposite place using received inventory | G3 gas; new order on the other side inside range | | M5 | Indexer lag | Fill visible on LCD before indexer `limit-fills` (documents keeper race) | | M6 | Pause | Place/cancel/claim blocked (**L6**); any proposed crank must pause too | | M7 | Expiry / dust park | Flip must not treat parked dust as live inventory (**L22**) | | M8 | UST1/USTR-style decimals | Range prices use **L20** human band, not raw-as-human | ### If a later UI is sketched only (not shipped) | # | Path | Assert | |---|------|--------| | U1 | IA | Not on `/pool` chrome; progressive disclosure | | U2 | Disconnect | Create/preview still visible ([#494](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/494) parity) | ## Test plan — attack, hack, and abuse vectors | # | Vector | What to prove | |---|--------|----------------| | A1 | **Taker griefing** | Filling many tiny rungs must not unbounded-increase swap gas. In-swap flip is rejected or hard-capped independently of `max_maker_fills`. | | A2 | **Cross-side batch** | Cannot smuggle asks into a bid `PlaceLimitOrderBatch` (escrow token mismatch / contract error). | | A3 | **Crossing grid** | On-chain still allows marketable limits; a keeper that places the opposite rung **through** the spread can self-trade or take itself. Spec must use post-only / mid+tick rules like the dApp guard. | | A4 | **Hint poisoning** | Malicious `hint_after_order_id` on re-place cannot reorder the book (**L14**); worst case = bounded walk / skip. | | A5 | **Keeper key theft** | Hot-wallet keeper can drain remaining grid via `CancelLimitOrders`. Document blast radius; prefer vault with strategy-only withdraw or authz spend limits. | | A6 | **Authz over-grant** | Generic `MsgExecuteContract` grant on the pair = cancel+place+sweep risk. If authz is recommended, the grant msg allowlist must be named. | | A7 | **Vault insolvency** | Crank places with vault inventory the vault does not have (fee, wrap tax, partial fill). Must fail closed; no minting unbacked limits. | | A8 | **Reentrancy / callback** | Any vault that sends CW20 then places must follow existing pair hook patterns; no untrusted `sender` as owner. | | A9 | **Indexer lie** | Keeper that trusts only `lifecycle_status=active` will cancel already-filled ids or double-place. Must LCD `OrderStatus` (**L21**). | | A10 | **MEV / sandwich on re-arm** | Public mempool; delayed opposite place can be picked off. Document; do not invent private-relay product in this spike ([#299](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/299)). | | A11 | **Blacklist / pause** | Mid-grid blacklist parks orders; crank must not loop place/fail burning user gas. | | A12 | **Fee drain** | Tight grid + high place-fee (unregistered) can grind inventory to treasury. Show break-even vs pair `fee_bps` / **I13**. | | A13 | **Range breakout** | Price leaves `[P_low, P_high]`; leftover one-sided inventory. Spec stop / cancel-remainder behavior (no silent conversion to AMM LP). | | A14 | **Dust / round-trip precision** | 6 vs 18 decimals; leftover too small to place (`min_remaining_*` force-clean). | | A15 | **DoS crank** | Permissionless `Rebalance` spam: must be no-op cheap when nothing to do, or gated, so attackers cannot grief vault gas (if they cannot pay the vault’s fee — attacker pays crank gas; still must not lock book). | ## Verification criteria 1. **Human review:** product + contracts can accept or reject the recommendation without reading wasm diffs (there should be none on `main`). 2. **Gas appendix:** table G1–G4, G6 with `gas_used`, tx hashes on LocalTerra, and comparison to `terraGas.ts` envelopes (over/under). 3. **Invariant checklist:** L5, L6, L14, L17, L20, L21, L22, I13 explicitly marked “unchanged” or “requires follow-up issue.” 4. **Go/no-go sentence** in the first paragraph of the write-up. 5. If docs landed: `make verify-issue-<iid>` greps the note for AC1–AC7 headings and the gas table. 6. No merge of prototype vault/wasm on this issue. ## Out of scope - Implementing the keeper, vault, or UI. - Uniswap-v3 / concentrated AMM ticks. - Changing `max_maker_fills`, batch mixed-side, or taker swap envelopes “to make grid work.” - Incentive programs, APR, or `/pool` marketing that this is LP.
PlasticDigits commented 2026-08-17 10:26:07 +00:00 (Migrated from gitlab.com)

marked as related to #206

marked as related to #206
PlasticDigits commented 2026-08-17 10:26:07 +00:00 (Migrated from gitlab.com)

marked as related to #247

marked as related to #247
PlasticDigits commented 2026-08-17 10:26:08 +00:00 (Migrated from gitlab.com)

marked as related to #266

marked as related to #266
PlasticDigits commented 2026-08-17 10:26:08 +00:00 (Migrated from gitlab.com)

marked as related to #152

marked as related to #152
PlasticDigits commented 2026-08-17 10:26:09 +00:00 (Migrated from gitlab.com)

marked as related to #514

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

Note that this grid provides an improved functionality over v3 lp, as it has more control and lower complexity than v3 lp, but for branding should be presented as "V3 Grid" so for users who are looking to provide liquidity over a range should be using V3 Grid instead of v2 lp.

Note that this grid provides an improved functionality over v3 lp, as it has more control and lower complexity than v3 lp, but for branding should be presented as "V3 Grid" so for users who are looking to provide liquidity over a range should be using V3 Grid instead of v2 lp.
PlasticDigits commented 2026-08-24 02:49:56 +00:00 (Migrated from gitlab.com)

User when creating/updating grid should be able to set the "Spread Fee" from 1 to 2500 bps (displayed as percent lp fee) so that trading in range generates profits for the user by having levels when triggered and flipping going up/down by the spread, for instance if a buy triggers at $1 and spread is 2% the sell would flip to 1.02, or if 1 bpos $1 would flip to 1.0001. Or if a sell triggers at $1.02 would go back to $1 (so theres no drift over time). We should also be tracking volatility and trading history on the pairs to estimate a 95% probability apr range (based on the spread, volatility, volume, and users cl8y tier) and for each grid the user has set up, show daily, weekly, monthly, and all time revenue growths and apr. Ideally, the asset growth from spread should be automatically readded in a gas efficient way (doesnt have to be perfectly optimal on where its allocated), but if its not then there should be a "reinvest" button so the user can redeploy the assets back into the grid, vs a "claim" button to claim the assets.

User when creating/updating grid should be able to set the "Spread Fee" from 1 to 2500 bps (displayed as percent lp fee) so that trading in range generates profits for the user by having levels when triggered and flipping going up/down by the spread, for instance if a buy triggers at $1 and spread is 2% the sell would flip to 1.02, or if 1 bpos $1 would flip to 1.0001. Or if a sell triggers at $1.02 would go back to $1 (so theres no drift over time). We should also be tracking volatility and trading history on the pairs to estimate a 95% probability apr range (based on the spread, volatility, volume, and users cl8y tier) and for each grid the user has set up, show daily, weekly, monthly, and all time revenue growths and apr. Ideally, the asset growth from spread should be automatically readded in a gas efficient way (doesnt have to be perfectly optimal on where its allocated), but if its not then there should be a "reinvest" button so the user can redeploy the assets back into the grid, vs a "claim" button to claim the assets.
PlasticDigits commented 2026-08-24 03:15:16 +00:00 (Migrated from gitlab.com)

mentioned in issue #617

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

marked as related to #617

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

mentioned in issue #618

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

marked as related to #618

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

mentioned in issue #619

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

marked as related to #619

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

Decision (go)

Go: option E — full V3 Grid as an on-chain vault with a permissionless Rebalance crank. Do not ship in-swap flip, hosted authz keepers, or a Claim/Reinvest retail loop.

This note closes the #546 architecture choice. Implementation is split into follow-up issues (below). #546 itself stays an investigation ticket: no vault/wasm merge here.

Picks (2026-08-24)

Axis Choice
Architecture Item 5 — vault owns orders; permissionless crank; not inside execute_swap
Crank tip Caller may claim 5% of realized grid fees (500 bps, integer floor) to cover gas / keeper
Keeper Separate package (grid-keeper/). Official policy: crank when claimable tip ≥ 2× estimated LUNC gas, then autoswap tip → LUNC
Ship scope S4 — full V3 Grid product
Economics E2 — paired spread, no drift (P ↔ P*(1+s)). Inventory growth is compounded on Rebalance
Rejected UX No Claim / Reinvest / Rebalance buttons
Surface Route /v3, copy “V3 Grid”, short links from /pool and /limits

Rejected alternatives

  • A / integrator-only — not enough for S4.
  • Manual one-click re-arm — rejected; crank is permissionless + keeper.
  • Hosted authz on user/pair MsgExecuteContract — cancel+sweep blast radius; columbus-5 has x/authz but CosmWasm grants are too wide.
  • In-swap auto-flip (C) — taker-gas tax; L5 griefing; deferred maker payouts (#248). Forbidden.
  • Uniswap-v3 ticks / v2 LP mint — different protocol. Brand V3 Grid, not “V3 LP shares.”
  • Claim / Reinvest harvest UI — fights vault compounding.

Semantics that implementers must not drift

  1. Fees for the 5% tip = vault-accrued realized spread surplus (completed buy+sell at the paired prices), not pair fee_bps / treasury commission.
  2. 2× gas is keeper policy, not a vault lock. Rebalance stays callable whenever there is work. First fill often has no realized fee yet; if the vault required tip ≥ 2× gas to flip, the book would sit one-sided. Searchers may crank earlier.
  3. Place discount (I13) is the vault address, not the depositor. /v3 APR may show the user’s CL8Y tier as an estimate; execution must not spoof trader.
  4. Copy: V3 Grid. Not LP shares, not an incentive program (#531 / #489). Discovery = /v3 + links, not a /pool lecture.

Follow-up issues

Issue Bundle
#617 Vault + factory index, E2 flip, optional 5% tip, compound-on-rebalance, LocalTerra G1–G6 + vault gas
#618 grid-keeper/ package — 2×-gas gate, LCD OrderStatus, tip→LUNC (blocked by #617)
#619 /v3 UI, indexer revenue/APR, Pool/Limits links; no harvest buttons (blocked by #617, not by #618)

Still on this spike (optional close-out)

#546 AC3 gas table for the existing ladder can land as an appendix on #617 (same LocalTerra session as vault benches). No prototype vault on main under this iid.

Related comments on this issue (spread 1–2500 bps, no-drift pairing, V3 Grid branding, APR/revenue windows, auto-reinvest via crank) are accepted into #617/#619. Auto-reinvest is Rebalance compounding, not a Reinvest button.

## Decision (go) **Go: option E — full V3 Grid** as an on-chain vault with a permissionless `Rebalance` crank. Do **not** ship in-swap flip, hosted `authz` keepers, or a Claim/Reinvest retail loop. This note closes the #546 architecture choice. Implementation is split into follow-up issues (below). #546 itself stays an investigation ticket: no vault/wasm merge here. ### Picks (2026-08-24) | Axis | Choice | |------|--------| | Architecture | **Item 5** — vault owns orders; **permissionless crank**; **not** inside `execute_swap` | | Crank tip | Caller **may** claim **5%** of **realized grid fees** (`500 bps`, integer floor) to cover gas / keeper | | Keeper | **Separate package** (`grid-keeper/`). Official policy: crank when **claimable tip ≥ 2× estimated LUNC gas**, then **autoswap tip → LUNC** | | Ship scope | **S4** — full V3 Grid product | | Economics | **E2** — paired spread, **no drift** (`P` ↔ `P*(1+s)`). Inventory growth is **compounded on `Rebalance`** | | Rejected UX | **No Claim / Reinvest / Rebalance buttons** | | Surface | Route **`/v3`**, copy **“V3 Grid”**, short links from **`/pool`** and **`/limits`** | ### Rejected alternatives - **A / integrator-only** — not enough for S4. - **Manual one-click re-arm** — rejected; crank is permissionless + keeper. - **Hosted authz on user/pair `MsgExecuteContract`** — cancel+sweep blast radius; columbus-5 has `x/authz` but CosmWasm grants are too wide. - **In-swap auto-flip (C)** — taker-gas tax; **L5** griefing; deferred maker payouts (#248). Forbidden. - **Uniswap-v3 ticks / v2 LP mint** — different protocol. Brand **V3 Grid**, not “V3 LP shares.” - **Claim / Reinvest harvest UI** — fights vault compounding. ### Semantics that implementers must not drift 1. **Fees for the 5% tip** = vault-accrued **realized spread surplus** (completed buy+sell at the paired prices), **not** pair `fee_bps` / treasury commission. 2. **2× gas is keeper policy**, not a vault lock. `Rebalance` stays callable whenever there is work. First fill often has **no** realized fee yet; if the vault required `tip ≥ 2× gas` to flip, the book would sit one-sided. Searchers may crank earlier. 3. **Place discount (I13)** is the **vault address**, not the depositor. `/v3` APR may show the user’s CL8Y tier as an estimate; execution must not spoof `trader`. 4. **Copy:** **V3 Grid**. Not LP shares, not an incentive program (#531 / #489). Discovery = `/v3` + links, not a `/pool` lecture. ### Follow-up issues | Issue | Bundle | |-------|--------| | [#617](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/617) | Vault + factory index, E2 flip, optional 5% tip, compound-on-rebalance, LocalTerra G1–G6 + vault gas | | [#618](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/618) | `grid-keeper/` package — 2×-gas gate, LCD `OrderStatus`, tip→LUNC (**blocked by #617**) | | [#619](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/619) | `/v3` UI, indexer revenue/APR, Pool/Limits links; **no** harvest buttons (**blocked by #617**, not by #618) | ### Still on this spike (optional close-out) #546 AC3 gas table for the **existing** ladder can land as an appendix on **#617** (same LocalTerra session as vault benches). No prototype vault on `main` under this iid. Related comments on this issue (spread 1–2500 bps, no-drift pairing, V3 Grid branding, APR/revenue windows, auto-reinvest via crank) are **accepted** into #617/#619. Auto-reinvest is **`Rebalance` compounding**, not a Reinvest button.
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:20 +00:00 (Migrated from gitlab.com)

marked as related to #650

marked as related to #650
PlasticDigits commented 2026-09-01 08:14:36 +00:00 (Migrated from gitlab.com)

marked as related to #717

marked as related to #717
PlasticDigits commented 2026-09-01 08:14:36 +00:00 (Migrated from gitlab.com)

mentioned in issue #717

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