fix: /protocol Global stats — keep inline Δ% next to its USD headline #667
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#667
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
/protocolGlobal stats already puts each Δ% in the correctStatBoxDOM (GitLab #652), but the value row usesflex justify-between. The headline ($61K,$3.7K) stays left; the chips (+19.74% 24h,+155.5% 24h) pin to the right edge of the cell and sit in the gutter under the next column’s label. Operators read+155.5% 24has belonging to Last 7d vol, not Last 24h vol.Same
StatBoxrow is used on Protocol fees. Bundle into one issue: group every inline Δ% with its own headline (Global stats + fees), and stop census tiles from rendering integer counts as14.00/8.000. Do not split “Global stats vs fees” vs “count formatting.”Related: #652 (inline Δ% + prior-window % — DOM/data shipped, visual grouping leftover), #569 P569-1 / P569-7 (one cell: USD + inline 24h/30d), #586 PFee-2 (fee Δ% is a child, not a sibling card), #653 (keep
StatBox variant="flat"), #550 (USD census), #489 (do not add a lecture banner).Current codebase
#652merged the data and the parentage. Tests only assertwithin(tile).getByTestId(...). They do not assert that the chip’s box sits next to the headline. Production (and LocalTerra) therefore still looks like the old “Δ% belongs to the next column” layout.ProtocolGlobalStats.tsx:shell-panel(protocol-global-stats) +grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4. Liquidity / 24h / 7d / 30d vol passdeltasinto flatStatBox. Census tiles (tokens, new tokens/pairs, active pairs, 24h trades) are value-only. UTC-day chart sits below.ProtocolFeeStats.tsx: same flatStatBoxwithdelta/deltaLabel/deltaTestId.grid-cols-2 sm:grid-cols-3. Same visual gutter risk on tablet/desktop.StatBox.tsxL93:flex items-baseline justify-between gap-2 min-w-0 flex-wrap. Headline<p>is left; Δ% cluster isshrink-0on the right.justify-betweenis the visual bug. Testids andtitle/aria-labelstay on the child spans (protocol-stat-liquidity-24h/-30d,protocol-stat-volume-*-chg,protocol-stat-fees-*-chg).variant="flat"+.stat-flat/.stat-box-flat(#653). No nestedcard-glassin the metric grid.formatProtocolPct+protocolPctToneFromDisplay: missing / non-finite / XSS-like → em-dash;0→0%; neverInfinity. Tone is--color-positive/--color-negative/--ink-dim. Gold is never a fill.formatProtocolCountcallsformatNum(n, 4)(significant figures). Integers become14.00,8.000,7.000,5.000,151.0. Same panel; same “number does not look like it belongs to its label” class.liquidity_change_{24h,30d}_pct,volume_change_{24h,7d,30d}_pct,fees_change_{24h,7d,30d}_pct. GET/overviewis 60s cache + O(1) rollup.ProtocolPage.test.tsxandStatBox.test.tsxcheck text + DOM parentage only.e2e/protocol-page.spec.tschecks visibility + panel order, not chip vs headline geometry.make verify-issue-652therefore stays green while the page still misleads.AGENTS_FRONTEND_PROTOCOL_STATS.mdP569-1 / P569-7 / PFee-2 / P652-6 say “same cell” and “inline.” They do not say “Δ% must sit immediately after the headline, notjustify-betweento the next column.”Why this is needed
+155.5% 24hsitting under Last 7d vol is not a polish nits — operators decide “is volume growing?” from that chip. DOM-correct + visually-wrong is still a product bug.#652already specified “same cell.” The leftover is layout, not a new feature. Shipping a second “inline Δ%” ticket that only re-parents nodes will not fix the screenshot.StatBoxonjustify-betweenwill regress the fee row the next time a 24h chip is long (+315.6% 7d).14.00/8.000looks like a USD or rate. Tokens / pairs / trades are integers.formatNumsig-figs are for compact USD, not counts.Constraints / guardrails
flow_change_pct, snapshot Δ%, orGET /api/v1/protocol/volume/daily.#652parentage. Δ% nodes stay children ofprotocol-stat-liquidity/protocol-stat-volume-*/protocol-stat-fees-*. Do not restore sibling cards (protocol-stat-liquidity-24has its own tile).#653chrome.StatBox variant="flat"insideshell-panel. No nestedcard-glass. No new gold fills. Tokens stay #488.null/ non-finite → em-dash. NeverInfinity/ fake0%.GET /defillama/dailyfrom/protocol.StatBoxto a new layout that breaks Charts / Trader isolatedcardtiles that have no Δ%. Call sites withoutdelta/deltasmust look unchanged.title+aria-labelcomposition (composeStatAriaLabel). Screen readers already hear the right pairing; do not drop that when fixing the flex.formatProtocolPctalready rejects<script>/javascript:. Do notdangerouslySetInnerHTML. Do not put raw overview strings intostyle=/className.Relevant files
frontend-dapp/src/components/ui/StatBox.tsxjustify-between; group chips with the headlinefrontend-dapp/src/index.css.stat-flat/.stat-box-flatfrontend-dapp/src/components/protocol/ProtocolGlobalStats.tsxdeltason liquidity + volume onlyfrontend-dapp/src/components/protocol/ProtocolFeeStats.tsxfrontend-dapp/src/utils/formatProtocolStats.tsformatProtocolCount→ integer / locale count, notformatNumsig-figsfrontend-dapp/src/components/ui/__tests__/StatBox.test.tsxjustify-betweenwhen deltas existfrontend-dapp/src/pages/ProtocolPage.test.tsxwithin(tile)parentage; add count integer assertionfrontend-dapp/src/utils/__tests__/formatProtocolStats.test.tsformatProtocolCount(14) === "14"(not14.00)frontend-dapp/e2e/protocol-page.spec.tsskills/AGENTS_FRONTEND_PROTOCOL_STATS.md+docs/frontend.mdjustify-between)scripts/qa/verify-issue-NNN.sh+make verify-issue-NNNRecommended direction
justify-betweenwithjustify-start(or a dedicated.stat-value-rowthat isflex items-baseline gap-x-2 gap-y-0.5 flex-wrap). Headline first; Δ% cluster immediately after. On wrap, chips go to the next line of the same cell, left-aligned under the headline — never to the cell’s far right.+19.74% 24hand— 30das one cluster (flex gap-2) after$61K, still insideprotocol-stat-liquidity.$3.7K++155.5% 24hcannot fit, wrap the cluster under the USD. Do not shrink the headline belowtext-sm/ do not truncate the%.formatProtocolCountfor finite integers →toLocaleString(orformatNumonly whenabs >= 1e3if you truly needK). No trailing.00on 14 tokens. Non-finite still em-dash.justify-start/ wrap), not spaced to the next column.”scripts/qa/verify-issue-652.sh(docs + RTL + e2e-smoke @ 5 workers). Keepverify-issue-652/550/569/586/653green.Do not invent a second
StatBoxvariant for Protocol only if a single value-row class can serve fees + any future Δ% tile.Acceptance criteria
md+4-column Global stats, each Δ% chip’s bounding box is inside its ownprotocol-stat-*tile and does not sit under the next column’s label.$headline +protocol-stat-liquidity-24h+protocol-stat-liquidity-30dread as one group (same cell; chips immediately after or wrapped under the$).protocol-stat-volume-*-chgsits with that tile’s USD, not the neighbor.protocol-stat-fees-*-chg.— 30d) still groups with its headline (dim tone, not a stray gutter glyph).--ink; +%--color-positive; −%--color-negative;—/0%--ink-dim.14tokens, not14.00;8new tokens, not8.000. CompactKonly if the count is actually ≥ 1000.title/aria-label, UTC-day chart, and page order (stats → fees → hub → oracle) unchanged.card-glassinprotocol-global-stats/protocol-fee-stats.volume_change_*) still renders tiles; Δ% em-dash; chart hidden (P652-6).Test plan — all paths
getBoundingClientRect():chip.left >= value.leftandchip.right <= tile.rightandchip.left < nextTile.left. Repeat for liquidity (2 chips), each volume tile, each fee tile.within(tile)parentage unchanged.+1234.5% 24h++12.34% 30don a narrow liquidity cell: both chips stay inprotocol-stat-liquidity.$0/$3.7K++155.5% 24h— nojustify-betweengap.—;0→0%; old overview withoutvolume_change_*→ em-dash; tiles still render.RetryErroron overview; no invented0%.formatProtocolCount(0|8|14|151|1000)unit tests; RTL tokens tile has no.00for 14.--ink-subtleon a missing--card-bg.cardandflattiles: label+value only; no layout regression (StatBox.test.tsx+ existing Charts/Trader verifies).within(liq).getByTestId('protocol-stat-liquidity-24h')stays green.e2e/protocol-page.spec.ts;--project=e2e-smoke --workers=5.Test plan — attack, hack, and abuse
This is a display-grouping ticket. Do not treat it as a reason to re-open fee/volume math. Still fail closed on anything that could mis-attribute a % or inject chrome.
volume_change_24h_pct/ labels /javascript:/<img onerror>/<script>stay em-dash or text. NoinnerHTML. No attribute injection intostyle/classNamefrom API strings. ExistingformatProtocolPcttests stay.%text cannot become a Tailwind class orurl(). Tone is only fromprotocolPctToneFromDisplayon the already-formatted display string.+9999% 24hmust not visually attach to the next headline. That is the abuse (misleading operators).1e309Infinity%in the gutter or as a “next column” chip.""0%.nullΔ% →—, not a fabricated green chip that looks like growth on the neighbor tile.titletooltips must not cover the next tile’s headline (hit-slop).whitespace-nowrapchips must not overflow into the next cell’s click box.token_countas14.00/ scientific / HTML must not render as HTML. Non-finite → em-dash. Do not parse user query params into counts (?ticker=stay allowlisted; this panel ignores ticker).SUMofswap_events. No Llama N+1./protocolis public census. No wallet prompt, no signing, no localStorage of Δ%.javascript:/../→ustc. Grouping CSS must not depend on?ticker=.Verification criteria
Done when:
make verify-issue-NNN(new script) is green: docs/skills mention the grouping invariant; StatBox has nojustify-betweenon the value+Δ% row;formatProtocolCountinteger tests; Protocol RTL; Playwright geometry on e2e-smoke (5 workers).make verify-issue-652550569586653stay green (skip related recursion as those scripts already do)./protocolat 390 / 820 / 1280, light + dark: each % reads as belonging to its$/ fee headline. Census tiles show14not14.00.Out of scope
marked as related to #652
marked as related to #569
marked as related to #586
marked as related to #653
marked as related to #550
mentioned in commit
e25bbb04f3mentioned in merge request !1158
Implementation is in !1158 (
fix/667-protocol-stat-delta-group).StatBox value+Δ% uses
.stat-value-row(justify-start/ wrap) so chips sit with their own headline.formatProtocolCountrenders locale integers (14, not14.00).make verify-issue-667is green (RTL + Playwright geometry @ 5 workers); relatedverify-issue-652/653stayed green.Leftover: live-indexer visual pass at 390 / 820 / 1280 light+dark on a page with real USD figures (e2e ran with market-data-unavailable em-dashes; grouping still held).
mentioned in commit
f5ffe9649bmentioned in commit
211055203dmentioned in commit
c9cf7106b6Merged to
mainvia !1158. Later #652/#668 Protocol chart work was kept; Δ% chips stay grouped with the USD headline.Leftover:
/protocol375/1280 light+dark after frontend rebuild — Δ% is visually grouped with the matching headline, not a second chrome row.mentioned in issue #664
mentioned in issue #668
mentioned in issue #673
mentioned in issue #677
marked as related to #677