W9-C5 Price validation bypassed when indexer is unavailable — buy and sell limit orders bypass market price enforcement #166

Closed
opened 2026-05-12 17:55:40 +00:00 by totdking · 15 comments
totdking commented 2026-05-12 17:55:40 +00:00 (Migrated from gitlab.com)
No description provided.
totdking commented 2026-05-12 18:06:04 +00:00 (Migrated from gitlab.com)

Issue Summary

When the indexer is running, the UI enforces price rules on limit orders: buy limits must be placed below the current market reference price, and sell (ask) limits must be placed above it. When the indexer goes down, this validation is lost entirely for both sides — the UI accepts buy limit orders above market price and ask limit orders below market price. The trader receives no warning that price validation is degraded.


Reproduction Steps

  1. Start the app with all services running and navigate to the Trade / Limits tab
  2. Attempt to place a buy limit order above the current market reference price — observe the UI blocks it with a validation message
  3. Attempt to place a sell (ask) limit order below the current market reference price — observe the UI blocks it
  4. Stop the indexer process
  5. Attempt the same invalid buy and sell orders again
  6. Observe the validation is no longer enforced for either side — both orders are accepted and submitted

Expected Behavior

Price validation for limit orders should not depend on the indexer being available. If the reference price cannot be retrieved, the UI should either source it from the chain directly or block order submission entirely until validation data is available, rather than silently dropping the guard.


Actual Behavior

With the indexer down, buy limit orders above market price and ask limit orders below market price are both accepted and submitted without any warning. The normal validation messages do not appear for either side.


Environment Details

Field Value
OS macOS (Apple M1 Pro, 14-inch)
Browser Google Chrome (desktop)
Viewport ~1440px desktop
Network localterra (local Docker)
Frontend VITE_NETWORK=local npm run dev
Indexer Stopped to simulate outage

Wallet / Device Details

  • Wallet: Any wallet
  • Wallet state: Connected — localterra

Severity / Impact

P2 / Pre-launch. If the contract enforces price rules on-chain the order will fail and waste gas. If the contract does not enforce the same rules, a trader could get an unintended fill at an adverse price. Either outcome is harmful — one wastes fees, the other risks funds. The silent removal of a validation guard during degraded operation is a trust and safety concern. Found under W9-C5 (Empty and error rows — Interactivity and Trader use dimensions).

cc: @PlasticDigits

### Issue Summary When the indexer is running, the UI enforces price rules on limit orders: buy limits must be placed below the current market reference price, and sell (ask) limits must be placed above it. When the indexer goes down, this validation is lost entirely for both sides — the UI accepts buy limit orders above market price and ask limit orders below market price. The trader receives no warning that price validation is degraded. --- ### Reproduction Steps 1. Start the app with all services running and navigate to the Trade / Limits tab 2. Attempt to place a buy limit order above the current market reference price — observe the UI blocks it with a validation message 3. Attempt to place a sell (ask) limit order below the current market reference price — observe the UI blocks it 4. Stop the indexer process 5. Attempt the same invalid buy and sell orders again 6. Observe the validation is no longer enforced for either side — both orders are accepted and submitted --- ### Expected Behavior Price validation for limit orders should not depend on the indexer being available. If the reference price cannot be retrieved, the UI should either source it from the chain directly or block order submission entirely until validation data is available, rather than silently dropping the guard. --- ### Actual Behavior With the indexer down, buy limit orders above market price and ask limit orders below market price are both accepted and submitted without any warning. The normal validation messages do not appear for either side. --- ### Environment Details | Field | Value | |-------|-------| | OS | macOS (Apple M1 Pro, 14-inch) | | Browser | Google Chrome (desktop) | | Viewport | \~1440px desktop | | Network | `localterra` (local Docker) | | Frontend | `VITE_NETWORK=local npm run dev` | | Indexer | Stopped to simulate outage | --- ### Wallet / Device Details - **Wallet:** Any wallet - **Wallet state:** Connected — localterra --- ### Severity / Impact **P2 / Pre-launch.** If the contract enforces price rules on-chain the order will fail and waste gas. If the contract does not enforce the same rules, a trader could get an unintended fill at an adverse price. Either outcome is harmful — one wastes fees, the other risks funds. The silent removal of a validation guard during degraded operation is a trust and safety concern. Found under **W9-C5** (Empty and error rows — Interactivity and Trader use dimensions). cc: @PlasticDigits
totdking commented 2026-05-12 18:06:15 +00:00 (Migrated from gitlab.com)

mentioned in issue #116

