Commit Graph

3 Commits

Author SHA1 Message Date
12190ba428 fix: store an absent explorer decimals as unknown instead of fabricating 18 (closes #349)
All checks were successful
check / check (push) Successful in 34s
e2e / e2e-chrome (push) Successful in 1m45s
e2e / e2e-firefox (push) Successful in 29s
fetchTokenBalances() did parseInt(item.token.decimals || "18", 10) before writing to state.wallets[].addresses[].tokenBalances[].decimals, so a token whose decimals() reverts -- one the block explorer reports no scale for -- was stored with a fabricated 18 that no reader could tell from a real one.

That is upstream of a rule already merged. #306 made the ERC-20 approval amount line resolve the real scale or refuse to format, and #340 extended it to the swap lines; both read this stored value as an authoritative source, so the guess walked straight past refusals that were intact and simply never fired. A 1,000-unit approval of such a token rendered 0.000000001 on the one screen whose job is to state what is being authorized.

The stored value is now the explorer's own answer or null, never a default. Both approval paths reach unknownDecimalsAmount() on a null, using the refusal that was already there. The history list's token transfers carried the same || "18" and now state exact base units with the scale unknown rather than a quantity at a guessed one.

A holding whose scale nothing knows has no quantity either, so its balance is stored as null -- unknown, never zero -- and the balance list, the address USD total, the Send screen and the confirmation screen each say so rather than printing 0.0000 for money that is really there. The zero-balance filter moved onto the base-unit integer, where it needs no scale at all. The bundled token list and the user's tracked tokens already outrank the explorer, so a token either of them knows still displays its real quantity when the explorer's entry omits decimals; only what none of the three knows is unknown.

The uint8 check is one shared toDecimals() rather than three copies of it, and it answers 0 for a real scale of zero: || "18" collapsed that to eighteen, the falsy-collapse trap of #246.

Existing installs hold 18s that cannot be told apart retroactively -- that is the defect, and no migration can undo it. They display exactly as they do today until the next balance refresh, which rewrites tokenBalances wholesale and needs no user action. The schema version is not bumped: version 1 records stay valid and are read exactly as before.

The only 18s left in src/ are native ETH's real scale in uniswap.js and the fixed-point comparison scale in txValidation.js.
2026-08-23 18:23:44 +00:00
43784cab3f harden: resolve or refuse the swap token scale instead of guessing 18 (closes #340)
All checks were successful
check / check (push) Successful in 29s
e2e / e2e-chrome (push) Successful in 1m47s
e2e / e2e-firefox (push) Successful in 31s
tokenInfo() returned decimals 18 for any token absent from the bundled list, so the swap approval line rendered a real 1000.00 of a 6-decimal token as 0.000000000001. The scale is now resolved from what the wallet already holds (bundled list, tracked tokens, explorer-reported decimals) or refused outright, matching the rule set for the ERC-20 path in #306. A refusal reuses unknownDecimalsAmount(), so it reads as "base units (decimals unknown)" with no decimal point and no symbol, and the same string propagates to rawValue so no downstream screen can render a figure the approval screen refused. No new network call on the approval path. Verified green on all three CI contexts: check, e2e-chrome, e2e-firefox.
2026-08-23 16:20:22 +02:00
50078b3566 fix: resolve approval-screen token decimals, and refuse to format an unknown scale (closes #306)
All checks were successful
check / check (push) Successful in 34s
e2e / e2e-chrome (push) Successful in 1m13s
e2e / e2e-firefox (push) Successful in 28s
decodeCalldata consulted only the 512-entry bundled list and defaulted to 18
decimals, so a transfer of 5,000 units of a 6-decimal token rendered
"Amount 0.0000" and the user confirmed a drain reading zero. The same
understatement applied to approve, where an unbounded allowance also rendered
0.0000.

Decimals now resolve from the bundled list, then trackedTokens, then the
address's explorer-reported entry, with uint8 validation and a refusal when
sources for one contract disagree. When no source knows the scale, no
formatUnits call is reached at all: the line renders raw base units with an
explicit "decimals unknown" warning, and the same string reaches
pendingTxDetails.amount so the status screens carry no formatted figure either.

Verified failing first two independent ways: restoring the old
`token ? token.decimals : 18` fails 6 of 15 new tests with the unknown case
reporting "0.0000"; making the resolver return 18 rather than null on the
unknown path fails a different 6, spanning resolver and render levels.
2026-08-20 12:57:38 +02:00