Commit Graph

56 Commits

Author SHA1 Message Date
21b158b3b6 fix: render the view "Back" lands on after the popup is reopened (closes #268)
All checks were successful
check / check (push) Successful in 44s
goBack() only unhid its target. A reopened popup renders the wallet list
and the one view restoreView() lands on, so every other view is still the
blank static template from index.html: pressing Back from Settings onto an
address showed an empty address line and no balances, and the same held for
address-token, receive, confirm-tx and transaction.

The per-view dispatch and its data guards move out of restoreView() into
src/popup/viewRouter.js, and goBack() now routes a popped view through the
same code by way of a renderer index.js registers with setBackRenderer().
A view whose backing state is gone falls back to Home the way the restore
does, rather than showing an empty template. The renderer declines any view
outside RESTORABLE_VIEWS, so goBack() unhides it as before: the restored
stack is filtered against that set, so such a view can only be on the stack
from the current page load, where forward navigation rendered it on the way
in. Forward navigation is untouched and nothing renders twice.

tests/backNavigation.test.js drives the real goBack() over the reproduction
and over each of address-token, receive, confirm-tx and transaction, with
and without their backing state, and pins that a live-session view is still
only unhidden. tests/e2e/run.js adds two cases against the real popup in a
real browser — a real close and reopen, then Back onto the address screen
and onto Receive — because make check cannot see a blank view. Both were
demonstrated failing against the unmodified sources: the address line read
"" where it should have read the address.
2026-08-12 10:41:26 +00:00
52c7c1b060 test: containerized Firefox end-to-end harness (closes #184)
All checks were successful
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
918e581ef3 harden: verify the signed transaction against what the popup displayed (closes #216)
Some checks failed
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
5af89a1b63 test: drive ConfirmTx in the e2e suite, gate assertion included (closes #238)
All checks were successful
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
c6a1f97247 fix: explain a rejected dust threshold instead of silently snapping back (closes #233)
All checks were successful
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
937f699fb1 feat: remove an address from an HD wallet, behind a confirmation (closes #162)
All checks were successful
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
1f41a07df2 fix: filter a fake ETH token from the balance list too (closes #235)
All checks were successful
check / check (push) Successful in 30s
2026-08-12 11:10:38 +02:00
78a1cb067e test: commit a verify-build failure-mode battery and run it from make check (closes #227)
All checks were successful
check / check (push) Successful in 51s
2026-08-12 11:05:08 +02:00
afe6ddaea0 fix: WaitTx timeout no longer overwrites a rendered success screen (closes #155)
All checks were successful
check / check (push) Successful in 30s
2026-08-12 10:58:36 +02:00
23712b53cb fix: wipe the exported private key from the DOM on any view leave (closes #221)
All checks were successful
check / check (push) Successful in 29s
2026-08-12 10:54:37 +02:00
bd4bdcafc7 fix: explain a stored non-master xprv wallet instead of throwing at signing time (closes #234)
All checks were successful
check / check (push) Successful in 30s
2026-08-12 10:41:49 +02:00
bf1dbec87c fix: run libsodium on WebAssembly under the extension CSP (closes #182)
All checks were successful
check / check (push) Successful in 26s
2026-08-12 10:30:15 +02:00
ba35282092 docs: describe the bundled token list by its criterion, not a drifting count (closes #239)
All checks were successful
check / check (push) Successful in 29s
2026-08-12 10:20:40 +02:00
158278d251 fix: count the network fee in the confirm-screen balance check (closes #154)
All checks were successful
check / check (push) Successful in 25s
2026-08-11 15:41:37 +02:00
6f6bc2e7b5 fix: drive background refresh and phishing update from alarms (closes #158)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 15:38:28 +02:00
74c137dadf fix: add a Settings toggle for known-symbol spoof verification (closes #176)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 15:38:05 +02:00
edea22f7ed fix: move the UTC Timestamps checkbox into the Display well (closes #212)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 15:34:16 +02:00
3e5d6323ce feat: password-gated recovery phrase display for HD wallets (closes #161)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 15:25:17 +02:00
12acf4dc8c fix: honour a dust threshold of 0 and compare addresses case-insensitively (closes #179)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 15:16:48 +02:00
f455b0ae7f test: known-answer coverage for HD derivation and the vault (closes #159)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 15:06:35 +02:00
86cdea5e4e chore: repo policy compliance sweep — test rerun, frozen lockfile, documented targets (closes #166)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 14:57:51 +02:00
cf5f582be9 docs: correct three README claims contradicted by the code (closes #213)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 14:41:16 +02:00
b9bc226ae1 docs: rebuild the README Screen Map from the code (closes #164)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 14:31:45 +02:00
b882cede9f fix: repair wallet state on delete (closes #156)
Some checks failed
check / check (push) Has been cancelled
2026-08-11 14:23:06 +02:00
e9fa8bec47 build: assert DEBUG is off in every emitted bundle as a post-build check (closes #170)
All checks were successful
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
e8ad8325c8 test: containerized Chrome end-to-end harness that drives the real popup (closes #181)
Some checks failed
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
f7f141a757 security: make DEBUG a build-time flag defaulting to off (closes #149) (#169)
Some checks failed
check / check (push) Has been cancelled
Fixes the highest-severity item in the repo: `src/shared/constants.js` had
`const DEBUG = true;`, so `generateMnemonic()` returned the publicly committed
`DEBUG_MNEMONIC` for every wallet created from a build of `main`, and the real
entropy path was dead code in every artifact we could produce.

## What changed

**`build.js`** — `AUTISTMASK_DEBUG` is read from the environment and injected
as a `__BUILD_DEBUG__` entry in the existing esbuild `define` map, next to the
other `__BUILD_*__` defines. Only the exact value `1` enables it; unset, empty,
`true`, or a typo all yield a release build, so the insecure direction requires
a deliberate opt-in and any mistake fails safe. The build prints
`Build mode: release (DEBUG off)` or
`Build mode: DEBUG (INSECURE - hardcoded test mnemonic, do not ship)`.

**`src/shared/constants.js`** — `DEBUG` now uses the same `typeof` guard that
`src/shared/buildInfo.js` already uses for the other build-time defines, and
defaults to `false` when the define is absent (jest, plain `require`).
`DEBUG_MNEMONIC` stays in the tree and stays exported.

**`Makefile`** — new `build-debug` target (`AUTISTMASK_DEBUG=1` + the same
build) so a debug build stays a one-liner for development.

**`README.md`** — new "Debug Builds" subsection under Getting Started, and the
DEBUG Mode Policy section now states that `DEBUG` is build-time-only and spells
out the boundary against the runtime toggle.

**`tests/wallet.test.js`** — new, covering both build modes.

**`TODO.md`** — refreshed in the same commit (details at the bottom).

No new `if (DEBUG)` branch was added and nothing about what DEBUG *does* changed:
still exactly the red banner plus the hardcoded test phrase, per the README
DEBUG Mode Policy and `RULES.md:76-80`.

## The interaction with the #145 settings toggle

This is the subtle part, so spelling out the reasoning.

There are two distinct debug flags in the tree after #145:

1. the compile-time `DEBUG` constant from `constants.js`, and
2. the runtime `debugMode` state flag, which the settings easter egg toggles
   and which `settings.js:379` pushes into `log.js` via `setRuntimeDebug()`.

`log.js` merges them: `isDebug()` is `DEBUG || _runtimeDebug`. That merged
value feeds exactly two things — the log level threshold (`log.js:24`) and the
red banner (`views/helpers.js:71`). Making the banner user-toggleable is the
intended behavior of #145, and this PR leaves it alone.

`generateMnemonic()` does **not** consult `isDebug()`. It reads the
compile-time `DEBUG` binding directly. That distinction is what makes a release
build coherent: with `__BUILD_DEBUG__` false, `DEBUG` is false in the bundle,
so no amount of clicking the version ten times and flipping the toggle can
reach `return DEBUG_MNEMONIC`. The user can turn the banner and verbose logging
on in a release build; they cannot turn the hardcoded phrase on.

The failure mode to guard against is someone later "tidying up" the two flags by
routing `wallet.js` through `isDebug()`, which would silently reintroduce this
exact vulnerability with the runtime toggle as the trigger. Three things now
guard that: a comment at the `wallet.js` call site saying it must stay the
compile-time constant and why, the same statement in the README DEBUG Mode
Policy, and a regression test that calls `setRuntimeDebug(true)`, asserts
`isDebug()` is genuinely true, and then asserts `generateMnemonic()` still
returns fresh entropy.

I considered instead making the runtime toggle unavailable in release builds,
but rejected it: that removes a feature #145 deliberately added, and it defends
the wrong boundary. The banner is not the dangerous part; the mnemonic path is,
and that one is already unreachable.

## Verification

`make check` — green, 5 suites, 55 tests, plus lint and fmt-check. It also ran
via the pre-commit hook on the commit itself.

The new tests, per the verification standard in the manager comment on the
issue (not just `a !== b`) — with the flag off: two successive
`generateMnemonic()` calls differ, both pass `isValidMnemonic`, both are 12
words, neither equals `DEBUG_MNEMONIC`, and the result derives a usable HD
wallet (`xpub` + a well-formed first address), so a broken implementation
returning a counter or a truncated phrase would fail. Same assertions again
with the runtime toggle forced on. With the flag on (`__BUILD_DEBUG__` defined
before a `jest.resetModules()` re-require): `DEBUG` is `true` and
`generateMnemonic()` returns `DEBUG_MNEMONIC`, so the debug path is proven
working rather than silently deleted.

Build artifacts — `make build` and `make build-debug` both produce `dist/chrome`
and `dist/firefox` successfully. Grepping the minified bundles for the emitted
`DEBUG` export value across all four bundles (chrome popup, chrome background,
firefox popup, firefox background):

    # after make build
    $ grep -roh 'DEBUG:![01]' dist/chrome dist/firefox | sort | uniq -c
          4 DEBUG:!1

    # after make build-debug
    $ grep -roh 'DEBUG:![01]' dist/chrome dist/firefox | sort | uniq -c
          4 DEBUG:!0

`!1` is minified `false`, `!0` is `true`. Also checked the fail-safe path:
`AUTISTMASK_DEBUG=true make build` prints `Build mode: release (DEBUG off)` and
likewise yields `4 DEBUG:!1`.

One thing a reviewer should know about the grep: the `DEBUG_MNEMONIC` string
literal is still present in the release bundle. That is not a leak of anything
(the phrase is in this public repo already) and it does not mean the branch is
live — esbuild cannot tree-shake a CommonJS `module.exports` object, so the
constant survives while `DEBUG` folds to `false`. The compiled function is
`function PL(){return ML?UL:f_.fromEntropy(globalThis.crypto.getRandomValues(new Uint8Array(16))).phrase}`
where `ML` is the `DEBUG:!1` export. So "the phrase string is absent" is *not*
the right test for a release build; "the exported `DEBUG` is `!1`" is, which is
what I checked.

## `TODO.md` refresh

Per the manager comment: Status rewritten (no branch in flight —
`feat/issue-144-settings-about` landed as #145, scripts-to-rule-them-all landed
as #148, so the `scripts/` question is resolved; `make check` recorded as
verified green on `main` at `23aeae4`); the completed "Verify main passes make
check" Future Step removed; Future Steps rewritten against the #149-#168
backlog in rough priority order, keeping branch pruning (now #167) and the
pre-1.0 security review (noting #149 and #157 are parts of it but it is
broader).

One deliberate deviation to flag rather than bury: the manager asked that Next
Step become this issue. Taken literally against the Workflow section, this
commit *completes* #149, which would normally move it into Completed Steps. I
followed the repo's existing convention for in-flight work instead — the
previous Next Step was phrased as "Land feat/issue-144-settings-about", so Next
Step is now "Land #149 ... PR open, awaiting review", which is accurate until
this merges. Whoever merges should move it to Completed Steps and promote the
first Future Step. Happy to change it if the reviewer prefers the strict
reading.

## Out of scope

`script/lint` being `prettier --check` only and unable to catch undefined
identifiers (#152) — noted in the TODO but not fixed here; I greped for `DEBUG`
consumers by hand rather than relying on lint, as advised. Nothing else in the
DEBUG consumer set (`log.js`, `helpers.js`, `state.js`, `settings.js`) changed
behavior.

Co-authored-by: sneak <sneak@sneak.berlin>
Reviewed-on: #169
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-08-09 16:19:09 +02:00
d046a24115 scripts-to-rule-them-all (#148)
All checks were successful
check / check (push) Successful in 5s
Reviewed-on: #148
Co-authored-by: sneak <sneak@sneak.berlin>
Co-committed-by: sneak <sneak@sneak.berlin>
2026-07-07 02:14:26 +02:00
d35bfb7d23 feat: expand confirm-tx warnings — closes #114 (#118)
All checks were successful
check / check (push) Successful in 5s
Expands the confirm-tx warning system with three new warning types, all using the existing `visibility:hidden/visible` pattern from PR #98 (no animations, no layout shift).

## Changes

1. **Scam address list expanded** (7 → 652 addresses): Sourced from [MyEtherWallet/ethereum-lists](https://github.com/MyEtherWallet/ethereum-lists) darklist (MIT license). Checked synchronously before sending.

2. **Contract address warning**: When the recipient is a smart contract (detected via `getCode`), shows a warning that sending directly to a contract may result in permanent loss of funds.

3. **Null/burn address warning**: Detects known burn addresses (`0x0000...0000`, `0x...dead`, `0x...deadbeef`) and warns that funds are permanently destroyed.

4. **No-history warning** (existing from #98): Unchanged, still shows for EOAs with zero transaction history.

All warnings use reserved-space `visibility:hidden/visible` elements — no layout shift, no animations.

closes #114

Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de>
Co-authored-by: user <user@Mac.lan guest wan>
Co-authored-by: clawbot <clawbot@eeqj.de>
Reviewed-on: #118
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
Co-committed-by: clawbot <sneak+clawbot@sneak.cloud>
2026-03-01 19:34:54 +01:00
3bf60ff162 Reorder transaction detail view: txid first, logical grouping (#133)
All checks were successful
check / check (push) Successful in 9s
Reorganizes the transaction detail view into logical blocks separated by thin horizontal rules for visual clarity.

**Identity block**: Transaction hash (first!), Type, Status
**Timing block**: Time, Block number
**Value block**: Amount, Native quantity, Token contract, From, To
**Decoded details**: Action/protocol/steps (for contract calls)
**Network details**: Nonce, Gas price, Gas used, Transaction fee
**Raw data**: Full calldata

README TransactionDetail screen map updated to reflect the new ordering and grouping.

closes #131

Co-authored-by: user <user@Mac.lan guest wan>
Co-authored-by: clawbot <clawbot@git.eeqj.de>
Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de>
Reviewed-on: #133
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-03-01 19:27:23 +01:00
clawbot
3f6f98dcaf docs: update TransactionDetail screen map with new fields
All checks were successful
check / check (push) Successful in 22s
Add Type, Token contract, Block, Nonce, Transaction fee, Gas price,
and Gas used fields to the TransactionDetail section in the README
screen map to match the implemented UI.
2026-03-01 06:17:40 -08:00
31b22c1325 style: format README.md and RULES.md with Prettier
All checks were successful
check / check (push) Successful in 21s
2026-02-27 11:39:44 -08:00
afc4868001 docs: document Blockscout as third external service in README
Some checks failed
check / check (push) Failing after 13s
2026-02-27 03:25:02 -08:00
6b301dee28 Resolve all README FIXMEs and enforce truncation safety
All checks were successful
check / check (push) Successful in 18s
- Update Architecture tree to match actual src/ structure
- Fix settings button to have border and hover state (Clickable Affordance)
- Cap truncateMiddle to remove at most 10 chars (anti-spoofing guard)
- Raise caller floor from 10 to 32 chars for address display
- Fill in default RPC URL (ethereum-rpc.publicnode.com)
- Fix dependencies table intro (four runtime libs, not two)
- Clean up TODO section: remove all completed items
2026-02-27 16:48:00 +07:00
d67023e80d Show exact amounts and address titles in transaction detail
All checks were successful
check / check (push) Successful in 5s
- Display full-precision amount (no 4-decimal truncation) in the
  transaction detail view, with native quantity (wei/base units) below
- Both amount and native quantity are click-copyable
- Show wallet/address title above from/to when the address is ours
- Update README Display Consistency to document the exception
2026-02-27 16:09:44 +07:00
9e45c75d29 Implement personal_sign and eth_signTypedData_v4 message signing
All checks were successful
check / check (push) Successful in 4s
Replace stub error handlers with full approval flow for personal_sign,
eth_sign, eth_signTypedData_v4, and eth_signTypedData. Uses toolbar
popup only (no fallback window) and keeps sign approvals pending across
popup close/reopen cycles so the user can respond via the toolbar icon.
2026-02-27 15:27:14 +07:00
6c3cc0c516 Update README Screen Map with all views and state transitions
All checks were successful
check / check (push) Successful in 17s
Complete rewrite of the Screen Map section documenting all 17 views:
Welcome, Home, AddWallet, ImportKey, AddressDetail, AddressToken,
Send, ConfirmTx, WaitTx, SuccessTx, ErrorTx, Receive,
TransactionDetail, AddToken, Settings, SiteApproval, TxApproval.

Each view documents its elements (with display details like blockies,
color dots, etherscan links, formatting) and all state transitions
with their destination screens and conditions.
2026-02-27 12:07:11 +07:00
b1b8807060 Add Etherscan-style blockie identicon to address detail page
All checks were successful
check / check (push) Successful in 27s
Show a 48px pixelated blockie (same style as Etherscan) centered
above the wallet title on the address detail page. Uses
ethereum-blockies-base64 which generates a base64 PNG from the
address. Replaces the previously added @metamask/jazzicon.
2026-02-26 16:27:47 +07:00
9a6d1f6255 Add dust transaction filter to catch native ETH poisoning
Some checks failed
check / check (push) Has been cancelled
Address poisoning attacks also use real native ETH dust transfers
(e.g. 1 gwei) from look-alike addresses. Token-level filters cannot
catch these. Add a configurable dust threshold (default 100,000 gwei
/ 0.0001 ETH) that hides transactions below the threshold from
history. The threshold is editable in Settings and the filter can be
disabled entirely. Document the specific attack tx in the README.
2026-02-26 15:29:48 +07:00
d05de16e9c Document address poisoning attack and AutistMask defenses in README
Some checks failed
check / check (push) Has been cancelled
Detail the specific attack observed against our test address: a fake
ERC-20 contract emitting spoofed Transfer events to plant a look-alike
scam address in transaction history. Document all planned defenses:
known token symbol verification, low-holder filtering, fraud contract
blocklist, send-side filtering, and the principle that all filters are
user-configurable.
2026-02-26 13:57:24 +07:00
fbff44ade6 Fix tx amount display to 4 decimal places, add relative time to tx detail
Some checks failed
check / check (push) Has been cancelled
- Transaction values now use exactly 4 decimal places (was 6),
  matching balance display everywhere else
- Transaction detail view shows "2026-02-25 15:04:23 (23 days ago)"
  instead of just the ISO date
- Added Display Consistency policy to README
2026-02-26 03:19:42 +07:00
3bd2b58543 Token auto-discovery, tx history, balance polling, EIP-6963, UI overhaul
All checks were successful
check / check (push) Successful in 14s
Major changes:
- Fetch token balances and tx history from Blockscout API (configurable)
- Remove manual token discovery (discoverTokens) in favor of Blockscout
- HD address gap scanning on mnemonic import
- Duplicate mnemonic detection on wallet add
- EIP-6963 multi-wallet discovery + selectedAddress updates in inpage
- Two-tier balance refresh: 10s while popup open, 60s background
- Fix $0.00 flash before prices load (return null when no prices)
- No-layout-shift: min-height on total value element
- Aligned balance columns (42ch address width, consistent USD column)
- All errors use flash messages instead of off-screen error divs
- Settings gear in global title bar, add-wallet moved to settings pane
- Settings wells with light grey background, configurable Blockscout URL
- Consistent "< Back" buttons top-left on all views
- Address titles (Address 1.1, 1.2, etc.) on main and detail views
- Send view shows current balance of selected asset
- Clickable affordance policy added to README
- Shortened mnemonic backup warning
- Fix broken background script constant imports
2026-02-26 02:13:39 +07:00
f2e22cadf2 Encrypt secrets with libsodium, password required to send
All checks were successful
check / check (push) Successful in 14s
vault.js: Argon2id key derivation + XSalsa20-Poly1305 encryption
via libsodium-wrappers-sumo. No raw crypto primitives.

Wallet creation now requires a password. The mnemonic or private
key is encrypted before storage — only the ciphertext blob
(salt, nonce, ciphertext) is persisted. The plaintext secret
is never stored.

Sending requires the password to decrypt the secret, derive
the signing key, and construct the transaction. Wrong password
is caught and reported.
2026-02-25 18:23:09 +07:00
bfecddf2f7 Implement ETH send and QR code receive
All checks were successful
check / check (push) Successful in 22s
Send: stores mnemonic/private key with wallet data, derives
signing key from mnemonic + address index via ethers HDNodeWallet,
constructs transaction with parseEther, broadcasts via
sendTransaction, waits for confirmation, shows block number
and tx hash. ENS resolution in To field preserved.

Receive: QR code rendered to canvas via qrcode library (1.5.4).
Shows scannable QR above the full address text.

README updated with qrcode dependency and TODO progress.
2026-02-25 18:17:23 +07:00
fc3f0e00c8 Rewrite TODO as 0.1.0 MVP checklist, add screen map
All checks were successful
check / check (push) Successful in 5s
Full screen map with iOS-style stack navigation: Welcome, Home,
AddWallet, ImportKey, AddressDetail, Send, Receive, AddToken,
Settings, Approval. Each screen documents its elements and
transitions. TODO reorganized into Done, Wallet Management,
Sending, Receiving, Display, Tokens, Testing, and Post-MVP.
External Services updated to include CoinDesk price API.
2026-02-25 18:04:28 +07:00
1b806fb9e9 Store xpubs unencrypted, remove password from viewing flow
All checks were successful
check / check (push) Successful in 12s
Xpubs and derived addresses stored unencrypted in extension
storage for instant read-only access without a password.
Password will only be required for signing transactions
(not yet implemented). Real addresses now derived from
mnemonic via ethers HDNodeWallet at wallet creation time.
Removed lock screen, password fields, and Lock button.
BIP-39 mnemonic validation added. README updated with split
storage model documentation.
2026-02-25 16:13:22 +07:00
147ffbeb92 Document full identifiers policy in README
All checks were successful
check / check (push) Successful in 13s
Addresses, tx hashes, and contract addresses must always be
shown in full to prevent address poisoning attacks. Truncation
only acceptable when full identifier is one tap away.
2026-02-25 16:07:17 +07:00
b166a96e87 Remove DEBUG lock-screen bypass, add DEBUG mode policy
All checks were successful
check / check (push) Successful in 11s
DEBUG mode must behave identically to normal mode except for
the red banner and hardcoded mnemonic. No other DEBUG branches
without explicit owner approval. Policy documented in README.
2026-02-25 16:06:03 +07:00
da30c0667f Use ethers.js Mnemonic for real BIP-39 phrase generation
All checks were successful
check / check (push) Successful in 22s
Replace stub wordlist with ethers.Mnemonic.fromEntropy() using
crypto.getRandomValues(). Add esbuild to bundle popup JS so it
can import ethers directly — no background messaging needed.
Each die click now generates a valid, random BIP-39 mnemonic.
2026-02-25 15:40:41 +07:00
a967029511 Add ethers.js and libsodium, document crypto policy
All checks were successful
check / check (push) Successful in 21s
Runtime deps: ethers 6.16.0 (all Ethereum operations) and
libsodium-wrappers-sumo 0.8.2 (Argon2id + XSalsa20-Poly1305 for
encrypting secrets at rest). README now documents all dependencies
with versions/licenses, a crypto policy forbidding raw primitives
in application code, and the updated encryption scheme.
2026-02-25 15:37:49 +07:00