feat: /pool Vol column + pair-list 24h volume in USD (leftover #544) #692
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#692
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
/poolVol still prints human quote-token 24h volume (formatQuoteVolume24h(volume_quote_24h)). Retailers cannot compare UST1/cUSTC vs UST1/USTR vs cLUNC/UST1. v2 LP USD (#655) is pool stock; this issue is 24h flow in USD — the leftover of #544 on pair-list surfaces.Bundle one MR: indexer list field + rollup + sort +
/poolVol cell + Manage line + Trade/Limits/Charts pair-search badges. Do not split “API field” vs “table cell.”Related (do not reopen as done): #544 (ingest + Charts/Protocol USD shipped; list JSON never got USD), #547 (
/pooltable), #534 (human quote badges — still not USD), #548 (Protocol/overview USD), #565 (Charts pair-detailGET …/statsvolume_usd), #655 (v2 LP USD stock, not Vol), #576 (trailing 24h copy), #577 (window zero), #243 (pair_volume_24hrollup), #489 (no lectures).Current codebase
Indexer — pair list is quote-raw; USD exists only on detail stats
GET /api/v1/pairs(indexer/src/api/pairs.rsPairResponse) exposes:volume_quote_24hLEFT JOIN pair_volume_24h(volume_quote)liquidity_usdpair_liquidity_usdstamp (#655 stock)refresh_pair_volumes(indexer/src/db/queries/volume.rs) rebuilds onlyvolume_quote:No
volume_usdcolumn onpair_volume_24h(migration20260531143000_pair_volume_24h_rollup.sql; later widened toNUMERIC(38, 0)).sort=volume_24hisORDER BY COALESCE(pv.volume_quote, 0)— 18-dec USTR always outranks 6-dec cUSTC.Per-swap USD is already ingested (
swap_events.volume_usdviavolume_usd_for_swap/ P522-Q + hub, #548 / #556).GET /api/v1/pairs/{addr}/statsalready returns humanvolume_usd(SUM(swap_events.volume_usd)on the request path). The list must not copy that liveSUM(V1 / #243). Overview already rollsSUM(volume_usd)intoglobal_stats_24h. Token windows already storevolume_usd. Pair list is the gap.IndexerPair(frontend-dapp/src/types/index.ts) has novolume_usd_24h.Frontend —
/poolVol is quote tokens; same list JSON feeds pickers/pooltable Vol (PoolPairsTable.tsxpool-row-vol)formatQuoteVolume24h(ip.volume_quote_24h, ip.asset_1.decimals)$. HeaderPOOL_VOL_HEADER_TITLE= “Quote-side volume…”. Sort keyvolume_24h(raw)./poolManage expand (PoolAdvancedManage.tsx)24h vol (quote, indexed): {volumeLabel}PairSearchSelect.tsx)vol {formatQuoteVolume24h(…, quoteDecimals ?? 6)}vol 19.3USTR, not USD. Mixed quotes still incomparable.pairCatalogRank.tsP534-3)raw / 10^decimalsv2 LP USD sits in the next column (
formatProtocolUsd(liquidity_usd)). Retailers already see one$column (stock) next to a token Vol (flow). That mismatch is the bug.Charts pair-detail Vol (USD) is out of scope (already #565 via
…/stats). Do not N+1GET /pairs/{addr}/statsfrom/pool(P547-9 / A8).Why this is needed
/pool.formatNum(raw)→…T. #544 shipped ingest + Charts/Protocol;GET /pairsnever gainedvolume_usd_24h, so/poolhad nothing truthful to render.volume_quote × hub-prices, CGliquidity_in_usd, or per-row LCD//statsare explicitly forbidden (#655 / P547-9). The indexer must stamp the list.Constraints / guardrails
$…. Tooltip: trailing 24h priced swaps, not a peg. Never imply redeemability.volume_usd_for_swap/swap_events.volume_usd. Do not invent a second catalog. Do not convert with vFDUSD (X4). Do not use$1UST1 or2.5×USTR — hub marks (#556), same as #548 / #565.SUM(volume_usd)of consolidated swaps only. Nopool_leg+book_leg, nolimit_order_fills.volume_quote_24hraw for integrators / #534. Add optionalvolume_usd_24h(human USD decimal string, same scale as stats/overview). Omit/nullwhen unpriced or overflow; do notCOALESCEto0for unpriced. Idle (no 24h swaps) may be0or omitted — UI — either way (today’s zero hide). Do not humanizevolume_quote_24hin the indexer.pair_volume_24h+refresh_pair_volumes(~5 min).GET /pairs(includingsort=volume_usd_24h) must notSUM(swap_events)per request. Idle pairs zero USD when swaps leave the window (#577 D3), same as quote. Pagination caps unchanged (limitclamp,offset≤ 10_000 → 400).sort=volume_24has raw quote (do not break integrators). Addsort=volume_usd_24h(default desc, NULLS LAST, same asliquidity_usd)./poolVol header must usevolume_usd_24hso caret matches the$cell. Invalidsort(injection /tvl/volume_usd) → 400./poolpaint: zero newgetPool/getPairFeeConfig/GET …/stats. ManagecolSpanstays 7 unless a column is added (it is not).volume_quote_24h, hub-prices, CG, or$1/leg.—, not$0, not raw fallback. Unpriced gems with quote volume must not show19297048Tor48.2M. Hostile / non-finite / negative /Infinity/ HTML →—(reuseformatIndexedVolumeUsdlength +<>guards, #548 A4).base_volume/target_volume, candle histogram, #522price/price_usd, #655liquidity_usd, or Charts pair-detail stats.POOL_VOL_HEADER_TITLEto priced-USD trailing 24h (not “quote-side”). Manage line: short24h vol $…or hide when—. Badgevol $12.4K. No oracle essays./poolbrowse stays catalog overlay (P547-3). Within a hub, prefer USD 24h when present, else existing human-quote fallback (P534-3). Do not re-apply catalog after the user clicks Vol.card-glass(#653).make verify-issue-<iid>in the same MR. Invariants PVol-1–PVol-8. Updatedocs/indexer-invariants.mdpair-list row,docs/frontend.mdP547-4 / P534-4,skills/AGENTS_FRONTEND_POOL_TABLE.md,skills/AGENTS_FRONTEND_PAIR_CATALOG_RANK.md,AGENTS.md,docs/testing.md.Relevant files
indexer/migrations/20260531143000_pair_volume_24h_rollup.sqlvolume_usdcolumn via new migrationindexer/src/db/queries/volume.rsrefresh_pair_volumes— stampSUM(volume_usd)+ zero idleindexer/src/db/queries/pairs.rsPairListRow+ SELECT +PairListSort::VolumeUsd24hNULLS LASTindexer/src/api/pairs.rsvolume_usd_24h; sort allowlist;pair_to_responseindexer/src/indexer/pair_price_usd.rsindexer/tests/indexer_pair_volume_pagination.rsswap_eventsfrontend-dapp/src/types/index.tsIndexerPair.volume_usd_24h; sort unionfrontend-dapp/src/utils/poolListQuery.tsPOOL_COLUMN_SORTSVol →volume_usd_24hfrontend-dapp/src/components/pool/PoolPairsTable.tsxfrontend-dapp/src/components/pool/PoolAdvancedManage.tsxfrontend-dapp/src/utils/trailingWindowCopy.tsPOOL_VOL_HEADER_TITLEfrontend-dapp/src/utils/chartsOverviewStats.tsformatIndexedVolumeUsdfrontend-dapp/src/components/trade/PairSearchSelect.tsxvol $…frontend-dapp/src/utils/pairCatalogRank.tsfrontend-dapp/src/pages/PoolPage.test.tsxdocs/frontend.md,docs/indexer-invariants.md, skills aboveRecommended direction
ALTER TABLE pair_volume_24h ADD COLUMN volume_usd NUMERIC(38, 18)(nullable; absent/NULL = unpriced). Index(volume_usd DESC NULLS LAST)if needed forsort=volume_usd_24h. Do not changevolume_quote.refresh_pair_volumes: In the same INSERT/UPDATE that setsvolume_quote, setvolume_usd = SUM(se.volume_usd) FILTER (WHERE volume_usd IS NOT NULL AND volume_usd > 0)clamped toNUMERIC(38, 18)/< 10^20(same overflow guard as overview). Idle UPDATE zeros both quote and USD.volume_usd_24h?: stringwithskip_serializing_if. List and token-pairs / sharedpair_to_responsestay one shape. Single-pairGET /pairs/{addr}may omit 24h USD (today it already omitsvolume_quote_24h) — do not live-scan there.formatIndexedVolumeUsd(ip.volume_usd_24h, /* idle→0 */ )or equivalent: priced →$+ compact; missing/invalid → —; neverformatQuoteVolume24h/formatNum(raw)on this cell. Old indexers omit the field → all —.—/ missing (same as today’s zero-quote hide). Do not keep a quote badge as the default once USD exists.$string; optionaltitlefor trailing window. Quote remainder is not required on/pool(Charts already has token vols).Acceptance criteria
/poolVol cells are compact USD ($+formatIndexedVolumeUsd/ equivalent) or —. Never raw 18-dec, never quote-token48.2M/19.3without$.volume_usd_24h. No per-row/stats, LCD, hub-prices, orvolume_quote_24h ×anything.GET /api/v1/pairsis additive:volume_quote_24hunchanged (raw).volume_usd_24homitted/nullwhen unpriced.sort=volume_usd_24hdefault desc, NULLS LAST.sort=volume_24hstill quote-raw.sort=volume_usd_24h&order=desc; toggle asc/desc;aria-sorton that<th>only. Catalog default (no click) unchanged (P547-3) except within-hub tie-break may use USD.GET /pairs/{addr}/statsvolume_usdwithin rollup lag (~5 min). Headertitleis trailing 24h USD, not midnight reset, not “quote-side.”$0next to empty quote). Production gems still hidden./stats. ManagecolSpan7. Manage + pair-search badges use the same USD field.#489: no architecture lecture.sort→ 400.limit=-1clamps to 1. Hostile USD strings cannot XSS or lock the tab.Test plan (all paths)
Indexer
refresh_pair_volumeswritesvolume_usd=SUM(swap_events.volume_usd)for priced 24h swaps; idle pair quote and USD go to 0 / NULL as specified (D3).GET /api/v1/pairsincludesvolume_usd_24hon priced rows; omits/null on unpriced;volume_quote_24hstill raw.sort=volume_usd_24h&order=descranks priced USD desc, unpriced last;ascranks low priced first, unpriced still last.sort=volume_24hstill quote-raw (regression).EXPLAINof list +sort=volume_usd_24husespair_volume_24h, not a 24hswap_eventsaggregate (indexer_pair_volume_pagination.rspattern).sort=volume_usd/tvl/ SQL injection → 400.limit=0/-1→ clamp 1.offset> 10_000 → 400.NUMERICclamp: hugevolume_usddoes not 500 the list.pair_to_response(list / token-pairs) stays one shape. utoipa/OpenAPI updated.Frontend —
/pool$…or —; v2 LP USD column unchanged; gems hidden in production.sort=volume_usd_24h&order=desc; caret +aria-sort; second click asc; other columns clear Volaria-sort.q=stillrelevanceuntil cleared (P534-6).volume_usd_24hInfinity/NaN/-1/1e309/<script>/ 10k-char → —, text-only.colSpan={7}; no extra LCD.overflow-x-auto.Frontend — pair search (bundled)
vol $12.4K(or hidden). Novol 19.3as the default for USTR.?? 6must not rescale a USD string as 6-dec raw.Vitest / Playwright / make
PoolPage.test.tsx,poolListQuery.test.ts,pairCatalogRank.test.ts,PairSearchSelect#534 tests (update expected badge),formatAmount/chartsOverviewStatsif helpers change,trailingWindowCopytitle.cargo test --test indexer_pair_volume_pagination --test api_pairs(+ new volume_usd list test)-- --test-threads=1.e2e/pool-table-547.spec.ts) Vol column$or — (dedicated Vite port; do not leakPLAYWRIGHT_WEB_PORT).make verify-issue-<iid>static + unit + relatedVERIFY_ISSUE_547_SKIP_E2E=1 make verify-issue-547/make verify-issue-655(no nested full e2e unless needed).Test plan — attack, hack, and abuse
sort=volume_usd_24h;DROP+TABLE+pairs,sort=volume_usd,order=desc--. Must 400, never interpolate into SQL (keepQueryBuilderbind + enum).limit=-1,limit=999999,offset=10001— clamp / 400; no full-table scan; no live 24hSUMas a DoS.volume_usd_24h=<img onerror=…>,javascript:, 1MB string,NaN,Infinity, scientific1e309. Render —; no HTML; cap display length (#548 A4).symbol=USTRmust not price (A1). Factory-listed clones with hub tickers stay residual (provenance #311). Unpriced wash volume must not sort as$0first.$1UST1 or2.5×USTR when hub is down — omit USD. Do not use Venus/vFDUSD (X4).volume_usdperswap_eventsrow.updated_atstale, same class as overview D6). Must not fall back to GET-path 24h scan.chartsPairHrefbech32-only (P547-5). Do not put USD into URLs.…T.Verification criteria
Issue is done only when all are true:
/poolVol column shows$compact USD (or —) for catalog + Vol sort + search; Manage and pair-search badges match.GET /api/v1/pairsdocumentsvolume_usd_24h;sort=volume_usd_24hworks;sort=volume_24hunchanged; EXPLAIN is rollup-only.GET /pairs/{addr}/statsvolume_usd(≤ ~5 min lag). Unpriced → — / omitted.make verify-issue-<iid>is green; #547 / #655 / #534 / #576 regressions updated and green.AGENTS.md+docs/testing.mdlist the new verify target and PVol-1–PVol-8.Out of scope
liquidity_usd/ v2 LP USD (#655 / #664).volume_quote_24h.PAIR_LIST_LIMIT_MAX(cataloglimit=500clamp is a separate concern).marked as related to #544
marked as related to #547
marked as related to #534
marked as related to #548
marked as related to #565
marked as related to #655
marked as related to #576
mentioned in merge request !1195
Merge review (!1195)
Local AC:
make verify-issue-6928/8 PASS (docs, source guards, indexer integration, Pool/catalog Vitest, Playwrighte2e/pool-table-547with 5 workers, related 547/655/534). GitLab CI on !1195 failed withci_quota_exceededonly — not a product signal; merging without automerge / CI wait.PVol-1–4, 6–8: met in source (list
volume_usd_24h, USD Vol/—, NULLS LAST, no quote fallback, no/statsN+1).PVol-5 gap (non-blocking): header title is trailing 24h USD. There is no automated check that list
volume_usd_24hequalsGET /pairs/{addr}/statsvolume_usdright afterrefresh_pair_volumes. Rollup usesSUM(volume_usd) FILTER (>0)+ NULL on ≥1e20;/statsuses liveSUM(volume_usd)without that clamp — fine for normal priced pairs. Needs a reviewer curl on a live indexer (or a small integration assert) after Coolify deploy.Manual leftovers: light+dark
/poolVol readability; production gem hide (existing P562 path, not re-proven in verify-issue-692).Do not reopen this issue for ops/QA after merge. Track Coolify + live list↔stats check on a leftover ops issue.
mentioned in commit
f4fa678838mentioned in issue #701
marked as related to #701
Merged
!1195 is on
main(f4fa6788). Localmake verify-issue-692was 8/8. Ops leftovers (Coolify migrate20260829120000_pair_volume_24h_usd.sql, live PVol-5 list↔stats, light+dark/pool) tracked on #701. Do not reopen this issue for ops/QA.mentioned in issue #693
mentioned in issue #702