fix(frontend): scale unlisted Swap CW20s from token_info decimals #1295

Merged
PlasticDigits merged 3 commits from issue/1255 into main 2026-09-21 05:32:13 +00:00

Summary

Home Swap (/) and Trade market quoted and executed unlisted factory CW20s at getDecimals ?? 6. Create Token / other factory assets with on-chain 18 decimals were off by 10^12 (typed 1 sent 10^6; Max showed 1e12 for a 1-token wallet).

This PR resolves amount scale fail-closed from registry → valid LCD token_info.decimals (wins over a stale indexer 6) → indexer pair-leg / GET /tokens while LCD is in flight or transport-failed. Hostile LCD (255, 1e9, "18e0", …) stays unresolved with no indexer fallback. LCD decimals are only fetched for picker getAllTokens ids (QS-4). Cache key is cl8y-dex-token-info-v2. getDecimals still defaults unknown to 6 for leftover chrome (Q1257-4).

Closes #1255.

Also: .githooks/pre-push now scans only unpublished commits on a new branch (git rev-list <oid> --not --remotes=<remote>). The previous range was the whole ancestry, so a first push walked published history, hit an old body-policy violation already on main, and hung git rev-list.

Invariants

Documented Q1255-1–Q1255-8 in docs/frontend.md § Swap amount scaling, docs/testing.md, docs/contracts-security-audit.md, and skills/AGENTS_FRONTEND_SWAP_AMOUNT_SCALE.md. Cross-links: QS-3/QS-4 in AGENTS_FRONTEND_SWAP_TOKENLIST_SYMBOLS.md, Q1257-4 in AGENTS_FRONTEND_SWAP_USTR_USDT_SCALE.md.

Test plan

  • make verify-issue-1255 (passed twice: 5/5, 71 Vitest)
  • Resolver: unknown CW20 is null not 6; 18 vs 6 unlisted; listed registry unchanged; hostile → null
  • RTL SwapPage: type 1 on mocked 18-dec unlisted factory token → sim/execute 10^18; Max human 1 for raw 10^18
  • SwapPage does not use getDecimals(...) ?? 6 as the execute scale
  • Manual LocalTerra: factory pair whose CW20 is absent from tokenlist.json and has 18 on-chain decimals — / Swap 1 must not quote as a 6-dec micro-unit. Listed UST1/cUSTC and CL8Y/USTR stay correct.
## Summary Home Swap (`/`) and Trade market quoted and executed unlisted factory CW20s at `getDecimals ?? 6`. Create Token / other factory assets with on-chain 18 decimals were off by `10^12` (typed `1` sent `10^6`; Max showed `1e12` for a 1-token wallet). This PR resolves amount scale fail-closed from registry → valid LCD `token_info.decimals` (wins over a stale indexer 6) → indexer pair-leg / `GET /tokens` while LCD is in flight or transport-failed. Hostile LCD (`255`, `1e9`, `"18e0"`, …) stays unresolved with no indexer fallback. LCD decimals are only fetched for picker `getAllTokens` ids (**QS-4**). Cache key is `cl8y-dex-token-info-v2`. `getDecimals` still defaults unknown to 6 for leftover chrome (**Q1257-4**). Closes #1255. Also: `.githooks/pre-push` now scans only unpublished commits on a new branch (`git rev-list <oid> --not --remotes=<remote>`). The previous range was the whole ancestry, so a first push walked published history, hit an old body-policy violation already on `main`, and hung `git rev-list`. ## Invariants Documented **Q1255-1–Q1255-8** in `docs/frontend.md` § Swap amount scaling, `docs/testing.md`, `docs/contracts-security-audit.md`, and [`skills/AGENTS_FRONTEND_SWAP_AMOUNT_SCALE.md`](skills/AGENTS_FRONTEND_SWAP_AMOUNT_SCALE.md). Cross-links: QS-3/QS-4 in `AGENTS_FRONTEND_SWAP_TOKENLIST_SYMBOLS.md`, Q1257-4 in `AGENTS_FRONTEND_SWAP_USTR_USDT_SCALE.md`. ## Test plan - [x] `make verify-issue-1255` (passed twice: 5/5, 71 Vitest) - [x] Resolver: unknown CW20 is `null` not 6; 18 vs 6 unlisted; listed registry unchanged; hostile → null - [x] RTL SwapPage: type `1` on mocked 18-dec unlisted factory token → sim/execute `10^18`; Max human `1` for raw `10^18` - [x] SwapPage does not use `getDecimals(...) ?? 6` as the execute scale - [ ] Manual LocalTerra: factory pair whose CW20 is absent from `tokenlist.json` and has 18 on-chain decimals — `/` Swap `1` must not quote as a 6-dec micro-unit. Listed UST1/cUSTC and CL8Y/USTR stay correct.
Home Swap treated factory CW20s missing the registry as 6-dec, so 18-dec
tokens quoted and executed 10^12 too small. Resolve decimals fail-closed
from registry, LCD token_info, or indexer pair legs.
fix(hooks): limit pre-push scan to unpublished commits
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
57764b1b39
A first push of a branch listed the whole ancestry, so already-published
history failed the body policy and hung git rev-list.
Author
Owner

