fix: an omitted holders_count is coerced to 0, so a legitimate token gets filtered as spam #230
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
src/shared/transactions.js:112readsholders_count || "0". When the block explorer omits or nulls that field — a fetch hiccup, a newly indexed token, an API change — the token is recorded as having zero holders rather than an unknown count, and the low-holder filter then hides it as spam.Two consequences, and the second is the more insidious:
tx.holders !== nullguard dead code for token transfers. That guard exists precisely to avoid filtering on an unknown holder count, and the|| "0"coercion upstream guarantees it can never fire.The same pattern appears at
src/popup/views/send.js:135((t.holders || 0) < 1000), where an unknown count likewise reads as zero and the token is withheld from the send selector — so the user cannot spend a token they hold.Found while fixing #179 and deliberately left out of its scope, since it may have been intentional.
Implementation requirements
parseTokenTransfermust emitholders: nullwhen the explorer omits or nullsholders_count, distinguishing "unknown" from "zero".send.js:135; a token the user holds must not vanish from the send selector because of an upstream omission.|| 0/|| "0"coercions on values where absent and zero mean different things, and cover them or rule them out in the PR body.Definition of done
holders_countyieldsholders: null, not0.holders !== nullguard demonstrably fires for such a transfer — with a test that fails against the current code.TODO.mdupdated in the same commit.make checkpasses.Implemented in #244.
The null-versus-zero rule and the 1,000-holder threshold now live in one place,
src/shared/holders.js, since the rule was open-coded at three call sites and wrong at all three.parseTokenTransferemitsholders: nullfor an omitted, null or unparseable count; the history filter andrenderSendTokenSelectboth judge only a reported count, which makes theholders !== nullguard live.Decision on an unknown count: shown in the two user-facing low-holder filters (both sit behind the setting, and an unspendable token is worse than a visible spam row). Still excluded in the balance-list spam gate in
fetchTokenBalances— that gate has no off switch and decides the whole balance list, and missing metadata correlates with newly-indexed tokens, i.e. spam airdrops. It now records the unknown asnullrather than0, so a token that reaches the list by being known or tracked is no longer hidden downstream by a zero it never reported. Rationale in full in the PR body.Verified: the three history tests and two send-selector tests were written first and watched fail on unmodified
next; the reported-zero cases pass before and after, so the anti-spam path is pinned in both directions.make checkgreen on the rebased branch — 16 suites, 388 tests,prettier --checkclean.