Commit Graph
80 Commits
Author SHA1 Message Date
sneak 65d4dd8e0f docs: name every approval-screen amount string, fix stale zero claim (closes #369)
e2e / e2e-chrome (push) Failing after 1s
e2e / e2e-firefox (push) Failing after 1s
check / check (push) Successful in 1m18s
The README's amount-display section claimed a genuine zero always renders
`0.0000`. That is corrected and scoped: it holds for the ERC-20 amount and 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 before the floor — the
swap's `Min. received` (any zero minimum) and its V4 exact-in `Amount` (an
`amountIn` of zero, V4's open delta).

A new list names every string an amount 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)`. It
also records that a zero `minBalance` on a `BALANCE_CHECK_ERC20` step now reads
the no-minimum wording where it once read `0.0000`. Each claim checked against
the tree. Docs only.

Model: opus-4-8
2026-09-21 23:30:10 +00:00
clawbot a1f082d686 docs: a release procedure from a green main to tagged, packaged artifacts (closes #387)
check / check (push) Failing after 1s
e2e / e2e-chrome (push) Failing after 1s
e2e / e2e-firefox (push) Failing after 1s
Add docs/RELEASE.md, linked from README.md's Release Artifacts section, giving
the release procedure as a numbered list: confirm main is green in CI, confirm
the one version in package.json and the two manifests matches the intended tag,
make package from a clean checkout, verify SHA256SUMS, create the annotated tag
vX.Y.Z, then distribute per browser. Each step names who performs it, marks the
owner-only ones, and states the check that it worked. Every repo command cited
(make setup, make check, make package) exists on next; tagging and verification
use standard git and coreutils, and the CRX pack line is README's own.

The per-browser distribution step is written as pending the owner's choice on
issue 386, with the Firefox and Chrome options named but none presented as
settled. Docs only: no code or test changes.

Model: opus-4-8
2026-09-21 22:00:18 +02:00
clawbot 1197d2171b fix: give every address a row of its own, so none wraps or is shortened (closes #380) (#381)
check / check (push) Successful in 56s
e2e / e2e-chrome (push) Successful in 1m51s
e2e / e2e-firefox (push) Successful in 40s
2026-08-30 05:25:00 +02:00
clawbot a098bb0c32 fix: floor malformed allowedSites, fraudContracts and selectedToken entries (closes #362)
check / check (push) Successful in 42s
e2e / e2e-chrome (push) Successful in 1m45s
e2e / e2e-firefox (push) Successful in 31s
A stored allowedSites whose value was not a list rendered a working popup and then made every subsequent save fail silently, so the user operated a wallet that persisted nothing -- worse than a blank popup, which is at least visibly broken. fraudContracts and selectedToken had the same shape: a container floored by truthiness or not at all, while its entries were dereferenced. Entries are now floored as well as containers, following the idiom #311 established, and a failed save raises a persistent banner instead of vanishing into a swallowed rejection.

The per-field justifications that used to live in a hand-written header are replaced by a contract test that drives each field's hostile and falsy values through a real popup boot, so a claim about a field answers to the code rather than to prose. Its guarantee is stated narrowly and deliberately: no structural dereference on the code paths a wholly-corrupted profile takes, which is not every path a stored record takes. The paths it does not drive are named where the claim is made, and are tracked in #379.
2026-08-23 23:06:17 +02:00
clawbot 1b52aa1723 fix: store an absent explorer decimals as unknown instead of fabricating 18 (closes #349)
check / check (push) Successful in 34s
e2e / e2e-chrome (push) Successful in 1m45s
e2e / e2e-firefox (push) Successful in 30s
parseInt(decimals || "18") ran before writing stored tokenBalances[].decimals, so an explorer reporting no decimals produced a fabricated 18 indistinguishable from a real one at read time. That defeated the resolve-or-refuse guarantees of #306 and #340: their refusal paths were intact but never fired, because the guess was laundered upstream of them.

An absent scale is now stored as unknown, and a holding whose scale nothing knows carries a null balance -- unknown, never zero -- with six reader sites saying so rather than printing 0.0000. The Send screen resolves the display scale rather than reading the stored one, so a bundled token whose explorer row omits decimals still sends; when the scale cannot be resolved the stored quantity is withdrawn too, so the user is told the balance is unknown rather than only that the fee failed.

Existing fabricated 18s cannot be told apart retroactively and are replaced wholesale on the next balance refresh. An explorer-sourced scale stays trusted -- only fabrication is removed; the reasoning is recorded on the issue.
2026-08-23 21:19:04 +02:00
clawbot ad6aa7b20d fix: version stored state, validate its shape, and give a corrupt blob a way out (closes #311)
check / check (push) Successful in 33s
e2e / e2e-chrome (push) Successful in 1m46s
e2e / e2e-firefox (push) Successful in 34s
Stored state had no version and no structural validation, so a corrupt blob produced a completely blank popup with no message and no recovery control, and made every dApp RPC call from every page answer a generic -32603. There was no reset or wipe control anywhere in the UI.

saveState() now stamps a schema version and loadState() validates the shape. A version it does not understand, or a wallets array it cannot parse, lands on a recovery screen that names the problem, offers the stored record verbatim for export, and offers a destructive reset behind a typed confirmation. Unversioned but valid state -- which every existing install has -- migrates in place and keeps working; it is never shown a wipe prompt. A dApp call against unusable state answers -32007, which EIP-1474 leaves unassigned, rather than -32603. networkById() refuses an unknown id loudly instead of returning mainnet, and networkId is validated so a corrupt value cannot be used as an object key.

Fields the gate does not refuse are floored by type, container and entries both: a malformed trackedTokens or tokenBalances entry is dropped rather than dereferenced. Verified by an independent sweep of 1152 corrupt blobs producing no blank popup, with the same harness showing 9 blanks against the previous revision.
2026-08-23 20:04:00 +02:00
clawbot 43784cab3f harden: resolve or refuse the swap token scale instead of guessing 18 (closes #340)
check / check (push) Successful in 29s
e2e / e2e-chrome (push) Successful in 1m47s
e2e / e2e-firefox (push) Successful in 31s
tokenInfo() returned decimals 18 for any token absent from the bundled list, so the swap approval line rendered a real 1000.00 of a 6-decimal token as 0.000000000001. The scale is now resolved from what the wallet already holds (bundled list, tracked tokens, explorer-reported decimals) or refused outright, matching the rule set for the ERC-20 path in #306. A refusal reuses unknownDecimalsAmount(), so it reads as "base units (decimals unknown)" with no decimal point and no symbol, and the same string propagates to rawValue so no downstream screen can render a figure the approval screen refused. No new network call on the approval path. Verified green on all three CI contexts: check, e2e-chrome, e2e-firefox.
2026-08-23 16:20:22 +02:00
clawbot 769f6a5289 release: produce a versioned per-browser artifact and pin the Chrome extension id (closes #310)
check / check (push) Successful in 32s
e2e / e2e-chrome (push) Successful in 1m44s
e2e / e2e-firefox (push) Successful in 32s
manifest/chrome.json now carries a fixed public key, so the extension id and the chrome.storage.local partition holding the wallet stay stable across checkout moves and re-clones instead of being derived from the absolute path. A release entrypoint produces a self-contained versioned artifact per browser, including the files that sit at dist/ root outside both browser directories. One version source of truth, enforced: the build fails naming the culprit when the two manifests and package.json disagree, and BUILD_COMMIT now marks a dirty tree as dirty. Firefox ships an unsigned XPI; the README states that release Firefox and ESR refuse it, that Developer Edition or Unbranded is required, and that Remove is irreversible except from the recovery phrase, which is asserted by test.
2026-08-23 16:13:22 +02:00
clawbot 669c443bf9 fix: never render a nonzero approval amount as zero (closes #322)
check / check (push) Successful in 31s
e2e / e2e-chrome (push) Successful in 1m12s
e2e / e2e-firefox (push) Successful in 22s
An amount below the 4-decimal display floor now extends to its first significant digit on the approval and confirmation screens, instead of stating a real transfer, allowance or swap Min. received as 0.0000. The rule had been implemented three times; all three now share src/shared/amountDisplay.js, which holds the plain truncation and the floored variant side by side. History and balance lists keep the unfloored rule, pinned by test.
2026-08-23 15:43:04 +02:00
clawbot 12b0c4d1c6 build: remove dist/ when a release build fails (closes #333)
check / check (push) Successful in 30s
e2e / e2e-chrome (push) Has been cancelled
e2e / e2e-firefox (push) Has been cancelled
A failed release build no longer leaves a complete, loadable debug bundle in dist/ whose every wallet uses the publicly committed test recovery phrase. Each step of the release build runs through script/discard-dist-on-failure, which removes dist/ on failure, says on stderr that it did and why, and returns the step's own status. build-debug is deliberately unwrapped. script/verify-build is untouched.
2026-08-23 15:39:04 +02:00
clawbot c36d8b6ddf docs: state the enforced dist/ verification scope precisely (closes #331)
check / check (push) Successful in 29s
e2e / e2e-chrome (push) Successful in 1m11s
e2e / e2e-firefox (push) Successful in 22s
README, the script synopsis, its header paragraph and the check_dist_tree comment now all say the same thing: regular files and symlinks under dist/ are covered; fifos, sockets, device nodes and empty directories are not, and why. No behaviour change — the walk is untouched.
2026-08-23 15:33:58 +02:00
clawbot 20e911059a fix: give a wallet whose password is lost a way out, and say the password cannot be reset (closes #312)
check / check (push) Successful in 31s
e2e / e2e-chrome (push) Successful in 1m10s
e2e / e2e-firefox (push) Successful in 23s
A user who forgot their password but held their recovery phrase was permanently
locked out: deletion was password-gated and re-importing the phrase was refused
as a duplicate. Their only escape was destroying extension storage through
browser internals, taking every other wallet with it.

DeleteWallet gains an "I have lost my password" route that destroys the stored
secret after the wallet's name is typed back. No password gate was added:
requiring one to discard a secret protects nothing, since an attacker who wants
destruction can uninstall the extension, and the only person it stops is the
legitimate user who lost it. The screen is excluded from RESTORABLE_VIEWS and
registers an onViewLeave cleanup.

Deletion was chosen over re-import because a key wallet is duplicate-checked by
address rather than xpub, so an xpub-only relaxation would leave that user
still wedged; because re-import makes the user retype their recovery phrase
into a live popup merely to change a password; and because it reaches no end
state that delete-then-import plus scanForAddresses() does not. The attacker
argument did not decide it — re-import clears the "no worse than the phrase
alone" bar.

All three AddWallet password hints now state the password cannot be recovered
or reset and name that mode's only backup, the xprv mode correctly claiming no
recovery phrase. deleteAddress.js no longer tells the user that deleting a
wallet asks for a password, which this change made false.

The typed confirmation collapses internal whitespace on both sides: a wallet
renamed with two spaces displays with one, so the string a user could see and
type could never match, making the confirmation untypable on the one screen
whose purpose is un-wedging a stuck user.

Measured, not reasoned, after review found the first reserve twice too large
and pushing the Import button below the fold: #btn-add-wallet-confirm bottom
628.13 -> 580.13 at 360x600, scrollHeight 636 -> 600, hint box 48px identical
across all three tabs and on re-entry. make check 40 suites / 828 tests,
test-e2e 55/55, test-e2e-firefox 8/8.
2026-08-20 15:12:28 +02:00
clawbot aea999db85 build: make verify-build take an explicit expectation and a build receipt (closes #309)
check / check (push) Successful in 30s
e2e / e2e-chrome (push) Successful in 1m10s
e2e / e2e-firefox (push) Successful in 22s
verify-build read its expectation from AUTISTMASK_DEBUG in its own environment
and the Makefile invoked it bare, so an operator with that variable exported
who ran the release target got an INSECURE debug build — every wallet it
creates uses the publicly committed test phrase — verified green, exit 0. It
also had no provenance: a 26-byte file containing the right marker string
passed, the content script and manifest.json were never inspected, and an
entire hand-written dist/ passed.

--expect release|debug and --receipt PATH are now both required, with no
defaults and nothing read from the environment. build.js records every file it
emits with its sha256 and writes the receipt; the Makefile mktemps it outside
the repo per invocation with a trap, and build.js refuses a receipt path inside
dist/. Verification runs three passes in a load-bearing order — receipt shape,
full dist/ walk, then per-file bytes — so an unwalkable subtree cannot make
files look absent. dist/constants-bundles.txt, which was an unsigned trust root
living inside the tree it vouched for, is gone.

What this proves is bounded and stated as such: dist/ is byte-for-byte the
output of the build.js run that just finished, within one make build
invocation. It proves nothing about the honesty of the source tree or build.js,
and nothing to anyone handed a dist/ from elsewhere — that is signing, #310.
The standalone make verify-build target is removed because its only input would
be dist/ itself, i.e. the artifact vouching for itself.

Verified: make check green, test-verify-build 39 cases (was 18), test-e2e 55/55
and test-e2e-firefox 8/8 with make build running uncached inside both images.
All four original bypasses now exit 1. Mutations: digests disabled fails
exactly 4 cases, dropping the dist/ walk fails exactly 8, restoring the ambient
fallback fails exactly 1.
2026-08-20 14:24:55 +02:00
clawbot c8c2af0c6b harden: escape every interpolation into popup innerHTML, and add default-src to both manifests (closes #307)
check / check (push) Successful in 28s
e2e / e2e-chrome (push) Successful in 1m10s
e2e / e2e-firefox (push) Successful in 22s
A hostile ERC-20's symbol() reached an innerHTML string unescaped, and neither
manifest declared default-src, so an attacker deploying a token with 1,000+
holders and airdropping one unit could render a full-viewport cross-origin
iframe over the wallet's own UI, on screens where the user types their
password.

escapeHtml is now a pure string replace over & < > " ' — the old version
round-tripped through textContent, which escapes neither quote, while already
being used inside data-copy="...". All 19 files in src/popup/views/ were
audited: beyond the reported symbol site, the explorer-supplied directionLabel
in all three transaction lists, wallet.name, addr.ensName, the blockie data:
URIs and two ad-hoc quote-only escapes were also unescaped. Explorer URLs now
go through one helper that percent-encodes the path segment.

Both manifests add default-src 'self', frame-src 'none', form-action 'none' and
base-uri 'none'. Three loosenings are pinned in tests/manifest.test.js and
justified in README.md: style-src 'unsafe-inline' (39 static style attributes;
Firefox implements neither style-src-attr nor 'unsafe-hashes'), img-src data:
(blockies), connect-src https: http: (user-configurable RPC).

Note frame-src 'none' blocks a frame loading, not the element existing, so the
zero-iframe assertion is a claim about the escaping alone; the test asserts the
element count and the literal rendered text separately, taking the count before
any click an overlay could intercept.

Verified: make check 39 suites / 811 tests, test-e2e 55/55 including the
WebAssembly-under-CSP assertion, test-e2e-firefox 8/8, zero CSP violations
asserted rather than merely unobserved. Reverting only balanceLine's
interpolation reproduces the attack as 2 iframes on the address screen.
2026-08-20 13:47:28 +02:00
clawbot 2f80a9bdb4 fix: sign the ERC-20 amount the confirmation screen displayed (closes #305)
check / check (push) Successful in 27s
e2e / e2e-chrome (push) Successful in 1m10s
e2e / e2e-firefox (push) Successful in 22s
The send screen was built from the indexer's decimals while the transfer was
encoded from the contract's decimals() read at signing time, with nothing
comparing them. A token whose scales disagree moved 10^12 times the approved
amount.

The displayed scale is now carried on pendingTx from the same tokenBalances
entry the amount, balance and symbol were rendered from, and both encode sites
use it. transferAmount.js refuses rather than falling back when the two scales
disagree or either is unusable.

Adds the first end-to-end coverage of the popup's own Send -> ConfirmTx ->
Sign & Send path; #btn-confirm-send had never been clicked by any test.
2026-08-20 12:31:28 +02:00
clawbot ff3387d8cf feat: vendor and censor the phishing blocklist at build time (closes #219)
check / check (push) Successful in 27s
e2e / e2e-chrome (push) Successful in 48s
e2e / e2e-firefox (push) Successful in 21s
2026-08-17 10:05:56 +02:00
clawbot a60c4a616a test: drive the Settings screen in a browser and guard every popup element id (closes #229)
check / check (push) Has been cancelled
e2e / e2e-chrome (push) Has been cancelled
e2e / e2e-firefox (push) Has been cancelled
2026-08-17 09:14:59 +02:00
clawbot 47bf38644d build: add ESLint to script/lint and containerize linting (closes #152)
check / check (push) Successful in 39s
e2e / e2e-chrome (push) Successful in 48s
e2e / e2e-firefox (push) Successful in 24s
2026-08-17 09:10:03 +02:00
clawbot 4b7a678a9b refactor: one shared extension-API module, and drive the dApp flows on Firefox (closes #153)
check / check (push) Successful in 27s
e2e / e2e-chrome (push) Successful in 45s
e2e / e2e-firefox (push) Successful in 23s
2026-08-17 09:06:13 +02:00
clawbot 743b1962a5 build: run the browser e2e suites in CI (closes #259)
check / check (push) Successful in 28s
e2e / e2e-chrome (push) Successful in 47s
e2e / e2e-firefox (push) Successful in 20s
2026-08-17 08:52:26 +02:00
clawbot c06765ef8f fix: one transaction approval at a time, and honest copy for a nonce collision (closes #271)
check / check (push) Successful in 28s
2026-08-17 08:38:26 +02:00
clawbot e07efb710a fix: an address holding only unpriced tokens is no longer totalled at $0.00 (closes #261)
check / check (push) Has been cancelled
2026-08-17 08:38:10 +02:00
clawbot 51e84aefa6 docs: drop the README limit that #274 fixed (closes #285)
check / check (push) Successful in 26s
The EIP-1193 error code now survives the last hop into the page, so the third standing limit in the End-to-End Tests section no longer describes the code. Verified against src/content/inpage.js (ProviderRpcError carries code and data) and tests/e2e/run.js (the page-side error is asserted, not only the wire).
2026-08-17 08:03:02 +02:00
clawbot 0be20d7270 fix: render the view "Back" lands on after the popup is reopened (closes #268)
check / check (push) Successful in 1m27s
2026-08-14 06:14:09 +02:00
clawbot d5595c0151 test: drive the EIP-1193 dApp approval round trips in the browser (closes #183)
check / check (push) Successful in 29s
The dApp signing path was the largest unverified surface in the milestone: the
only place where the content script, the inpage provider, the background worker
and the popup all have to work together, with unit tests covering each side in
isolation and none covering the seam.

A page served by the harness speaks EIP-1193 to the real provider -- asserted by
EIP-6963 object identity, not by shape -- and eth_requestAccounts, personal_sign,
eth_signTypedData_v4 and eth_sendTransaction are each driven through to approval
and to rejection.

Every signature is recovered and compared to the approved address; the broadcast
transaction is parsed from the bytes captured at eth_sendRawTransaction and
checked for signer, recipient, value, calldata and chain. A signature that
merely came back would pass against a wrong key, a wrong message or a wrong
chain, so each assertion was demonstrated failing against a variant that is
wrong in exactly one of those ways.

The password is asserted absent from every message crossing the extension
boundary, which gives #157's fix a permanent floor rather than a one-time
review.

Two defects this surfaced are tracked separately: EIP-1193 error codes never
reach the page (#274), and approving a site connection races the popup teardown
(#275). Neither is asserted as correct here. A real dApp with real funds against
mainnet remains an uncovered human pass and is documented as such.
2026-08-12 13:23:57 +02:00
clawbot 52c7c1b060 test: containerized Firefox end-to-end harness (closes #184)
check / check (push) Successful in 34s
Drives the real popup in a real Firefox with dist/firefox/ installed as an
unpacked MV2 temporary add-on via geckodriver. make test-e2e-firefox, outside
make check like the Chrome suite. Zero npm dependencies: plain fetch and
child_process against geckodriver's HTTP API. Base image, Firefox tarball and
geckodriver are each pinned by digest and verified at build time.

Error capture reads the privileged console service through Marionette's chrome
context, not WebDriver BiDi. BiDi delivers nothing at all for extension pages,
so a BiDi-based harness would observe zero events and report success -- the
vacuous-check shape this repo has shipped twice. Both the driver and the README
say so where someone would be tempted to simplify.

Demonstrated to discriminate: a background page that throws at the top of the
file, a missing import, and an async throw where every UI assertion still
passes each fail the run.

Three limits are measured and documented rather than papered over: capture is
poll-based so an error is attributed to a step, not a moment; the console ring
buffer holds 250 messages and evicts the oldest, measured against a clean-run
peak of 4; and the drained window ends roughly 1.5s after the last step, with
observed jitter rather than a hard boundary. Content-script capture is marked
unverified because --network none leaves no page to inject into, and that same
choice inverts coverage of network-dependent code.
2026-08-12 12:20:14 +02:00
clawbot 918e581ef3 harden: verify the signed transaction against what the popup displayed (closes #216)
check / check (push) Has been cancelled
Verification compared the signed artifact against the dApp's request object.
For every field the dApp omitted -- normally nonce, gas limit and all the fee
fields, since the popup filled them in -- the number the user actually read on
screen was verified by nothing, and only absolute ceilings stood behind it.

The transaction is now populated in the background before the approval window
opens, and that populated object is both what the popup displays and what the
signed artifact is verified against. Every consequential field becomes an
equality comparison; the ceilings remain as a backstop. Population failing
means no approval and no window, and the error goes to the requesting page --
earlier than before, where the same estimate failed after the password had been
typed.

The account is pinned too: `from` is compared against the address named at
approval time rather than whichever address is active at signing, so switching
accounts mid-flow refuses instead of signing from an account the approval did
not name. The message-signing path had the same defect and gets the same fix.

Nonce selection moves earlier as a consequence; the concurrent-approval case
that follows from it is tracked at #271.
2026-08-12 12:15:25 +02:00
clawbot 5af89a1b63 test: drive ConfirmTx in the e2e suite, gate assertion included (closes #238)
check / check (push) Successful in 38s
ConfirmTx -- the screen that decides what gets signed -- had no automated
coverage of its own behaviour. The arithmetic underneath was well tested; the
wiring was not, so a mutant making the spend gate read the displayed fee
estimate instead of the reserve would have reintroduced the #154 overspend with
the suite still green.

Nine end-to-end tests now drive it for both the native and ERC-20 paths,
covering the pending, funded, over-balance and estimate-failed states, and
asserting that the gate reads the reserve rather than the estimate. Swapping the
two makes the suite fail. The view height is asserted constant across every
state transition rather than merely printed.

Reaching the screen needs a funded balance and a gas estimate, so the route
interception gains fixtures for both. Testing the estimate-failed state means
provoking the console error the code is supposed to emit, which the harness
otherwise fails a run on; an expectation mechanism consumes exactly one matching
record, is scoped to the declaring test, and fails that test if nothing matched,
so it cannot mask an unrelated error.
2026-08-12 11:35:20 +02:00
clawbot c6a1f97247 fix: explain a rejected dust threshold instead of silently snapping back (closes #233)
check / check (push) Successful in 33s
The dust-threshold field was the only validated input in Settings that rejected
without saying anything: the value silently changed back to the stored one with
no explanation. It now flashes "Please enter a whole number of gwei, zero or
greater." alongside the existing resync, matching the idiom the RPC URL field
already uses.

The parse moves to its own module and accepts plain decimal digits only, zero
or greater. Hex and exponent notation are refused rather than accepted: Number()
reads "0x10" as 16 and "1e3" as 1000, neither of which the previous parseInt
produced, and storing a number the user did not type is the same silent
substitution this change exists to remove.

The message must fit one line of the reserved flash area -- a wrapped message
pushes the settings view down, which the No Layout Shift policy forbids. That is
pinned by an end-to-end test measuring the rendered line height and the position
of the elements below it, in a single round trip because the flash clears after
two seconds.
2026-08-12 11:29:09 +02:00
clawbot 937f699fb1 feat: remove an address from an HD wallet, behind a confirmation (closes #162)
check / check (push) Successful in 36s
Address rows on Home gain an [x] control, on wallets that derive addresses from
an extended key and hold more than one, opening a DeleteAddress confirmation
screen.

Removal cannot destroy anything: the key material stays. Derivation indices are
not renumbered, so the next "+" derives the next unused index rather than
resurrecting the removed one. The confirmation states the real route back --
delete the whole wallet in Settings, which asks for the password and destroys
the stored recovery phrase, then import it again -- and notes that the scan
which follows only finds addresses with on-chain activity. The copy varies by
wallet type, since an xprv wallet has no recovery phrase.

Removing an address that holds a balance is allowed, with a warning naming no
figure; the funds are at the address on-chain and stay there either way.
Selection and active address move only when the removed address was the one
selected, and site permissions are dropped for it alone.

The state transition shares its address comparison, permission cleanup and
active-changed broadcast with the wallet-level removal.
2026-08-12 11:16:29 +02:00
clawbot 1f41a07df2 fix: filter a fake ETH token from the balance list too (closes #235)
check / check (push) Successful in 30s
2026-08-12 11:10:38 +02:00
clawbot 78a1cb067e test: commit a verify-build failure-mode battery and run it from make check (closes #227)
check / check (push) Successful in 51s
2026-08-12 11:05:08 +02:00
clawbot afe6ddaea0 fix: WaitTx timeout no longer overwrites a rendered success screen (closes #155)
check / check (push) Successful in 30s
2026-08-12 10:58:36 +02:00
clawbot 23712b53cb fix: wipe the exported private key from the DOM on any view leave (closes #221)
check / check (push) Successful in 29s
2026-08-12 10:54:37 +02:00
clawbot bd4bdcafc7 fix: explain a stored non-master xprv wallet instead of throwing at signing time (closes #234)
check / check (push) Successful in 30s
2026-08-12 10:41:49 +02:00
clawbot bf1dbec87c fix: run libsodium on WebAssembly under the extension CSP (closes #182)
check / check (push) Successful in 26s
2026-08-12 10:30:15 +02:00
clawbot ba35282092 docs: describe the bundled token list by its criterion, not a drifting count (closes #239)
check / check (push) Successful in 29s
2026-08-12 10:20:40 +02:00
clawbot 158278d251 fix: count the network fee in the confirm-screen balance check (closes #154)
check / check (push) Successful in 25s
2026-08-11 15:41:37 +02:00
clawbot 6f6bc2e7b5 fix: drive background refresh and phishing update from alarms (closes #158)
check / check (push) Has been cancelled
2026-08-11 15:38:28 +02:00
clawbot 74c137dadf fix: add a Settings toggle for known-symbol spoof verification (closes #176)
check / check (push) Has been cancelled
2026-08-11 15:38:05 +02:00
clawbot edea22f7ed fix: move the UTC Timestamps checkbox into the Display well (closes #212)
check / check (push) Has been cancelled
2026-08-11 15:34:16 +02:00
clawbot 3e5d6323ce feat: password-gated recovery phrase display for HD wallets (closes #161)
check / check (push) Has been cancelled
2026-08-11 15:25:17 +02:00
clawbot 12acf4dc8c fix: honour a dust threshold of 0 and compare addresses case-insensitively (closes #179)
check / check (push) Has been cancelled
2026-08-11 15:16:48 +02:00
clawbot f455b0ae7f test: known-answer coverage for HD derivation and the vault (closes #159)
check / check (push) Has been cancelled
2026-08-11 15:06:35 +02:00
clawbot 86cdea5e4e chore: repo policy compliance sweep — test rerun, frozen lockfile, documented targets (closes #166)
check / check (push) Has been cancelled
2026-08-11 14:57:51 +02:00
clawbot cf5f582be9 docs: correct three README claims contradicted by the code (closes #213)
check / check (push) Has been cancelled
2026-08-11 14:41:16 +02:00
clawbot b9bc226ae1 docs: rebuild the README Screen Map from the code (closes #164)
check / check (push) Has been cancelled
2026-08-11 14:31:45 +02:00
clawbot b882cede9f fix: repair wallet state on delete (closes #156)
check / check (push) Has been cancelled
2026-08-11 14:23:06 +02:00
clawbot e9fa8bec47 build: assert DEBUG is off in every emitted bundle as a post-build check (closes #170)
check / check (push) Successful in 18s
build.js records which emitted bundles contain src/shared/constants.js, and
constants.js carries a marker constant-folded from DEBUG itself. script/verify-build
cross-checks the two and fails on every way of not knowing, so deleting the
__BUILD_DEBUG__ define now breaks the build instead of shipping a live debug branch.
2026-08-10 16:15:22 +02:00
clawbot e8ad8325c8 test: containerized Chrome end-to-end harness that drives the real popup (closes #181)
check / check (push) Has been cancelled
Runs the real popup in a pinned containerized Chrome and fails on any uncaught
page error or console.error. Also fixes the two defects it caught: the missing
showView import in addToken.js and the missing addressDotHtml import in
transactionDetail.js.

closes #150
closes #151
2026-08-10 15:49:32 +02:00