design: investigate automated range ladder LP (buy↔sell grid) — off-chain vs on-chain gas #546
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#546
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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.
Cw20HookMsg::PlaceLimitOrderBatch/PlaceLimitOrderLadder. Ladder expands on-chain (equaldistribution only) to the same batch rules. One side per tx — bid escrows token1, ask escrows token0. Mixed buy+sell grids require two placements.max_batch_rungs(factory default /SetPairLimitBatchMax). Hard ceilingMAX_LIMIT_BATCH_RUNGS_HARD_CAP= 100 (dex-commonlimit_placement.rs; LocalTerra gas #263). Retail ladder UI is typically far below that.execute_swapwalks the book undermax_maker_fills(hard cap 100) andMAX_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.UpdateLimitOrderPricedoes not re-charge place fee, but it cannot change side or size.400_000 + 180_000 × Nvs N separate places at ~950_000each (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.400_000 + 80_000 × N(#246)./limitsLadder panel +/tradesingle limit. Crossing guard is client-only. Deep-book path probes indexerlimit-book+insert-hints. Disconnect still renders create fields (#494).limit_order_placementsrow peraction=place_limit_order. Fills go tolimit_fills/ traderlimit-fills. Placementlifecycle_status=activeis not proof the row is still inORDERS(#530) — bots must use LCDOrderStatus(L21).Product confusion to avoid: this is maker inventory on the FIFO limit book, not v2 AMM LP shares (
provide_liquidity) and not/ust1mint. Support already tells users those are different (#531).Why this is needed
/limitshas 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.execute_swapwould 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.Constraints / guardrails
ExecuteMsg, no vault deploy, no dApp Grid tab, no indexer schema in this issue. Follow-up implementation issues after the write-up is accepted.insert_bid/insert_askinsideexecute_swap(or insidemax_maker_fillswalk) is disallowed unless the write-up proves taker gas stays within existing envelopes and cannot be griefed. Default assumption: forbidden.OrderStatusremains the custody oracle.UpdateLimitOrderPrice-like and same-side, which cannot flip). Partial fills, dust parks (L22 / #504), blacklist, and expiry must be in the model./limitsor/tradeAdvanced), not a lecture on/pool. Do not call this “LP” in retail copy if it is book escrow.SKIP (no LocalTerra)without provisioning (make setup-cloud-localterra).Relevant files
smartcontracts/packages/dex-common/src/pair.rsPlaceLimitOrderBatch/PlaceLimitOrderLadder, hybrid params,max_maker_fillssmartcontracts/packages/dex-common/src/limit_placement.rsMAX_LIMIT_BATCH_RUNGS_HARD_CAPsmartcontracts/contracts/pair/src/limit_placement.rssmartcontracts/contracts/pair/src/orderbook.rssmartcontracts/contracts/pair/src/contract.rsexecute_swapdocs/limit-orders.mddocs/contracts-security-audit.mdfrontend-dapp/src/services/terraclassic/terraGas.tsfrontend-dapp/src/components/trade/LimitOrderLadderPanel.tsxfrontend-dapp/src/utils/limitOrderLadder.tssumLadderAmountsRawindexer/src/indexer/parser.rsskills/AGENTS_LIMIT_ORDER_BATCH_LADDER.mdRecommended 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)
limit-fills(or LCDOrderStatus+ fills) and, on fill, places the opposite rung atfill_price ± grid_stepinside the range, using the received token as new escrow.authzif available on this chain, or a documented “run this script” path. The hosted dApp must not custody keys.OrderStatusisUnknownafter a full fill (#530).B — On-chain vault / strategy contract (only if A is insufficient)
ownerof resting orders.Rebalance/OnFill) paid by the caller (user, keeper, or searcher) — never by the unrelated taker’s swap.C — In-swap auto-flip (discouraged)
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.tsenvelopes as the dApp baseline, then measure actualgas_usedon LocalTerra for:gas_useddelta per extra insert and show it vs current swap envelopeReport 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:
/limitsAdvanced vs not on/pool) and copy (“range maker grid”, not “LP”).Acceptance criteria
docs/draft linked from this issue) chooses A / B / C / none, with reasons.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.make verify-issue-<iid>exists if code/docs landed./poolessays 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)
cargo test -p cl8y-dex-tests limit_batch place_limit_order_ladderlimit_order_tests.rslimitOrderLadder/sumLadderAmountsRawLimitOrderLadderPanel.crossingMeasurement / LocalTerra
gas_usedrecorded per Nlimit-bookOrderStatus→ notActive; token1→token0 (or vice versa) in walletlimit-fills(documents keeper race)If a later UI is sketched only (not shipped)
/poolchrome; progressive disclosureTest plan — attack, hack, and abuse vectors
max_maker_fills.PlaceLimitOrderBatch(escrow token mismatch / contract error).hint_after_order_idon re-place cannot reorder the book (L14); worst case = bounded walk / skip.CancelLimitOrders. Document blast radius; prefer vault with strategy-only withdraw or authz spend limits.MsgExecuteContractgrant on the pair = cancel+place+sweep risk. If authz is recommended, the grant msg allowlist must be named.senderas owner.lifecycle_status=activewill cancel already-filled ids or double-place. Must LCDOrderStatus(L21).fee_bps/ I13.[P_low, P_high]; leftover one-sided inventory. Spec stop / cancel-remainder behavior (no silent conversion to AMM LP).min_remaining_*force-clean).Rebalancespam: 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
main).gas_used, tx hashes on LocalTerra, and comparison toterraGas.tsenvelopes (over/under).make verify-issue-<iid>greps the note for AC1–AC7 headings and the gas table.Out of scope
max_maker_fills, batch mixed-side, or taker swap envelopes “to make grid work.”/poolmarketing that this is LP.marked as related to #206
marked as related to #247
marked as related to #266
marked as related to #152
marked as related to #514
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.
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.
mentioned in issue #617
marked as related to #617
mentioned in issue #618
marked as related to #618
mentioned in issue #619
marked as related to #619
Decision (go)
Go: option E — full V3 Grid as an on-chain vault with a permissionless
Rebalancecrank. Do not ship in-swap flip, hostedauthzkeepers, 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)
execute_swap500 bps, integer floor) to cover gas / keepergrid-keeper/). Official policy: crank when claimable tip ≥ 2× estimated LUNC gas, then autoswap tip → LUNCP↔P*(1+s)). Inventory growth is compounded onRebalance/v3, copy “V3 Grid”, short links from/pooland/limitsRejected alternatives
MsgExecuteContract— cancel+sweep blast radius; columbus-5 hasx/authzbut CosmWasm grants are too wide.Semantics that implementers must not drift
fee_bps/ treasury commission.Rebalancestays callable whenever there is work. First fill often has no realized fee yet; if the vault requiredtip ≥ 2× gasto flip, the book would sit one-sided. Searchers may crank earlier./v3APR may show the user’s CL8Y tier as an estimate; execution must not spooftrader./v3+ links, not a/poollecture.Follow-up issues
grid-keeper/package — 2×-gas gate, LCDOrderStatus, tip→LUNC (blocked by #617)/v3UI, 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
mainunder 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
Rebalancecompounding, not a Reinvest button.mentioned in issue #650
marked as related to #650
marked as related to #717
mentioned in issue #717