harden: a token scale above 80 decimal places is refused as unknown (closes #350)
check / check (push) Failing after 2s
e2e / e2e-chrome (push) Failing after 3s
e2e / e2e-firefox (push) Failing after 2s

toDecimals() accepted any uint8 scale, but formatUnits() and parseUnits()
refuse more than 80 decimal places. A token reporting 81 to 255 made the
formatter throw, and the catch in the swap decoder and in the ERC-20 decoder
turned that into an undecoded approval screen with nothing saying why.

MAX_DECIMALS is now 80, the formatter's own limit, so such a scale is
treated exactly like an unknown one: both approval paths show the base-unit
amount with the scale stated as unknown. The balance list, the history list
and the Send screen use the same check.

Model: opus-5-5
This commit was merged in pull request #438.
This commit is contained in:
2026-10-04 21:43:11 +02:00
parent 8ac2c87c2c
commit 3b713809c8
7 changed files with 65 additions and 12 deletions
+12 -2
View File
@@ -4,7 +4,7 @@
// contract at signing time, with nothing comparing the two, so a token whose
// on-chain scale differed signed an amount that was never displayed.
const { parseUnits } = require("ethers");
const { formatUnits, parseUnits } = require("ethers");
const {
displayedDecimals,
transferAmountUnits,
@@ -25,7 +25,17 @@ describe("displayedDecimals", () => {
expect(displayedDecimals(MAX_DECIMALS)).toBe(MAX_DECIMALS);
});
test("refuses anything that is not a uint8", () => {
// decimals() is a uint8, but formatUnits() and parseUnits() stop at 80
// places, so a larger scale cannot be shown or encoded
// (https://git.eeqj.de/sneak/AutistMask/issues/350).
test("accepts exactly the scales the formatter accepts", () => {
expect(() => formatUnits(1n, MAX_DECIMALS)).not.toThrow();
expect(() => parseUnits("1", MAX_DECIMALS)).not.toThrow();
expect(() => formatUnits(1n, MAX_DECIMALS + 1)).toThrow();
expect(() => parseUnits("1", MAX_DECIMALS + 1)).toThrow();
});
test("refuses anything that is not a uint8 the formatter accepts", () => {
for (const bad of [
null,
undefined,