feat: V3 Grid keeper package — 2x-gas crank + tip to LUNC (from #546) #618

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

Summary

Add a separate repo package (grid-keeper/) that permissionlessly cranks V3 Grid vaults: call Rebalance { claim_tip: true } when the claimable 5% fee tip is worth at least 2× the estimated LUNC gas, then autoswap the tip to LUNC.

This is the operator half of #546 option E. It must not live inside indexer/ (no signing keys in the indexer process) and must not be a CosmWasm contract.

Blocked by #617 (message shapes, fee pot query, events).

Current codebase

Layer Behavior today
Pair / ladder One-sided PlaceLimitOrderBatch / PlaceLimitOrderLadder. Fills pay the order owner. No auto-flip.
Indexer One limit_order_placements row per action=place_limit_order. Fills in limit_fills. lifecycle_status=active is not proof the row is still in ORDERS (#530). Bots must use LCD OrderStatus (L21).
Insert hints Indexer GET .../limit-book/insert-hints (#267). Advisory only (L14).
Hub USD GET /api/v1/hub-prices (#556) — useful to value a CW20 tip in USD/LUNC terms, not a signing path.
Route GET/POST /api/v1/route/solve for hybrid quotes when swapping tip → LUNC (#596).
Gas dApp envelopes in terraGas.ts: batch place 400_000 + 180_000×N; columbus-5 floor 28.325 uluna/gas.
Docs-only watcher CleanLimitBook future watcher is docs only (docs/limit-orders.md § Permissionless limit book clean). No in-repo crank binary.
Not present No grid-keeper crate, no vault poll loop, no tip→LUNC swap bot, no authz helper. Simulated Wallet is LocalTerra-only and must not be the production signer.

Community-tax AutoLP has a permissionless SkimToLp but no keeper package. Do not bolt this loop onto the indexer or the frontend.

Why this is needed

  1. The vault’s Rebalance is permissionless but nobody calls it unless a keeper (or a searcher) does. Without a crank, the grid dies after the first fill — the same failure mode as today’s ladder.
  2. #546 rejected a hosted wallet that can CancelLimitOrders. The official keeper must be able to Rebalance only. Withdraw stays beneficiary-only on the vault.
  3. Operator economics: crank gas is paid in LUNC. The vault tip is CW20 grid-fee assets. The keeper must not run at a loss as policy: fire when tip_value_in_lunc >= 2 × gas_fee_lunc, then convert the tip to LUNC.
  4. A separate package keeps signing keys, rustc edition, and deploy cadence out of indexer/ and out of CosmWasm optimizer images.

Constraints / guardrails

  1. Separate package at repo root: grid-keeper/ (Rust recommended, sibling of indexer/). Not a workspace member of smartcontracts/. Not compiled into the indexer binary.
  2. No user key custody. The keeper key is an ops hot wallet that only needs LUNC for gas. It must not be granted authz MsgExecuteContract on user wallets or the pair. It only executes vault Rebalance + its own CW20/router swap.
  3. Custody oracle is LCD OrderStatus + vault queries, not indexer lifecycle_status=active (L21, #530). Indexer may be used as a hint to find candidate vaults.
  4. Trigger policy (official keeper): run Rebalance { claim_tip: true } only when estimated claimable tip (5% of that vault’s unclaimed_grid_fees, valued to LUNC) ≥ 2× estimated LUNC gas for that tx (including a small safety margin). If claim_tip would be 0, do not submit (leave unpaid cranks to searchers; no retail Rebalance button).
  5. On-chain Rebalance remains always allowed when there is work. This package is policy, not a vault gate. Do not add a vault min_tip that blocks flips — that would leave the book one-sided until 5% ≥ 2× gas (first fill often has no realized fee yet).
  6. Autoswap tip → LUNC after a successful claimed Rebalance. Use official router + always-on hybrid quote path. Prefer cLUNC/wrap routes already used by the dApp; native uluna is the destination for gas. Fail closed on spread / slippage; do not leave the keeper stuck in an obscure CW20 if the swap errors (retry; do not push through with max_spread none).
  7. Pause / blacklist: if pair or vault reports paused / beneficiary blacklisted, skip that vault (no hot loop).
  8. Do not cancel, withdraw, or UpdateLimitOrderPrice. Keeper allowlist of execute msgs: vault Rebalance, then keeper-owned CW20 increase_allowance / send / router swap only.
  9. Never bind-mount indexer/ (or this package) into a root Docker cargo. Host compile; if Docker is unavoidable: --user $(id -u):$(id -g) + CARGO_HOME / CARGO_TARGET_DIR off the bind mount (AGENTS.md).
  10. No LocalTerra skip without make setup-cloud-localterra when measuring gas or running the swap path.
  11. No farm/APR chrome in the keeper. No /pool copy.

Relevant files

File Role
Vault crate (sibling issue) Rebalance, fee-pot query, vault list / factory index, wasm attrs
docs/limit-orders.md OrderStatus, batch gas
skills/AGENTS_ORDER_STATUS_QUERY.md L21 — do not treat transport errors as Unknown
indexer/src/api/limit_book_lcd.rs Book walk (hint only)
indexer/src/api/hub_prices.rs Tip valuation
indexer/src/api/route_solver.rs Tip → LUNC quote
frontend-dapp/src/services/terraclassic/terraGas.ts Gas × effectiveGasPriceUluna()
skills/AGENTS_TERRACLASSIC_GAS.md Fee envelopes, wrap/unwrap combo
skills/AGENTS_HYBRID_QUOTING.md Quote = execute
scripts/lib/localterra-host-curl.sh LCD from Cloud Agent hosts
docs/localterra-sdk53.md LocalTerra ops
  1. New package grid-keeper/ with its own Cargo.toml, .env.example (KEEPER_MNEMONIC / keyring, LCD, vault-factory, router, indexer URL, gas price, TIP_TO_GAS_RATIO=2.0, poll interval). Document Coolify / systemd / tmux (grid-keeper) — not indexer-dev.
  2. Discovery: poll grid-factory AllVaults (paginated) + optional wasm event stream. For each vault, query on-chain: paused?, beneficiary blacklisted?, unclaimed_grid_fees, idle work flag / live order ids, OrderStatus per id.
  3. Eligibility:
    • Pair and vault not paused; beneficiary not blacklisted.
    • At least one filled/parked/unknown-not-active rung or idle inventory that the vault will compound (vault query needs_rebalance).
    • value_lunc(unclaimed_grid_fees * 5%) >= 2 * estimate_gas_lunc(Rebalance).
  4. Gas estimate: start from measured vault Rebalance gas_used (vault issue appendix) × 1.3, × chain gas price (LocalTerra deploy price vs columbus-5 28.325). Do not use a flat 15M.
  5. Tip valuation: convert fee-asset amounts to LUNC via hub-prices and/or route/solve (same as dApp). Stale oracle → skip vault (fail closed), do not crank on a guessed FX.
  6. Execute: Rebalance { claim_tip: true } from the keeper wallet. Parse tip-paid attrs. Then swap received CW20s → LUNC with slippage cap. Persist last-crank height to avoid double-submit on the same vault in one block.
  7. Idle / empty: if needs_rebalance=false, do not send a tx (rely on vault cheap no-op only as a safety net).
  8. Tests: unit-test the 2× predicate, valuation skip, pause skip, and “do not cancel/withdraw” msg allowlist without a live mnemonic. LocalTerra integration: one vault, fill one rung, fund the fee pot so 5% ≥ 2× gas (or lower the ratio in .env for QA), assert crank + LUNC balance up.
  9. make verify-issue-<iid> greps the package README for the 2× rule, LCD OrderStatus, and “not indexer lifecycle”; runs cargo test in grid-keeper/.

Acceptance criteria

  • AC1 — Separate package. grid-keeper/ exists; not part of indexer or smartcontracts workspace. README states key threat model.
  • AC2 — Rebalance only. Code paths cannot submit pair CancelLimitOrder* or vault Withdraw.
  • AC3 — 2× gas gate. Official keeper broadcasts only when estimated tip LUNC ≥ 2× estimated gas LUNC. Configurable constant default 2.0.
  • AC4 — LCD custody. Eligibility uses vault queries + OrderStatus. Indexer lifecycle_status=active is never sufficient.
  • AC5 — Tip → LUNC. After a claimed crank, keeper swaps tip assets to LUNC (or cLUNC then unwrap only if that is the documented gas asset path). Failure does not withdraw user funds.
  • AC6 — Skip list. Pause, blacklist, stale oracle, tip below 2×, nothing to do → no tx.
  • AC7 — LocalTerra. Scripted happy path (or documented hash) with gas_used vs estimate.
  • AC8 — Verify target. make verify-issue-<iid>.
  • AC9 — No authz grants in the runbook. No Simulated Wallet in production env examples.

Test plan (all paths)

# Path Assert
T1 needs_rebalance=false No broadcast
T2 Tip LUNC = 1.9× gas No broadcast
T3 Tip LUNC = 2.0× gas Broadcast Rebalance { claim_tip: true }
T4 Oracle / hub-prices missing Skip; no broadcast
T5 Pair paused Skip
T6 Beneficiary blacklisted Skip
T7 Indexer says active, LCD Unknown after fill Eligible if vault needs_rebalance (LCD wins)
T8 Indexer says active, LCD Active Not eligible from fills alone
T9 Msg allowlist unit test Cancel / withdraw constructors absent
T10 Tip swap slippage exceeded Rebalance already landed; swap retries; no vault withdraw
T11 Two keepers Second Rebalance is no-op or vault rejects double-tip on same pot
T12 QA ratio override (TIP_TO_GAS_RATIO=0) Documented test-only; default remains 2.0

LocalTerra

# Path Assert
M1 Deploy vault + seed grid + taker fill + accrue fees Keeper submits once; tip attrs present
M2 Keeper LUNC after autoswap Balance increased vs pre-swap CW20 (net of gas)
M3 Second poll No duplicate economic tip

Test plan — attack, hack, and abuse vectors

# Vector What to prove
A1 Stolen keeper key Attacker can Rebalance + steal future tips and the keeper’s LUNC/CW20, not vault inventory. Document blast radius.
A2 Authz over-grant Runbook never recommends GenericAuthorization on the pair or user. Tests/docs forbid it.
A3 Indexer lie Keeper that used only lifecycle_status would double-place or miss fills. Suite forces LCD.
A4 Stale hint Bad hint_after must not be required; vault/pair L14 fallback.
A5 Oracle manipulation Overstated tip FX could crank at a loss. Fail closed on missing/stale hub price.
A6 Sandwich on tip swap Slippage cap; no max_spread = None (same spirit as AutoLP M610-3).
A7 Grief spam Keeper does not send no-op txs that an attacker can induce via indexer noise.
A8 Pause / blacklist burn No tight retry that drains keeper LUNC.
A9 MEV on re-arm Document public mempool; do not add a private relay product (#299).
A10 Wrong chain env Refuse to start without chain-id allowlist (localterra / columbus-5).
A11 Mnemonic in git .env gitignored; verify script greps that secrets are not in-tree.
A12 Tip below gas after slippage 2× pre-trade buffer exists so post-swap LUNC can still cover the crank already paid.

Verification criteria

  1. Reviewers can confirm the keeper cannot withdraw or cancel by reading the execute allowlist — no wasm pair diff required.
  2. README first paragraph states: separate package, 2× gas, tip→LUNC, LCD OrderStatus.
  3. make verify-issue-<iid> + cargo test in grid-keeper/ (host, not root Docker bind-mount).
  4. LocalTerra note with at least one crank tx hash and gas vs 2× predicate.
  5. Does not block /v3 UI (users deposit/withdraw without the keeper), but production “set and forget” needs this package running.

Out of scope

  • Vault wasm (sibling).
  • /v3 APR UI.
  • In-swap flip.
  • Hosted authz on user keys.
  • Paying gas via x/feegrant from users (not required).
  • Incentive programs.
## Summary Add a **separate repo package** (`grid-keeper/`) that permissionlessly cranks V3 Grid vaults: call `Rebalance { claim_tip: true }` when the **claimable 5% fee tip** is worth **at least 2×** the estimated LUNC gas, then **autoswap the tip to LUNC**. This is the operator half of [#546](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/546) option **E**. It must **not** live inside `indexer/` (no signing keys in the indexer process) and must **not** be a CosmWasm contract. **Blocked by** [#617](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/617) (message shapes, fee pot query, events). ## Current codebase | Layer | Behavior today | |-------|----------------| | **Pair / ladder** | One-sided `PlaceLimitOrderBatch` / `PlaceLimitOrderLadder`. Fills pay the **order owner**. No auto-flip. | | **Indexer** | One `limit_order_placements` row per `action=place_limit_order`. Fills in `limit_fills`. `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**). | | **Insert hints** | Indexer `GET .../limit-book/insert-hints` ([#267](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/267)). Advisory only (**L14**). | | **Hub USD** | `GET /api/v1/hub-prices` ([#556](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/556)) — useful to value a CW20 tip in USD/LUNC terms, not a signing path. | | **Route** | `GET`/`POST /api/v1/route/solve` for hybrid quotes when swapping tip → LUNC ([#596](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/596)). | | **Gas** | dApp envelopes in `terraGas.ts`: batch place `400_000 + 180_000×N`; columbus-5 floor **28.325 uluna/gas**. | | **Docs-only watcher** | `CleanLimitBook` future watcher is **docs only** (`docs/limit-orders.md` § Permissionless limit book clean). No in-repo crank binary. | | **Not present** | No `grid-keeper` crate, no vault poll loop, no tip→LUNC swap bot, no authz helper. Simulated Wallet is LocalTerra-only and **must not** be the production signer. | Community-tax AutoLP has a permissionless `SkimToLp` but **no** keeper package. Do not bolt this loop onto the indexer or the frontend. ## Why this is needed 1. The vault’s `Rebalance` is permissionless but **nobody calls it** unless a keeper (or a searcher) does. Without a crank, the grid dies after the first fill — the same failure mode as today’s ladder. 2. #546 rejected a hosted wallet that can `CancelLimitOrders`. The official keeper must be able to **`Rebalance` only**. Withdraw stays beneficiary-only on the vault. 3. Operator economics: crank gas is paid in LUNC. The vault tip is CW20 grid-fee assets. The keeper must **not** run at a loss as policy: fire when `tip_value_in_lunc >= 2 × gas_fee_lunc`, then convert the tip to LUNC. 4. A separate package keeps signing keys, rustc edition, and deploy cadence out of `indexer/` and out of CosmWasm optimizer images. ## Constraints / guardrails 1. **Separate package** at repo root: `grid-keeper/` (Rust recommended, sibling of `indexer/`). Not a workspace member of `smartcontracts/`. Not compiled into the indexer binary. 2. **No user key custody.** The keeper key is an **ops hot wallet** that only needs LUNC for gas. It must not be granted `authz` `MsgExecuteContract` on user wallets or the pair. It only executes vault `Rebalance` + its own CW20/router swap. 3. **Custody oracle is LCD `OrderStatus` + vault queries**, not indexer `lifecycle_status=active` (**L21**, #530). Indexer may be used as a *hint* to find candidate vaults. 4. **Trigger policy (official keeper):** run `Rebalance { claim_tip: true }` only when estimated **claimable tip** (5% of that vault’s `unclaimed_grid_fees`, valued to LUNC) **≥ 2×** estimated LUNC gas for that tx (including a small safety margin). If `claim_tip` would be 0, **do not** submit (leave unpaid cranks to searchers; no retail Rebalance button). 5. **On-chain `Rebalance` remains always allowed** when there is work. This package is **policy**, not a vault gate. Do not add a vault `min_tip` that blocks flips — that would leave the book one-sided until 5% ≥ 2× gas (first fill often has **no** realized fee yet). 6. **Autoswap tip → LUNC** after a successful claimed `Rebalance`. Use official router + always-on hybrid quote path. Prefer cLUNC/wrap routes already used by the dApp; native `uluna` is the destination for gas. Fail closed on spread / slippage; do not leave the keeper stuck in an obscure CW20 if the swap errors (retry; do not push through with `max_spread` none). 7. **Pause / blacklist:** if pair or vault reports paused / beneficiary blacklisted, skip that vault (no hot loop). 8. **Do not cancel, withdraw, or `UpdateLimitOrderPrice`.** Keeper allowlist of execute msgs: vault `Rebalance`, then keeper-owned CW20 `increase_allowance` / `send` / router swap only. 9. **Never bind-mount `indexer/` (or this package) into a root Docker `cargo`.** Host compile; if Docker is unavoidable: `--user $(id -u):$(id -g)` + `CARGO_HOME` / `CARGO_TARGET_DIR` off the bind mount (`AGENTS.md`). 10. **No LocalTerra skip** without `make setup-cloud-localterra` when measuring gas or running the swap path. 11. **No farm/APR chrome** in the keeper. No `/pool` copy. ## Relevant files | File | Role | |------|------| | Vault crate (sibling issue) | `Rebalance`, fee-pot query, vault list / factory index, wasm attrs | | [`docs/limit-orders.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/limit-orders.md) | `OrderStatus`, batch gas | | [`skills/AGENTS_ORDER_STATUS_QUERY.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_ORDER_STATUS_QUERY.md) | **L21** — do not treat transport errors as `Unknown` | | [`indexer/src/api/limit_book_lcd.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/limit_book_lcd.rs) | Book walk (hint only) | | [`indexer/src/api/hub_prices.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/hub_prices.rs) | Tip valuation | | [`indexer/src/api/route_solver.rs`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/indexer/src/api/route_solver.rs) | Tip → LUNC quote | | [`frontend-dapp/src/services/terraclassic/terraGas.ts`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/frontend-dapp/src/services/terraclassic/terraGas.ts) | Gas × `effectiveGasPriceUluna()` | | [`skills/AGENTS_TERRACLASSIC_GAS.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_TERRACLASSIC_GAS.md) | Fee envelopes, wrap/unwrap combo | | [`skills/AGENTS_HYBRID_QUOTING.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_HYBRID_QUOTING.md) | Quote = execute | | [`scripts/lib/localterra-host-curl.sh`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/scripts/lib/localterra-host-curl.sh) | LCD from Cloud Agent hosts | | [`docs/localterra-sdk53.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/localterra-sdk53.md) | LocalTerra ops | ## Recommended direction 1. **New package** `grid-keeper/` with its own `Cargo.toml`, `.env.example` (`KEEPER_MNEMONIC` / keyring, LCD, vault-factory, router, indexer URL, gas price, `TIP_TO_GAS_RATIO=2.0`, poll interval). Document Coolify / systemd / tmux (`grid-keeper`) — not `indexer-dev`. 2. **Discovery:** poll grid-factory `AllVaults` (paginated) + optional wasm event stream. For each vault, query on-chain: paused?, beneficiary blacklisted?, `unclaimed_grid_fees`, idle work flag / live order ids, `OrderStatus` per id. 3. **Eligibility:** - Pair and vault not paused; beneficiary not blacklisted. - At least one filled/parked/unknown-not-active rung **or** idle inventory that the vault will compound (vault query `needs_rebalance`). - `value_lunc(unclaimed_grid_fees * 5%) >= 2 * estimate_gas_lunc(Rebalance)`. 4. **Gas estimate:** start from measured vault `Rebalance` `gas_used` (vault issue appendix) × 1.3, × chain gas price (LocalTerra deploy price vs columbus-5 28.325). Do not use a flat 15M. 5. **Tip valuation:** convert fee-asset amounts to LUNC via `hub-prices` and/or `route/solve` (same as dApp). Stale oracle → skip vault (fail closed), do not crank on a guessed FX. 6. **Execute:** `Rebalance { claim_tip: true }` from the keeper wallet. Parse tip-paid attrs. Then swap received CW20s → LUNC with slippage cap. Persist last-crank height to avoid double-submit on the same vault in one block. 7. **Idle / empty:** if `needs_rebalance=false`, do not send a tx (rely on vault cheap no-op only as a safety net). 8. **Tests:** unit-test the 2× predicate, valuation skip, pause skip, and “do not cancel/withdraw” msg allowlist **without** a live mnemonic. LocalTerra integration: one vault, fill one rung, fund the fee pot so 5% ≥ 2× gas (or lower the ratio in `.env` for QA), assert crank + LUNC balance up. 9. **`make verify-issue-<iid>`** greps the package README for the 2× rule, LCD `OrderStatus`, and “not indexer lifecycle”; runs `cargo test` in `grid-keeper/`. ## Acceptance criteria - [ ] **AC1 — Separate package.** `grid-keeper/` exists; not part of `indexer` or `smartcontracts` workspace. README states key threat model. - [ ] **AC2 — Rebalance only.** Code paths cannot submit pair `CancelLimitOrder*` or vault `Withdraw`. - [ ] **AC3 — 2× gas gate.** Official keeper broadcasts only when estimated tip LUNC ≥ 2× estimated gas LUNC. Configurable constant default **2.0**. - [ ] **AC4 — LCD custody.** Eligibility uses vault queries + `OrderStatus`. Indexer `lifecycle_status=active` is never sufficient. - [ ] **AC5 — Tip → LUNC.** After a claimed crank, keeper swaps tip assets to LUNC (or cLUNC then unwrap only if that is the documented gas asset path). Failure does not withdraw user funds. - [ ] **AC6 — Skip list.** Pause, blacklist, stale oracle, tip below 2×, nothing to do → no tx. - [ ] **AC7 — LocalTerra.** Scripted happy path (or documented hash) with `gas_used` vs estimate. - [ ] **AC8 — Verify target.** `make verify-issue-<iid>`. - [ ] **AC9 — No authz grants** in the runbook. No Simulated Wallet in production env examples. ## Test plan (all paths) | # | Path | Assert | |---|------|--------| | T1 | `needs_rebalance=false` | No broadcast | | T2 | Tip LUNC = 1.9× gas | No broadcast | | T3 | Tip LUNC = 2.0× gas | Broadcast `Rebalance { claim_tip: true }` | | T4 | Oracle / hub-prices missing | Skip; no broadcast | | T5 | Pair paused | Skip | | T6 | Beneficiary blacklisted | Skip | | T7 | Indexer says `active`, LCD `Unknown` after fill | Eligible if vault `needs_rebalance` (LCD wins) | | T8 | Indexer says `active`, LCD `Active` | Not eligible from fills alone | | T9 | Msg allowlist unit test | Cancel / withdraw constructors absent | | T10 | Tip swap slippage exceeded | Rebalance already landed; swap retries; no vault withdraw | | T11 | Two keepers | Second `Rebalance` is no-op or vault rejects double-tip on same pot | | T12 | QA ratio override (`TIP_TO_GAS_RATIO=0`) | Documented test-only; default remains 2.0 | ### LocalTerra | # | Path | Assert | |---|------|--------| | M1 | Deploy vault + seed grid + taker fill + accrue fees | Keeper submits once; tip attrs present | | M2 | Keeper LUNC after autoswap | Balance increased vs pre-swap CW20 (net of gas) | | M3 | Second poll | No duplicate economic tip | ## Test plan — attack, hack, and abuse vectors | # | Vector | What to prove | |---|--------|----------------| | A1 | **Stolen keeper key** | Attacker can `Rebalance` + steal **future tips** and the keeper’s LUNC/CW20, **not** vault inventory. Document blast radius. | | A2 | **Authz over-grant** | Runbook never recommends `GenericAuthorization` on the pair or user. Tests/docs forbid it. | | A3 | **Indexer lie** | Keeper that used only `lifecycle_status` would double-place or miss fills. Suite forces LCD. | | A4 | **Stale hint** | Bad `hint_after` must not be required; vault/pair **L14** fallback. | | A5 | **Oracle manipulation** | Overstated tip FX could crank at a loss. Fail closed on missing/stale hub price. | | A6 | **Sandwich on tip swap** | Slippage cap; no `max_spread = None` (same spirit as AutoLP **M610-3**). | | A7 | **Grief spam** | Keeper does not send no-op txs that an attacker can induce via indexer noise. | | A8 | **Pause / blacklist burn** | No tight retry that drains keeper LUNC. | | A9 | **MEV on re-arm** | Document public mempool; do not add a private relay product ([#299](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/299)). | | A10 | **Wrong chain env** | Refuse to start without chain-id allowlist (localterra / columbus-5). | | A11 | **Mnemonic in git** | `.env` gitignored; verify script greps that secrets are not in-tree. | | A12 | **Tip below gas after slippage** | 2× pre-trade buffer exists so post-swap LUNC can still cover the crank already paid. | ## Verification criteria 1. Reviewers can confirm the keeper cannot withdraw or cancel by reading the execute allowlist — no wasm pair diff required. 2. README first paragraph states: **separate package**, **2× gas**, **tip→LUNC**, **LCD `OrderStatus`**. 3. `make verify-issue-<iid>` + `cargo test` in `grid-keeper/` (host, not root Docker bind-mount). 4. LocalTerra note with at least one crank tx hash and gas vs 2× predicate. 5. Does not block `/v3` UI (users deposit/withdraw without the keeper), but production “set and forget” needs this package running. ## Out of scope - Vault wasm (sibling). - `/v3` APR UI. - In-swap flip. - Hosted `authz` on user keys. - Paying gas via `x/feegrant` from users (not required). - Incentive programs.
PlasticDigits commented 2026-08-24 03:15:38 +00:00 (Migrated from gitlab.com)

marked as related to #546

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

marked as related to #617

marked as related to #617
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:16:05 +00:00 (Migrated from gitlab.com)

mentioned in issue #546

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

mentioned in issue #617

mentioned in issue #617
PlasticDigits commented 2026-08-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-09-01 08:14:36 +00:00 (Migrated from gitlab.com)

mentioned in issue #717

mentioned in issue #717
PlasticDigits commented 2026-09-01 08:14:38 +00:00 (Migrated from gitlab.com)

marked as related to #717

marked as related to #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#618
No description provided.