Contract: emit swap_index on limit_order_fill wasm events (GitLab #316 follow-up) #331

Closed
opened 2026-06-05 13:44:29 +00:00 by PlasticDigits · 8 comments
PlasticDigits commented 2026-06-05 13:44:29 +00:00 (Migrated from gitlab.com)

Parent

Follow-up to GitLab #316 (indexer-side swap_index linkage — closed). Short-term parser walk-order attribution is merged; this issue tracks the long-term contract change for durable LCD replay.

Current codebase

  • Indexer (shipped): parse_limit_order_fills in indexer/src/indexer/parser.rs assigns each fill a per-pair swap_index by walking wasm events in tx order — fills emitted before their parent swap action receive the upcoming ordinal (same counter as parse_swaps). process_limit_order_fill links via limit_order_fills::swap_id_for_tx_pair_index(tx_hash, pair_id, swap_index) (see indexer/tests/limit_fill_swap_linkage.rs).
  • Contract (gap): limit_order_fill_event in smartcontracts/contracts/pair/src/orderbook.rs emits action=limit_order_fill with order_id, side, maker, price, token amounts, and commission_amount — no swap_index attribute.
  • Parser fallback: When swap_index is absent from wasm attrs, the indexer infers it from event ordering. This breaks if LCD merges/splits wasm events differently than the live chain, or on historical replay after parser logic changes.

Why this is needed

Without an on-chain swap_index on each limit_order_fill event:

  • LCD-only replay (indexer cold start, integrator backfill) cannot reliably attribute fills to the correct swap_events row when a tx has multiple swaps on the same pair (router revisit, batch).
  • Parser ordering heuristics are fragile across node versions and merged-event columnar layouts.
  • Integrator analytics (docs/integrators-hybrid-volume.md) depend on correct fill→swap linkage for hybrid book attribution.

Emitting swap_index on-chain makes attribution self-describing and matches the (tx_hash, pair_id, swap_index) unique key added in GitLab #287.

Constraints / guardrails

  • Backward compatible: Indexer must continue to accept fills without swap_index (infer from walk order) for historical txs.
  • Ordinal semantics: swap_index on a fill must match the per-pair swap ordinal the pair contract uses for that tx — same definition as swap event ordering (0-based, incremented once per completed swap on the pair within the tx).
  • No schema migration on limit_order_fills dedup key (tx_hash, pair_id, order_id).
  • Gas: One extra string attribute per fill event — negligible vs existing fill attrs.
  • Coordinate wasm change with indexer parser (prefer emitted attr when present, fall back to inference).
  • Update docs/contracts-security-audit.md event catalog and integrator docs.

Relevant files

Area Path
Fill event builder smartcontracts/contracts/pair/src/orderbook.rs — limit_order_fill_event, fill_events.push(...) call sites
Swap execute / hybrid smartcontracts/contracts/pair/src/contract.rs, orderbook.rs match walk
Integration tests smartcontracts/tests/src/limit_order_tests.rs — hybrid_swap_emits_limit_order_fill_events, multi-fill cases
Indexer parser indexer/src/indexer/parser.rs — parse_limit_order_fill_segment, parse_limit_order_fills
Indexer linkage indexer/src/db/queries/limit_order_fills.rs
Regression test indexer/tests/limit_fill_swap_linkage.rs
Docs docs/integrators-hybrid-volume.md, docs/limit-orders.md
  1. Contract: Thread a per-pair swap_index counter through hybrid execute (increment after each swap wasm event is finalized). Pass the current ordinal into limit_order_fill_event(..., swap_index) for each maker fill produced by that swap.
  2. Event attr: Add swap_index (stringified u32) to the limit_order_fill wasm event attributes.
  3. Indexer: In parse_limit_order_fill_segment, read optional swap_index attr; when present and valid, use it instead of walk-order inference. Keep inference as fallback.
  4. Tests: Contract — assert attr present on hybrid fills; multi-swap-same-pair tx — fills carry distinct indices. Indexer — LCD fixture with explicit swap_index attrs links correctly.
  5. Backfill: No on-chain backfill possible; historical rows remain on inference. Document limitation.

Acceptance criteria

  • Every limit_order_fill wasm event emitted during hybrid execute includes swap_index matching the parent swap's per-pair ordinal.
  • Indexer prefers on-chain swap_index when present; walk-order inference unchanged for legacy txs.
  • limit_fill_swap_linkage test extended with wasm fixture using explicit attrs.
  • make test-contracts and indexer integration tests green.
  • Integrator / audit docs updated.

Test plan — all paths

Path Expected
Single hybrid swap, one maker fill swap_index=0 on fill event
Single tx, two swaps same pair, one fill each Fills carry swap_index 0 and 1 respectively
Pool-only swap (no fills) No limit_order_fill events
Book-only hybrid (pool_input=0) with fills swap_index present
Columnar merged wasm events (multiple fills) Each fill row has correct swap_index
Indexer replay of pre-upgrade txs (no attr) Inference still links correctly
Indexer replay of post-upgrade txs (with attr) Uses emitted attr, ignores stale inference

Test plan — attack / abuse / hack vectors

Vector Expected
Forged swap_index in LCD attrs (indexer ingest) Indexer validates attr matches walk-order or rejects mismatched linkage; no cross-swap mis-attribution
Inflated swap_index with no matching swap row swap_id_for_tx_pair_index returns None; fill stored without bogus swap_event_id
Event reordering attack on merged txs On-chain emission order is authoritative; off-chain cannot reorder attrs within a signed tx
Duplicate fill attrs with conflicting swap_index fill_exists dedup prevents double-insert

Verification criteria

  • cargo test -p cl8y-dex-tests hybrid_swap_emits_limit_order_fill passes with swap_index attr assertions.
  • cargo test --test limit_fill_swap_linkage passes (extended).
  • Grep contract events: limit_order_fill_event includes swap_index.
  • Manual LocalTerra hybrid swap → LCD tx query shows swap_index on fill wasm attrs.
## Parent Follow-up to GitLab **#316** (indexer-side `swap_index` linkage — **closed**). Short-term parser walk-order attribution is merged; this issue tracks the **long-term contract** change for durable LCD replay. ## Current codebase - **Indexer (shipped):** `parse_limit_order_fills` in `indexer/src/indexer/parser.rs` assigns each fill a per-pair `swap_index` by walking wasm events in tx order — fills emitted before their parent `swap` action receive the *upcoming* ordinal (same counter as `parse_swaps`). `process_limit_order_fill` links via `limit_order_fills::swap_id_for_tx_pair_index(tx_hash, pair_id, swap_index)` (see `indexer/tests/limit_fill_swap_linkage.rs`). - **Contract (gap):** `limit_order_fill_event` in `smartcontracts/contracts/pair/src/orderbook.rs` emits `action=limit_order_fill` with `order_id`, `side`, `maker`, `price`, token amounts, and `commission_amount` — **no `swap_index` attribute**. - **Parser fallback:** When `swap_index` is absent from wasm attrs, the indexer infers it from event ordering. This breaks if LCD merges/splits wasm events differently than the live chain, or on historical replay after parser logic changes. ## Why this is needed Without an on-chain `swap_index` on each `limit_order_fill` event: - LCD-only replay (indexer cold start, integrator backfill) cannot reliably attribute fills to the correct `swap_events` row when a tx has **multiple swaps on the same pair** (router revisit, batch). - Parser ordering heuristics are fragile across node versions and merged-event columnar layouts. - Integrator analytics (`docs/integrators-hybrid-volume.md`) depend on correct fill→swap linkage for hybrid book attribution. Emitting `swap_index` on-chain makes attribution **self-describing** and matches the `(tx_hash, pair_id, swap_index)` unique key added in GitLab **#287**. ## Constraints / guardrails - **Backward compatible:** Indexer must continue to accept fills **without** `swap_index` (infer from walk order) for historical txs. - **Ordinal semantics:** `swap_index` on a fill must match the per-pair swap ordinal the pair contract uses for that tx — same definition as `swap` event ordering (0-based, incremented once per completed swap on the pair within the tx). - **No schema migration** on `limit_order_fills` dedup key `(tx_hash, pair_id, order_id)`. - **Gas:** One extra string attribute per fill event — negligible vs existing fill attrs. - Coordinate wasm change with indexer parser (prefer emitted attr when present, fall back to inference). - Update `docs/contracts-security-audit.md` event catalog and integrator docs. ## Relevant files | Area | Path | |------|------| | Fill event builder | `smartcontracts/contracts/pair/src/orderbook.rs` — `limit_order_fill_event`, `fill_events.push(...)` call sites | | Swap execute / hybrid | `smartcontracts/contracts/pair/src/contract.rs`, `orderbook.rs` match walk | | Integration tests | `smartcontracts/tests/src/limit_order_tests.rs` — `hybrid_swap_emits_limit_order_fill_events`, multi-fill cases | | Indexer parser | `indexer/src/indexer/parser.rs` — `parse_limit_order_fill_segment`, `parse_limit_order_fills` | | Indexer linkage | `indexer/src/db/queries/limit_order_fills.rs` | | Regression test | `indexer/tests/limit_fill_swap_linkage.rs` | | Docs | `docs/integrators-hybrid-volume.md`, `docs/limit-orders.md` | ## Recommended direction 1. **Contract:** Thread a per-pair `swap_index` counter through hybrid execute (increment after each swap wasm event is finalized). Pass the **current** ordinal into `limit_order_fill_event(..., swap_index)` for each maker fill produced by that swap. 2. **Event attr:** Add `swap_index` (stringified `u32`) to the `limit_order_fill` wasm event attributes. 3. **Indexer:** In `parse_limit_order_fill_segment`, read optional `swap_index` attr; when present and valid, use it instead of walk-order inference. Keep inference as fallback. 4. **Tests:** Contract — assert attr present on hybrid fills; multi-swap-same-pair tx — fills carry distinct indices. Indexer — LCD fixture with explicit `swap_index` attrs links correctly. 5. **Backfill:** No on-chain backfill possible; historical rows remain on inference. Document limitation. ## Acceptance criteria - [ ] Every `limit_order_fill` wasm event emitted during hybrid execute includes `swap_index` matching the parent swap's per-pair ordinal. - [ ] Indexer prefers on-chain `swap_index` when present; walk-order inference unchanged for legacy txs. - [ ] `limit_fill_swap_linkage` test extended with wasm fixture using explicit attrs. - [ ] `make test-contracts` and indexer integration tests green. - [ ] Integrator / audit docs updated. ## Test plan — all paths | Path | Expected | |------|----------| | Single hybrid swap, one maker fill | `swap_index=0` on fill event | | Single tx, two swaps same pair, one fill each | Fills carry `swap_index` 0 and 1 respectively | | Pool-only swap (no fills) | No `limit_order_fill` events | | Book-only hybrid (`pool_input=0`) with fills | `swap_index` present | | Columnar merged wasm events (multiple fills) | Each fill row has correct `swap_index` | | Indexer replay of pre-upgrade txs (no attr) | Inference still links correctly | | Indexer replay of post-upgrade txs (with attr) | Uses emitted attr, ignores stale inference | ## Test plan — attack / abuse / hack vectors | Vector | Expected | |--------|----------| | Forged `swap_index` in LCD attrs (indexer ingest) | Indexer validates attr matches walk-order or rejects mismatched linkage; no cross-swap mis-attribution | | Inflated `swap_index` with no matching swap row | `swap_id_for_tx_pair_index` returns `None`; fill stored without bogus `swap_event_id` | | Event reordering attack on merged txs | On-chain emission order is authoritative; off-chain cannot reorder attrs within a signed tx | | Duplicate fill attrs with conflicting `swap_index` | `fill_exists` dedup prevents double-insert | ## Verification criteria - [ ] `cargo test -p cl8y-dex-tests hybrid_swap_emits_limit_order_fill` passes with `swap_index` attr assertions. - [ ] `cargo test --test limit_fill_swap_linkage` passes (extended). - [ ] Grep contract events: `limit_order_fill_event` includes `swap_index`. - [ ] Manual LocalTerra hybrid swap → LCD tx query shows `swap_index` on fill wasm attrs.
PlasticDigits commented 2026-06-05 13:44:30 +00:00 (Migrated from gitlab.com)

marked as related to #316

marked as related to #316
ghost1 commented 2026-06-05 14:06:40 +00:00 (Migrated from gitlab.com)

mentioned in commit b6c3cae169

mentioned in commit b6c3cae16914356aad3f22ae7b81debc7d8fe500
PlasticDigits commented 2026-06-05 14:07:07 +00:00 (Migrated from gitlab.com)

mentioned in merge request !817

mentioned in merge request !817
PlasticDigits commented 2026-06-05 14:07:09 +00:00 (Migrated from gitlab.com)

Implementation in MR !817: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/99

Contract stamps swap_index on limit_order_fill events; indexer prefers on-chain attr. Agent labels removed; issue stays open for merge + deploy verification.

Implementation in MR !817: https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/99 Contract stamps `swap_index` on `limit_order_fill` events; indexer prefers on-chain attr. Agent labels removed; issue stays open for merge + deploy verification.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-05 14:27:14 +00:00
PlasticDigits commented 2026-06-05 14:27:15 +00:00 (Migrated from gitlab.com)

mentioned in commit 13be1a3398

mentioned in commit 13be1a33985aa859af18c645f7769df04996201b
PlasticDigits commented 2026-06-08 08:43:13 +00:00 (Migrated from gitlab.com)

mentioned in commit cd58a65edb

mentioned in commit cd58a65edb07dfb182834de908b1511538cab309
PlasticDigits commented 2026-06-08 13:42:28 +00:00 (Migrated from gitlab.com)

mentioned in commit 767cdd7092

mentioned in commit 767cdd7092adf24ecc71e229b306ba2931456cd9
PlasticDigits commented 2026-06-08 13:42:28 +00:00 (Migrated from gitlab.com)

mentioned in commit 21f6eb6ada

mentioned in commit 21f6eb6ada6ea0d62108b565591dab3c07546194
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#331
No description provided.