fix: humanize Amount in / Amount out / Price on Charts, Trade, Trader tape #557
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#557
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
Amount in, Amount out, and Price on the public tape (and the same table on Trade / Trader / Portfolio / wallet history) still pass raw chain integers through
formatNum. On UST1/cUSTC (6/6) a 1-token swap prints as1.000M/206.0M. On UST1/USTR (6/18) outflows print asT. Retail must see human token amounts and a human quote-per-base Price that matches the pair (and display invert when the rest of the page is inverted).This is display scale, not USD oracle. Hub DEX USD for Charts Price (USD) is #556. Related: #522 (tape Price must not compact as
T; still human not USD), #534 (formatQuoteVolume24h), #548 / #553 (sameformatNum(raw)class), #551 (portfolio P&L — out of scope here), #524 (pair invert). Wallet history comment already defers decimal-aware format to “later (#479)”.Current codebase
Shared tape:
TradesTablefrontend-dapp/src/components/ui/TradesTable.tsx:formatNum(t.offer_amount)formatNum(t.return_amount)formatPairPrice(t.price, 6)with tooltip “Human quote per base … Not USD”formatNumis a human compact formatter (K/M/B/T). Indexeroffer_amount/return_amountare raw integers (TradeResponse).formatTokenAmount(raw, decimals)already exists and is the correct helper ("1000000"+ 6 →"1").Used on:
activePairpassed?/charts/:pairRecent TradesChartsPage.tsx/trade/:pairRecent tradesTradeRecentTradesSection.tsx/trader/:addrTraderPage.tsx/portfolioactivityPortfolioPage.tsxWallet pair history
WalletIndexerHistoryPanel.tsxformatHistoryAmount=formatNum(raw)on purpose, with a comment thatformatTokenAmountwaits on decimals. Same bug on swap and limit filltoken0_amount/token1_amount.Indexer trade JSON
IndexerTrade/TradeResponseexposeoffer_asset/ask_assetsymbols and raw amounts. Nooffer_decimals/ask_decimals. Trader/Portfolio mixed-pair tables cannot humanize fromactivePair. Matching decimals by symbol string is unsafe (A1 spoof / two CW20s with the same ticker).Price after #522 is already human quote-per-base. On UST1/cUSTC that is
206 cUSTC per UST1, which looks “wrong” next to Amount in1.000Mand next to chart Price (USD) ($1factory or ~$0.00xinverted). Two bugs stack: amounts are 10^decimals too large, and tape Price does not follow the #524 invert pill (chart says cUSTC/UST1 while tape Price stays UST1-as-base).Limit fills on the same history panel are raw token0/token1 (6 vs 18 on UST1/USTR →
T).Why the new implementation is needed
Retail cannot read the tape. UST1/cUSTC is the primary economic market; showing millions for single-digit swaps is the same class of bug that #522 / #534 / #548 already fixed for USD and volume. Trade and Trader reuse the same component, so Charts-only patches will drift.
Constraints / guardrails
price_usd/ invertUsd. Tape Price stays human quote-per-base (or the display reciprocal when inverted). NeverformatNumcompactTon Price (#522).1e6. USTR is 18; UST1/cUSTC are 6.offer_decimals/ask_decimals(and filltoken0_decimals/token1_decimals). Pair-scoped UI may useactivePairas a fallback only whenpair_addressmatches and both symbols match that pair’s two legs./tradeor/chartsdisplay invert is on, tape Price must be the reciprocal human price (display quote per display base) or the column header must stay explicit factory orientation. Prefer matching the pill (cUSTC per UST1 vs UST1 per cUSTC). Amount in/out stay pay → receive (offer → ask) and must not be swapped by invert — invert is display price, not fill direction.pool_return_amount/book_return_amount) stay raw in JSON; if shown, same decimal as ask. Do not sum legs into Amount out.0or—(pick one per column, document). NoNaN, noInfinity.Relevant files
frontend-dapp/src/components/ui/TradesTable.tsxfrontend-dapp/src/components/ui/__tests__/TradesTable.test.tsxfrontend-dapp/src/utils/formatAmount.tsformatTokenAmount/formatPairPrice/formatNumfrontend-dapp/src/components/trade/WalletIndexerHistoryPanel.tsxfrontend-dapp/src/pages/ChartsPage.tsxfrontend-dapp/src/components/trade/TradeRecentTradesSection.tsxfrontend-dapp/src/pages/TraderPage.tsxfrontend-dapp/src/pages/PortfolioPage.tsxfrontend-dapp/src/types/index.tsIndexerTradeindexer/src/api/pairs.rsTradeResponseindexer/src/api/text_csv.rsfrontend-dapp/src/utils/tradePairDisplayOrientation.tsdocs/frontend.mdRecommended direction
offer_decimals,ask_decimalson trade JSON (from offer/askassets.decimals). Same for limit-fill rows if the wallet panel shows them. CSV: additive columns OK; do not rewrite existing raw columns (breaking integrators).formatTradeAmount(raw, decimals)→formatTokenAmount.TradesTableuses trade decimals when present, else pair-leg fallback whenactivePair.pair_address === t.pair_address.formatPairPrice(human, 6). Ifinvertedprop is true, show1/humanonly when finite and> 0(reuse invert helpers; never1/xofprice_usd). Optional subtitle in tooltip:{displayQuote} per {displayBase}.invertedfrom Charts/Trade via existingusePairDisplayOrientation. Trader/Portfolio: no invert (mixed pairs) — factory human price + human amounts via API decimals./trade//limits; stopformatNum(raw).1and206(compact OK for large human sizes only). UST1/USTR 1e6 / 1e19 →1and10notT. Pre-#522 raw price still must not showTif a legacy indexer appears (keep existing test; human79.72prints as79.72).Acceptance criteria
1class, not1.000M).Tunless the human size is actually ≥ 1e12.formatPairPrice); never USD; neverTfrom raw 18/6.make verify-issue-<iid>; docs mention the contract.Test plan — functional paths
Indexer
Frontend RTL / unit
TradesTable: UST1/cUSTC raw1000000/206000000/ price206→ human in/out, price206(or inverted0.00xclass wheninverted).TradesTable: USTR raw10000000000000000000+ 18 dec →10(example), not10.00E/T.—or documented raw fallback withoutTfor typical hub sizes (prefer—over lying).≤0/ non-finite price →—.return_amounthumanized, not a raw split.WalletIndexerHistoryPanel: swap + fill rows humanized; CSV download still raw file from API.activePairstill humanizes via trade decimals.E2E / manual
https://dex.cl8y.com/chartsUST1/cUSTC Recent Trades vs explorer/LCD raw ÷ 10^6./trade./trader/{addr}with mixed UST1/cUSTC and UST1/USTR rows.make verify-issue-<iid>after frontend unit tests.Test plan — attack, hack, and abuse
offer_decimals=0on an 18-dec USTR to inflate UIassetsrow for that offer asset id, not from the wasm event. Ignore/clamp out-of-range (<0or>38) →—.UST1offer_assetstring.1/price_usdin Price columndangerouslySetInnerHTML.formatTokenAmountBigInt path; no crash; compact human if huge.0or—; no negative compactT.formatNumin the file.activePairdecimals applied to another pair’s rowt.pair_addressmatches; otherwise API decimals or—.Verification criteria
Out of scope
marked as related to #556
mentioned in issue #556
changed the description
mentioned in merge request !1082
mentioned in merge request !1083
mentioned in issue #560
marked as related to #560
mentioned in issue #561
Production QA 2026-08-18 (
https://dex.cl8y.com/charts)Retail report: UST1/cUSTC Recent Trades Amount in / Amount out “looks wrong.” Confirmed against live indexer
GET /api/v1/pairs/{addr}/trades.Pair
terra1ceprjsxp86ggftf5e38wwt34l83e5gq7penkdnv4wsatkwcs8v6qccw55f(UST1 6-dec / cUSTC 6-dec).TradesTablestill doesformatNum(t.offer_amount)/formatNum(t.return_amount)on raw integers.Worked example matching the screenshot class:
45.39M~453900007.909B~7909000000174.2507909 / 45.39)Price is internally consistent with the raw ratio. Amounts are 10^decimals too large because
formatNumtreats micro-units as human compact (M/B). Live row at 2026-08-18T07:37:45Z:offer_amount=43844964→ UI43.84M, human 43.85 UST1;return_amount=8818873964→ UI8.819B, human 8,819 cUSTC;price=201.137…;price_usd≈0.956.Extra ACs (do not split)
UST1 → cUSTCcolumn as the only unit). Do not leave a bare45.39/7.91Kwith no association after humanizing.offer_asset === asset_0.symbol) must follow the #524 invert pill the same way tape Price does. Paying UST1 while inverted (display-base = cUSTC) is a buy of display-base; paying UST1 when not inverted is a sell of UST1. Amounts stay offer → ask (A7).Related leftover on the same page (24h Vol (UST1) / Vol (cUSTC) still
formatNum(raw)) is filed separately so this issue stays tape + wallet history.mentioned in issue #564
marked as related to #564
mentioned in issue #565
marked as related to #565
mentioned in merge request !1089
mentioned in commit
db630256fdmentioned in merge request !1093
mentioned in merge request !1095
mentioned in commit
f36aedac2aPost-merge note (!1093 stacked onto main with !1086–!1095). Code for T557-1–T557-11 is on
main. Indexer JSON now addsoffer_decimals/ask_decimals(fills:token0_decimals/token1_decimals) and plain integer amount strings.Remaining (not done in merge):
activePairfallback)./trader/{addr}mixed UST1/cUSTC and UST1/USTR rows.marked as related to #573
mentioned in issue #573
Sanity after stack: frontend tape tests PASS. Indexer
cargo testrungs FAIL on this agent VM:Permission deniedon root-ownedindexer/target/debug/.cargo-artifact-lock(not a product defect). Re-runmake verify-issue-557aftersudo chown -R $(whoami) indexer/target.