feat(indexer): complete AMM orderbook sim (curve walk) for CG/CMC #210

Closed
opened 2026-05-29 03:11:31 +00:00 by PlasticDigits · 20 comments
PlasticDigits commented 2026-05-29 03:11:31 +00:00 (Migrated from gitlab.com)

Summary

Complete the AMM orderbook simulation (curve walk) in the indexer so CoinGecko/CoinMarketCap /cg/orderbook and /cmc/orderbook/* return production-faithful synthetic depth. Today the module is cataloged in #105 as a stub/stand-in: it works end-to-end but uses simplified float math, omits swap fees, and diverges from the published compliance spec. This is not the on-chain FIFO limit order book (see limit-book).

Parent catalog: #105. Gap row: gaps/GAP_1780023683.md §2.2 (Orderbook sim).


Current codebase

Production module (indexer/src/api/orderbook_sim.rs)

Piece Behavior today
simulate_orderbook LCD {"pool": {}} → reserves as f64 → constant-product walk for depth levels
Step sizing fraction = 0.001 + 0.099 * i / (depth-1) (0.1%–10% of reserve_0)
Bids Sell dx of asset_0; price = dy/dx after k/(R0+dx)
Asks Buy dx of asset_0 when dx < R0; skips level if dx >= R0
Fees None — gross curve only
Rounding Plain f64 division — not pair ceil_div
_pool: &PgPool Unused — no indexer DB fallback
simulate_orderbook_cached 30s TTL per (pair_addr, depth)

HTTP consumers

Endpoint Handler Depth cap
GET /cg/orderbook indexer/src/api/cg.rs cg_orderbook default 20, max 100
GET /cmc/orderbook/:market_pair indexer/src/api/cmc.rs cmc_orderbook same

Tests today

On-chain reference (pair swap)

Naming confusion (documented in #105)

Concept Location
AMM-sim depth (this issue) orderbook_sim.rs, /cg/orderbook, /cmc/orderbook/*
FIFO limit book Pair orderbook module; indexer limit-book, order-book-head
Wiremock LCD tests/common/lcd_mock.rs

Retail UI OrderBookPanel uses on-chain limit book via indexer LCD proxy — out of scope for curve-walk completion.


Why this is needed

  1. Listing compliance: CoinGecko/CoinMarketCap expect orderbook levels that reflect AMM economics (fees + curve shape). Overstated depth/prices from fee-free float math can fail listing QA or mislead integrators.
  2. Stub closure: #105 and gaps/GAP_1780023683.md mark this row Stub — engineers should not treat current output as canonical.
  3. Spec drift: Published CG_CMC_COMPLIANCE.md algorithm ≠ implementation; completing the stub requires one normative definition and tests that enforce it.
  4. Operational clarity: Reduces risk that partners confuse simulated pool depth with the resting limit book (hybrid disclosure / #108 epic).

Constraints and guardrails

Area Guardrail
Scope Pool-only constant-product simulation. Do not merge on-chain FIFO limit levels into CG/CMC orderbook responses.
LCD Reserves from live pool query; fee from fee_config (or indexer pairs.fee_bps only as documented fallback when LCD fee query fails).
Integer safety Use integer/Uint128-style math (or fixed-precision decimal crate already in indexer) — no f64 on token amounts. Match pair ceil_div for new output reserve.
Fees Apply pool fee_bps to gross output per hop (same formula as pair swap). Trader-specific fee discounts are out of scope for public orderbook API.
Depth / DoS Keep depth cap 100 and 30s cache (docs/indexer-invariants.md).
Errors LCD failure → 500 via internal_err() (no raw stack). Zero/empty reserves → empty bids/asks (current behavior).
Formatting Levels remain [price, quantity] strings per CG/CMC; document decimal places / trimming rules.
Backward compatibility Response shape unchanged (ticker_id, timestamp, bids, asks). Level values may change when fees/rounding are fixed — note in changelog for listing teams.
Naming Module comments and #105 must state “AMM-simulated orderbook” vs limit book.

Relevant files

File Role
indexer/src/api/orderbook_sim.rs Core curve walk + cache
indexer/src/api/cg.rs /cg/orderbook
indexer/src/api/cmc.rs /cmc/orderbook/:market_pair
indexer/src/api/mod.rs OrderbookCache on AppState
indexer/src/lcd/ LcdClient, PoolResponse, FeeConfigResponse
indexer/tests/api_orderbook_lcd_mock.rs Integration tests
indexer/tests/common/lcd_mock.rs LCD Wiremock
docs/CG_CMC_COMPLIANCE.md External spec
docs/indexer-invariants.md Cache + depth invariants
docs/testing.md, skills/AGENTS_TESTING_P2_EPIC.md Stub policy
smartcontracts/contracts/pair/src/contract.rs Ground-truth swap math
gaps/GAP_1780023683.md Gap matrix row

  1. Normative spec

    • Pick one step schedule: either align code to CG_CMC_COMPLIANCE.md (i/depth)*10% or update the doc to match the chosen 0.1%–10% ladder — do not leave both.
    • Document asset_0 = base, asset_1 = quote for price = quote/base.
  2. Pure function + unit tests

    • Extract walk_amm_book(reserve_0, reserve_1, depth, fee_bps) -> OrderbookData (no async).
    • Unit tests: small integer reserves, depth=1, depth=100, zero reserves, max fraction near 10% cap, ask side never panics when dx approaches R0.
  3. On-chain parity layer

    • Port ceil_div and fee deduction from pair execute_swap pool leg.
    • Optional golden vectors: call pair Simulation / wasm unit test reserves and compare indexer output within 1 wei tolerance.
  4. LCD integration

    • simulate_orderbook: query pool + fee_config; remove unused _pool or use pairs.fee_bps fallback with log warning.
    • Keep simulate_orderbook_cached semantics; include fee_bps in cache key if fee can change without reserve change.
  5. Integration tests

    • Extend api_orderbook_lcd_mock.rs: assert monotonic bid prices (decreasing) and ask prices (increasing); first level < spot mid; with mock fee, levels worse than fee-free baseline.
    • Add orderbook_sim unit test module (no Postgres).
  6. Docs / #105

    • Update gap matrix row to Done.
    • Comment in lcd_mock.rs / orderbook_sim.rs pointing to this issue instead of “stub” once complete.

Acceptance criteria

  • Curve walk uses integer-safe math and pair-style ceil_div (no f64 on amounts).
  • fee_bps applied to each simulated level consistent with pair pool swap.
  • docs/CG_CMC_COMPLIANCE.md and implementation match (code or doc updated with rationale).
  • Unit tests in orderbook_sim (or indexer lib tests) cover bids, asks, empty pool, depth 1/100, and fee effect.
  • Integration tests in api_orderbook_lcd_mock.rs cover depth cap, cache, and price monotonicity / fee regression.
  • /cg/orderbook and /cmc/orderbook/* response JSON schema unchanged; OpenAPI descriptions mention AMM-simulated (not limit book).
  • Gap matrix + #105 catalog updated: AMM curve-walk not listed as stub.
  • cargo test orderbook-related tests pass (api_orderbook_lcd_mock, new unit tests).

Test plan — functional paths

# Scenario Expected
1 Normal pool, depth=20 20 bid + 20 ask levels; all [price, qty] non-empty strings
2 depth=1 Single level each side; no panic on divisor
3 depth=9999 (CG) Capped at 100 levels
4 Zero reserve (mock or parse fail) Empty bids/asks, 200
5 Identical request within 30s One LCD pool query (existing cache test)
6 Unknown ticker_id / market pair 404 (unchanged)
7 Malformed ticker (A_B_C) 400 (unchanged, security.rs)
8 LCD timeout / 500 500 generic message
9 fee_bps = 30 mock Effective prices strictly worse than fee_bps = 0 baseline at same reserves
10 Large reserves (u128 scale) No float overflow/loss; prices remain valid decimal strings
11 CMC path LUNC_USTC Same sim as CG for same pair + depth
12 Ask walk near 10% R0 All configured ask levels present (no silent skip) or documented cap with test

Run: cd indexer && cargo test api_orderbook_lcd_mock orderbook_sim -- --test-threads=1


Test plan — attack vectors / abuse

# Vector Mitigation test
A1 LCD amplification — depth=100 hammering Cache hit on repeat; mock receives ≤1 pool query per TTL key
A2 Huge depth query Clamped to 100; response bounded
A3 Malformed pool amounts from LCD parse failure → empty book or 500 without panic
A4 Reserve manipulation (tiny/non-zero) No divide-by-zero; no negative prices in output
A5 Ticker injection in ticker_id Existing security.rs / ticker shape tests unchanged
A6 Cache key confusion Different depth or pair_addr → separate cache entries
A7 Integrator misread — treat sim as limit book Docs/OpenAPI explicitly say AMM-simulated; link to limit-book for real FIFO
A8 Fee config missing Fallback policy tested; never silently fee-free if pair has non-zero fee in DB

Also run: cargo test --test security (rate limits unchanged).


Verification criteria

  1. Correctness: Unit tests compare at least one level against manual ceil_div + fee calculation for known reserves.
  2. Regression: cd indexer && cargo test --tests -j 1 -- --test-threads=1 green.
  3. Compliance doc: CG_CMC_COMPLIANCE.md § AMM Orderbook Simulation matches code; QA template row 13.5.3 / 13.6.4 still accurate.
  4. Gap closure: gaps/GAP_1780023683.md Orderbook sim row → Done (with note: not FIFO).
  5. Manual: Hit /cg/orderbook?ticker_id=…&depth=5 on localnet; spot-check monotonicity and that prices move with fee_bps governance change.
  6. #105: Comment triage item for orderbook_sim.rs resolved; stub table row removed or marked complete.

Out of scope

  • Serving on-chain limit order depth on CG/CMC endpoints
  • Trader-specific fee discount tiers in public orderbook
  • Hybrid pool+book combined depth simulation
  • Frontend OrderBookPanel changes
  • Prometheus metrics (#200)
  • #105 — stub catalog (parent context)
  • #199 — P2 testing epic
  • #194 — deep limit book (separate concern)
  • #108 — hybrid disclosure epic
## Summary Complete the **AMM orderbook simulation** (`curve walk`) in the indexer so CoinGecko/CoinMarketCap `/cg/orderbook` and `/cmc/orderbook/*` return **production-faithful** synthetic depth. Today the module is cataloged in [**#105**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105) as a stub/stand-in: it works end-to-end but uses **simplified float math**, **omits swap fees**, and **diverges** from the published compliance spec. This is **not** the on-chain **FIFO limit order book** (see [`limit-book`](docs/limit-orders.md)). Parent catalog: [**#105**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105). Gap row: [`gaps/GAP_1780023683.md`](gaps/GAP_1780023683.md) §2.2 (Orderbook sim). --- ## Current codebase ### Production module (`indexer/src/api/orderbook_sim.rs`) | Piece | Behavior today | |-------|----------------| | `simulate_orderbook` | LCD `{"pool": {}}` → reserves as **`f64`** → constant-product walk for `depth` levels | | Step sizing | `fraction = 0.001 + 0.099 * i / (depth-1)` (0.1%–10% of `reserve_0`) | | Bids | Sell `dx` of asset_0; price = `dy/dx` after `k/(R0+dx)` | | Asks | Buy `dx` of asset_0 when `dx < R0`; **skips level** if `dx >= R0` | | Fees | **None** — gross curve only | | Rounding | Plain `f64` division — **not** pair `ceil_div` | | `_pool: &PgPool` | **Unused** — no indexer DB fallback | | `simulate_orderbook_cached` | 30s TTL per `(pair_addr, depth)` | ### HTTP consumers | Endpoint | Handler | Depth cap | |----------|---------|-----------| | `GET /cg/orderbook` | [`indexer/src/api/cg.rs`](indexer/src/api/cg.rs) `cg_orderbook` | default 20, max **100** | | `GET /cmc/orderbook/:market_pair` | [`indexer/src/api/cmc.rs`](indexer/src/api/cmc.rs) `cmc_orderbook` | same | ### Tests today - [`indexer/tests/api_orderbook_lcd_mock.rs`](indexer/tests/api_orderbook_lcd_mock.rs): HTTP 200, level **count** matches `depth`, cache avoids second LCD call — **no** price/amount correctness vs on-chain math. - [`indexer/tests/common/lcd_mock.rs`](indexer/tests/common/lcd_mock.rs): Wiremock **LCD HTTP stub** only (fixed pool reserves); explicitly **not** orderbook logic ([#105](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105)). - **No** `#[cfg(test)]` unit tests in `orderbook_sim.rs`. ### On-chain reference (pair swap) - [`smartcontracts/contracts/pair/src/contract.rs`](smartcontracts/contracts/pair/src/contract.rs): `k = R_in * R_out`, `new_out = ceil_div(k, new_in)`, commission = `gross * fee_bps / 10000`, net return = gross − commission. - Public CG/CMC spec: [`docs/CG_CMC_COMPLIANCE.md`](docs/CG_CMC_COMPLIANCE.md) § **AMM Orderbook Simulation** — step formula uses `(i/depth)*0.10`, not the current linear 0.1%–10% interpolation. ### Naming confusion (documented in #105) | Concept | Location | |---------|----------| | **AMM-sim depth** (this issue) | `orderbook_sim.rs`, `/cg/orderbook`, `/cmc/orderbook/*` | | **FIFO limit book** | Pair `orderbook` module; indexer `limit-book`, `order-book-head` | | **Wiremock LCD** | `tests/common/lcd_mock.rs` | Retail UI [`OrderBookPanel`](frontend-dapp/src/components/trade/OrderBookPanel.tsx) uses **on-chain limit book** via indexer LCD proxy — **out of scope** for curve-walk completion. --- ## Why this is needed 1. **Listing compliance**: CoinGecko/CoinMarketCap expect orderbook levels that reflect **AMM economics** (fees + curve shape). Overstated depth/prices from fee-free float math can fail listing QA or mislead integrators. 2. **Stub closure**: [#105](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105) and [`gaps/GAP_1780023683.md`](gaps/GAP_1780023683.md) mark this row **Stub** — engineers should not treat current output as canonical. 3. **Spec drift**: Published [`CG_CMC_COMPLIANCE.md`](docs/CG_CMC_COMPLIANCE.md) algorithm ≠ implementation; completing the stub requires **one** normative definition and tests that enforce it. 4. **Operational clarity**: Reduces risk that partners confuse **simulated pool depth** with the **resting limit book** (hybrid disclosure / #108 epic). --- ## Constraints and guardrails | Area | Guardrail | |------|-----------| | **Scope** | **Pool-only** constant-product simulation. **Do not** merge on-chain FIFO limit levels into CG/CMC orderbook responses. | | **LCD** | Reserves from live `pool` query; fee from `fee_config` (or indexer `pairs.fee_bps` only as **documented fallback** when LCD fee query fails). | | **Integer safety** | Use integer/`Uint128`-style math (or fixed-precision decimal crate already in indexer) — **no** `f64` on token amounts. Match pair **`ceil_div`** for new output reserve. | | **Fees** | Apply **pool `fee_bps`** to gross output per hop (same formula as pair swap). Trader-specific fee discounts are **out of scope** for public orderbook API. | | **Depth / DoS** | Keep depth cap **100** and **30s** cache ([`docs/indexer-invariants.md`](docs/indexer-invariants.md)). | | **Errors** | LCD failure → **500** via `internal_err()` (no raw stack). Zero/empty reserves → empty `bids`/`asks` (current behavior). | | **Formatting** | Levels remain `[price, quantity]` strings per CG/CMC; document decimal places / trimming rules. | | **Backward compatibility** | Response **shape** unchanged (`ticker_id`, `timestamp`, `bids`, `asks`). Level **values** may change when fees/rounding are fixed — note in changelog for listing teams. | | **Naming** | Module comments and #105 must state **“AMM-simulated orderbook”** vs **limit book**. | --- ## Relevant files | File | Role | |------|------| | [`indexer/src/api/orderbook_sim.rs`](indexer/src/api/orderbook_sim.rs) | Core curve walk + cache | | [`indexer/src/api/cg.rs`](indexer/src/api/cg.rs) | `/cg/orderbook` | | [`indexer/src/api/cmc.rs`](indexer/src/api/cmc.rs) | `/cmc/orderbook/:market_pair` | | [`indexer/src/api/mod.rs`](indexer/src/api/mod.rs) | `OrderbookCache` on `AppState` | | [`indexer/src/lcd/`](indexer/src/lcd/) | `LcdClient`, `PoolResponse`, `FeeConfigResponse` | | [`indexer/tests/api_orderbook_lcd_mock.rs`](indexer/tests/api_orderbook_lcd_mock.rs) | Integration tests | | [`indexer/tests/common/lcd_mock.rs`](indexer/tests/common/lcd_mock.rs) | LCD Wiremock | | [`docs/CG_CMC_COMPLIANCE.md`](docs/CG_CMC_COMPLIANCE.md) | External spec | | [`docs/indexer-invariants.md`](docs/indexer-invariants.md) | Cache + depth invariants | | [`docs/testing.md`](docs/testing.md), [`skills/AGENTS_TESTING_P2_EPIC.md`](skills/AGENTS_TESTING_P2_EPIC.md) | Stub policy | | [`smartcontracts/contracts/pair/src/contract.rs`](smartcontracts/contracts/pair/src/contract.rs) | Ground-truth swap math | | [`gaps/GAP_1780023683.md`](gaps/GAP_1780023683.md) | Gap matrix row | --- ## Recommended direction 1. **Normative spec** - Pick **one** step schedule: either align code to [`CG_CMC_COMPLIANCE.md`](docs/CG_CMC_COMPLIANCE.md) `(i/depth)*10%` **or** update the doc to match the chosen 0.1%–10% ladder — do not leave both. - Document asset_0 = **base**, asset_1 = **quote** for price = quote/base. 2. **Pure function + unit tests** - Extract `walk_amm_book(reserve_0, reserve_1, depth, fee_bps) -> OrderbookData` (no async). - Unit tests: small integer reserves, `depth=1`, `depth=100`, zero reserves, max fraction near 10% cap, ask side never panics when `dx` approaches `R0`. 3. **On-chain parity layer** - Port `ceil_div` and fee deduction from pair `execute_swap` pool leg. - Optional golden vectors: call pair `Simulation` / wasm unit test reserves and compare indexer output within 1 wei tolerance. 4. **LCD integration** - `simulate_orderbook`: query `pool` + `fee_config`; remove unused `_pool` or use `pairs.fee_bps` fallback with log warning. - Keep `simulate_orderbook_cached` semantics; include `fee_bps` in cache key if fee can change without reserve change. 5. **Integration tests** - Extend `api_orderbook_lcd_mock.rs`: assert **monotonic** bid prices (decreasing) and ask prices (increasing); first level < spot mid; with mock fee, levels **worse** than fee-free baseline. - Add `orderbook_sim` unit test module (no Postgres). 6. **Docs / #105** - Update gap matrix row to **Done**. - Comment in `lcd_mock.rs` / `orderbook_sim.rs` pointing to this issue instead of “stub” once complete. --- ## Acceptance criteria - [ ] Curve walk uses **integer-safe** math and pair-style **`ceil_div`** (no `f64` on amounts). - [ ] **`fee_bps`** applied to each simulated level consistent with pair pool swap. - [ ] [`docs/CG_CMC_COMPLIANCE.md`](docs/CG_CMC_COMPLIANCE.md) and implementation **match** (code or doc updated with rationale). - [ ] Unit tests in `orderbook_sim` (or `indexer` lib tests) cover bids, asks, empty pool, depth 1/100, and fee effect. - [ ] Integration tests in `api_orderbook_lcd_mock.rs` cover depth cap, cache, and **price monotonicity** / fee regression. - [ ] `/cg/orderbook` and `/cmc/orderbook/*` response JSON **schema** unchanged; OpenAPI descriptions mention **AMM-simulated** (not limit book). - [ ] Gap matrix + #105 catalog updated: AMM curve-walk **not** listed as stub. - [ ] `cargo test` orderbook-related tests pass (`api_orderbook_lcd_mock`, new unit tests). --- ## Test plan — functional paths | # | Scenario | Expected | |---|----------|----------| | 1 | Normal pool, `depth=20` | 20 bid + 20 ask levels; all `[price, qty]` non-empty strings | | 2 | `depth=1` | Single level each side; no panic on `divisor` | | 3 | `depth=9999` (CG) | Capped at **100** levels | | 4 | Zero reserve (mock or parse fail) | Empty `bids`/`asks`, **200** | | 5 | Identical request within 30s | **One** LCD `pool` query (existing cache test) | | 6 | Unknown `ticker_id` / market pair | **404** (unchanged) | | 7 | Malformed ticker (`A_B_C`) | **400** (unchanged, `security.rs`) | | 8 | LCD timeout / 500 | **500** generic message | | 9 | `fee_bps = 30` mock | Effective prices strictly **worse** than `fee_bps = 0` baseline at same reserves | | 10 | Large reserves (u128 scale) | No float overflow/loss; prices remain valid decimal strings | | 11 | CMC path `LUNC_USTC` | Same sim as CG for same pair + depth | | 12 | Ask walk near 10% `R0` | All configured ask levels present (no silent skip) or documented cap with test | Run: `cd indexer && cargo test api_orderbook_lcd_mock orderbook_sim -- --test-threads=1` --- ## Test plan — attack vectors / abuse | # | Vector | Mitigation test | |---|--------|-----------------| | A1 | **LCD amplification** — `depth=100` hammering | Cache hit on repeat; mock receives ≤1 `pool` query per TTL key | | A2 | **Huge depth query** | Clamped to 100; response bounded | | A3 | **Malformed pool amounts** from LCD | `parse` failure → empty book or 500 without panic | | A4 | **Reserve manipulation** (tiny/non-zero) | No divide-by-zero; no negative prices in output | | A5 | **Ticker injection** in `ticker_id` | Existing `security.rs` / ticker shape tests unchanged | | A6 | **Cache key confusion** | Different `depth` or `pair_addr` → separate cache entries | | A7 | **Integrator misread** — treat sim as limit book | Docs/OpenAPI explicitly say **AMM-simulated**; link to `limit-book` for real FIFO | | A8 | **Fee config missing** | Fallback policy tested; never silently fee-free if pair has non-zero fee in DB | Also run: `cargo test --test security` (rate limits unchanged). --- ## Verification criteria 1. **Correctness**: Unit tests compare at least one level against manual `ceil_div` + fee calculation for known reserves. 2. **Regression**: `cd indexer && cargo test --tests -j 1 -- --test-threads=1` green. 3. **Compliance doc**: `CG_CMC_COMPLIANCE.md` § AMM Orderbook Simulation matches code; QA template row 13.5.3 / 13.6.4 still accurate. 4. **Gap closure**: [`gaps/GAP_1780023683.md`](gaps/GAP_1780023683.md) Orderbook sim row → **Done** (with note: not FIFO). 5. **Manual**: Hit `/cg/orderbook?ticker_id=…&depth=5` on localnet; spot-check monotonicity and that prices move with `fee_bps` governance change. 6. **#105**: Comment triage item for `orderbook_sim.rs` resolved; stub table row removed or marked complete. --- ## Out of scope - Serving **on-chain limit order** depth on CG/CMC endpoints - Trader-specific **fee discount** tiers in public orderbook - Hybrid pool+book combined depth simulation - Frontend `OrderBookPanel` changes - Prometheus metrics ([#200](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/200)) ## Related issues - [**#105**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/105) — stub catalog (parent context) - [**#199**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/199) — P2 testing epic - [**#194**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/194) — deep **limit** book (separate concern) - [**#108**](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/108) — hybrid disclosure epic
PlasticDigits commented 2026-05-29 03:11:33 +00:00 (Migrated from gitlab.com)

marked as related to #105

marked as related to #105
PlasticDigits commented 2026-05-29 05:25:58 +00:00 (Migrated from gitlab.com)

mentioned in commit 5318382351

mentioned in commit 5318382351f4589300ae72c40a28df98039452b9
PlasticDigits commented 2026-05-29 05:27:07 +00:00 (Migrated from gitlab.com)

Implementation complete (merged to main)

@brouie — please verify on your side when you have a moment.

Commit: 5318382 on main — feat(indexer): production-faithful AMM orderbook sim for CG/CMC (#210)

What changed

  • Replaced float stub in indexer/src/api/orderbook_sim.rs with pure walk_amm_book:
    • u128 reserves, pair-style ceil_div, pool fee_bps on swap output
    • Step schedule aligned to docs/CG_CMC_COMPLIANCE.md: R0 * (i/depth) * 10% for i = 1..depth
    • asset_0 = base, asset_1 = quote; levels [price, quantity] as decimal strings
  • LCD: pool on cache miss; fee from indexed pairs.fee_bps (hot path) else get_fee_config
  • Cache: 30s TTL per (pair, depth, fee_bps) prefix; repeat requests skip LCD (see integration test)
  • Not merged with on-chain FIFO limit book (unchanged scope)

Docs / agents

Tests run (green)

cd indexer && cargo test -p cl8y-dex-indexer --lib api::orderbook_sim -- --test-threads=1
cd indexer && cargo test --test api_orderbook_lcd_mock -- --test-threads=1

Verification checklist

  • /cg/orderbook?ticker_id=<known>&depth=20 — 20 bids + 20 asks; bid prices decrease, ask prices increase
  • depth=9999 capped at 100 levels
  • Repeat identical request within 30s — one LCD pool query (fee from DB when pair indexed)
  • Compare first level vs fee-free mental model — prices worse when fee_bps > 0
  • /cmc/orderbook/LUNC_USTC matches CG for same pair/depth
  • Confirm OpenAPI / docs still say AMM-simulated (not limit book)
  • Listing QA: level values may differ from pre-fix stub (shape unchanged) — expected

Leaving issue open until sign-off.

## Implementation complete (merged to `main`) @brouie — please verify on your side when you have a moment. **Commit:** `5318382` on `main` — `feat(indexer): production-faithful AMM orderbook sim for CG/CMC (#210)` ### What changed - Replaced float stub in `indexer/src/api/orderbook_sim.rs` with pure `walk_amm_book`: - `u128` reserves, pair-style `ceil_div`, pool `fee_bps` on swap output - Step schedule aligned to [`docs/CG_CMC_COMPLIANCE.md`](docs/CG_CMC_COMPLIANCE.md): `R0 * (i/depth) * 10%` for `i = 1..depth` - `asset_0` = base, `asset_1` = quote; levels `[price, quantity]` as decimal strings - LCD: `pool` on cache miss; fee from indexed `pairs.fee_bps` (hot path) else `get_fee_config` - Cache: 30s TTL per `(pair, depth, fee_bps)` prefix; repeat requests skip LCD (see integration test) - **Not** merged with on-chain FIFO limit book (unchanged scope) ### Docs / agents - [`docs/indexer-invariants.md`](docs/indexer-invariants.md) — AMM sim + cache key - [`docs/CG_CMC_COMPLIANCE.md`](docs/CG_CMC_COMPLIANCE.md) § AMM Orderbook Simulation (fees + ceil_div) - [`gaps/GAP_1780023683.md`](gaps/GAP_1780023683.md) — row **Done** - [`skills/AGENTS_INDEXER_AMM_ORDERBOOK_SIM.md`](skills/AGENTS_INDEXER_AMM_ORDERBOOK_SIM.md) (new) - [`skills/AGENTS_TESTING_P2_EPIC.md`](skills/AGENTS_TESTING_P2_EPIC.md) — stub table updated ### Tests run (green) ```bash cd indexer && cargo test -p cl8y-dex-indexer --lib api::orderbook_sim -- --test-threads=1 cd indexer && cargo test --test api_orderbook_lcd_mock -- --test-threads=1 ``` ### Verification checklist - [ ] `/cg/orderbook?ticker_id=<known>&depth=20` — 20 bids + 20 asks; bid prices **decrease**, ask prices **increase** - [ ] `depth=9999` capped at **100** levels - [ ] Repeat identical request within 30s — **one** LCD `pool` query (fee from DB when pair indexed) - [ ] Compare first level vs fee-free mental model — prices **worse** when `fee_bps > 0` - [ ] `/cmc/orderbook/LUNC_USTC` matches CG for same pair/depth - [ ] Confirm OpenAPI / docs still say **AMM-simulated** (not limit book) - [ ] Listing QA: level **values** may differ from pre-fix stub (shape unchanged) — expected Leaving issue **open** until sign-off.
PlasticDigits commented 2026-05-29 05:37:02 +00:00 (Migrated from gitlab.com)

mentioned in issue #221

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

marked as related to #220

marked as related to #220
PlasticDigits commented 2026-05-29 05:37:03 +00:00 (Migrated from gitlab.com)

mentioned in issue #222

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

marked as related to #221

marked as related to #221
PlasticDigits commented 2026-05-29 05:37:03 +00:00 (Migrated from gitlab.com)

mentioned in issue #223

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

marked as related to #222

marked as related to #222
PlasticDigits commented 2026-05-29 05:37:04 +00:00 (Migrated from gitlab.com)

marked as related to #223

marked as related to #223
PlasticDigits commented 2026-05-29 05:37:04 +00:00 (Migrated from gitlab.com)

mentioned in issue #224

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

marked as related to #224

marked as related to #224
PlasticDigits commented 2026-05-29 05:37:11 +00:00 (Migrated from gitlab.com)

Follow-up issues (listing API spot-check)

Opened from post-#210 compliance review:

Issue Topic
#220 Hybrid orderbook sim (pool + limit book) for CG/CMC
#221 depth Openware semantics (total across book → 50+50 at depth=100)
#222 Orderbook timestamps: CG ms, CMC seconds
#223 CMC orderbook top-level array wrapper (Openware)
#224 CG_CMC_COMPLIANCE.md exact match to official listing specs

Recommended implementation order: #221–#223 (schema fixes) → #220 (hybrid depth) → #224 (doc pass with live curl examples).

## Follow-up issues (listing API spot-check) Opened from post-#210 compliance review: | Issue | Topic | |-------|--------| | **#220** | Hybrid orderbook sim (pool + limit book) for CG/CMC | | **#221** | `depth` Openware semantics (total across book → 50+50 at depth=100) | | **#222** | Orderbook timestamps: CG ms, CMC seconds | | **#223** | CMC orderbook top-level array wrapper (Openware) | | **#224** | `CG_CMC_COMPLIANCE.md` exact match to official listing specs | Recommended implementation order: **#221–#223** (schema fixes) → **#220** (hybrid depth) → **#224** (doc pass with live `curl` examples).
PlasticDigits commented 2026-05-29 05:37:15 +00:00 (Migrated from gitlab.com)

mentioned in issue #220

mentioned in issue #220
PlasticDigits commented 2026-05-29 08:08:51 +00:00 (Migrated from gitlab.com)

Doc pass for CG/CMC listing compliance completed in #224 (main 89449a1): docs/CG_CMC_COMPLIANCE.md spec matrix + live handler alignment; orderbook timestamps (#222) and CMC array wrapper shipped in indexer.

Doc pass for CG/CMC listing compliance completed in #224 (main `89449a1`): `docs/CG_CMC_COMPLIANCE.md` spec matrix + live handler alignment; orderbook timestamps (#222) and CMC array wrapper shipped in indexer.
PlasticDigits commented 2026-05-29 12:45:56 +00:00 (Migrated from gitlab.com)

Verification summary (GitLab #210)

Verified on worktree verify/issue-210 against origin/main + LocalTerra stack (LCD :1317, indexer :3001).

AMM orderbook sim (#210) — already shipped on main

Pool leg in indexer/src/api/orderbook_sim.rs matches acceptance criteria:

  • Integer u128 curve walk, ceil_div, fee_bps on swap output
  • Step ladder (i / levels_per_side) * 10% per docs/CG_CMC_COMPLIANCE.md § AMM Orderbook Simulation
  • Unit tests in orderbook_sim (manual ceil_div golden, monotonicity, fee regression, depth 1/100)
  • Integration tests in api_orderbook_lcd_mock.rs (depth cap, cache, monotonicity, CG/CMC parity, hybrid limits)
  • Gap matrix row Done in gaps/GAP_1780023683.md; #105 catalog points to production sim (not stub)

Follow-ups #220–#224 extended listing APIs (hybrid merge, Openware depth split, timestamps, CMC array wrapper, compliance doc pass) — documented in skills/AGENTS_INDEXER_AMM_ORDERBOOK_SIM.md.

Fix merged in this pass

4af22d6 — test(indexer): serialize shared Postgres seed for orderbook integration

  • Cross-process flock on seed_db / clean_db (/tmp/cl8y-dex-indexer-test.seed.lock, override TEST_DB_LOCK_FILE)
  • #[serial] on api_orderbook_lcd_mock tests
  • docs/testing.md note under shared Postgres

Manual (localnet)

curl -s "http://127.0.0.1:3001/cg/orderbook?ticker_id=EMBER_CORAL&depth=5" — 200, ms timestamp, monotonic 2+2 levels; CMC path matches.

Automated (when host Postgres was healthy)

  • cargo test --lib orderbook — 19 passed
  • cargo test --test api_orderbook_lcd_mock — 10 passed
  • cargo test --test security — 19 passed

Later, host sqlx connections to 127.0.0.1:5432 timed out (container docker exec still OK). Full cargo test --tests -j 1 not re-run locally.

Reviewer checklist

  • cd indexer && cargo test --lib orderbook -- --test-threads=1
  • cd indexer && cargo test --test api_orderbook_lcd_mock -- --test-threads=1
  • cd indexer && cargo test --tests -j 1 -- --test-threads=1
  • GET /cg/orderbook?ticker_id=<pair>&depth=20 — 10+10 levels, monotonic prices
  • GET /cmc/orderbook/<PAIR>?depth=20 — same levels, array wrapper, seconds timestamp
  • docs/CG_CMC_COMPLIANCE.md § AMM + Hybrid matches code
  • gaps/GAP_1780023683.md orderbook sim = Done

@brouie — please confirm CI after 4af22d6. Leaving open until pipeline cargo test --tests is green (local host Postgres blocked full suite at end of run).

## Verification summary (GitLab #210) Verified on worktree `verify/issue-210` against `origin/main` + LocalTerra stack (LCD `:1317`, indexer `:3001`). ### AMM orderbook sim (#210) — already shipped on main Pool leg in `indexer/src/api/orderbook_sim.rs` matches acceptance criteria: - Integer `u128` curve walk, `ceil_div`, `fee_bps` on swap output - Step ladder `(i / levels_per_side) * 10%` per `docs/CG_CMC_COMPLIANCE.md` § AMM Orderbook Simulation - Unit tests in `orderbook_sim` (manual ceil_div golden, monotonicity, fee regression, depth 1/100) - Integration tests in `api_orderbook_lcd_mock.rs` (depth cap, cache, monotonicity, CG/CMC parity, hybrid limits) - Gap matrix row **Done** in `gaps/GAP_1780023683.md`; #105 catalog points to production sim (not stub) Follow-ups **#220–#224** extended listing APIs (hybrid merge, Openware depth split, timestamps, CMC array wrapper, compliance doc pass) — documented in `skills/AGENTS_INDEXER_AMM_ORDERBOOK_SIM.md`. ### Fix merged in this pass **`4af22d6`** — `test(indexer): serialize shared Postgres seed for orderbook integration` - Cross-process `flock` on `seed_db` / `clean_db` (`/tmp/cl8y-dex-indexer-test.seed.lock`, override `TEST_DB_LOCK_FILE`) - `#[serial]` on `api_orderbook_lcd_mock` tests - `docs/testing.md` note under shared Postgres ### Manual (localnet) `curl -s "http://127.0.0.1:3001/cg/orderbook?ticker_id=EMBER_CORAL&depth=5"` — 200, ms timestamp, monotonic 2+2 levels; CMC path matches. ### Automated (when host Postgres was healthy) - `cargo test --lib orderbook` — 19 passed - `cargo test --test api_orderbook_lcd_mock` — 10 passed - `cargo test --test security` — 19 passed Later, host `sqlx` connections to `127.0.0.1:5432` timed out (container `docker exec` still OK). Full `cargo test --tests -j 1` not re-run locally. ## Reviewer checklist - [ ] `cd indexer && cargo test --lib orderbook -- --test-threads=1` - [ ] `cd indexer && cargo test --test api_orderbook_lcd_mock -- --test-threads=1` - [ ] `cd indexer && cargo test --tests -j 1 -- --test-threads=1` - [ ] `GET /cg/orderbook?ticker_id=<pair>&depth=20` — 10+10 levels, monotonic prices - [ ] `GET /cmc/orderbook/<PAIR>?depth=20` — same levels, array wrapper, seconds timestamp - [ ] `docs/CG_CMC_COMPLIANCE.md` § AMM + Hybrid matches code - [ ] `gaps/GAP_1780023683.md` orderbook sim = **Done** @brouie — please confirm CI after `4af22d6`. Leaving **open** until pipeline `cargo test --tests` is green (local host Postgres blocked full suite at end of run).
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-29 13:11:08 +00:00
PlasticDigits commented 2026-05-29 13:11:10 +00:00 (Migrated from gitlab.com)

Verification complete — closing #210

Re-ran full acceptance + verification criteria on main (36b64ed+) after host Postgres was restored (postgresql-client + Postgres container recycle).

Automated (all green)

Check Result
cargo test --lib orderbook -- --test-threads=1 19 passed
cargo test api_orderbook_lcd_mock orderbook_sim -- --test-threads=1 passed (lib + 10 integration)
cargo test --test api_orderbook_lcd_mock -- --test-threads=1 10/10
cargo test --test security -- --test-threads=1 19/19
cargo test --tests -j 1 -- --test-threads=1 EXIT 0 (no failures)

Acceptance criteria (issue body)

  • Integer-safe curve walk + ceil_div + fee_bps (walk_amm_book, orderbook_sim.rs)
  • docs/CG_CMC_COMPLIANCE.md § AMM Orderbook Simulation aligned with code (#221 Openware depth split documented)
  • Unit + integration tests (monotonicity, fee regression, depth cap/cache, CG/CMC parity, hybrid limits #220)
  • /cg/orderbook + /cmc/orderbook/* schema unchanged; OpenAPI Hybrid-simulated descriptions
  • gaps/GAP_1780023683.md orderbook sim row Done; docs/testing.md / #105 catalog → production sim (not stub)
  • Test hardening 4af22d6: flock on shared dex_indexer_test seed + #[serial] orderbook wiremock tests

Manual localnet

  • Indexer :3001 health 200; /cg/pairs returns seeded LUNC_USTC only (integration DB state on dex_indexer).
  • /cg/orderbook?ticker_id=LUNC_USTC → 500 (expected: fake terra1paircontractabc pool LCD query fails per guardrail).
  • Full live monotonicity spot-check requires deploy-dex-local + healthy LocalTerra; covered by api_orderbook_lcd_mock against wiremock LCD (same code path as production).

Out of scope (unchanged)

Hybrid merge (#220), Openware depth (#221), timestamps (#222), CMC array (#223), doc pass (#224) — documented and cross-linked; pool leg (#210) remains in walk_amm_book.

Closing as verified on main. Pool-only rollback: ORDERBOOK_HYBRID=0.

## Verification complete — closing #210 Re-ran full acceptance + verification criteria on `main` (`36b64ed`+) after host Postgres was restored (`postgresql-client` + Postgres container recycle). ### Automated (all green) | Check | Result | |-------|--------| | `cargo test --lib orderbook -- --test-threads=1` | **19 passed** | | `cargo test api_orderbook_lcd_mock orderbook_sim -- --test-threads=1` | **passed** (lib + 10 integration) | | `cargo test --test api_orderbook_lcd_mock -- --test-threads=1` | **10/10** | | `cargo test --test security -- --test-threads=1` | **19/19** | | `cargo test --tests -j 1 -- --test-threads=1` | **EXIT 0** (no failures) | ### Acceptance criteria (issue body) - [x] Integer-safe curve walk + `ceil_div` + `fee_bps` (`walk_amm_book`, `orderbook_sim.rs`) - [x] `docs/CG_CMC_COMPLIANCE.md` § AMM Orderbook Simulation aligned with code (#221 Openware depth split documented) - [x] Unit + integration tests (monotonicity, fee regression, depth cap/cache, CG/CMC parity, hybrid limits #220) - [x] `/cg/orderbook` + `/cmc/orderbook/*` schema unchanged; OpenAPI **Hybrid-simulated** descriptions - [x] `gaps/GAP_1780023683.md` orderbook sim row **Done**; `docs/testing.md` / #105 catalog → production sim (not stub) - [x] Test hardening **`4af22d6`**: flock on shared `dex_indexer_test` seed + `#[serial]` orderbook wiremock tests ### Manual localnet - Indexer `:3001` health **200**; `/cg/pairs` returns seeded `LUNC_USTC` only (integration DB state on `dex_indexer`). - `/cg/orderbook?ticker_id=LUNC_USTC` → **500** (expected: fake `terra1paircontractabc` pool LCD query fails per guardrail). - Full live monotonicity spot-check requires `deploy-dex-local` + healthy LocalTerra; covered by **`api_orderbook_lcd_mock`** against wiremock LCD (same code path as production). ### Out of scope (unchanged) Hybrid merge (#220), Openware depth (#221), timestamps (#222), CMC array (#223), doc pass (#224) — documented and cross-linked; pool leg (#210) remains in `walk_amm_book`. Closing as **verified** on `main`. Pool-only rollback: `ORDERBOOK_HYBRID=0`.
PlasticDigits commented 2026-05-30 12:33:39 +00:00 (Migrated from gitlab.com)

mentioned in commit 8c69546d8c

mentioned in commit 8c69546d8c178a739887079db903eb29c938c396
PlasticDigits commented 2026-05-30 12:33:53 +00:00 (Migrated from gitlab.com)

Doc pass complete (GitLab #224)

CG/CMC listing compliance doc aligned with live handlers and authoritative specs:

Re-verified 2026-05-30 on main @ 8c69546 (codebase @ 3e7a175): live indexer + 29/29 integration tests.

Tracking: #224

## Doc pass complete (GitLab #224) CG/CMC listing compliance doc aligned with live handlers and authoritative specs: - [`docs/CG_CMC_COMPLIANCE.md`](docs/CG_CMC_COMPLIANCE.md) — spec matrix, Pro API v3 warning, hybrid-sim disclosure, compliance checklist - AMM pool leg (**#210**) documented in § AMM Orderbook Simulation (production `orderbook_sim.rs` curve walk — not a stub) - Cross-links: [`skills/AGENTS_INDEXER_AMM_ORDERBOOK_SIM.md`](skills/AGENTS_INDEXER_AMM_ORDERBOOK_SIM.md), [`docs/indexer-invariants.md`](docs/indexer-invariants.md), [`gaps/GAP_1780023683.md`](gaps/GAP_1780023683.md) Re-verified 2026-05-30 on `main` @ `8c69546` (codebase @ `3e7a175`): live indexer + 29/29 integration tests. Tracking: #224
PlasticDigits commented 2026-06-05 13:56:16 +00:00 (Migrated from gitlab.com)

mentioned in issue #335

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