1 Commits
Author SHA1 Message Date
sneak 67a036fc6f fix: the balance list and the add-token screens apply the spoof rule to tracked tokens (closes #705)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s
The balance list gave a tracked token a zero row while "Show tracked
tokens with zero balance" was on, even when the spoof rule refuses the
token. balanceLinesForAddress() now asks isSpoofedSymbol() before it adds
that row.

AddToken and SettingsAddToken now refuse such a token after the contract
lookup, with a flash message that says why. The bundled-list buttons on
SettingsAddToken need no check, because the rule never refuses a bundled
token.

The README and docs/README.md now say that the balance list drops such a
token whatever the settings.

Model: opus-5-5
2026-11-02 04:03:46 +00:00
4 changed files with 30 additions and 21 deletions

No files matched your search

+20 -18
View File
@@ -1473,20 +1473,21 @@ tracked.
A tracked token is shown when its balance is nonzero, and any other ERC-20 when A tracked token is shown when its balance is nonzero, and any other ERC-20 when
its balance is nonzero and it is in the bundled known-token list or has 1,000 or its balance is nonzero and it is in the bundled known-token list or has 1,000 or
more holders. The balance list always drops a token that claims a bundled-list more holders. A token claiming a symbol from the bundled list from any other
symbol from another contract address, whatever the settings. The list also drops contract address is always dropped, whatever the settings, and so is any token
every token that claims the native asset's symbol: `"ETH"`, or any network's claiming a symbol that belongs to the native asset and therefore has no
`nativeCurrency`, such as `"SepoliaETH"`. That filter is unconditional — the legitimate contract at all (`"ETH"`, and every network's `nativeCurrency`, such
"Hide tokens with fewer than 1,000 holders" setting governs the transaction as `"SepoliaETH"`, on every network). That filter is unconditional — the "Hide
history and the send-screen token selector, not this list. A token's holder tokens with fewer than 1,000 holders" setting governs the transaction history
count is unknown when the explorer reports none, or reports anything other than and the send-screen token selector, not this list. A token's holder count is
a whole number written in digits alone, such as `1,000` or `1e3` unknown when the explorer reports none, or reports anything other than a whole
(`parseHoldersCount()` in `src/shared/holders.js`). This list does not take an number written in digits alone, such as `1,000` or `1e3` (`parseHoldersCount()`
unknown count as 1,000 or more, so such a token is shown only when it is on the in `src/shared/holders.js`). This list does not take an unknown count as 1,000
bundled list or tracked. Every nonzero holding of a token shown is stored or more, so such a token is shown only when it is on the bundled list or
exactly, however small, but a holding below 0.000001 is left out of the balance tracked. Every nonzero holding of a token shown is stored exactly, however
lists, the send-screen token selector, the address total and the remove-address small, but a holding below 0.000001 is left out of the balance lists, the
warning (`isBelowOneMillionth()` in `src/shared/amountDisplay.js`). The Send and send-screen token selector, the address total and the remove-address warning
(`isBelowOneMillionth()` in `src/shared/amountDisplay.js`). The Send and
confirmation screens show it when its token is the one being sent. Tracked confirmation screens show it when its token is the one being sent. Tracked
tokens with a zero balance are listed as well while "Show tracked tokens with tokens with a zero balance are listed as well while "Show tracked tokens with
zero balance" is on. zero balance" is on.
@@ -2238,8 +2239,9 @@ view would leave a wallet one click from deletion.
**AddressDetail** **AddressDetail**
- "Add" with a token already tracked, a scam-listed address, or a failed - "Add" with a token already tracked, a scam-listed address, or a failed
contract lookup → flash message, no screen change contract lookup → flash message, no screen change
- "Add" with a token that the spoof rule refuses → nothing is tracked; "Not - "Add" with a token that the spoof rule refuses → nothing is tracked; "This
added: this token fakes a known symbol." flash message, no screen change is a fake token impersonating a known symbol, so it was not added." flash
message, no screen change
- "Add", then the contract lookup answers while another screen shows, or - "Add", then the contract lookup answers while another screen shows, or
after AddToken was opened again → tracks the token, no screen change after AddToken was opened again → tracks the token, no screen change
- "Add", then a network switch in Settings before the contract lookup - "Add", then a network switch in Settings before the contract lookup
@@ -2549,8 +2551,8 @@ view would leave a wallet one click from deletion.
- A duplicate, a scam-listed address, or a failed contract lookup → flash - A duplicate, a scam-listed address, or a failed contract lookup → flash
message, no screen change message, no screen change
- "Add" by contract address with a token that the spoof rule refuses → - "Add" by contract address with a token that the spoof rule refuses →
nothing is tracked; "Not added: this token fakes a known symbol." flash nothing is tracked; "This is a fake token impersonating a known symbol, so
message, no screen change it was not added." flash message, no screen change
- "Add" by contract address, then a network switch in Settings before the - "Add" by contract address, then a network switch in Settings before the
lookup answers → nothing is tracked; "The active network changed before lookup answers → nothing is tracked; "The active network changed before
the lookup finished, so the token was not added." flash message, no screen the lookup finished, so the token was not added." flash message, no screen
+4 -1
View File
@@ -81,7 +81,10 @@ function init(ctx) {
// The balance list never shows a token that the spoof rule // The balance list never shows a token that the spoof rule
// refuses, so such a token is not tracked. // refuses, so such a token is not tracked.
if (isSpoofedSymbol(info.symbol, contractAddr)) { if (isSpoofedSymbol(info.symbol, contractAddr)) {
showFlash("Not added: this token fakes a known symbol."); showFlash(
"This is a fake token impersonating a known symbol," +
" so it was not added.",
);
if (addTokenScreenOpenings !== opening) return; if (addTokenScreenOpenings !== opening) return;
infoEl.textContent = ""; infoEl.textContent = "";
infoEl.style.visibility = "hidden"; infoEl.style.visibility = "hidden";
+4 -1
View File
@@ -161,7 +161,10 @@ function init(_ctx) {
// a bundled token, so the "Top tokens:" buttons and the top 100 // a bundled token, so the "Top tokens:" buttons and the top 100
// dropdown need no such check. // dropdown need no such check.
if (isSpoofedSymbol(info.symbol, addr)) { if (isSpoofedSymbol(info.symbol, addr)) {
showFlash("Not added: this token fakes a known symbol."); showFlash(
"This is a fake token impersonating a known symbol," +
" so it was not added.",
);
infoEl.textContent = ""; infoEl.textContent = "";
infoEl.style.visibility = "hidden"; infoEl.style.visibility = "hidden";
return; return;
+2 -1
View File
@@ -25,7 +25,8 @@ const TEST_TOKEN = {
decimals: 18, decimals: 18,
name: "Test Token", name: "Test Token",
}; };
const REFUSED_FLASH = "Not added: this token fakes a known symbol."; const REFUSED_FLASH =
"This is a fake token impersonating a known symbol, so it was not added.";
afterEach(() => { afterEach(() => {
cleanupPopup(); cleanupPopup();