mentioned in issue #116
PlasticDigits commented 2026-05-13 04:32:59 +00:00 (Migrated from gitlab.com)

mentioned in commit eadda1c182

mentioned in commit eadda1c1825ae57b261777fc2cf7f859e0d35418
PlasticDigits commented 2026-05-13 04:34:30 +00:00 (Migrated from gitlab.com)

mentioned in commit 534769b384

mentioned in commit 534769b3842ba98306b2ebfefe0cba8794925e52
PlasticDigits commented 2026-05-13 04:35:09 +00:00 (Migrated from gitlab.com)

Implementation summary (GitLab #166)

Merged to main (commit on main includes merge + conflict resolution with #164 trade banner copy).

What changed

  • Reference price: Prefer indexed last trade; if missing/unparseable, resolve token1/token0 from on-chain AMM pool reserves (getPool / LCD) when decimals are known (indexer pair row or token registry only — no guessed decimals for unknown CW20s).
  • Submit gate: evaluateLimitOrderPricePlaceGate now takes a resolved ref + optional refResolutionLoading / refResolutionError. A positive typed limit cannot submit without a valid reference (no silent bypass when indexer tape is gone).
  • Wiring: New hook useLimitOrderPriceRefBundle shared by TradeOrderTicket and LimitOrdersPage; LimitOrderPriceInputWithContext shows last trade vs AMM pool spot in the reference line.
  • Indexer outage banner (#164 + #166): Tail copy in indexerTradeOutageCopy.ts documents pool/LCD fallback for limit reference; TradePage keeps data-testid="trade-indexer-outage-banner".
  • Docs / agents: docs/frontend.md, docs/limit-orders.md, skills/AGENTS_FRONTEND_LIMIT_ORDER_PRICE.md updated and cross-linked.

Verification checklist

  • With indexer up: invalid buy (limit ≥ tape ref) and invalid sell (limit ≤ tape ref) still blocked; valid limits place.
  • Stop indexer, keep wallet LCD: same invalid prices blocked using pool reference (context line shows AMM pool spot); valid limits still place if pool has liquidity and both tokens are in the registry (or indexer pair row was cached with decimals).
  • Empty/zero price field does not show ref gate error (other gates still apply for actual submit).
  • npm run test:unit and npm run build in frontend-dapp/ pass on your machine.

@totdking Could you verify on localterra (indexer stopped) that the regression from the issue is gone? Leaving #166 open until you sign off.

## Implementation summary (GitLab #166) Merged to `main` (commit on `main` includes merge + conflict resolution with #164 trade banner copy). ### What changed - **Reference price:** Prefer indexed **last trade**; if missing/unparseable, resolve **token1/token0** from on-chain **AMM pool** reserves (`getPool` / LCD) when decimals are known (indexer pair row or **token registry** only — no guessed decimals for unknown CW20s). - **Submit gate:** `evaluateLimitOrderPricePlaceGate` now takes a resolved ref + optional `refResolutionLoading` / `refResolutionError`. A **positive** typed limit **cannot** submit without a valid reference (no silent bypass when indexer tape is gone). - **Wiring:** New hook `useLimitOrderPriceRefBundle` shared by `TradeOrderTicket` and `LimitOrdersPage`; `LimitOrderPriceInputWithContext` shows **last trade** vs **AMM pool spot** in the reference line. - **Indexer outage banner (#164 + #166):** Tail copy in `indexerTradeOutageCopy.ts` documents pool/LCD fallback for limit reference; `TradePage` keeps `data-testid="trade-indexer-outage-banner"`. - **Docs / agents:** `docs/frontend.md`, `docs/limit-orders.md`, `skills/AGENTS_FRONTEND_LIMIT_ORDER_PRICE.md` updated and cross-linked. ### Verification checklist - [ ] With indexer **up:** invalid buy (limit ≥ tape ref) and invalid sell (limit ≤ tape ref) still **blocked**; valid limits **place**. - [ ] Stop indexer, keep wallet LCD: same invalid prices **blocked** using **pool** reference (context line shows **AMM pool spot**); valid limits still **place** if pool has liquidity and both tokens are in the registry (or indexer pair row was cached with decimals). - [ ] Empty/zero price field does **not** show ref gate error (other gates still apply for actual submit). - [ ] `npm run test:unit` and `npm run build` in `frontend-dapp/` pass on your machine. @totdking Could you verify on **localterra** (indexer stopped) that the regression from the issue is gone? Leaving **#166** open until you sign off.
PlasticDigits commented 2026-05-13 04:35:56 +00:00 (Migrated from gitlab.com)

mentioned in commit ab7b62bd79

mentioned in commit ab7b62bd79a1084eb0013047a6201ab0c616c688
totdking commented 2026-05-20 20:27:54 +00:00 (Migrated from gitlab.com)

mentioned in issue #174

mentioned in issue #174
PlasticDigits commented 2026-05-27 05:14:25 +00:00 (Migrated from gitlab.com)

mentioned in issue #154

mentioned in issue #154
PlasticDigits commented 2026-05-27 05:56:40 +00:00 (Migrated from gitlab.com)

mentioned in commit f4c8cbc656

mentioned in commit f4c8cbc6563ba5d3c984f986a8488c517df428a7
PlasticDigits commented 2026-05-27 05:57:05 +00:00 (Migrated from gitlab.com)

mentioned in commit fb7c8b883a

mentioned in commit fb7c8b883abadc779b475e1d71325f5403728e6b
PlasticDigits commented 2026-05-27 05:57:05 +00:00 (Migrated from gitlab.com)

mentioned in commit 3325cd5422

mentioned in commit 3325cd5422453f6fba0839ddc665a2894edceaca
PlasticDigits commented 2026-05-27 05:57:11 +00:00 (Migrated from gitlab.com)

Verification complete — closing #166

Verified on localterra with full stack (LocalTerra, host Postgres, indexer, frontend, 30/30 bot swarm).

Root cause (remaining gap after initial #166 merge)

When the indexer is down, pool-based limit price reference requires token decimals. Local deploy pairs (e.g. EMBER/CORAL) are not in the static token registry, so pairDecimalsForLimitPriceRef returned null, the pool query never ran, and validation was bypassed for positive limit prices.

Fix (merged to main in 3325cd5)

  • Added resolvePairDecimalsForLimitPriceRefFromChain — resolves CW20 decimals via on-chain token_info when registry/indexer row is missing.
  • Wired through useLimitOrderPriceRefBundle with loading/error flags for the place gate.
  • Added unit + integration tests (hook + LCD pool spot path).

Checklist

  • Indexer up: invalid buy/sell blocked; valid buy below ref clears price gate (Playwright on /limits, EMBER/CORAL).
  • Indexer stopped, wallet LCD up: invalid buy/sell blocked; context shows AMM pool spot (Playwright).
  • Empty price field does not show ref gate error (guard hidden when no message).
  • npm run test:unit and npm run build pass.
  • Integration tests: limitOrderPriceReference.integration.test.ts, useLimitOrderPriceRefBundle.integration.test.tsx.

Visual verification via Playwright (Cursor browser MCP not available in this session).

## Verification complete — closing #166 Verified on **localterra** with full stack (LocalTerra, host Postgres, indexer, frontend, 30/30 bot swarm). ### Root cause (remaining gap after initial #166 merge) When the indexer is down, pool-based limit price reference requires token decimals. Local deploy pairs (e.g. **EMBER/CORAL**) are not in the static token registry, so `pairDecimalsForLimitPriceRef` returned null, the pool query never ran, and validation was bypassed for positive limit prices. ### Fix (merged to `main` in `3325cd5`) - Added `resolvePairDecimalsForLimitPriceRefFromChain` — resolves CW20 decimals via on-chain `token_info` when registry/indexer row is missing. - Wired through `useLimitOrderPriceRefBundle` with loading/error flags for the place gate. - Added unit + integration tests (hook + LCD pool spot path). ### Checklist - [x] Indexer **up**: invalid buy/sell blocked; valid buy below ref clears price gate (Playwright on `/limits`, EMBER/CORAL). - [x] Indexer **stopped**, wallet LCD up: invalid buy/sell blocked; context shows **AMM pool spot** (Playwright). - [x] Empty price field does not show ref gate error (guard hidden when no message). - [x] `npm run test:unit` and `npm run build` pass. - [x] Integration tests: `limitOrderPriceReference.integration.test.ts`, `useLimitOrderPriceRefBundle.integration.test.tsx`. Visual verification via Playwright (Cursor browser MCP not available in this session).
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-27 05:57:13 +00:00
PlasticDigits commented 2026-05-27 13:53:51 +00:00 (Migrated from gitlab.com)

mentioned in issue #205

mentioned in issue #205
PlasticDigits commented 2026-05-29 03:17:55 +00:00 (Migrated from gitlab.com)

mentioned in issue #215

mentioned in issue #215
PlasticDigits commented 2026-05-29 05:35:41 +00:00 (Migrated from gitlab.com)

mentioned in issue #218

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