Integrators: accurate hybrid trade volume reporting and reconciliation docs #216

Closed
opened 2026-05-29 03:19:28 +00:00 by PlasticDigits · 8 comments
PlasticDigits commented 2026-05-29 03:19:28 +00:00 (Migrated from gitlab.com)

Summary

CoinGecko / CoinMarketCap integrators and Vyntrex-style aggregators need trustworthy, documented hybrid swap volume semantics. On-chain wasm attrs and indexer DB columns exist (#82, consolidated CG/CMC in #189), but end-to-end accuracy and integrator-facing guidance remain partial — see gap analysis gaps/GAP_1780023683.md §6.3.


Current codebase

On-chain (pair contract)

Hybrid swaps (Pattern C: pool + limit book in one tx) emit Terraport-compatible baseline wasm attrs plus CL8Y leg breakdown:

Attribute Role
offer_amount / return_amount Total offer consumed and total ask output to receiver
spread_amount / commission_amount Pool leg only (Terraport baseline)
book_commission_amount Book taker fees when book leg > 0 (#196)
pool_return_amount / book_return_amount Ask-side net per leg; return_amount = sum
limit_book_offer_consumed Offer-side amount matched on the book
effective_fee_bps Effective swap fee after discount registry

Per-maker fills also emit separate limit_order_fill wasm events (not duplicated in baseline commission_amount).

Indexer

  • Persistence: swap_events columns pool_return_amount, book_return_amount, limit_book_offer_consumed, effective_fee_bps (migration indexer/migrations/20260326000001_limit_order_fills.sql); parser in indexer/src/indexer/parser.rs.
  • Internal API: GET /api/v1/pairs/{addr}/trades returns hybrid columns on TradeResponse (indexer/src/api/pairs.rs).
  • CG/CMC: Consolidated 24h volumes use offer_amount / return_amount; optional cl8y_extensions on tickers/summary (hybrid_trade_count_24h, book_leg_volume_quote_24h, etc.) via indexer/src/api/consolidated_stats.rs and get_24h_hybrid_breakdown in indexer/src/db/queries/swap_events.rs; per-trade pool_leg_volume / book_leg_volume on historical trade feeds (indexer/src/api/cg.rs, cmc.rs).
  • Derived stats: Pair 24h stats, candles, token volume rollups, and trader total_volume aggregate from swap_events offer/return totals, not from summing limit_order_fills (by design — avoids double-count).
  • Tests: indexer/tests/swap_events_hybrid_columns.rs, indexer/tests/api_consolidated_reporting.rs; invariant row in docs/indexer-invariants.md.

Documentation (partial)

  • Field mapping table: docs/integrators.md § Vyntrex / Terraport (GitLab #189).
  • Listing semantics: docs/CG_CMC_COMPLIANCE.md § Consolidated hybrid + pool-only reporting.
  • Gaps: No single “volume reconciliation playbook” for integrators; leg volume units (ask-side vs offer-side) easy to misread; older pairs may have null hybrid columns (DEX-P2-015); dApp charts/trades UI does not surface leg breakdown for retail.

Why this is needed

  1. Listing accuracy: Aggregators that sum limit_order_fills and swap_events, or that use pool-only commission_amount as “volume,” will misstate hybrid activity.
  2. Product expectation: Gap analysis rates integrator “accurate hybrid trade volume” as Partial despite shipped columns — operators cannot hand CG/CMC/Vyntrex a single doc that defines consolidated vs leg volumes, null handling, and forbidden double-count paths.
  3. Downstream metrics: volume_usd, candles, leaderboards, and token_volume_stats inherit consolidated swap rows; integrators need explicit rules for when USD/leg attribution is valid vs unknown.
  4. Follow-on to #189: Consolidated API fields landed; accuracy + documentation for all integrator paths (not only CG/CMC JSON) is the remaining deliverable.

Constraints and guardrails

  • No double-count: Hybrid economic activity for a taker swap must appear once in headline volume — in swap_events consolidated offer_amount / return_amount, not again by summing limit_order_fills for the same tx.
  • Terraport baseline preserved: Do not change wasm commission_amount / spread_amount on swaps to include book leg (breaks Vyntrex baseline parsers); use extension attrs and indexer columns.
  • Backward compatibility: Pool-only pairs and pre-hybrid txs must continue to work; null hybrid columns must be documented, not treated as zero-volume.
  • Unit clarity: Document which fields are ask-side (book_return_amount, pool_return_amount, CG pool_leg_volume/book_leg_volume) vs offer-side (limit_book_offer_consumed, offer_amount).
  • Invariant L7/L8: Hook commission_amount is total (pool+book); swap attr commission_amount is pool-only — integrator docs must not conflate them (docs/integrators.md).
  • Scope boundary: This issue is reporting + docs + indexer/API consistency, not hybrid quote simulation (L8 / #108) or default route solver (#101).

Relevant files

Area Path
Wasm attrs / pair swap smartcontracts/contracts/pair/src/contract.rs (swap execution + attrs)
Shared types smartcontracts/packages/dex-common/
Parser indexer/src/indexer/parser.rs
DB / aggregations indexer/src/db/queries/swap_events.rs, volume.rs, indexer/src/indexer/candle_builder.rs, trader_tracker.rs
CG/CMC / extensions indexer/src/api/consolidated_stats.rs, cg.rs, cmc.rs
REST trades indexer/src/api/pairs.rs, text_csv.rs
Limit fills (do not double-count) indexer/src/db/queries/limit_order_fills.rs, indexer/src/api/pairs.rs (limit-fills)
Docs docs/integrators.md, docs/CG_CMC_COMPLIANCE.md, docs/limit-orders.md, docs/terraport.md, docs/indexer-invariants.md
Tests indexer/tests/api_consolidated_reporting.rs, swap_events_hybrid_columns.rs
Frontend types (optional alignment) frontend-dapp/src/types/index.ts, TradesTable.tsx
Gap / backlog gaps/GAP_1780023683.md, docs/reviews/20260409T030009Z/ISSUE_BACKLOG.md

  1. Author integrator “Hybrid volume reconciliation” guide (new section in docs/integrators.md or docs/integrators-hybrid-volume.md linked from README/docs index):
    • Decision tree: headline volume → swap_events; leg attribution → optional columns; maker-level detail → limit_order_fills only when not already counted in parent swap.
    • Worked examples: pool-only swap, single-fill hybrid, multi-fill hybrid (sum of legs = return_amount).
    • Null / legacy matrix: pairs deployed before hybrid attrs.
    • CG/CMC field mapping + internal /api/v1 mapping in one table.
  2. Audit indexer aggregations for consistency: confirm get_24h_hybrid_breakdown leg sums use the same asset side as CG book_leg_volume / pool_leg_volume; align pair stats / overview if any endpoint exposes misleading partial volumes.
  3. Optional API polish (if audit finds gaps): e.g. expose pool_leg_volume / book_leg_volume on /api/v1/pairs/{addr}/trades for parity with CG/CMC; document book_commission_amount in OpenAPI if parsed/stored.
  4. Cross-link from docs/CG_CMC_COMPLIANCE.md and Terraport comparison (docs/terraport.md) to the reconciliation guide.
  5. Do not change consolidated totals to sum fills + swaps.

Acceptance criteria

  • Published integrator doc defines consolidated vs leg vs fill volumes, units, and double-count rules with ≥2 worked numeric examples.
  • docs/CG_CMC_COMPLIANCE.md and docs/indexer-invariants.md link to the guide; Terraport mapping table references volume reconciliation.
  • All listing and internal trade endpoints document which fields are safe for headline 24h volume vs attribution-only.
  • Indexer: for indexed hybrid swaps, pool_return_amount + book_return_amount = return_amount (within rounding) is asserted in tests; documented behavior when attrs missing.
  • cl8y_extensions 24h leg volumes are documented as ask-side (quote/target) raw units (or corrected in code if audit finds mismatch).
  • Explicit “anti-patterns” section: summing limit_order_fills into pair volume, using pool-only commission_amount as volume, counting parked/expired placements as trade volume.
  • Optional: /api/v1/pairs/{addr}/trades exposes leg volume aliases consistent with CG/CMC if audit recommends parity.

Test plan — functional paths

# Path Setup Expected
1 Pool-only swap Tx without hybrid attrs swap_events row; null leg columns; consolidated volume = offer_amount; CG ticker counts as pool_only_trade_count
2 Hybrid single-fill E2E or fixture tx with all hybrid attrs Leg columns populated; return_amount = pool + book legs; CG trade has pool_leg_volume + book_leg_volume; extensions counts hybrid +1
3 Hybrid multi-fill Contract test tx with 2+ limit_order_fill events One swap_events row; multiple limit_order_fills linked via swap_event_id; headline volume still single row
4 Parser round-trip swap_events_hybrid_columns.rs Wasm attrs → DB columns unchanged
5 CG/CMC consolidated api_consolidated_reporting.rs cl8y_extensions matches seeded hybrid row; standard volumes unchanged
6 Internal trades API GET /api/v1/pairs/{addr}/trades Hybrid columns match DB; documented mapping to CG fields
7 CSV export text_csv.rs / download path Hybrid columns present when indexed
8 24h pair stats GET pair stats after hybrid swap volume_quote / volume_base use consolidated amounts, not leg double-count
9 Candles Hybrid swap in same interval as pool-only Candle volume accumulates consolidated offer/quote per existing builder rules
10 Token / global volume refresh volume_aggregator job token_volume_stats sums offer_amount once per swap
11 Trader leaderboard Hybrid swap by trader total_volume increments by consolidated offer (or documented rule), not per-fill sum
12 Legacy / null attrs Old pair or stripped attrs tx Documented null behavior; no fabricated leg volumes in API
13 Buy vs sell orientation Hybrid buy and sell vs pair asset_1_id CG/CMC base_volume / target_volume orientation matches compliance doc

Test plan — attack vectors / abuse scenarios

# Threat Test approach Expected mitigation
A1 Integrator double-counts fills + swaps Doc + optional integration test documenting forbidden query Guide + invariant tests; no API that returns “total volume” summing both
A2 Inflated volume via duplicate tx indexing Re-submit same tx_hash trade_exists / ON CONFLICT prevents duplicate swap_events
A3 Forged wasm attrs (malicious node) N/A on indexer (trusts chain); contract tests On-chain return_amount must equal sum of legs; contract rejects inconsistent settlement
A4 Misleading cl8y_extensions after reorg Reorg handling / idempotency tests if present No duplicate rows; extensions recompute from DB
A5 Rate-limit bypass via huge trade export indexer/tests/security.rs patterns Limits on list endpoints
A6 Null legs interpreted as zero volume API test: null columns Headline volume still from offer_amount/return_amount; legs omitted, not "0" fake attribution
A7 USD volume manipulation Oracle price edge cases in compute_volume_usd Document USD as best-effort; hybrid does not change USD formula independently of consolidated amounts
A8 CG/CMC client sums book_leg_volume_quote_24h + pool_leg_volume_quote_24h + standard volume Doc anti-pattern Extensions documented as attribution subset, not additive to target_volume

Verification criteria

  • cd indexer && cargo test swap_events_hybrid_columns api_consolidated_reporting --tests passes.
  • make check-fee-discount-tier-docs (if fee bps examples touched) — N/A otherwise.
  • Manual: run one local hybrid swap (make test-e2e-tx / hybrid-swap.spec.ts path), query /cg/tickers, /cg/historical_trades, /api/v1/pairs/{addr}/trades; confirm leg sum equals return_amount and extensions match.
  • Docs review: third-party integrator can answer “what number do I publish as 24h volume?” without reading Rust.
  • Sign-off from compliance checklist in docs/CG_CMC_COMPLIANCE.md updated with link to reconciliation guide.

  • Closed: #82 (parser columns), #189 (consolidated CG/CMC)
  • Related: #196 (book commission), #108 (hybrid quoting epic — out of scope here)
  • Backlog: DEX-P2-015 (older pair null attrs)

Priority

P1 — integrator/listings trust; unblocks honest CG/CMC and Vyntrex volume reporting without waiting for full hybrid routing product work.

Labels

indexer, docs, hybrid, integrator

## Summary CoinGecko / CoinMarketCap integrators and Vyntrex-style aggregators need **trustworthy, documented hybrid swap volume** semantics. On-chain wasm attrs and indexer DB columns exist ([#82](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/82), consolidated CG/CMC in [#189](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/189)), but **end-to-end accuracy and integrator-facing guidance remain partial** — see [gap analysis `gaps/GAP_1780023683.md`](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/gaps/GAP_1780023683.md) §6.3. --- ## Current codebase ### On-chain (pair contract) Hybrid swaps (Pattern C: pool + limit book in one tx) emit **Terraport-compatible baseline** wasm attrs plus CL8Y leg breakdown: | Attribute | Role | |-----------|------| | `offer_amount` / `return_amount` | Total offer consumed and total ask output to receiver | | `spread_amount` / `commission_amount` | **Pool leg only** (Terraport baseline) | | `book_commission_amount` | Book taker fees when book leg > 0 ([#196](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/196)) | | `pool_return_amount` / `book_return_amount` | Ask-side net per leg; `return_amount` = sum | | `limit_book_offer_consumed` | Offer-side amount matched on the book | | `effective_fee_bps` | Effective swap fee after discount registry | Per-maker fills also emit separate `limit_order_fill` wasm events (not duplicated in baseline `commission_amount`). ### Indexer - **Persistence:** `swap_events` columns `pool_return_amount`, `book_return_amount`, `limit_book_offer_consumed`, `effective_fee_bps` (migration `indexer/migrations/20260326000001_limit_order_fills.sql`); parser in `indexer/src/indexer/parser.rs`. - **Internal API:** `GET /api/v1/pairs/{addr}/trades` returns hybrid columns on `TradeResponse` (`indexer/src/api/pairs.rs`). - **CG/CMC:** Consolidated 24h volumes use `offer_amount` / `return_amount`; optional `cl8y_extensions` on tickers/summary (`hybrid_trade_count_24h`, `book_leg_volume_quote_24h`, etc.) via `indexer/src/api/consolidated_stats.rs` and `get_24h_hybrid_breakdown` in `indexer/src/db/queries/swap_events.rs`; per-trade `pool_leg_volume` / `book_leg_volume` on historical trade feeds (`indexer/src/api/cg.rs`, `cmc.rs`). - **Derived stats:** Pair 24h stats, candles, token volume rollups, and trader `total_volume` aggregate from `swap_events` **offer/return totals**, not from summing `limit_order_fills` (by design — avoids double-count). - **Tests:** `indexer/tests/swap_events_hybrid_columns.rs`, `indexer/tests/api_consolidated_reporting.rs`; invariant row in `docs/indexer-invariants.md`. ### Documentation (partial) - Field mapping table: `docs/integrators.md` § Vyntrex / Terraport (GitLab #189). - Listing semantics: `docs/CG_CMC_COMPLIANCE.md` § Consolidated hybrid + pool-only reporting. - **Gaps:** No single “volume reconciliation playbook” for integrators; leg volume units (ask-side vs offer-side) easy to misread; older pairs may have **null** hybrid columns ([DEX-P2-015](docs/reviews/20260409T030009Z/ISSUE_BACKLOG.md)); dApp charts/trades UI does not surface leg breakdown for retail. --- ## Why this is needed 1. **Listing accuracy:** Aggregators that sum `limit_order_fills` **and** `swap_events`, or that use pool-only `commission_amount` as “volume,” will **misstate** hybrid activity. 2. **Product expectation:** Gap analysis rates integrator “accurate hybrid trade volume” as **Partial** despite shipped columns — operators cannot hand CG/CMC/Vyntrex a single doc that defines consolidated vs leg volumes, null handling, and forbidden double-count paths. 3. **Downstream metrics:** `volume_usd`, candles, leaderboards, and `token_volume_stats` inherit consolidated swap rows; integrators need explicit rules for when USD/leg attribution is valid vs unknown. 4. **Follow-on to #189:** Consolidated API fields landed; **accuracy + documentation** for all integrator paths (not only CG/CMC JSON) is the remaining deliverable. --- ## Constraints and guardrails - **No double-count:** Hybrid economic activity for a taker swap must appear **once** in headline volume — in `swap_events` consolidated `offer_amount` / `return_amount`, **not** again by summing `limit_order_fills` for the same tx. - **Terraport baseline preserved:** Do not change wasm `commission_amount` / `spread_amount` on swaps to include book leg (breaks Vyntrex baseline parsers); use extension attrs and indexer columns. - **Backward compatibility:** Pool-only pairs and pre-hybrid txs must continue to work; null hybrid columns must be documented, not treated as zero-volume. - **Unit clarity:** Document which fields are **ask-side** (`book_return_amount`, `pool_return_amount`, CG `pool_leg_volume`/`book_leg_volume`) vs **offer-side** (`limit_book_offer_consumed`, `offer_amount`). - **Invariant L7/L8:** Hook `commission_amount` is **total** (pool+book); swap attr `commission_amount` is **pool-only** — integrator docs must not conflate them ([`docs/integrators.md`](docs/integrators.md)). - **Scope boundary:** This issue is **reporting + docs + indexer/API consistency**, not hybrid quote simulation (L8 / #108) or default route solver (#101). --- ## Relevant files | Area | Path | |------|------| | Wasm attrs / pair swap | `smartcontracts/contracts/pair/src/contract.rs` (swap execution + attrs) | | Shared types | `smartcontracts/packages/dex-common/` | | Parser | `indexer/src/indexer/parser.rs` | | DB / aggregations | `indexer/src/db/queries/swap_events.rs`, `volume.rs`, `indexer/src/indexer/candle_builder.rs`, `trader_tracker.rs` | | CG/CMC / extensions | `indexer/src/api/consolidated_stats.rs`, `cg.rs`, `cmc.rs` | | REST trades | `indexer/src/api/pairs.rs`, `text_csv.rs` | | Limit fills (do not double-count) | `indexer/src/db/queries/limit_order_fills.rs`, `indexer/src/api/pairs.rs` (`limit-fills`) | | Docs | `docs/integrators.md`, `docs/CG_CMC_COMPLIANCE.md`, `docs/limit-orders.md`, `docs/terraport.md`, `docs/indexer-invariants.md` | | Tests | `indexer/tests/api_consolidated_reporting.rs`, `swap_events_hybrid_columns.rs` | | Frontend types (optional alignment) | `frontend-dapp/src/types/index.ts`, `TradesTable.tsx` | | Gap / backlog | `gaps/GAP_1780023683.md`, `docs/reviews/20260409T030009Z/ISSUE_BACKLOG.md` | --- ## Recommended direction 1. **Author integrator “Hybrid volume reconciliation” guide** (new section in `docs/integrators.md` or `docs/integrators-hybrid-volume.md` linked from README/docs index): - Decision tree: headline volume → `swap_events`; leg attribution → optional columns; maker-level detail → `limit_order_fills` **only when not already counted in parent swap**. - Worked examples: pool-only swap, single-fill hybrid, multi-fill hybrid (sum of legs = `return_amount`). - Null / legacy matrix: pairs deployed before hybrid attrs. - CG/CMC field mapping + internal `/api/v1` mapping in one table. 2. **Audit indexer aggregations** for consistency: confirm `get_24h_hybrid_breakdown` leg sums use the same asset side as CG `book_leg_volume` / `pool_leg_volume`; align pair stats / overview if any endpoint exposes misleading partial volumes. 3. **Optional API polish (if audit finds gaps):** e.g. expose `pool_leg_volume` / `book_leg_volume` on `/api/v1/pairs/{addr}/trades` for parity with CG/CMC; document `book_commission_amount` in OpenAPI if parsed/stored. 4. **Cross-link** from `docs/CG_CMC_COMPLIANCE.md` and Terraport comparison (`docs/terraport.md`) to the reconciliation guide. 5. **Do not** change consolidated totals to sum fills + swaps. --- ## Acceptance criteria - [ ] Published integrator doc defines consolidated vs leg vs fill volumes, units, and double-count rules with ≥2 worked numeric examples. - [ ] `docs/CG_CMC_COMPLIANCE.md` and `docs/indexer-invariants.md` link to the guide; Terraport mapping table references volume reconciliation. - [ ] All listing and internal trade endpoints document which fields are safe for **headline 24h volume** vs attribution-only. - [ ] Indexer: for indexed hybrid swaps, `pool_return_amount + book_return_amount = return_amount` (within rounding) is asserted in tests; documented behavior when attrs missing. - [ ] `cl8y_extensions` 24h leg volumes are documented as **ask-side (quote/target) raw units** (or corrected in code if audit finds mismatch). - [ ] Explicit “anti-patterns” section: summing `limit_order_fills` into pair volume, using pool-only `commission_amount` as volume, counting parked/expired placements as trade volume. - [ ] Optional: `/api/v1/pairs/{addr}/trades` exposes leg volume aliases consistent with CG/CMC if audit recommends parity. --- ## Test plan — functional paths | # | Path | Setup | Expected | |---|------|-------|----------| | 1 | Pool-only swap | Tx without hybrid attrs | `swap_events` row; null leg columns; consolidated volume = `offer_amount`; CG ticker counts as `pool_only_trade_count` | | 2 | Hybrid single-fill | E2E or fixture tx with all hybrid attrs | Leg columns populated; `return_amount` = pool + book legs; CG trade has `pool_leg_volume` + `book_leg_volume`; extensions counts hybrid +1 | | 3 | Hybrid multi-fill | Contract test tx with 2+ `limit_order_fill` events | One `swap_events` row; multiple `limit_order_fills` linked via `swap_event_id`; headline volume still single row | | 4 | Parser round-trip | `swap_events_hybrid_columns.rs` | Wasm attrs → DB columns unchanged | | 5 | CG/CMC consolidated | `api_consolidated_reporting.rs` | `cl8y_extensions` matches seeded hybrid row; standard volumes unchanged | | 6 | Internal trades API | GET `/api/v1/pairs/{addr}/trades` | Hybrid columns match DB; documented mapping to CG fields | | 7 | CSV export | `text_csv.rs` / download path | Hybrid columns present when indexed | | 8 | 24h pair stats | GET pair stats after hybrid swap | `volume_quote` / `volume_base` use consolidated amounts, not leg double-count | | 9 | Candles | Hybrid swap in same interval as pool-only | Candle volume accumulates consolidated offer/quote per existing builder rules | | 10 | Token / global volume refresh | `volume_aggregator` job | `token_volume_stats` sums `offer_amount` once per swap | | 11 | Trader leaderboard | Hybrid swap by trader | `total_volume` increments by consolidated offer (or documented rule), not per-fill sum | | 12 | Legacy / null attrs | Old pair or stripped attrs tx | Documented null behavior; no fabricated leg volumes in API | | 13 | Buy vs sell orientation | Hybrid buy and sell vs pair `asset_1_id` | CG/CMC `base_volume` / `target_volume` orientation matches compliance doc | --- ## Test plan — attack vectors / abuse scenarios | # | Threat | Test approach | Expected mitigation | |---|--------|---------------|---------------------| | A1 | Integrator double-counts fills + swaps | Doc + optional integration test documenting forbidden query | Guide + invariant tests; no API that returns “total volume” summing both | | A2 | Inflated volume via duplicate tx indexing | Re-submit same `tx_hash` | `trade_exists` / ON CONFLICT prevents duplicate `swap_events` | | A3 | Forged wasm attrs (malicious node) | N/A on indexer (trusts chain); contract tests | On-chain `return_amount` must equal sum of legs; contract rejects inconsistent settlement | | A4 | Misleading `cl8y_extensions` after reorg | Reorg handling / idempotency tests if present | No duplicate rows; extensions recompute from DB | | A5 | Rate-limit bypass via huge trade export | `indexer/tests/security.rs` patterns | Limits on list endpoints | | A6 | Null legs interpreted as zero volume | API test: null columns | Headline volume still from `offer_amount`/`return_amount`; legs omitted, not `"0"` fake attribution | | A7 | USD volume manipulation | Oracle price edge cases in `compute_volume_usd` | Document USD as best-effort; hybrid does not change USD formula independently of consolidated amounts | | A8 | CG/CMC client sums `book_leg_volume_quote_24h` + `pool_leg_volume_quote_24h` + standard volume | Doc anti-pattern | Extensions documented as **attribution subset**, not additive to `target_volume` | --- ## Verification criteria - [ ] `cd indexer && cargo test swap_events_hybrid_columns api_consolidated_reporting --tests` passes. - [ ] `make check-fee-discount-tier-docs` (if fee bps examples touched) — N/A otherwise. - [ ] Manual: run one local hybrid swap (`make test-e2e-tx` / `hybrid-swap.spec.ts` path), query `/cg/tickers`, `/cg/historical_trades`, `/api/v1/pairs/{addr}/trades`; confirm leg sum equals `return_amount` and extensions match. - [ ] Docs review: third-party integrator can answer “what number do I publish as 24h volume?” without reading Rust. - [ ] Sign-off from compliance checklist in `docs/CG_CMC_COMPLIANCE.md` updated with link to reconciliation guide. --- ## Related issues - Closed: [#82](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/82) (parser columns), [#189](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/189) (consolidated CG/CMC) - Related: [#196](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/196) (book commission), [#108](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/108) (hybrid quoting epic — out of scope here) - Backlog: DEX-P2-015 (older pair null attrs) ## Priority **P1** — integrator/listings trust; unblocks honest CG/CMC and Vyntrex volume reporting without waiting for full hybrid routing product work. ## Labels `indexer`, `docs`, `hybrid`, `integrator`
PlasticDigits commented 2026-05-29 05:37:30 +00:00 (Migrated from gitlab.com)

mentioned in commit 2097452008

mentioned in commit 2097452008fb629954e29877a33681e9f7053d02
PlasticDigits commented 2026-05-29 05:37:44 +00:00 (Migrated from gitlab.com)

Implemented (#216) — merged to main @2097452

Delivered integrator-facing hybrid volume reconciliation (reporting + docs + API parity), without changing consolidated volume totals or Terraport baseline wasm attrs.

What changed

  • Guide: docs/integrators-hybrid-volume.md — decision tree, units (ask vs offer), 3 worked examples, null/legacy matrix, API mapping table, anti-patterns.
  • Invariant L10: docs/indexer-invariants.md — headline volume rules; pool_return_amount + book_return_amount = return_amount when legs indexed.
  • API: GET /api/v1/pairs/{addr}/trades now exposes pool_leg_volume / book_leg_volume (CG/CMC aliases); CSV export includes the same columns.
  • Cross-links: CG_CMC_COMPLIANCE.md (listing sign-off checklist), integrators.md, terraport.md, docs/README.md, gap analysis §6.3.
  • Agent skill: skills/AGENTS_INTEGRATOR_HYBRID_VOLUME.md

Tests run (pass)

cd indexer && cargo test --test api_integrator_hybrid_volume --test api_consolidated_reporting --test swap_events_hybrid_columns -j 1 -- --test-threads=1

Verification checklist (please confirm)

  • Read integrators-hybrid-volume.md — can answer “what is 24h volume?” without reading Rust.
  • Hybrid swap: /cg/tickers cl8y_extensions leg sums are ask-side attribution, not added to target_volume.
  • Hybrid swap: /cg/historical_trades and /api/v1/pairs/{addr}/trades show pool_leg_volume + book_leg_volume summing to return_amount.
  • Pool-only swap: leg aliases omitted (not "0"); headline volume still from offer_amount / return_amount.
  • No query path sums limit_order_fills into pair headline volume.
  • CG/CMC listing sign-off section in CG_CMC_COMPLIANCE.md links to the guide.

@brouie — could you verify the checklist above (docs + indexer API) when you have a moment? Leaving this issue open until sign-off.

/cc @PlasticDigits

## Implemented (#216) — merged to `main` @2097452 Delivered integrator-facing **hybrid volume reconciliation** (reporting + docs + API parity), without changing consolidated volume totals or Terraport baseline wasm attrs. ### What changed - **Guide:** [docs/integrators-hybrid-volume.md](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/integrators-hybrid-volume.md) — decision tree, units (ask vs offer), 3 worked examples, null/legacy matrix, API mapping table, anti-patterns. - **Invariant L10:** [docs/indexer-invariants.md](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/indexer-invariants.md) — headline volume rules; `pool_return_amount + book_return_amount = return_amount` when legs indexed. - **API:** `GET /api/v1/pairs/{addr}/trades` now exposes `pool_leg_volume` / `book_leg_volume` (CG/CMC aliases); CSV export includes the same columns. - **Cross-links:** `CG_CMC_COMPLIANCE.md` (listing sign-off checklist), `integrators.md`, `terraport.md`, `docs/README.md`, gap analysis §6.3. - **Agent skill:** [skills/AGENTS_INTEGRATOR_HYBRID_VOLUME.md](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/skills/AGENTS_INTEGRATOR_HYBRID_VOLUME.md) ### Tests run (pass) ```bash cd indexer && cargo test --test api_integrator_hybrid_volume --test api_consolidated_reporting --test swap_events_hybrid_columns -j 1 -- --test-threads=1 ``` ### Verification checklist (please confirm) - [ ] Read [integrators-hybrid-volume.md](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/integrators-hybrid-volume.md) — can answer “what is 24h volume?” without reading Rust. - [ ] Hybrid swap: `/cg/tickers` `cl8y_extensions` leg sums are **ask-side attribution**, not added to `target_volume`. - [ ] Hybrid swap: `/cg/historical_trades` and `/api/v1/pairs/{addr}/trades` show `pool_leg_volume` + `book_leg_volume` summing to `return_amount`. - [ ] Pool-only swap: leg aliases **omitted** (not `"0"`); headline volume still from `offer_amount` / `return_amount`. - [ ] No query path sums `limit_order_fills` into pair headline volume. - [ ] CG/CMC listing sign-off section in [CG_CMC_COMPLIANCE.md](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/blob/main/docs/CG_CMC_COMPLIANCE.md) links to the guide. @brouie — could you verify the checklist above (docs + indexer API) when you have a moment? Leaving this issue **open** until sign-off. /cc @PlasticDigits
PlasticDigits commented 2026-05-29 15:55:21 +00:00 (Migrated from gitlab.com)

Verification complete (agent, 2026-05-29)

Verified GitLab #216 on main @ 13fef65 (implementation landed in 2097452). Worktree verify/issue-216 had no additional code changes — reconciliation guide, L10 invariant, internal trades leg aliases, and tests were already merged.

What was verified

Area Result
Indexer integration tests cargo test --test swap_events_hybrid_columns --test api_consolidated_reporting --test api_integrator_hybrid_volume — 7/7 passed
Integrator docs docs/integrators-hybrid-volume.md — decision tree, units, 3 worked examples, null matrix, API mapping, anti-patterns
Cross-links docs/CG_CMC_COMPLIANCE.md (sign-off + compliance table), docs/indexer-invariants.md L10, docs/terraport.md, docs/integrators.md, skills/AGENTS_INTEGRATOR_HYBRID_VOLUME.md, gaps/GAP_1780023683.md
Internal API parity GET /api/v1/pairs/{addr}/trades exposes pool_leg_volume / book_leg_volume (CG/CMC aliases)
Manual LocalTerra hybrid swap Book-only leg tx 543D8DFF…: on-chain pool_return_amount=0, book_return_amount=5202750, return_amount=5202750; indexer trade id 3506 matches; pool_leg_volume + book_leg_volume = return_amount; /cg/tickers hybrid_trade_count_24h incremented
Trade UI smoke /trade/{pair} loads order book + recent trades against live indexer (leg breakdown in retail UI remains optional/out of scope per issue)

Checklist for operators / integrators

  • Read integrators-hybrid-volume.md before publishing 24h volume.
  • Headline volume = swap_events offer_amount / return_amount (or CG base_volume / target_volume) once per taker tx.
  • Do not add limit_order_fills notionals on top of parent swaps.
  • Treat cl8y_extensions.*_leg_volume_quote_24h as ask-side attribution, not additive to target_volume.
  • When legs are present: pool_return_amount + book_return_amount = return_amount (ask-side).
  • NULL leg columns: use consolidated totals; do not treat NULL as zero volume.
  • Re-run: cd indexer && cargo test --test swap_events_hybrid_columns --test api_consolidated_reporting --test api_integrator_hybrid_volume
  • Optional manual: resting bid (scripts/e2e-seed-hybrid-book.sh) + hybrid swap; query /cg/tickers, /cg/historical_trades, /api/v1/pairs/{addr}/trades.

Closing as all acceptance criteria and verification items from the issue body pass on current main.

## Verification complete (agent, 2026-05-29) Verified GitLab **#216** on `main` @ `13fef65` (implementation landed in `2097452`). Worktree `verify/issue-216` had **no additional code changes** — reconciliation guide, L10 invariant, internal trades leg aliases, and tests were already merged. ### What was verified | Area | Result | |------|--------| | Indexer integration tests | `cargo test --test swap_events_hybrid_columns --test api_consolidated_reporting --test api_integrator_hybrid_volume` — **7/7 passed** | | Integrator docs | [docs/integrators-hybrid-volume.md](docs/integrators-hybrid-volume.md) — decision tree, units, 3 worked examples, null matrix, API mapping, anti-patterns | | Cross-links | [docs/CG_CMC_COMPLIANCE.md](docs/CG_CMC_COMPLIANCE.md) (sign-off + compliance table), [docs/indexer-invariants.md](docs/indexer-invariants.md) **L10**, [docs/terraport.md](docs/terraport.md), [docs/integrators.md](docs/integrators.md), [skills/AGENTS_INTEGRATOR_HYBRID_VOLUME.md](skills/AGENTS_INTEGRATOR_HYBRID_VOLUME.md), [gaps/GAP_1780023683.md](gaps/GAP_1780023683.md) | | Internal API parity | `GET /api/v1/pairs/{addr}/trades` exposes `pool_leg_volume` / `book_leg_volume` (CG/CMC aliases) | | Manual LocalTerra hybrid swap | Book-only leg tx `543D8DFF…`: on-chain `pool_return_amount=0`, `book_return_amount=5202750`, `return_amount=5202750`; indexer trade id **3506** matches; `pool_leg_volume` + `book_leg_volume` = `return_amount`; `/cg/tickers` `hybrid_trade_count_24h` incremented | | Trade UI smoke | `/trade/{pair}` loads order book + recent trades against live indexer (leg breakdown in retail UI remains optional/out of scope per issue) | ### Checklist for operators / integrators - [ ] Read [integrators-hybrid-volume.md](docs/integrators-hybrid-volume.md) before publishing 24h volume. - [ ] Headline volume = `swap_events` `offer_amount` / `return_amount` (or CG `base_volume` / `target_volume`) **once per taker tx**. - [ ] Do **not** add `limit_order_fills` notionals on top of parent swaps. - [ ] Treat `cl8y_extensions.*_leg_volume_quote_24h` as **ask-side attribution**, not additive to `target_volume`. - [ ] When legs are present: `pool_return_amount + book_return_amount = return_amount` (ask-side). - [ ] NULL leg columns: use consolidated totals; do not treat NULL as zero volume. - [ ] Re-run: `cd indexer && cargo test --test swap_events_hybrid_columns --test api_consolidated_reporting --test api_integrator_hybrid_volume` - [ ] Optional manual: resting bid (`scripts/e2e-seed-hybrid-book.sh`) + hybrid swap; query `/cg/tickers`, `/cg/historical_trades`, `/api/v1/pairs/{addr}/trades`. Closing as all acceptance criteria and verification items from the issue body pass on current `main`.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-29 15:55:26 +00:00
PlasticDigits commented 2026-06-07 12:14:16 +00:00 (Migrated from gitlab.com)

mentioned in issue #337

mentioned in issue #337
PlasticDigits commented 2026-08-17 03:52:29 +00:00 (Migrated from gitlab.com)

mentioned in issue #544

mentioned in issue #544
PlasticDigits commented 2026-08-25 01:55:34 +00:00 (Migrated from gitlab.com)

marked as related to #631

marked as related to #631
PlasticDigits commented 2026-08-25 01:55:34 +00:00 (Migrated from gitlab.com)

mentioned in issue #631

mentioned in issue #631
PlasticDigits commented 2026-08-30 05:24:14 +00:00 (Migrated from gitlab.com)

mentioned in issue #707

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