fix: store an absent explorer decimals as unknown instead of fabricating 18 (closes #349)
parseInt(decimals || "18") ran before writing stored tokenBalances[].decimals, so an explorer reporting no decimals produced a fabricated 18 indistinguishable from a real one at read time. That defeated the resolve-or-refuse guarantees of #306 and #340: their refusal paths were intact but never fired, because the guess was laundered upstream of them. An absent scale is now stored as unknown, and a holding whose scale nothing knows carries a null balance -- unknown, never zero -- with six reader sites saying so rather than printing 0.0000. The Send screen resolves the display scale rather than reading the stored one, so a bundled token whose explorer row omits decimals still sends; when the scale cannot be resolved the stored quantity is withdrawn too, so the user is told the balance is unknown rather than only that the fee failed. Existing fabricated 18s cannot be told apart retroactively and are replaced wholesale on the next balance refresh. An explorer-sourced scale stays trusted -- only fabrication is removed; the reasoning is recorded on the issue.
This commit was merged in pull request #367.
This commit is contained in:
40
TODO.md
40
TODO.md
@@ -122,6 +122,46 @@ but the review is broader than any of them.
|
||||
`src/shared/restorableViews.js`, since `persistedState.js` requires it and
|
||||
that module is in the background bundle.
|
||||
|
||||
- 2026-08-23: An explorer that reports no `decimals` for a token no longer has a
|
||||
scale invented for it before storage
|
||||
([#349](https://git.eeqj.de/sneak/AutistMask/issues/349)).
|
||||
`fetchTokenBalances()` did `parseInt(item.token.decimals || "18", 10)` on the
|
||||
way in, so a token whose `decimals()` reverts was written to
|
||||
`tokenBalances[].decimals` as a fabricated `18` that no reader could tell from
|
||||
a real one. That is upstream of the resolve-or-refuse rule
|
||||
([#306](https://git.eeqj.de/sneak/AutistMask/issues/306),
|
||||
[#340](https://git.eeqj.de/sneak/AutistMask/issues/340)): both approval paths
|
||||
read this stored value as an authoritative source, so the guess walked past
|
||||
refusals that were intact and simply never fired. The stored value is now the
|
||||
explorer's own answer or `null`, and both the ERC-20 amount line and the swap
|
||||
lines reach `unknownDecimalsAmount()` on it. The history list's token
|
||||
transfers carried the same `|| "18"` and now state base units with the scale
|
||||
unknown rather than a quantity. A holding whose scale nothing knows carries
|
||||
`balance: null` — unknown, not zero — and the balance list, the USD total, the
|
||||
Send screen and the confirmation screen each say so instead of printing
|
||||
`0.0000` for money that is really there. The uint8 check is one shared
|
||||
`toDecimals()` rather than three copies, and it answers `0` for a real scale
|
||||
of zero: `|| "18"` collapsed that to eighteen, the trap of
|
||||
[#246](https://git.eeqj.de/sneak/AutistMask/issues/246). Existing installs
|
||||
hold `18`s that cannot be told apart retroactively; they display exactly as
|
||||
they do today until the next balance refresh, which rewrites `tokenBalances`
|
||||
wholesale and needs no user action. No `|| 18` or `?? 18` fallback remains
|
||||
anywhere in `src/`; the literal `18`s that do remain are real data, not
|
||||
defaults — 432 per-token `decimals: 18` entries in the bundled
|
||||
`src/shared/tokenList.js`, and, outside that file, only native ETH's
|
||||
protocol-defined scale in `src/shared/uniswap.js` and the fixed-point
|
||||
comparison scale in `src/shared/txValidation.js`. `tokenBalances[].decimals`
|
||||
is the explorer's answer alone and not the scale a screen renders at, so the
|
||||
Send screen resolves through `resolveTokenDecimals()` like every other
|
||||
consumer: reading the stored field raw carried a `null` into `estimateGas()`
|
||||
for a bundled token such as WETH, which reported an unestimable network fee
|
||||
and left Send disabled behind a message no retry could clear. Send resolves
|
||||
with `wallets`, which adds the cross-address disagreement check the balance
|
||||
list does not make, so the two can differ; where they do, the stored quantity
|
||||
was computed at a scale Send has refused, and it is withdrawn with it. An
|
||||
unknown scale is an unknown balance, and the user is told that rather than
|
||||
that the fee could not be estimated.
|
||||
|
||||
- 2026-08-23: The background no longer reads or writes the shared `state`
|
||||
singleton ([#324](https://git.eeqj.de/sneak/AutistMask/issues/324)), which
|
||||
also closes the cold-worker wrong-chain send
|
||||
|
||||
Reference in New Issue
Block a user