fix: filter a fake ETH token from the balance list too (closes #235)
All checks were successful
check / check (push) Successful in 26s
All checks were successful
check / check (push) Successful in 26s
KNOWN_SYMBOLS maps "ETH" to null, and the three surfaces that show tokens disagreed about what that means. The transaction history and the Send token selector read it as "no contract may bear this symbol" and filtered a fake ETH ERC-20; the balance list's guard required a non-null mapping, so the same token was listed as a holding named ETH next to the user's real ETH. That is the surface where the user forms their belief about what they own. The rule now lives in src/shared/symbolSpoof.js and all three sites call it, so a fourth reading is not available to a future call site. A symbol mapped to null belongs to the native asset and may be borne by no contract at all; the native exemption is "has no contract address", not "the symbol is ETH", so a second null-mapped entry needs no call-site change. The user's real ETH balance is untouched: it is read over RPC in refreshBalances and never enters fetchTokenBalances, whose loop only considers explorer rows of type ERC-20. tests/symbolSpoof.test.js drives the same fake ETH token through all three surfaces plus refreshBalances, which reports the native balance unchanged while the fake token is gone. Its two balance-list cases were watched failing against the unmodified call sites first.
This commit is contained in:
42
README.md
42
README.md
@@ -221,6 +221,7 @@ src/
|
||||
prices.js — ETH/USD and token/USD via CoinDesk API
|
||||
scamlist.js — known fraud contract addresses
|
||||
state.js — persisted state (extension storage)
|
||||
symbolSpoof.js — the known-symbol spoof rule, shared by all surfaces
|
||||
tokenList.js — top ERC-20 tokens by market cap (hardcoded)
|
||||
transactions.js — tx history fetching + anti-poisoning filters
|
||||
uniswap.js — Uniswap Universal Router calldata decoder
|
||||
@@ -450,11 +451,12 @@ Which tokens an address shows is decided by `fetchTokenBalances()` in
|
||||
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. 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.
|
||||
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.
|
||||
|
||||
#### Navigation
|
||||
|
||||
@@ -1246,14 +1248,15 @@ indexes it as a real token transfer.
|
||||
that is the only thing that populates it. In the transaction history the check
|
||||
is the "Hide fake tokens impersonating a known symbol" setting, on by default;
|
||||
with it off, spoofed transfers are shown and no new blocklist entries are
|
||||
learned from them. The send-screen token selector applies the same check
|
||||
unconditionally, because it decides which tokens the user can act on rather
|
||||
than what the history displays. The balance list applies it unconditionally
|
||||
too, but not identically: it exempts symbols that `KNOWN_SYMBOLS` maps to
|
||||
`null`, and `"ETH"` is the only one. So the fake "Ethereum" token above is
|
||||
filtered from the transaction history and from the send selector, but a
|
||||
fake-`ETH` ERC-20 that clears the balance list's own 1,000-holder floor — or
|
||||
that the user tracked manually — is still shown in the balance list.
|
||||
learned from them. The send-screen token selector and the balance list apply
|
||||
the same check unconditionally, because they decide which tokens the user can
|
||||
act on and what the user believes they own rather than what the history
|
||||
displays. All three surfaces read the rule from `src/shared/symbolSpoof.js`,
|
||||
so they cannot answer the question differently. A symbol the list maps to no
|
||||
contract at all — `"ETH"`, the native asset, is the only one — may be borne by
|
||||
no contract, so every ERC-20 claiming it is a spoof on all three. The user's
|
||||
real ETH balance is not an ERC-20 and is read over RPC, so the rule never sees
|
||||
it.
|
||||
|
||||
- **Low-holder token filtering**: Token transfers from ERC-20 contracts with
|
||||
fewer than 1,000 holders are hidden from transaction history by default.
|
||||
@@ -1289,13 +1292,12 @@ indexes it as a real token transfer.
|
||||
a sharp tool — users who understand the risks can configure the wallet to show
|
||||
everything unfiltered, unix-style. All four settings govern the transaction
|
||||
history; what else each one reaches varies. The known-symbol check also runs
|
||||
unconditionally on the send-screen token selector, and on the balance list
|
||||
except for symbols mapped to `null` (`"ETH"` alone), which the balance list
|
||||
does not filter. The fraud contract blocklist is applied unconditionally on
|
||||
that selector and is not consulted by the balance list at all. The low-holder
|
||||
setting also gates the send selector, while the balance list's own
|
||||
1,000-holder floor is unconditional (see Data Model). The dust threshold
|
||||
applies to the transaction history alone.
|
||||
unconditionally on the send-screen token selector and on the balance list, in
|
||||
both cases identically to the history. The fraud contract blocklist is applied
|
||||
unconditionally on that selector and is not consulted by the balance list at
|
||||
all. The low-holder setting also gates the send selector, while the balance
|
||||
list's own 1,000-holder floor is unconditional (see Data Model). The dust
|
||||
threshold applies to the transaction history alone.
|
||||
|
||||
#### Phishing Domain Protection
|
||||
|
||||
|
||||
Reference in New Issue
Block a user