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
This commit is contained in:
@@ -45,6 +45,17 @@ but the review is broader than any of them.
|
|||||||
|
|
||||||
# Completed Steps
|
# 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
|
- 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
|
([#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
|
trip switched the theme and the network and closed the popup at once, and a
|
||||||
|
|||||||
@@ -31,11 +31,15 @@ const iface = new Interface(ERC20_ABI);
|
|||||||
const NOVEL_TOKEN = "0xE2E0000000000000000000000000000000000E2e";
|
const NOVEL_TOKEN = "0xE2E0000000000000000000000000000000000E2e";
|
||||||
// In the bundled list, at 6 decimals.
|
// In the bundled list, at 6 decimals.
|
||||||
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
|
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
|
||||||
|
// In the bundled list, at 0 decimals.
|
||||||
|
const SLP = "0xCC8Fa225D80b9c7D42F96e9570156c65D6cAAa25";
|
||||||
const RECIPIENT = "0xC0FfEE0000000000000000000000000000c0fFEe";
|
const RECIPIENT = "0xC0FfEE0000000000000000000000000000c0fFEe";
|
||||||
const SPENDER = "0x1111111111111111111111111111111111111111";
|
const SPENDER = "0x1111111111111111111111111111111111111111";
|
||||||
|
|
||||||
// 5,000 units of a 6-decimal token, the amount from the issue.
|
// 5,000 units of a 6-decimal token, the amount from the issue.
|
||||||
const FIVE_THOUSAND_AT_SIX = 5000000000n;
|
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;
|
const MAX_UINT256 = (1n << 256n) - 1n;
|
||||||
|
|
||||||
function transferData(amount) {
|
function transferData(amount) {
|
||||||
@@ -113,6 +117,21 @@ describe("resolveTokenDecimals", () => {
|
|||||||
expect(resolveTokenDecimals(NOVEL_TOKEN, state)).toBe(6);
|
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", () => {
|
test("refuses a scale the explorer's own entries disagree about", () => {
|
||||||
const wallets = walletsHolding(NOVEL_TOKEN, 6);
|
const wallets = walletsHolding(NOVEL_TOKEN, 6);
|
||||||
wallets[0].addresses.push({
|
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", () => {
|
test("the amount carried to the status screens is the same string", () => {
|
||||||
const decoded = decodeCalldata(
|
const decoded = decodeCalldata(
|
||||||
transferData(FIVE_THOUSAND_AT_SIX),
|
transferData(FIVE_THOUSAND_AT_SIX),
|
||||||
|
|||||||
Reference in New Issue
Block a user