// Holder counts, and the one rule that decides whether a count is "low". // // The block explorer's holders_count is optional: it is absent on a token it // has only just indexed, and it goes missing on a degraded or changed API. // Absent means the count is unknown. It does not mean the token has no // holders, and collapsing the two hides a token the user really holds as if // it were spam. Every call site reads the count through here so the // distinction cannot be lost again in one place while holding in the others. const LOW_HOLDER_THRESHOLD = 1000; // Parse an explorer-supplied holders_count into a number, or null when it is // not one. Only a whole number of zero or more, or a string made of nothing // but the digits 0-9, is a count. Anything else is null, never read in part: // "1,000", "0x10" and "1e3" are unknown, not 1, 0 and 1, because a count we // cannot read is not a low count. A count above Number.MAX_SAFE_INTEGER is // null too: a number cannot hold it exactly, so it would come back rounded, // or as Infinity. function parseHoldersCount(raw) { if (typeof raw === "number") { return Number.isSafeInteger(raw) && raw >= 0 ? raw : null; } if (typeof raw === "string" && /^[0-9]+$/.test(raw)) { const count = Number(raw); return Number.isSafeInteger(count) ? count : null; } return null; } // True only for a token the explorer reported as having fewer holders than // the threshold. An unknown count is never low: showing a spam token the // user can see is unusual costs less than hiding an asset they own. function isLowHolderCount(holders) { return holders != null && holders < LOW_HOLDER_THRESHOLD; } module.exports = { LOW_HOLDER_THRESHOLD, parseHoldersCount, isLowHolderCount, };