Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee24afafd1 |
@@ -884,16 +884,12 @@ instead: `0.000000000000000001 DAI`, not `0.0000 DAI`. The test is on the whole
|
||||
truncated string, integer part included, so `1.00005` still shows as `1.0000` —
|
||||
the exception only fires where the entire displayed figure would read as zero.
|
||||
Truncation stays truncation: `0.99999` shows as `0.9999`, never rounded up. A
|
||||
genuine zero reaching this rule renders `0.0000`. The ERC-20
|
||||
`approve`/`transfer` amount does exactly that, and so does the swap's `Amount`
|
||||
line for a literal-zero `amountIn` on a V2 or V3 exact-in swap, or a zero
|
||||
`WRAP_ETH` (`0.0000 ETH`). Only two zeros are stated in words before the floor:
|
||||
the swap's `Min. received` line reads `None (no minimum guaranteed)` for any
|
||||
zero minimum, and its `Amount` line reads `All available (V4 open delta)` for a
|
||||
V4 exact-in `amountIn` of zero, which V4 treats as the whole open credit rather
|
||||
than a quantity. So the guarantee that a zero is never shown as `0.0000` covers
|
||||
the `Min. received` line and the V4 exact-in `Amount`; a V2/V3 or `WRAP_ETH`
|
||||
`Amount` still renders it (see the list of amount-slot strings below).
|
||||
genuine zero reaching this rule renders `0.0000`, and the ERC-20
|
||||
`approve`/`transfer` amount does exactly that. The swap's `Amount` and
|
||||
`Min. received` lines never hand it one: the two zeros that would mislead there
|
||||
— a V4 open-delta amount and a zero minimum — are stated in words before the
|
||||
floor is reached, so on those lines a zero is never shown as `0.0000` (see the
|
||||
list of amount-slot strings below).
|
||||
|
||||
The rule and its exception live in `src/shared/amountDisplay.js` as
|
||||
`truncateAmount()` and `truncateAmountNeverZero()`. Everything the approval and
|
||||
|
||||
@@ -132,32 +132,18 @@ but the review is broader than any of them.
|
||||
constant rather than `isDebug()`, so it survives only in a debug build; a
|
||||
testnet or the runtime debug toggle still raises the banner but without the
|
||||
view id.
|
||||
|
||||
- 2026-09-21: The Confirm Delete button on the delete-wallet screen no longer
|
||||
stays dead after a successful delete
|
||||
([#335](https://git.eeqj.de/sneak/AutistMask/issues/335)). The password route
|
||||
disabled the button before the decrypt and never re-enabled it, so a second
|
||||
delete in the same popup session needed a reopen; the lost-password route
|
||||
re-enabled its own button in its leave hook, so the two screens behaved
|
||||
differently. Both now reset through the shared `finishDelete()`, the one path
|
||||
both routes take, so they behave the same and the button is live for the next
|
||||
delete.
|
||||
|
||||
- 2026-09-21: `README.md` now documents the approval screen's amount-slot
|
||||
vocabulary and no longer contradicts itself
|
||||
([#369](https://git.eeqj.de/sneak/AutistMask/issues/369)). The stale claim
|
||||
that a genuine zero still renders `0.0000` is corrected: it holds for the
|
||||
ERC-20 amount, and also for the swap's `Amount` line on a literal-zero
|
||||
`amountIn` in a V2/V3 exact-in swap or a zero `WRAP_ETH`. Only two zeros are
|
||||
stated in words upstream — the swap's `Min. received` (any zero minimum) and
|
||||
its V4 exact-in `Amount` (an `amountIn` of zero, V4's open delta). The
|
||||
amount-display section now names every string a slot can show — a formatted
|
||||
quantity, `Unlimited`, `All available (V4 open delta)`,
|
||||
`None (no minimum guaranteed)`, base units with decimals unknown, and
|
||||
`Unknown (not named in the calldata)` — and records that a zero `minBalance`
|
||||
on a `BALANCE_CHECK_ERC20` step now reads `None (no minimum guaranteed)` where
|
||||
it once read `0.0000`. Docs only; each claim checked against the tree.
|
||||
|
||||
ERC-20 amount, but the swap's `Amount` and `Min. received` state their two
|
||||
misleading zeros in words upstream. The amount-display section now names every
|
||||
string a slot can show — a formatted quantity, `Unlimited`,
|
||||
`All available (V4 open delta)`, `None (no minimum guaranteed)`, base units
|
||||
with decimals unknown, and `Unknown (not named in the calldata)` — and records
|
||||
that a zero `minBalance` on a `BALANCE_CHECK_ERC20` step now reads
|
||||
`None (no minimum guaranteed)` where it once read `0.0000`. Docs only; each
|
||||
claim checked against the tree.
|
||||
- 2026-08-30: An address no longer wraps, or is shortened to fit, in any of the
|
||||
common views ([#380](https://git.eeqj.de/sneak/AutistMask/issues/380)). The
|
||||
wallet list was the reported case: the address shared one row with the
|
||||
|
||||
@@ -51,12 +51,16 @@ function clear() {
|
||||
// The lost-password screen holds no secret — a wallet name is not one —
|
||||
// but it is wiped on leave for the neighbouring reason: a typed
|
||||
// confirmation left standing in a hidden view is one click away from
|
||||
// destroying a wallet the user has since navigated off.
|
||||
// destroying a wallet the user has since navigated off. The button is
|
||||
// re-enabled here too, so a screen left mid-delete is usable on re-entry.
|
||||
function clearLostPassword() {
|
||||
lostPasswordIndex = null;
|
||||
$("delete-wallet-lost-name-input").value = "";
|
||||
$("delete-wallet-lost-flash").textContent = "";
|
||||
$("delete-wallet-lost-flash").style.visibility = "hidden";
|
||||
const btn = $("btn-delete-wallet-lost-confirm");
|
||||
btn.disabled = false;
|
||||
btn.classList.remove("text-muted");
|
||||
}
|
||||
|
||||
function show(walletIdx) {
|
||||
@@ -94,17 +98,6 @@ function showLostPassword() {
|
||||
// cleanup and the accountsChanged broadcast cannot drift apart between
|
||||
// them.
|
||||
async function finishDelete(walletIdx) {
|
||||
// Each route's confirm button was disabled by its own click handler
|
||||
// before the delete ran. Re-enable both here, on the one path they
|
||||
// share, so the two routes reset the same way and a second delete in
|
||||
// the same popup session finds a live button instead of a dead one.
|
||||
const passwordBtn = $("btn-delete-wallet-confirm");
|
||||
passwordBtn.disabled = false;
|
||||
passwordBtn.classList.remove("text-muted");
|
||||
const lostPasswordBtn = $("btn-delete-wallet-lost-confirm");
|
||||
lostPasswordBtn.disabled = false;
|
||||
lostPasswordBtn.classList.remove("text-muted");
|
||||
|
||||
const { activeAddressChanged } = removeWalletFromState(state, walletIdx);
|
||||
|
||||
deleteWalletIndex = null;
|
||||
@@ -194,8 +187,8 @@ function init(_ctx) {
|
||||
btn.disabled = true;
|
||||
btn.classList.add("text-muted");
|
||||
|
||||
// finishDelete() re-enables the button; navigating away then runs
|
||||
// the leave hook that wipes the typed name.
|
||||
// finishDelete() navigates, and the leave hook re-enables the
|
||||
// button and wipes the typed name on the way out.
|
||||
await finishDelete(lostPasswordIndex);
|
||||
});
|
||||
|
||||
|
||||
@@ -172,15 +172,6 @@ async function openLostPassword(deleteWallet, walletIdx) {
|
||||
await click("btn-delete-wallet-lost-password");
|
||||
}
|
||||
|
||||
// Delete a wallet through the password route: open its confirm screen,
|
||||
// enter the password, and confirm. The vault is mocked, so the password
|
||||
// text itself is irrelevant — decryptWithPassword decides pass or fail.
|
||||
async function deleteWithPassword(deleteWallet, walletIdx) {
|
||||
deleteWallet.show(walletIdx);
|
||||
node("delete-wallet-password").value = "any password";
|
||||
await click("btn-delete-wallet-confirm");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ tests
|
||||
|
||||
// The stub is what every persistence assertion below rests on, so its one
|
||||
@@ -465,21 +456,15 @@ describe("what the screen leaves behind", () => {
|
||||
);
|
||||
});
|
||||
|
||||
// Both routes now re-enable through finishDelete(), not their leave
|
||||
// hooks, so the button comes back live once a delete completes.
|
||||
test("the confirm button is re-enabled after a delete", async () => {
|
||||
const { deleteWallet } = load();
|
||||
// Left mid-delete, the screen has to come back usable.
|
||||
test("the confirm button is re-enabled on the way out", async () => {
|
||||
const { helpers, deleteWallet } = load();
|
||||
await openLostPassword(deleteWallet, 1);
|
||||
|
||||
node("delete-wallet-lost-name-input").value = "Wallet 2";
|
||||
await click("btn-delete-wallet-lost-confirm");
|
||||
node("btn-delete-wallet-lost-confirm").disabled = true;
|
||||
helpers.showView("settings");
|
||||
|
||||
expect(node("btn-delete-wallet-lost-confirm").disabled).toBe(false);
|
||||
expect(
|
||||
node("btn-delete-wallet-lost-confirm").classList.contains(
|
||||
"text-muted",
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
// A wallet name is not a secret, so the screen is excluded for the
|
||||
@@ -490,48 +475,3 @@ describe("what the screen leaves behind", () => {
|
||||
expect(RESTORABLE_VIEWS.has("delete-wallet-confirm")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// The password route is the pre-existing bug this file's fix addresses:
|
||||
// its Confirm Delete button was disabled before the decrypt and never
|
||||
// re-enabled on success, so a second delete in the same popup session
|
||||
// found a dead button. Now both routes re-enable through finishDelete().
|
||||
//
|
||||
// Against head these tests fail: with the re-enable absent, the button
|
||||
// stays disabled after the first delete, so the disabled assertions read
|
||||
// true where they expect false.
|
||||
describe("the password route's confirm button", () => {
|
||||
test("is re-enabled after a successful delete", async () => {
|
||||
const { deleteWallet, vault } = load();
|
||||
vault.decryptWithPassword.mockResolvedValue();
|
||||
|
||||
await deleteWithPassword(deleteWallet, 1);
|
||||
|
||||
expect(node("btn-delete-wallet-confirm").disabled).toBe(false);
|
||||
expect(
|
||||
node("btn-delete-wallet-confirm").classList.contains("text-muted"),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
// The reported symptom: delete one wallet, then open Delete Wallet for
|
||||
// a second one without reopening the popup. The button must be live on
|
||||
// that second visit, and the second delete must actually persist.
|
||||
test("a second delete works in the same popup session", async () => {
|
||||
const { deleteWallet, vault, storage } = load();
|
||||
vault.decryptWithPassword.mockResolvedValue();
|
||||
|
||||
await deleteWithPassword(deleteWallet, 1);
|
||||
|
||||
// Wallet 2 is gone; the list is now [Wallet 1, Wallet 3]. Opening
|
||||
// the confirm screen for the wallet now at index 1 (Wallet 3) must
|
||||
// find its button live, not the dead one the first delete left.
|
||||
deleteWallet.show(1);
|
||||
expect(node("btn-delete-wallet-confirm").disabled).toBe(false);
|
||||
|
||||
node("delete-wallet-password").value = "any password";
|
||||
await click("btn-delete-wallet-confirm");
|
||||
|
||||
expect((await persistedWallets(storage)).map((w) => w.name)).toEqual([
|
||||
"Wallet 1",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user