fix: Send refuses every amount the confirmation screen would refuse (closes #532)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s

Send accepted any amount parseFloat() read as above zero, so "1e-3",
"1abc" and an amount with more decimal places than the token has reached
ConfirmTx, which then refused them. isValidAmount() in
src/shared/txValidation.js is now the one check: Review and Max on Send
call it with the token's decimal places, and validateTransfer() calls it
for ConfirmTx, which now also refuses a zero amount and a token amount
with more places than the token has.

Model: opus-5-5
This commit is contained in:
2026-10-18 18:40:36 +00:00
parent e48308c5b6
commit 1452d3c4d7
6 changed files with 337 additions and 33 deletions
+58
View File
@@ -6,6 +6,7 @@ const {
FEE_UNAVAILABLE,
feeReserveWei,
feeEstimateWei,
isValidAmount,
maxEthAmount,
maxTokenAmount,
toFixedPoint,
@@ -33,6 +34,42 @@ describe("toFixedPoint", () => {
});
});
// The one amount check the Send screen and the confirmation screen share.
// The screens themselves are driven in tests/sendAmountRule.test.js.
describe("isValidAmount", () => {
test("accepts a number above zero written as digits and a decimal point", () => {
expect(isValidAmount("0.5")).toBe(true);
expect(isValidAmount("12")).toBe(true);
expect(isValidAmount(".5")).toBe(true);
expect(isValidAmount("0.000000000000000001")).toBe(true);
});
test("refuses an exponent, trailing text, zero and a negative amount", () => {
expect(isValidAmount("1e-3")).toBe(false);
expect(isValidAmount("1abc")).toBe(false);
expect(isValidAmount("0")).toBe(false);
expect(isValidAmount("0.0")).toBe(false);
expect(isValidAmount("-1")).toBe(false);
expect(isValidAmount("")).toBe(false);
expect(isValidAmount(undefined)).toBe(false);
});
test("refuses more decimal places than the token has", () => {
expect(isValidAmount("0.000001", 6)).toBe(true);
expect(isValidAmount("0.0000001", 6)).toBe(false);
expect(isValidAmount("5", 0)).toBe(true);
expect(isValidAmount("0.5", 0)).toBe(false);
});
test("holds ETH, a token of unknown places, and any token to 18 places", () => {
const nineteen = "0.0000000000000000001";
expect(isValidAmount(nineteen)).toBe(false);
expect(isValidAmount(nineteen, null)).toBe(false);
expect(isValidAmount(nineteen, 24)).toBe(false);
expect(isValidAmount("0.000000000000000001", 24)).toBe(true);
});
});
describe("validateTransfer, native ETH", () => {
const eth = (over) => ({
isErc20: false,
@@ -134,6 +171,17 @@ describe("validateTransfer, native ETH", () => {
).toEqual({ canSend: false, codes: [CODES.AMOUNT_INVALID] });
});
test("rejects a zero amount and an exponent", () => {
expect(validateTransfer(eth({ amount: "0" }))).toEqual({
canSend: false,
codes: [CODES.AMOUNT_INVALID],
});
expect(validateTransfer(eth({ amount: "1e-3" }))).toEqual({
canSend: false,
codes: [CODES.AMOUNT_INVALID],
});
});
test("treats a missing balance as zero, not as unlimited", () => {
const r = validateTransfer(eth({ ethBalance: undefined }));
expect(r.codes).toEqual([CODES.INSUFFICIENT_ETH]);
@@ -205,6 +253,16 @@ describe("validateTransfer, ERC-20", () => {
expect(r).toEqual({ canSend: false, codes: [CODES.AMOUNT_INVALID] });
});
test("rejects more decimal places than the token has", () => {
const r = validateTransfer(
erc20({ amount: "0.0000001", tokenDecimals: 6 }),
);
expect(r).toEqual({ canSend: false, codes: [CODES.AMOUNT_INVALID] });
expect(
validateTransfer(erc20({ amount: "0.000001", tokenDecimals: 6 })),
).toEqual({ canSend: true, codes: [] });
});
test("treats a missing token balance as zero", () => {
const r = validateTransfer(erc20({ tokenBalance: undefined }));
expect(r.codes).toEqual([CODES.INSUFFICIENT_TOKEN]);