drain skip: no occupying job for rebase/fix-pr/CI-wait; remaining continue

drain skip: no occupying job for rebase/fix-pr/CI-wait; remaining continue
Merge origin/main into issue/1255
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
793eb7f311
Keep unlisted CW20 LCD decimals fail-closed with TaxPreview send_msg leftover and LUNC gas-gate. Combine pre-push unpublished-commit range with origin/main skip.
PlasticDigits 2026-09-21 05:32:04 +00:00
PlasticDigits deleted branch issue/1255 2026-09-21 05:32:13 +00:00
Author
Owner

cl8y-agent-control: queued implement job c660a902-c794-48f8-9df5-0b9330f1ac71 (not executed; no Hetzner VM).

cl8y-agent-control: queued `implement` job `c660a902-c794-48f8-9df5-0b9330f1ac71` (not executed; no Hetzner VM).
Author
Owner

Implement job validation (gate-impl)

PR reuse: No open issue/1295 / Fixes #1295 PR — this delivery is already merged to main (merge 4577edc8, closes #1255).

Acceptance criteria (#1255): Covered on main via resolveSwapAssetDecimals / useAssetDecimals, token-info cache v2, Swap + Trade market fail-closed execute scale, hybrid swapDisclosure pay decimals, hostile LCD handling (Q1255-1–Q1255-8).

Automated validation: make verify-issue-1255 — 5/5 steps, 71 Vitest (resolver, hook, SwapPage RTL 18-dec vs 6-dec, cache, disclosure).

Gaps:

  • Manual LocalTerra still unchecked in original test plan (no chain in this VM: make has-localterra failed).
  • Log reader not used (no runtime incident).

Worktree: c660a902-c794-48f8-9df5-0b9330f1ac71 checked out at origin/main (28207c0e); no additional commits pushed (implementation already on protected main).

## Implement job validation (gate-impl) **PR reuse:** No open `issue/1295` / `Fixes #1295` PR — this delivery is already **merged** to `main` (merge `4577edc8`, closes #1255). **Acceptance criteria (#1255):** Covered on `main` via `resolveSwapAssetDecimals` / `useAssetDecimals`, token-info cache v2, Swap + Trade market fail-closed execute scale, hybrid `swapDisclosure` pay decimals, hostile LCD handling (**Q1255-1–Q1255-8**). **Automated validation:** `make verify-issue-1255` — 5/5 steps, 71 Vitest (resolver, hook, SwapPage RTL 18-dec vs 6-dec, cache, disclosure). **Gaps:** - Manual LocalTerra still unchecked in original test plan (no chain in this VM: `make has-localterra` failed). - Log reader not used (no runtime incident). **Worktree:** `c660a902-c794-48f8-9df5-0b9330f1ac71` checked out at `origin/main` (`28207c0e`); no additional commits pushed (implementation already on protected main).
Author
Owner

Implement re-check (job 164a2e8e)

No related open PR. Open pulls are renovate digest bumps only (#1312, #1313). Head issue/1255 is already merged here. No new branch or sibling PR.

Re-ran make verify-issue-1255 on origin/main (0d6eaeee): 5/5 steps, 71 Vitest.

Covered (Q1255-1–Q1255-8): Swap and Trade market execute scale via resolveSwapAssetDecimals / useAssetDecimals. Unknown CW20 is null. Precedence is registry, then valid LCD token_info (beats a stale indexer 6), then indexer while LCD is in flight or transport-failed. Hostile LCD stays unresolved. LCD decimals only for picker getAllTokens ids. Cache key cl8y-dex-token-info-v2. Max/balance use the same scale. getDecimals still defaults unknown chrome to 6.

Gap: Manual LocalTerra 18-dec unlisted factory pair on / was not run. make has-localterra reports the chain is not running, and this job does not deploy. That leftover stays the manual item already noted on this PR.

No runtime incident, so log reads were not used.

## Implement re-check (job 164a2e8e) No related open PR. Open pulls are renovate digest bumps only (#1312, #1313). Head `issue/1255` is already merged here. No new branch or sibling PR. Re-ran `make verify-issue-1255` on `origin/main` (`0d6eaeee`): 5/5 steps, 71 Vitest. **Covered (Q1255-1–Q1255-8):** Swap and Trade market execute scale via `resolveSwapAssetDecimals` / `useAssetDecimals`. Unknown CW20 is `null`. Precedence is registry, then valid LCD `token_info` (beats a stale indexer 6), then indexer while LCD is in flight or transport-failed. Hostile LCD stays unresolved. LCD decimals only for picker `getAllTokens` ids. Cache key `cl8y-dex-token-info-v2`. Max/balance use the same scale. `getDecimals` still defaults unknown chrome to 6. **Gap:** Manual LocalTerra 18-dec unlisted factory pair on `/` was not run. `make has-localterra` reports the chain is not running, and this job does not deploy. That leftover stays the manual item already noted on this PR. No runtime incident, so log reads were not used.
Author
Owner

Implement re-check (gate-impl)

No related open PR. Open pulls are renovate digest bumps only (#1312, #1313). Head issue/1255 is already merged in this pull. No new branch, commit, or sibling PR.

Re-ran make verify-issue-1255 on origin/main (0d6eaeee): 5/5 steps, 71 Vitest.

Covered (Q1255-1–Q1255-8): Swap / and Trade market execute scale via resolveSwapAssetDecimals / useAssetDecimals. Unknown CW20 is null. Precedence is registry, then valid LCD token_info (beats a stale indexer 6), then indexer while LCD is in flight or transport-failed. Hostile LCD stays unresolved. LCD decimals only for picker getAllTokens ids. Cache key cl8y-dex-token-info-v2. Max/balance use the same scale. getDecimals still defaults unknown chrome to 6.

Gap: Manual LocalTerra 18-dec unlisted factory pair on / was not run. make has-localterra reports the chain is not running, and this job does not deploy. That leftover stays the unchecked manual item on this PR.

No runtime incident, so log reads were not used.

## Implement re-check (gate-impl) No related open PR. Open pulls are renovate digest bumps only (#1312, #1313). Head `issue/1255` is already merged in this pull. No new branch, commit, or sibling PR. Re-ran `make verify-issue-1255` on `origin/main` (`0d6eaeee`): 5/5 steps, 71 Vitest. **Covered (Q1255-1–Q1255-8):** Swap `/` and Trade market execute scale via `resolveSwapAssetDecimals` / `useAssetDecimals`. Unknown CW20 is `null`. Precedence is registry, then valid LCD `token_info` (beats a stale indexer 6), then indexer while LCD is in flight or transport-failed. Hostile LCD stays unresolved. LCD decimals only for picker `getAllTokens` ids. Cache key `cl8y-dex-token-info-v2`. Max/balance use the same scale. `getDecimals` still defaults unknown chrome to 6. **Gap:** Manual LocalTerra 18-dec unlisted factory pair on `/` was not run. `make has-localterra` reports the chain is not running, and this job does not deploy. That leftover stays the unchecked manual item on this PR. No runtime incident, so log reads were not used.
Sign in to join this conversation.
No reviewers
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!1295
No description provided.