From cd50e940dc9345198d453fcb51ed110d574d3b75 Mon Sep 17 00:00:00 2001 From: sneak Date: Mon, 5 Oct 2026 03:11:59 +0000 Subject: [PATCH] test: a token scale of zero decimals is used, not skipped (closes #325) resolveTokenDecimals() treats a scale of 0 from the bundled list or a tracked token as an answer, but nothing tested it: changing either `d !== null` check to a plain truthiness check left every test green while a zero-decimal token fell through to the next source or to "decimals unknown". The approval tests now assert a scale of 0 from each source, both from the resolver and on the approval screen's Amount line. toDecimals() was already shared from transferAmount.js since #349. Model: opus-5-5 --- TODO.md | 11 +++++++++++ tests/approvalAmount.test.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/TODO.md b/TODO.md index 333ef45..e34755e 100644 --- a/TODO.md +++ b/TODO.md @@ -45,6 +45,17 @@ but the review is broader than any of them. # Completed Steps +- 2026-10-05: A token scale of zero decimals is tested + ([#325](https://git.eeqj.de/sneak/AutistMask/issues/325)). + `resolveTokenDecimals()` already used a scale of 0 from the bundled list or + from a tracked token, but no test said so: turning either of its `d !== null` + checks into a plain truthiness check left every test green while a + zero-decimal token fell through to the next source, or to "decimals unknown". + The approval tests now assert a scale of 0 from each of those two sources, + both where it is resolved and on the approval screen's Amount line. The second + half of the issue, one shared `toDecimals()`, had already landed with + [#349](https://git.eeqj.de/sneak/AutistMask/issues/349). + - 2026-10-05: The e2e suite waits for a save to land before it closes the popup ([#446](https://git.eeqj.de/sneak/AutistMask/issues/446)). The Settings round trip switched the theme and the network and closed the popup at once, and a diff --git a/tests/approvalAmount.test.js b/tests/approvalAmount.test.js index 8324c23..bed4b05 100644 --- a/tests/approvalAmount.test.js +++ b/tests/approvalAmount.test.js @@ -31,11 +31,15 @@ const iface = new Interface(ERC20_ABI); const NOVEL_TOKEN = "0xE2E0000000000000000000000000000000000E2e"; // In the bundled list, at 6 decimals. const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; +// In the bundled list, at 0 decimals. +const SLP = "0xCC8Fa225D80b9c7D42F96e9570156c65D6cAAa25"; const RECIPIENT = "0xC0FfEE0000000000000000000000000000c0fFEe"; const SPENDER = "0x1111111111111111111111111111111111111111"; // 5,000 units of a 6-decimal token, the amount from the issue. const FIVE_THOUSAND_AT_SIX = 5000000000n; +// 5,000 units of a 0-decimal token, which are 5,000 tokens. +const FIVE_THOUSAND_AT_ZERO = 5000n; const MAX_UINT256 = (1n << 256n) - 1n; function transferData(amount) { @@ -113,6 +117,21 @@ describe("resolveTokenDecimals", () => { expect(resolveTokenDecimals(NOVEL_TOKEN, state)).toBe(6); }); + // Zero decimals is a real scale, not a missing one, so a source that + // answers 0 is used rather than fallen past like the unusable entry above. + test("uses a bundled scale of zero", () => { + state.trackedTokens = [{ address: SLP, symbol: "SLP", decimals: 18 }]; + expect(resolveTokenDecimals(SLP, state)).toBe(0); + }); + + test("uses a tracked scale of zero", () => { + state.trackedTokens = [ + { address: NOVEL_TOKEN, symbol: "NOVEL", decimals: 0 }, + ]; + state.wallets = walletsHolding(NOVEL_TOKEN, 18); + expect(resolveTokenDecimals(NOVEL_TOKEN, state)).toBe(0); + }); + test("refuses a scale the explorer's own entries disagree about", () => { const wallets = walletsHolding(NOVEL_TOKEN, 6); wallets[0].addresses.push({ @@ -213,6 +232,21 @@ describe("decodeCalldata amount", () => { ); }); + test("a bundled token with zero decimals shows the true quantity", () => { + expect(amountLine(transferData(FIVE_THOUSAND_AT_ZERO), SLP)).toBe( + "5000.0000 SLP", + ); + }); + + test("a tracked token with zero decimals shows the true quantity", () => { + state.trackedTokens = [ + { address: NOVEL_TOKEN, symbol: "NOVEL", decimals: 0 }, + ]; + expect( + amountLine(transferData(FIVE_THOUSAND_AT_ZERO), NOVEL_TOKEN), + ).toBe("5000.0000 NOVEL"); + }); + test("the amount carried to the status screens is the same string", () => { const decoded = decodeCalldata( transferData(FIVE_THOUSAND_AT_SIX),