feat(frontend): compact vs full number display in Settings #1259

Open
opened 2026-09-12 13:48:07 +00:00 by PlasticDigits · 1 comment

Summary

Retail chrome hard-codes compact K/M/B/T via formatNum / formatTokenAmount / formatTokenAmountAbbrev. There is no Settings control to pick another display format. Traders who need full grouped digits (or who find compact suffixes misleading next to 18-decimal human amounts) have no supported switch.

Bundle (do not split):

  1. A persisted display-mode preference (default Compact, current behavior).
  2. A Full grouped mode: en-US thousands grouping, no K/M/B/T.
  3. One retail Settings control on Swap (not the #413 integrator Advanced dump) that applies across existing format helpers.

Do not reopen closed #46 (tiers-page consistency; verified with K/M remaining). Do not reopen closed #413 (hybrid/indexer Advanced disclosure). Those are not this ticket.

Swap Settings has no number-format control

Retail Settings (#swap-slippage-settings in SwapPage.tsx) is slippage presets, transaction deadline, and Expert Mode. SwapAdvancedSettings (#413) is hybrid book-leg override + indexer route debug. Neither exposes a display-format choice. swapSettingsAdvanced.ts only persists cl8y-dex-swap-settings-advanced-open.

Compact is the only chrome path

frontend-dapp/src/utils/formatAmount.ts:

  • formatNum always abbreviates at 1e3 / 1e6 / 1e9 / 1e12 with significant figures.
  • formatTokenAmount / formatTokenAmountAbbrev humanize with fromRawAmount then compact.
  • formatPairPrice is already non-compact (GitLab #522) — that exception stays.
  • A short-lived formatTokenAmountGrouped() existed for #46 then was withdrawn for /tiers; it is not on main.

Callers include Swap, Charts pair stats / overview, Pool vol/TVL, Protocol USD headlines (formatProtocolStats.ts), Trade tape (tradeTapeDisplay.ts / T557), Trader volume (#553), Tiers, Portfolio, wallet LUNC chip. There is no preference argument today.

Execute and inputs are a different path

toRawAmount / fromRawAmount and amount <input> values must stay plain decimal strings. Display mode must not rewrite typed You Pay / book-leg / limit-price fields, and must not change simulate/execute JSON.

Why the new implementation is needed

  1. Compact suffixes hide magnitude for some users and clash with the #564/#557 class of bugs (compact applied to the wrong scale). A first-party Full grouped mode lets traders read 1,234,567.89 without teaching them K/M/T.
  2. The formatters already exist as a family. A mode switch at the helper layer is cheaper and safer than per-page forks that will drift.
  3. Settings is the existing preference surface. Putting this in #413 Advanced would hide it behind integrator debug (hop addresses). Display format is a retail chrome pref, same class as slippage chips.

This is frontend chrome only. No contract migrate, no indexer JSON change, no Coolify env.

Constraints / guardrails

  • Display only. Never change toRawAmount, fromRawAmount, quote/simulate/execute payloads, min-received raw, or wallet Msg amounts. Mode is string rendering after humanize.
  • Amount inputs stay unformatted. You Pay, You Receive when typeable, book-leg override, custom slippage, deadline, limit price/size: plain decimal drafts (isDecimalAmountDraft / fromRawAmount). Do not inject grouping commas into controlled inputs (W10-C4 / #169).
  • Pre-submit / signing chrome (SwapPreSubmitSummary, limit pre-submit, pool provide/withdraw summary): always Full grouped (or fromRawAmount + grouping), never compact, regardless of Settings mode. Confirmation digits must match what will be signed.
  • formatPairPrice stays non-compact in both modes (#522 / T557-6 / P551-4). Spot, TWAP, limit avg-entry, tape Price: no T/B/M/K from a raw ratio.
  • Humanize before format. Never formatNum(raw) on indexer integers (#534 / #540 / #553 / #557 / #564 / P565-2 / P534-4). Preference applies only after pair-leg / token_info decimals (0…18). Unresolved decimals still — (do not guess 6). Do not expand #1255 here.
  • PAIR_STATS_VOLUME_DISPLAY_CAP (24) and hostile-length / <> / non-integer guards stay. Full grouped of an adversarial integer must still return —, not a megabyte text node. Do not raise the cap to “make grouped work.”
  • en-US grouping only. Do not switch decimal comma/period from navigator.language. That fights amount parsers (#169) and Vitest. No i18n library.
  • No URL / query persistence. Do not honor ?format=, ?locale=, ?compact= (#596: Settings execution prefs are not inbound query). Persist localStorage only, same failure mode as writeSwapSettingsAdvancedOpen (quota / private mode → Compact default).
  • Do not persist inside Swap search (from/to/exactAmount). Display mode is not shareable.
  • Gems (#562). Mode must not honor ?showGems=1 or reveal hidden ids in formatted strings.
  • Expert Mode, slippage, hybrid always-on (#596), 5% confirm-again / 30% Expert / 99% / blacklist / pause / freeze (#678) unchanged. Display mode is not an execution bypass.
  • C653. Chips go inside the existing #swap-slippage-settings card-glass. No new shell-panel* / extra card-glass. python3 scripts/check_chrome_nesting.py stays green.
  • No scientific notation in execute or inputs. Compact/full are the only two modes. Dust below 1e-6 may still use existing sigfig rendering; do not emit 1e-18 into an input or broadcast.
  • Hostile localStorage values (HTML, javascript:, unknown keys) → Compact. Text nodes only; no dangerouslySetInnerHTML; no eval.

Relevant files

Path Why
frontend-dapp/src/utils/formatAmount.ts formatNum / formatTokenAmount / formatTokenAmountAbbrev; add mode; keep formatPairPrice non-compact
frontend-dapp/src/utils/__tests__/formatAmount.test.ts Table-driven Compact vs Full; cap / hostile / price exceptions
frontend-dapp/src/utils/numberDisplayPreference.ts (new) compact | full enum; localStorage key; read/write like swapSettingsAdvanced.ts
frontend-dapp/src/utils/numberDisplayPreference.test.ts (new) Hostile / missing / private-mode → Compact
frontend-dapp/src/pages/SwapPage.tsx Retail Settings chips after deadline / before Expert Mode
frontend-dapp/src/pages/SwapPage.test.tsx Settings visibility + persistence; no rewrite of Pay input
frontend-dapp/src/utils/formatProtocolStats.ts USD / count / Δ% headlines consume the same mode
frontend-dapp/src/utils/tradeTapeDisplay.ts T557 amounts follow mode; Price stays formatPairPrice
frontend-dapp/src/utils/chartsPairStats.ts / chartsOverviewStats.ts Vol USD / token vol after humanize
docs/frontend.md New display-mode invariants; cross-link #522 / #557 / #564 / #46 / #413
skills/AGENTS_FRONTEND_CHROME_NESTING.md No new nested chrome for the chips

A React context or tiny store is OK if formatNum cannot read localStorage during SSR; do not add a second formatter family. Prefer one dispatcher used by formatTokenAmount and formatProtocolUsd.

  1. NumberDisplayMode = 'compact' | 'full'. Default 'compact' (today’s formatNum). Key e.g. cl8y-dex-number-display-mode. Unknown stored values → compact.
  2. formatNum(val, sigfigs, mode?) and token helpers take an explicit mode or read the preference once per call. full: toLocaleString('en-US') after humanize, no suffix; BigInt-safe path for whole tokens above MAX_SAFE_INTEGER (reuse formatWholeTokensAbbrev integer grouping without suffix).
  3. formatPairPrice ignores mode. formatPairStatsVolume still humanizes + length-cap; in full it groups instead of K/M, still — when over cap.
  4. Settings: two chips Compact / Full in #swap-slippage-settings (role="group", aria-label="Number display"), data-testid="swap-number-display-compact" / swap-number-display-full. sounds.playButtonPress. 44px hit target. Live preview of a fixture (e.g. 1,234,567 vs 1.235M) as text, not a new card.
  5. Changing mode re-renders existing queries; do not refetch indexer. No setSearchParams.
  6. Docs: document Compact default, Full grouped, confirmation always-full, price never-compact, cap fail-closed.

Acceptance criteria

  • AC1. Default (no key / first visit): all existing compact strings match current tests (13.23K, 1.235M, …).
  • AC2. Selecting Full in Swap Settings shows grouped amounts without K/M/B/T on Swap balances / Receive chrome, Charts 24h Vol (after humanize), /pool Vol USD, Protocol USD headlines, tape Amount in/out, /tiers CL8Y thresholds.
  • AC3. Price surfaces stay formatPairPrice (no compact T) in both modes.
  • AC4. You Pay / other amount inputs do not gain grouping commas; typing 1000.5 still toRawAmounts the same raw.
  • AC5. Pre-submit summary amounts are full grouped even when Compact is selected.
  • AC6. Reload restores the last legal mode. Hostile / missing storage → Compact. Mode is absent from the address bar and from Share URLs (#713).
  • AC7. Unresolved / out-of-range decimals still —. PAIR_STATS_VOLUME_DISPLAY_CAP still fail-closes.
  • AC8. No new shell-panel / nested card-glass. check_chrome_nesting.py green.
  • AC9. Expert Mode, hybrid Advanced, slippage, execute path, and #678 gates unchanged.
  • AC10. Docs + unit tests cover both modes and the price/input/cap exceptions.

Test plan (functional paths)

# Path Expect
T1 Fresh visit, Settings closed Compact; no localStorage key required
T2 Settings → Full Receive / balance chrome grouped; no suffix
T3 Settings → Compact Restores K/M/T
T4 Reload after Full Still Full
T5 Direct formatNum(1234567, 4, 'full') 1,234,567 (or 1,234,567.00 only if tests pick a fixed-dp rule — prefer no trailing zeros for integers)
T6 formatPairPrice with mode full Still non-compact ratio; never T
T7 Charts Vol token, 18-dec raw, Full Humanize then group; never formatNum(raw)
T8 Tape Amount in, Compact vs Full Compact abbreviates human size; Full groups; Price unchanged
T9 Type 1000 in You Pay under Full Input stays 1000; raw 10^(decimals) unchanged
T10 Pre-submit with Compact selected Summary shows grouped full digits
T11 /tiers Full Thresholds grouped, consistent cards vs table
T12 Protocol USD headline Full $ + grouped; non-finite still —
T13 Share Swap URL No format query key
T14 make test-frontend + existing verify-issue-522/553/557/564/565 Green
T15 Playwright smoke Settings chips (PLAYWRIGHT_SKIP_CHAIN=1 OK) Chips present; Full changes a known compact fixture on /

Vitest: table-driven formatNum / formatTokenAmount / preference parser. RTL: Settings chips + persistence mock. 5 workers.

Test plan (attack, hack, and abuse)

Display-only; not a DeFi drain suite. Still isolate money chrome from execute.

# Vector Expect
A1 localStorage value <img> / javascript: Compact; text node only
A2 ?format=full / ?compact=0 on / Ignored; no search rewrite
A3 Full mode + 78-digit hostile volume raw — (cap / bigint guards); tab stays responsive
A4 Grouping commas in You Pay Parser rejects or strips; no BigInt throw (#169)
A5 Mode switch during quote Execute still offer-in + min received; same raw
A6 Share / copy link Canonical swap keys only; no mode
A7 Gem id in a formatted string Production hide unchanged (#562)
A8 Confirmation compact while Settings Compact Forbidden — summary stays full digits

Verification criteria

  • New make verify-issue-* greps: Compact default, Full grouped helper, Settings testids, formatPairPrice ignores mode, no formatNum(raw) regressions, chrome nesting script green.
  • make test-frontend includes preference + formatAmount tables.
  • Playwright: Settings Compact/Full chips; Full changes a compact fixture; URL has no format key.
  • Manual: make dev → Settings → Full → Charts / Pool / Protocol / Trade tape still priced correctly; flip back to Compact; Expert Mode still blocks >30% route slippage.

Out of scope

  • Locale decimal comma, ICU, or navigator.language.
  • Scientific-notation mode or per-token decimal-place pickers.
  • Unlisted CW20 decimal resolution (#1255).
  • Charts OHLC invert / Price (USD) scale (#543 / #1258).
  • Indexer JSON shape; Compact vs Full is UI-only.
  • Reopening #46 or #413.
  • Putting the control in SwapAdvancedSettings (integrator dump).

First-pass model recommendation

Recommendation: grok-high

Rationale: Display-only and low/medium product risk, but it is a cross-cutting chrome preference: formatNum / formatTokenAmount feed Swap, Charts, Pool, Protocol, Trade tape, Trader, Tiers, and Portfolio. A correct pass must keep formatPairPrice non-compact, confirmation summaries full, inputs ungrouped, and volume length-caps fail-closed. That exceeds a local three-file edit in one subsystem and is not a known isolated helper change. Verification is the existing formatAmount / Charts / tape suites plus Settings persistence — deterministic, but the blast radius is dApp-wide. Composer’s first-pass bar fails on file/subsystem count and cross-cutting display state.

## Summary Retail chrome hard-codes compact `K`/`M`/`B`/`T` via `formatNum` / `formatTokenAmount` / `formatTokenAmountAbbrev`. There is no Settings control to pick another **display** format. Traders who need full grouped digits (or who find compact suffixes misleading next to 18-decimal human amounts) have no supported switch. Bundle (do not split): 1. A persisted display-mode preference (default **Compact**, current behavior). 2. A **Full grouped** mode: en-US thousands grouping, no `K`/`M`/`B`/`T`. 3. One retail Settings control on Swap (not the #413 integrator Advanced dump) that applies across existing format helpers. Do not reopen closed [#46](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/46) (tiers-page consistency; verified with K/M remaining). Do not reopen closed [#413](https://git.cl8y.com/code/cl8y-dex-terraclassic/issues/413) (hybrid/indexer Advanced disclosure). Those are not this ticket. ### Swap Settings has no number-format control Retail Settings (`#swap-slippage-settings` in `SwapPage.tsx`) is slippage presets, transaction deadline, and Expert Mode. `SwapAdvancedSettings` (#413) is hybrid book-leg override + indexer route debug. Neither exposes a display-format choice. `swapSettingsAdvanced.ts` only persists `cl8y-dex-swap-settings-advanced-open`. ### Compact is the only chrome path `frontend-dapp/src/utils/formatAmount.ts`: - `formatNum` always abbreviates at 1e3 / 1e6 / 1e9 / 1e12 with significant figures. - `formatTokenAmount` / `formatTokenAmountAbbrev` humanize with `fromRawAmount` then compact. - `formatPairPrice` is already non-compact (GitLab #522) — that exception stays. - A short-lived `formatTokenAmountGrouped()` existed for #46 then was withdrawn for `/tiers`; it is not on `main`. Callers include Swap, Charts pair stats / overview, Pool vol/TVL, Protocol USD headlines (`formatProtocolStats.ts`), Trade tape (`tradeTapeDisplay.ts` / T557), Trader volume (#553), Tiers, Portfolio, wallet LUNC chip. There is no preference argument today. ### Execute and inputs are a different path `toRawAmount` / `fromRawAmount` and amount `<input>` values must stay plain decimal strings. Display mode must not rewrite typed You Pay / book-leg / limit-price fields, and must not change simulate/execute JSON. ## Why the new implementation is needed 1. Compact suffixes hide magnitude for some users and clash with the #564/#557 class of bugs (compact applied to the wrong scale). A first-party **Full grouped** mode lets traders read `1,234,567.89` without teaching them K/M/T. 2. The formatters already exist as a family. A mode switch at the helper layer is cheaper and safer than per-page forks that will drift. 3. Settings is the existing preference surface. Putting this in #413 Advanced would hide it behind integrator debug (hop addresses). Display format is a retail chrome pref, same class as slippage chips. This is frontend chrome only. No contract migrate, no indexer JSON change, no Coolify env. ## Constraints / guardrails - **Display only.** Never change `toRawAmount`, `fromRawAmount`, quote/simulate/execute payloads, min-received raw, or wallet `Msg` amounts. Mode is string rendering after humanize. - **Amount inputs stay unformatted.** You Pay, You Receive when typeable, book-leg override, custom slippage, deadline, limit price/size: plain decimal drafts (`isDecimalAmountDraft` / `fromRawAmount`). Do not inject grouping commas into controlled inputs (W10-C4 / #169). - **Pre-submit / signing chrome** (`SwapPreSubmitSummary`, limit pre-submit, pool provide/withdraw summary): always **Full grouped** (or `fromRawAmount` + grouping), never compact, regardless of Settings mode. Confirmation digits must match what will be signed. - **`formatPairPrice` stays non-compact** in both modes (#522 / T557-6 / P551-4). Spot, TWAP, limit avg-entry, tape Price: no `T`/`B`/`M`/`K` from a raw ratio. - **Humanize before format.** Never `formatNum(raw)` on indexer integers (#534 / #540 / #553 / #557 / #564 / P565-2 / P534-4). Preference applies only after pair-leg / `token_info` decimals (0…18). Unresolved decimals still `—` (do not guess 6). Do not expand #1255 here. - **`PAIR_STATS_VOLUME_DISPLAY_CAP` (24)** and hostile-length / `<>` / non-integer guards stay. Full grouped of an adversarial integer must still return `—`, not a megabyte text node. Do not raise the cap to “make grouped work.” - **en-US grouping only.** Do not switch decimal comma/period from `navigator.language`. That fights amount parsers (#169) and Vitest. No i18n library. - **No URL / query persistence.** Do not honor `?format=`, `?locale=`, `?compact=` (#596: Settings execution prefs are not inbound query). Persist `localStorage` only, same failure mode as `writeSwapSettingsAdvancedOpen` (quota / private mode → Compact default). - **Do not persist inside Swap search** (`from`/`to`/`exactAmount`). Display mode is not shareable. - **Gems (#562).** Mode must not honor `?showGems=1` or reveal hidden ids in formatted strings. - **Expert Mode, slippage, hybrid always-on (#596), 5% confirm-again / 30% Expert / 99% / blacklist / pause / freeze (#678)** unchanged. Display mode is not an execution bypass. - **C653.** Chips go inside the existing `#swap-slippage-settings` `card-glass`. No new `shell-panel*` / extra `card-glass`. `python3 scripts/check_chrome_nesting.py` stays green. - **No scientific notation in execute or inputs.** Compact/full are the only two modes. Dust below 1e-6 may still use existing sigfig rendering; do not emit `1e-18` into an input or broadcast. - Hostile `localStorage` values (HTML, `javascript:`, unknown keys) → Compact. Text nodes only; no `dangerouslySetInnerHTML`; no `eval`. ## Relevant files | Path | Why | | --- | --- | | `frontend-dapp/src/utils/formatAmount.ts` | `formatNum` / `formatTokenAmount` / `formatTokenAmountAbbrev`; add mode; keep `formatPairPrice` non-compact | | `frontend-dapp/src/utils/__tests__/formatAmount.test.ts` | Table-driven Compact vs Full; cap / hostile / price exceptions | | `frontend-dapp/src/utils/numberDisplayPreference.ts` (new) | `compact` \| `full` enum; `localStorage` key; read/write like `swapSettingsAdvanced.ts` | | `frontend-dapp/src/utils/numberDisplayPreference.test.ts` (new) | Hostile / missing / private-mode → Compact | | `frontend-dapp/src/pages/SwapPage.tsx` | Retail Settings chips after deadline / before Expert Mode | | `frontend-dapp/src/pages/SwapPage.test.tsx` | Settings visibility + persistence; no rewrite of Pay input | | `frontend-dapp/src/utils/formatProtocolStats.ts` | USD / count / Δ% headlines consume the same mode | | `frontend-dapp/src/utils/tradeTapeDisplay.ts` | T557 amounts follow mode; Price stays `formatPairPrice` | | `frontend-dapp/src/utils/chartsPairStats.ts` / `chartsOverviewStats.ts` | Vol USD / token vol after humanize | | `docs/frontend.md` | New display-mode invariants; cross-link #522 / #557 / #564 / #46 / #413 | | `skills/AGENTS_FRONTEND_CHROME_NESTING.md` | No new nested chrome for the chips | A React context or tiny store is OK if `formatNum` cannot read `localStorage` during SSR; do not add a second formatter family. Prefer one dispatcher used by `formatTokenAmount` and `formatProtocolUsd`. ## Recommended direction 1. `NumberDisplayMode = 'compact' | 'full'`. Default `'compact'` (today’s `formatNum`). Key e.g. `cl8y-dex-number-display-mode`. Unknown stored values → compact. 2. `formatNum(val, sigfigs, mode?)` and token helpers take an explicit mode **or** read the preference once per call. `full`: `toLocaleString('en-US')` after humanize, no suffix; BigInt-safe path for whole tokens above `MAX_SAFE_INTEGER` (reuse `formatWholeTokensAbbrev` integer grouping without suffix). 3. `formatPairPrice` ignores mode. `formatPairStatsVolume` still humanizes + length-cap; in `full` it groups instead of K/M, still `—` when over cap. 4. Settings: two chips **Compact** / **Full** in `#swap-slippage-settings` (`role="group"`, `aria-label="Number display"`), `data-testid="swap-number-display-compact"` / `swap-number-display-full`. `sounds.playButtonPress`. 44px hit target. Live preview of a fixture (e.g. `1,234,567` vs `1.235M`) as text, not a new card. 5. Changing mode re-renders existing queries; do not refetch indexer. No `setSearchParams`. 6. Docs: document Compact default, Full grouped, confirmation always-full, price never-compact, cap fail-closed. ## Acceptance criteria - AC1. Default (no key / first visit): all existing compact strings match current tests (`13.23K`, `1.235M`, …). - AC2. Selecting **Full** in Swap Settings shows grouped amounts without `K`/`M`/`B`/`T` on Swap balances / Receive chrome, Charts 24h Vol (after humanize), `/pool` Vol USD, Protocol USD headlines, tape Amount in/out, `/tiers` CL8Y thresholds. - AC3. **Price** surfaces stay `formatPairPrice` (no compact `T`) in both modes. - AC4. You Pay / other amount inputs do not gain grouping commas; typing `1000.5` still `toRawAmount`s the same raw. - AC5. Pre-submit summary amounts are full grouped even when Compact is selected. - AC6. Reload restores the last legal mode. Hostile / missing storage → Compact. Mode is absent from the address bar and from Share URLs (#713). - AC7. Unresolved / out-of-range decimals still `—`. `PAIR_STATS_VOLUME_DISPLAY_CAP` still fail-closes. - AC8. No new `shell-panel` / nested `card-glass`. `check_chrome_nesting.py` green. - AC9. Expert Mode, hybrid Advanced, slippage, execute path, and #678 gates unchanged. - AC10. Docs + unit tests cover both modes and the price/input/cap exceptions. ## Test plan (functional paths) | # | Path | Expect | | --- | --- | --- | | T1 | Fresh visit, Settings closed | Compact; no localStorage key required | | T2 | Settings → Full | Receive / balance chrome grouped; no suffix | | T3 | Settings → Compact | Restores K/M/T | | T4 | Reload after Full | Still Full | | T5 | Direct `formatNum(1234567, 4, 'full')` | `1,234,567` (or `1,234,567.00` only if tests pick a fixed-dp rule — prefer no trailing zeros for integers) | | T6 | `formatPairPrice` with mode full | Still non-compact ratio; never `T` | | T7 | Charts Vol token, 18-dec raw, Full | Humanize then group; never `formatNum(raw)` | | T8 | Tape Amount in, Compact vs Full | Compact abbreviates human size; Full groups; Price unchanged | | T9 | Type `1000` in You Pay under Full | Input stays `1000`; raw `10^(decimals)` unchanged | | T10 | Pre-submit with Compact selected | Summary shows grouped full digits | | T11 | `/tiers` Full | Thresholds grouped, consistent cards vs table | | T12 | Protocol USD headline Full | `$` + grouped; non-finite still `—` | | T13 | Share Swap URL | No format query key | | T14 | `make test-frontend` + existing verify-issue-522/553/557/564/565 | Green | | T15 | Playwright smoke Settings chips (`PLAYWRIGHT_SKIP_CHAIN=1` OK) | Chips present; Full changes a known compact fixture on `/` | Vitest: table-driven `formatNum` / `formatTokenAmount` / preference parser. RTL: Settings chips + persistence mock. 5 workers. ## Test plan (attack, hack, and abuse) Display-only; not a DeFi drain suite. Still isolate money chrome from execute. | # | Vector | Expect | | --- | --- | --- | | A1 | `localStorage` value `<img>` / `javascript:` | Compact; text node only | | A2 | `?format=full` / `?compact=0` on `/` | Ignored; no search rewrite | | A3 | Full mode + 78-digit hostile volume raw | `—` (cap / bigint guards); tab stays responsive | | A4 | Grouping commas in You Pay | Parser rejects or strips; no `BigInt` throw (#169) | | A5 | Mode switch during quote | Execute still offer-in + min received; same raw | | A6 | Share / copy link | Canonical swap keys only; no mode | | A7 | Gem id in a formatted string | Production hide unchanged (#562) | | A8 | Confirmation compact while Settings Compact | Forbidden — summary stays full digits | ## Verification criteria - New `make verify-issue-*` greps: Compact default, Full grouped helper, Settings testids, `formatPairPrice` ignores mode, no `formatNum(raw)` regressions, chrome nesting script green. - `make test-frontend` includes preference + formatAmount tables. - Playwright: Settings Compact/Full chips; Full changes a compact fixture; URL has no format key. - Manual: `make dev` → Settings → Full → Charts / Pool / Protocol / Trade tape still priced correctly; flip back to Compact; Expert Mode still blocks >30% route slippage. ## Out of scope - Locale decimal comma, ICU, or `navigator.language`. - Scientific-notation mode or per-token decimal-place pickers. - Unlisted CW20 decimal resolution (#1255). - Charts OHLC invert / Price (USD) scale (#543 / #1258). - Indexer JSON shape; Compact vs Full is UI-only. - Reopening #46 or #413. - Putting the control in `SwapAdvancedSettings` (integrator dump). ## First-pass model recommendation Recommendation: grok-high Rationale: Display-only and low/medium product risk, but it is a **cross-cutting chrome preference**: `formatNum` / `formatTokenAmount` feed Swap, Charts, Pool, Protocol, Trade tape, Trader, Tiers, and Portfolio. A correct pass must keep `formatPairPrice` non-compact, confirmation summaries full, inputs ungrouped, and volume length-caps fail-closed. That exceeds a local three-file edit in one subsystem and is not a known isolated helper change. Verification is the existing formatAmount / Charts / tape suites plus Settings persistence — deterministic, but the blast radius is dApp-wide. Composer’s first-pass bar fails on file/subsystem count and cross-cutting display state.
Author
Owner

+1 Compact (K/M/B/T, e.g. 9.12K) remains the first-visit default; Full grouped stays opt-in via retail Settings.

+1 Compact (K/M/B/T, e.g. 9.12K) remains the first-visit default; Full grouped stays opt-in via retail Settings.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-dex-terraclassic#1259
No description provided.