feat(protocol): top-5 30d pair volume, TVL, and vol/LP on /protocol #1263
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#1263
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?
A request to update the statistics collection to also include a chart or table showing the top 5 pairs by volume over the last 30days, table should also include pool liquidity/TVL - a ratio should also be displayed showing volume divided by the TVL - this will provide a cost to benefit analysis of current deployed liquidity.
This is to help inform liquidity deployment for voting
Do we need to start tracking other pairs like BTC, ETH, stocks to see their viability, maturity and feasibility for the CMM?
Approved for issue repair -> implementation. No other pair tracking at this time, should be seperate issue
Summary
Add a top-5 factory pairs table on
/protocolranked by trailing 30-day USD volume, with current v2 AMM TVL and volume ÷ TVL (turnover of currently deployed liquidity). Bundle table + 30d volume stamp + TVL column + ratio into one issue. Do not split “chart vs table” vs “API field” vs “ratio cell” vs “gem hide.”This is a census ranking so operators can compare which pools are working relative to locked USD. It does not execute CMM LP, open a vote, advertise APR, or track off-DEX underlyings.
Related, not duplicates (keyword overlap on “protocol” / “volume” / “TVL” is not enough):
total_volume_30d_usd, not a per-pair ranking./poolfull sortable list with 24h Vol USD + v2 LP USD. No 30d pair volume, no vol/LP ratio, not on/protocol. Do not reopen those to “add a ratio column.”/chartsis pair-scoped; global census stays on/protocol. Do not put this table on Charts.COLUMBUS5_GEM_ADDRESSES, not a new list.Not already implemented (checked on current
main):GET /api/v1/pairshasvolume_usd_24h+liquidity_usdonly;pair_volume_24hhas no 30d column;GET /pairs/{addr}/statsis 24h;/protocolhas global 30d volume and total TVL, not a top-5 pair table or vol/LP ratio.Current codebase
/protocol(frontend-dapp/src/pages/ProtocolPage.tsx) is the DEX USD census page (P550-1): title → Global stats (protocol-global-stats) → Protocol fees → DEX hub prices → one CEX oracle card → audit contracts → hooks./pooltablepair_liquidity_usdGET /api/v1/overviewglobal_stats_24hrollup, 60s cache (P550-5). Must notSUM30dswap_eventson GETGET /api/v1/pairspair_volume_24h+pair_liquidity_usd. Sortvolume_usd_24h/liquidity_usd. Limit default 50, max 100volume_usd_30d; fetching 100 rows to slice top-5 is the wrong APIGET /pairs/{addr}/statsvolume_usd)refresh_pair_volumesstamps 24h quote + USD (~5 min). Idle → 0 (D3). Unpriced / overflow ≥ 10^20 → NULLrefresh_protocol_liquiditystampspair_liquidity_usdfromprotocol_pair_tvl(P522-Q / hub; never$1UST1 /2.5×USTR / vFDUSD)filterRetailDiscoveryIndexerPairson production/pool. Indexer Llama/GT excludeCOLUMBUS5_GEM_ADDRESSES. Protocol global volume still includes gems (P652-4)USD volume ingest is one notional per swap (L10 /
volume_usd_for_swap). TVL is both legsh0×usd0 + h1×usd1(one catalogued leg2×; neither → omit, not$0) (P569-5). Book escrow, parked dust, wallet LP share, wrap-mapper inventory, and V3 Grid vaults are not TVL (P655).Why this work is needed
/poolalready has 24h Vol and v2 LP, but operators would have to sort 24h, ignore 30d, and divide by hand. A dedicated Protocol table with a hard cap of 5 is the census surface (#666 left global ranking on/protocol).volume_usd_24h / liquidity_usdwould lie about the requested 30d window. LiveSUM(swap_events)on GET would violate P550-5 / PVol-3.Constraints / guardrails
GETJOINs that stamp +pair_liquidity_usd.EXPLAINmust not scanswap_events/pair_reserves. 60s response cache keyed only by allowlisted params.limitomitted or5only; any otherlimit/window/from/to/sort/ticker/ junk → 400. Extra query keys must not bust the cache (same as P668-5).pair_idasc. Unpriced 30d volume (NULL) does not rank. Idle"0"does not take a slot unless every priced pair is idle (then show empty / “No pairs yet,” not five$0rows).volume_usd_30d / liquidity_usd(current stock). Document as trailing-30d flow over current pool USD — not 30d-average TVL (no per-pair history table).liquidity_usdmissing /≤ 0/ overflow → JSONnull, UI —. NeverInfinity/NaN/0. Display as a compact multiplier (2.4×), not a percent (2.4 is not+2.4%).COLUMBUS5_GEM_ADDRESSES(lockstep with #562 / #631). Production/protocolmust not list EMBER…PEARL even if they dominate 30d flow. Do not burn gem rows.$1UST1 / CL8Y, never2.5×USTR, never CGliquidity_in_usd. Identity is contract/denom.token0.shell-panelprotocol-top-pairsafter Global stats, before Protocol fees. Flat table (or one inner table well). Nocard-glassper row. Do not nest this table insideprotocol-global-stats(already tiles + UTC chart).chartsPairHref(/charts/:pairAddr) only when the address is valid Terra bech32. Invalid /javascript:/ HTML → text, noLink(T541 / A1).GET /overviewshape. Do not addsort=volume_usd_30dtoGET /pairsin this MR (that would become a/pool30d column — out of scope). Do not N+1GET …/stats.Relevant files
indexer/src/db/queries/volume.rsrefresh_pair_volumes— add 30d pair USD stamp (sibling of 24h)indexer/src/indexer/volume_aggregator.rsindexer/migrations/pair_volume_30d(or equivalently named) rollup:pair_id, nullablevolume_usd,updated_at;NULLS LASTindexindexer/src/api/protocol_top_pairs.rs(new)GET /api/v1/protocol/top-pairs— join stamps, gem exclude, cap 5, ratio, 60s cacheindexer/src/api/mod.rsindexer/src/indexer/defillama.rsCOLUMBUS5_GEM_ADDRESSES/gem_addresses_lowercased— do not fork the listindexer/src/db/queries/pair_liquidity.rspair_liquidity_usd(do not recompute TVL on GET)indexer/tests/indexer_pair_volume_usd.rsswap_events; idle zero; overflow NULLindexer/tests/(new)frontend-dapp/src/pages/ProtocolPage.tsxfrontend-dapp/src/components/protocol/ProtocolTopPairs.tsx(new)frontend-dapp/src/services/indexer/client.tsgetProtocolTopPairs()frontend-dapp/src/types/index.tsfrontend-dapp/src/utils/formatProtocolStats.tsformatProtocolTurnover(N×/ —; never Inf)frontend-dapp/src/utils/chartsPairRoute.tsfrontend-dapp/src/utils/trailingWindowCopy.tstitles (#576)frontend-dapp/src/pages/ProtocolPage.test.tsxskills/AGENTS_FRONTEND_PROTOCOL_STATS.mddocs/frontend.md/docs/indexer-invariants.mdscripts/qa/verify-issue-1263.shRecommended direction
SUM(swap_events.volume_usd)priced legs only; unpriced activity →NULL; overflow ≥ 10^20 →NULL; no 30d swaps →0(decay, D3 analog). Do not COALESCE unpriced to0for ranking.GET /api/v1/protocol/top-pairs(window implicit 30d; if a query param exists it is allowlistwindow=30donly):pair_volume_30d+pair_liquidity_usd+ pair/asset symbols.WHEREnot gem-legged,volume_usd IS NOT NULL,volume_usd > 0.ORDER BY volume_usd DESC NULLS LAST, pair_id ASC LIMIT 5.pair_address,asset_0/asset_1briefs (existingAssetBrief),volume_usd_30d,liquidity_usd(omit/nullif unpriced),volume_per_tvl(nullif TVL missing/≤0).items: [](not five synthetic zeros).protocol-top-pairssibling panel. Compact USD viaformatProtocolUsd/formatPairListVolumeUsd. Ratio via new helper. ≤5 rows. Phone 390: table mayoverflow-x-auto; do not drop the ratio column. Hide panel on 404/501 (old indexer), same as the UTC chart.make verify-issue-1263: skill/docs mention P1263; EXPLAIN assertion; RTL;GET400 cases; keepverify-issue-550/569/655/692/562/653green.Do not compute the ratio in the browser from
/pairs?sort=volume_usd_24h&limit=5.Acceptance criteria
/protocolshows Top pairs (30d) after Global stats and before Protocol fees, with at most five factory pairs ranked by trailing 30d USD volume.N×or —). Headertitles name trailing 30d flow vs current pool USD (#576). Not calendar month. Not 24h Vol reused as 30d.EXPLAINof the GET query contains neitherswap_eventsnorpair_reserves.GET /overviewstill O(1) (no new 30d scan).volume_usd_30d / liquidity_usdcomputed server-side. TVL missing/≤0/overflow → JSONnull, UI —, never Inf. Hostile / non-finite strings → —.COLUMBUS5_GEM_ADDRESSES). Fixture: high 30d volume on a gem pair loses to a lower-volume economic pair.$0theater.GET /api/v1/protocol/top-pairs:limit=6/window=24h/from=/to=/sort=/ticker=→ 400. Cache ignores extra junk. Cap is 5 even if a client asks for more (400, not truncate).chartsPairHref; invalid bech32 /javascript:is not an href. Symbols / XSS strings render as text (noinnerHTML).card-glassper row; no nestedshell-panel.python3 scripts/check_chrome_nesting.pygreen. Production copy has no “voting” / “farm” / “APR” lecture (#489 / H531-4).AGENTS_FRONTEND_PROTOCOL_STATS.md+ indexer invariant +make verify-issue-1263. Existingverify-issue-550/569/655/692/653/562stay green.Test plan (all paths)
pair_idasc after volumeblock_timestamp, no 31-day sleepvolume_usd$0stand-inpair_liquidity_usdrowliquidity_usd = 0null/ —, not Infitems: []; UI empty copy; panel not five dashes of fake USDlimit=5OK;limit=1/6,window=7d,from,to,sort=volume_usd_24hswap_events/pair_reservesGET /overviewstill rollup; no extra 30d SUM1234.5vol,500TVL$vol/LP; ratio2.47×class (compact, not+247%)/protocoldetectMarketDataOutagejavascript:alert(1)not an href\bfarm(ing)?\b, no “vote”, no “cost to benefit” lectureNegative / skip:
GET /pairs?sort=volume_usd_24h&limit=5as 30d → fail AC2.24h vol / TVL→ fail AC2 / AC4./poolor/chartsinstead of/protocol→ fail AC1.Test plan (attack, hack, and abuse)
Evaluate each vector. Do not write exploits, PoCs, or attack procedures.
symbol,volume_usd_30d,liquidity_usdcontain<script>/javascript:innerHTML/evaljavascript:href0,"","0", negative,1e309null; UI —Infinity/NaNas a “good” poolUST1on a gem contract still excluded — address wins)$0liquidity$0pool as cheapestlimit=100000,from/toforcing a 30dswap_eventsscan/stats/ hub-prices in the browseruusdprints as USTC TVL/overviewburstVerification criteria
make verify-issue-1263green (Postgres indexer tests + frontend RTL; no CosmWasm migrate)./protocolshows ≤5 economic pairs; 30d vol + v2 LP +N×; gems absent; 390 and 1280 layouts; Charts link on a valid pair.curlGET /api/v1/protocol/top-pairsand see ≤5 items,volume_per_tvlnull-safe, 400 onlimit=6.make verify-issue-550569655692653562still green./pool30d column, or BTC/ETH rows unless product files them separately (#1223 for off-DEX names).Out of scope
/pool30d volume column orGET /pairs?sort=volume_usd_30d.GET /overviewnew fields, unique-trader headline, 30d chip on Total liquidity.First-pass model recommendation
Recommendation: grok-high
Rationale: This is a new public ranking API plus a Protocol census table. It crosses the volume aggregator (
volume.rs), a new rollup table, gem-address lockstep with #562/#631, fail-closed DECIMAL ratio, 60s/400 GET contract (API4-class caps), and/protocolchrome/copy. That is more than a local known edit toProtocolGlobalStats.tsx. Composer criteria fail on subsystem count and ranking/ratio edge cases (NULL vs 0 vs Inf vs gems). Verification is indexer EXPLAIN + RTL + GET 400, not a CSS-only patch. Advisory planning text only — not a runner argument.Updated statistics on the protocol pageto feat(protocol): top-5 30d pair volume, TVL, and vol/LP on /protocolMerged as PR #1298. Top pairs (30d) cap 5.
sqlx:
pair_volume_30dis 20260921120001 so it does not collide with #127720260921120000_traders_rolling_volume_numeric_38_0. Coolify must apply both in that order.A worktree
git add -Aalso tracked afrontend-dapp/node_modulessymlink; removed in PR #1299.Leftover ops: #1300