fix: Charts 24h Vol and TWAP still compact-format raw mixed-decimal amounts (UST1 385.8M / USTR 36T) #564
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#564
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
Production
/chartson UST1 / USTR (mainnetdex.cl8y.com) shows:formatNum→ ~385.8 human UST1 (15 trades ≈ 26 UST1/trade)T= 1e12). Human ≈ 36,113 USTRreserve_b/reserve_aDecimal (token1 units / token0 units). Human ≈ 111 USTR per UST1 after× 10^(6−18)This is not 385.8 million UST1 of flow. Pair-catalog vol badges already humanize
volume_quote_24h(#534); Charts overview volume is USD-only (#548). The pair 24h Stats and TWAP Oracle panels were never converted and still callformatNumon raw integers / raw Decimal.Related but out of scope: tape Amount in/out (#557).
Current codebase
24h Stats —
formatNumon raw indexer sumsfrontend-dapp/src/pages/ChartsPage.tsx(pair 24h Stats grid):USD OHLC uses
formatNum(highUsd, 6)(magnitudes today are< 1e3so noT, but it is still the compact formatter forbidden for prices in P522-5).GET /api/v1/pairs/{addr}/stats(indexer/src/api/pairs.rsPairStatsResponse/get_24h_stats_for_pair):volume_base/volume_quote=SUMof rawoffer_amount/return_amount(orientation #466: base = asset_0, quote = asset_1).volume_usd=SUM(swap_events.volume_usd)(P522-Q). Already returned by the API. FrontendIndexerPairStatsomits the field, so Charts never shows pair 24h USD.high/low/open_price/close_price= human quote-per-base after #522.*_usd= USD of 1 humanasset_0. Charts already prefers these viapairStatsUsdField.formatNumis a human compact helper (K/M/B/T). Passing a raw 18-dec string is the documented anti-pattern:formatQuoteVolume24h/formatTokenAmountalready scale by decimals. Pool table and pair pickers use them; Charts 24h Stats does not.UST1 = 6 dec, USTR = 18 dec (
tokenRegistry.ts; indexerasset_0.decimals/asset_1.decimalson the pair row).TWAP Oracle — raw Decimal +
formatNumgetTwapPricesqueries pairobserveandcomputeTwapPrice=(cum_end − cum_start) / dt / 1e18. That is CosmWasmDecimalof rawreserve_b / reserve_a(token1 base units per token0 base unit) — same units as on-chain limitprice(#529 L529-1). Human quote-per-base israw × 10^(decimals0 − decimals1).Charts then:
On UST1(6)/USTR(18), raw ≈
111 × 10^12→ 111.009T. After human scale ≈ 111.009 USTR per UST1, which is consistent with ~36k USTR / ~386 UST1 24h volume (ratio ~94) vs a 24h TWAP that can differ from volume-weighted spot.The panel title uses factory
indexerPairMenuLabel(UST1 / USTR), not the #524 invert pill. Keep TWAP factory human token1-per-token0 unless product later asks invert (do not1/xof USD).docs/twap-oracle.mdstill describes a geometric tick oracle; the live pair contract is arithmetic cumulative Decimal (smartcontracts/packages/dex-common/src/oracle.rs). Docs drift can be fixed in this issue.Candle volume histogram — same raw integers
indexerCandlesToVolumeHistogramPointsplotsparseFloat(volume_quote)elsevolume_base. Relative bar heights within a pair survive, but the volume axis is 1e6 / 1e18 too large. Same bug class on/trade+/chartsPriceChart. C543-8 said volume is not inverted as price — it did not bless raw-vs-human display.What is already correct (do not regress)
/poolvol badgeformatQuoteVolume24h(#534).formatPairPrice(#522); tape amounts are #557.volume_base/volume_quote/ CGbase_volume(integrators).Why the new implementation is needed
Retail cannot tell real UST1/USTR flow from a unit bug. 385.8M UST1 next to 15 trades looks like wash or a broken market; 36T USTR and 111T TWAP next to ~$0.68–$0.97 USD makes the oracle look corrupted. The DEX already solved this class for prices, catalog volume, and overview USD. Charts pair stats is the remaining public surface on the same page as the screenshot.
Showing human token volumes plus pair 24h USD (
stats.volume_usd, already indexed) is the same product pattern as overview (#548) + catalog (#534).Constraints / guardrails
volume_base/volume_quotestay raw integers (same asvolume_quote_24h/ CG). UI scales withasset_0.decimals/asset_1.decimalsfrom the pair row (not symbol string matching — same A1 as #557).formatNumraw 18-dec (or 6-dec) volume. UseformatTokenAmount/formatQuoteVolume24h. CompactK/M/B/Tis allowed only on human sizes.formatPairPrice, never compactT. Headline Price (USD) staysprice_usd/ invertUsd (#543).raw × 10^(d0 − d1)— reuserawLimitPriceToHuman/scaleDecimalStringByPow10, notNumber×10 ** (d0-d1)(float). On-chain oracle stays raw.asset_*.decimals(0…18). Missing / out of range →—, never assume 6 (getDecimalsdefault would make USTR look 1e12 too large again).volume_usdonIndexerPairStats. Format withformatIndexedVolumeUsd(volume_usd, trade_count). Unpriced + trades > 0 →—. Idle 0 trades →$0. Do not invent USD from raw token volume × last price in the client.1/xUSD OHLC or TWAP for the #524 invert pill in this issue (MAX/MIN USD do not invert cleanly). Invert remains chart + tape (#524 / #557).formatPairPrice(or$+ that), notformatNum. No compactT/B/Mon High/Low/Open/Close (USD).price_change_pct(from human open/close). Do not recompute from unscaled raw.token0/base unitsin retail chrome.Relevant files
frontend-dapp/src/pages/ChartsPage.tsxformatNumfrontend-dapp/src/pages/ChartsPage.test.tsxfrontend-dapp/src/utils/formatAmount.tsformatTokenAmount,formatQuoteVolume24h,formatPairPricefrontend-dapp/src/utils/chartsOverviewStats.tsformatIndexedVolumeUsdfrontend-dapp/src/utils/limitOrderPriceScale.ts10^(d0−d1)frontend-dapp/src/services/terraclassic/oracle.tscomputeTwapPrice/getTwapPricesfrontend-dapp/src/types/index.tsIndexerPairStatsmissingvolume_usdfrontend-dapp/src/components/charts/priceChartCandles.tsfrontend-dapp/src/components/charts/PriceChart.tsxfrontend-dapp/src/components/ui/StatBox.tsxdata-testidon Charts boxesindexer/src/api/pairs.rsvolume_usdalready present)indexer/src/db/queries/swap_events.rsvolume_usdsmartcontracts/packages/dex-common/src/oracle.rsdocs/frontend.mddocs/twap-oracle.mddocs/indexer-invariants.mdskills/AGENTS_FRONTEND_PAIR_CATALOG_RANK.mdRecommended direction
formatPairStatsVolume(raw, decimals)):formatTokenAmount;null/0/—policy documented. Unit tests: raw385800000+ 6 →385.8class not385.8M; raw36113437940000000000000+ 18 →36.1Kclass not36,113,437,940T.stats.volume_usdviaformatIndexedVolumeUsd. USD OHLC viaformatPairPrice.data-testids:charts-pair-vol-base,charts-pair-vol-quote,charts-pair-vol-usd,charts-twap-5m, etc.computeTwapPrice, convert to decimal string andrawLimitPriceToHuman(raw, d0, d1)thenformatPairPrice. Equal-decimal pairs unchanged (UST1/cUSTC ~1 class, notT). Failed observe stays—/ “TWAP building…”.indexerCandlesToVolumeHistogramPoints(..., { quoteDecimals, baseDecimals })divides raw by10^decusing string/BigInt thenNumberonly if finite and in range; drop bar if overflow. Pass decimals fromPriceChartpair query.make verify-issue-<iid>(vitest + greps that ChartsPage 24h/TWAP no longerformatNum(stats.volume_*/formatNum(entry.price).pair_volume_24hrollup or CG fields.Acceptance criteria
385.8M) for the production-scale raw 6-dec sum.KOK) and neverTunless human ≥ 1e12.volume_usd; unpriced + trades > 0 →—; 0 trades →$0.111class), not111T.formatPairPriceonly.× 10^12applied by mistake).T/M; still factory*_usd(not tapeprice).make verify-issue-<iid>;docs/frontend.mdpair-stats contract;docs/twap-oracle.mdmatches arithmetic oracle.—; noNaN/Infinity/ crash.Test plan — functional paths
Unit / RTL
formatPairStatsVolume: 6-dec385800000→ not/M$/; 18-dec USTR screenshot-scale → not/T$/.formatPairStatsVolume:0,'','abc', negative →—or0(documented).111.009e12+ (6,18) →111.009class; (6,6) identity; (18,6) scales the other way withoutT.36,113,437,940Tor111.009T.1000000/206000000→1/206class.volume_usd: null,trade_count: 15→ Vol (USD)—;trade_count: 0, usd0→$0.0.682427still prints as that class (not$682.4K/T).priceChartCandleshistogram: 18-dec quote1e19with 18 dec → value10class, not1e19.0, base vol used: scaled by base decimals, not quote.—+ existing unavailable copy; stats still render.Indexer (no unit change required unless types/docs)
GET /pairs/{addr}/statsstill returns rawvolume_base/volume_quotestrings andvolume_usdon a mixed-decimal fixture (existing swap ingest tests).Manual / production
https://dex.cl8y.com/chartsUST1/USTR: Vol (UST1) hundreds-not-millions; Vol (USTR) noT; TWAP noT; USD OHLC still ~$0.7–$1 class./tradechart volume pane not 1e18 scale on UST1/USTR.$human (#548); catalog vol badges still #534.Test plan — attack, hack, and abuse
getDecimalsdefault 6 on USTR0…18→—.activePair.asset_0/1for thatpair_addressonly.111in a control labeled USD1/price_usdinto TWAP or High (USD)*_usd.volume_quote / 1e18 × lastUsdvolume_usd.volume_base/ TWAP string<>like overview USD cap if compact string is huge.formatTokenAmountBigInt path; cap display length (overview A4 24-char class) →—rather than lock the tab.—.computeTwapPricealready returns 0 on inverted cum; treat≤ 0as—.observeis chain state; UI must not execute strings. Noeval.volume_quote=1e309Infinity.Verification criteria
Production check: UST1/USTR 24h Stats must not show
385.8MUST1 or…TUSTR/TWAP for current mainnet raw magnitudes.Out of scope
marked as related to #557
mentioned in merge request !1088
mentioned in commit
e9a6270966mentioned in commit
975e062004Post-merge note (!1088 on main, then combined with !1089 / #565). TWAP human quote-per-base, USD OHLC via
formatPairStatsUsdOhlc, and candle histogram decimal scaling are onmain. Volume strip layout follows #565 (Vol USD primary + token row; testidscharts-pair-volume-*).Remaining:
/chartsUST1/USTR Vol hundreds-not-millions / noTon TWAP;/tradevolume pane not 1e18.make verify-issue-564andmake verify-issue-565together.mentioned in issue #565
mentioned in commit
4f24107fa7Sanity after stack:
make verify-issue-564initially failed because the grep still expectedformatPairStatsVolume/charts-pair-vol-*after combining with #565 (formatChartsPairTokenVolume/charts-pair-volume-*). Fixed onmainin4f24107f. Re-run is 3/3 PASS. Remaining work is production Charts smoke (UST1/USTR TWAP notT).mentioned in issue #573
marked as related to #573
mentioned in issue #666
marked as related to #666
mentioned in issue #680
mentioned in issue #692