fix: show balances and fees below 0.000001 as nonzero on the send screens (closes #343)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run

The stored ETH and token balances and the send-confirm screen's fee were each
cut to six decimal places, so a value below 0.000001 read as zero. Balances are
now stored exactly, whatever decimals a token declares; the balance check reads
a token balance to its first 18 places, the most an amount can have. A token
holding below 0.000001 is still left off the lists as dust, except for a token
the user tracks. The send and send-confirm screens' balances, reserve and
insufficient-balance messages go through truncateAmountNeverZero(). The
send-confirm and approval screens both render the fee through formatFee(),
which prices the exact fee in USD. The balance lists still round with
toFixed(4).

Model: opus-5-5
This commit is contained in:
2026-10-04 05:50:41 +00:00
committed by sneak
parent 49a7da87e8
commit 504a25dead
11 changed files with 531 additions and 83 deletions
+26 -13
View File
@@ -892,12 +892,21 @@ shows is a V4 exact-in `amountIn` of zero.
The rule and its exception live in `src/shared/amountDisplay.js` as
`truncateAmount()` and `truncateAmountNeverZero()`. Everything the approval and
confirmation screens display goes through the floored one — the ERC-20 amount,
the ETH value and max fee (`src/popup/views/approval.js`), and the swap's
`Amount` and `Min. received` lines (`src/shared/uniswap.js`). The history and
balance lists (`src/shared/transactions.js`) use the unfloored one: the
transaction detail view is the authoritative record and already shows exact
precision. The 4-decimal rule is unchanged everywhere else, including for
amounts at or above the floor on the approval screens.
the ETH value and max fee (`src/popup/views/approval.js`), the swap's `Amount`
and `Min. received` lines (`src/shared/uniswap.js`), the Send screen's
`Current balance` (`src/popup/views/send.js`), and the balance and network fee
on the confirmation screen for the wallet's own send
(`src/popup/views/confirmTx.js`). Both screens render a network fee through
`formatFee()` in `src/popup/views/helpers.js`, which prices the exact fee in USD
rather than its truncated figure, so the same fee reads the same on both, USD
value included. Balances are stored exactly (`src/shared/balances.js`), whatever
decimals a token declares, so a balance below the floor reaches these screens as
it is. The history list (`src/shared/transactions.js`) uses the unfloored one:
the transaction detail view is the authoritative record and already shows exact
precision. The balance lists use neither: they round to four places with
`toFixed(4)` (`balanceLine()` in `src/popup/views/helpers.js`). The 4-decimal
rule is unchanged everywhere else, including for amounts at or above the floor
on the approval screens.
The floor applies only where the token's scale is known. Where it is not, the
approval screen states base units instead of a quantity — see Unknown token
@@ -1050,13 +1059,17 @@ Which tokens an address shows is decided by `fetchTokenBalances()` in
`src/shared/balances.js`, from the Blockscout `token-balances` response, so
tokens do appear without the user adding them. An ERC-20 is shown when its
balance is nonzero and it is in the bundled known-token list, is tracked by the
user, or has 1,000 or more holders; a token claiming a symbol from the bundled
list from any other contract address is always dropped, and so is any token
claiming a symbol that belongs to the native asset and therefore has no
legitimate contract at all (`"ETH"`). That filter is unconditional — the "Hide
tokens with fewer than 1,000 holders" setting governs the transaction history
and the send-screen token selector, not this list. Tracked tokens with a zero
balance are listed as well while "Show tracked tokens with zero balance" is on.
user, or has 1,000 or more holders. A holding below 0.000001 of a token the user
does not track is dust and is not shown; a tracked token's holding is shown
whatever its size, also while "Show tracked tokens with zero balance" is off,
and one too small for the list's four decimal places reads `0.0000`. A token
claiming a symbol from the bundled list from any other contract address is
always dropped, and so is any token claiming a symbol that belongs to the native
asset and therefore has no legitimate contract at all (`"ETH"`). That filter is
unconditional — the "Hide tokens with fewer than 1,000 holders" setting governs
the transaction history and the send-screen token selector, not this list.
Tracked tokens with a zero balance are listed as well while "Show tracked tokens
with zero balance" is on.
#### Stored state and its version