README: second rework after review
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s

Fix the 23 findings of the second review and the two leftovers from the
first: SiteApproval's connection lifetime, the stateSchema.js module line,
the flash line and title bar, the six network-bound screens, the token-only
0.000001 cutoff, the 12-character symbol cap, the time display, the native
currency labels, the truncateAmountNeverZero screens, the browser suite
rules, the build receipt contents, the External services list, the worker
memory exceptions, POPUP_ONLY_TYPES, the undefined terms, the comma lists,
the Rationale, Entrypoints and TODO sections, and the dropped crypto comment
rule. A fact pass then checked every statement about the code and every
absolute against each case, and a newcomer read removed repeats.

Model: fable-5-1
This commit is contained in:
2026-11-02 02:15:46 +00:00
parent a17563b5bc
commit 93b54c85e8
+236 -178
View File
@@ -1,10 +1,9 @@
# AutistMask # AutistMask
AutistMask is a browser extension. It is an Ethereum wallet for Chrome and AutistMask is a GPL-3.0-licensed browser extension by
Firefox. It is free software under the GPL-3.0 license, written by [@sneak](https://sneak.berlin): an Ethereum wallet for Chrome and Firefox.
[@sneak](https://sneak.berlin).
The wallet does four things: AutistMask does four things:
- It holds wallets made from a recovery phrase, a private key or an extended - It holds wallets made from a recovery phrase, a private key or an extended
private key. private key.
@@ -20,7 +19,7 @@ It does nothing else. It has:
- no advertising; - no advertising;
- no tracking. - no tracking.
## Why it exists ## Rationale
Popular browser wallets have grown: Popular browser wallets have grown:
@@ -48,7 +47,8 @@ Settings. The wallet uses that endpoint to:
The RPC endpoint sees every address that the wallet queries and every The RPC endpoint sees every address that the wallet queries and every
transaction that the wallet sends. A user who wants privacy can point the RPC transaction that the wallet sends. A user who wants privacy can point the RPC
URL, and the Blockscout URL, at a node of their own. URL at a node of their own, and the Blockscout URL at a Blockscout server of
their own.
Two more services receive the user's data only after the user turns on a Two more services receive the user's data only after the user turns on a
setting. Both settings are off in a new install. The Welcome screen and Settings setting. Both settings are off in a new install. The Welcome screen and Settings
@@ -67,7 +67,7 @@ sends nothing anywhere.
The section External services below lists each request. The section External services below lists each request.
## Getting started ## Getting Started
Build the extension from a clone: Build the extension from a clone:
@@ -113,12 +113,12 @@ which this repository does not produce.
`manifest/firefox.json` requires Firefox 115 or newer. `manifest/firefox.json` requires Firefox 115 or newer.
The `.xpi` that `make package` writes is not signed. Release Firefox and Firefox The `.xpi` (Firefox's add-on package file) that `make package` writes is not
ESR (the extended support release) refuse to install an unsigned add-on, and signed. Release Firefox and Firefox ESR (the extended support release) refuse to
`xpinstall.signatures.required` has no effect on them. A permanent install of install an unsigned add-on, and `xpinstall.signatures.required` has no effect on
the unsigned file needs Firefox Developer Edition, Nightly or an unbranded build them. A permanent install of the unsigned file needs Firefox Developer Edition,
(Mozilla's build without the Firefox branding). On those, Nightly or an unbranded build (Mozilla's build without the Firefox branding). On
`xpinstall.signatures.required` must be set to `false` in `about:config`. those, `xpinstall.signatures.required` must be set to `false` in `about:config`.
Every Firefox, release builds included, can load the extension as a temporary Every Firefox, release builds included, can load the extension as a temporary
add-on from `about:debugging`. A temporary add-on is unloaded when Firefox add-on from `about:debugging`. A temporary add-on is unloaded when Firefox
@@ -173,8 +173,8 @@ offers "Use Blockscout" and "Check recipients on Etherscan".
- "Allow weak passwords": off. While off, AddWallet refuses a password shorter - "Allow weak passwords": off. While off, AddWallet refuses a password shorter
than 12 characters. While on, one character is enough. than 12 characters. While on, one character is enough.
- "Show tracked tokens with zero balance": on. - "Show tracked tokens with zero balance": on.
- "UTC Timestamps": off. While off, timestamps are shown in local time with the - "UTC Timestamps": off. While off, a transaction's datetime is shown in local
UTC offset. time with the UTC offset.
- Theme: `System`, `Light` or `Dark`. Default `System`. - Theme: `System`, `Light` or `Dark`. Default `System`.
- "Hide fake tokens impersonating a known symbol": on. - "Hide fake tokens impersonating a known symbol": on.
- "Hide tokens with fewer than 1,000 holders": on. - "Hide tokens with fewer than 1,000 holders": on.
@@ -184,9 +184,9 @@ offers "Use Blockscout" and "Check recipients on Etherscan".
- Tracked Tokens: the ERC-20 tokens tracked on the active network. Each network - Tracked Tokens: the ERC-20 tokens tracked on the active network. Each network
has its own list. has its own list.
- Allowed Sites: the sites allowed with "Remember my choice for this site". - Allowed Sites: the sites allowed with "Remember my choice for this site".
- Connected Sites: the sites allowed without it. Such a connection ends when the - Connected Sites: the sites allowed without "Remember my choice for this site".
browser closes, when the active address changes, or when the site is removed Such a connection ends when the browser closes, when the active address
in Settings. changes, or when the site is removed in Settings.
- Denied Sites: the sites denied with "Remember my choice for this site". - Denied Sites: the sites denied with "Remember my choice for this site".
- "Remember my choice for this site": the checkbox on SiteApproval. Its last - "Remember my choice for this site": the checkbox on SiteApproval. Its last
state is remembered. Default ticked. state is remembered. Default ticked.
@@ -222,8 +222,8 @@ src/
index.html the markup of every screen index.html the markup of every screen
index.js popup entry point: loads the profile, restores the index.js popup entry point: loads the profile, restores the
screen, runs the refresh screen, runs the refresh
viewRouter.js renders a screen the popup lands on without navigating viewRouter.js draws a screen that "Back" returns to when this page
to it load has not drawn it yet
theme.js applies the theme theme.js applies the theme
dustThreshold.js parses the dust threshold field dustThreshold.js parses the dust threshold field
styles/main.css Tailwind source, plus four classes of its own styles/main.css Tailwind source, plus four classes of its own
@@ -234,8 +234,9 @@ manifest/
firefox.json Manifest V2 for Firefox firefox.json Manifest V2 for Firefox
icons/ the toolbar icon at the four sizes the manifests declare icons/ the toolbar icon at the four sizes the manifests declare
build.js bundles the extension into dist/ build.js bundles the extension into dist/
script/ the entry points (see Development) script/ the entrypoints (see Development)
tests/ jest unit tests; tests/e2e/ holds the browser suites tests/ jest unit tests and their helpers; tests/e2e/ holds the
browser suites
``` ```
The shared modules, each in one line: The shared modules, each in one line:
@@ -253,7 +254,8 @@ The shared modules, each in one line:
address scan on import. address scan on import.
- `browserApi.js`: the one module that names `chrome` or `browser`; storage, - `browserApi.js`: the one module that names `chrome` or `browser`; storage,
messaging, windows and the lock. messaging, windows and the lock.
- `buildInfo.js`: the version, commit and date compiled into About. - `buildInfo.js`: the version, license, author, commit and date compiled into
About.
- `chainSwitch.js`: the popup's network switch. - `chainSwitch.js`: the popup's network switch.
- `chainSwitchFields.js`: the fields a network switch changes. - `chainSwitchFields.js`: the fields a network switch changes.
- `constants.js`: `DEBUG`, the default endpoints, the ERC-20 ABI and the burn - `constants.js`: `DEBUG`, the default endpoints, the ERC-20 ABI and the burn
@@ -272,8 +274,8 @@ The shared modules, each in one line:
- `restorableViews.js`: the screens a reopened popup may land on. - `restorableViews.js`: the screens a reopened popup may land on.
- `scamlist.js`: known scam addresses. - `scamlist.js`: known scam addresses.
- `state.js`: the popup's in-memory profile and `saveState()`. - `state.js`: the popup's in-memory profile and `saveState()`.
- `stateSchema.js`: `schemaVersion` and the check every profile read goes - `stateSchema.js`: `schemaVersion` and `assertStateUsable()`, the check that
through. the two state modules run on each read.
- `storedSettings.js`: reads "Use Blockscout" and "Check recipients on - `storedSettings.js`: reads "Use Blockscout" and "Check recipients on
Etherscan" from storage. Etherscan" from storage.
- `symbolDisplay.js`: the 12-character cap on a displayed symbol. - `symbolDisplay.js`: the 12-character cap on a displayed symbol.
@@ -287,8 +289,8 @@ The shared modules, each in one line:
- `uniswap.js`: the Uniswap Universal Router decoder. - `uniswap.js`: the Uniswap Universal Router decoder.
- `vault.js`: password-based encryption with libsodium. - `vault.js`: password-based encryption with libsodium.
- `wallet.js`: recovery phrases, key derivation and signers. - `wallet.js`: recovery phrases, key derivation and signers.
- `walletDefects.js`: the notice for an xprv wallet whose stored key is not a - `walletDefects.js`: the notice for an xprv wallet imported from a key that was
master key. not a master key.
- `walletDelete.js`: removing a wallet or an address from the profile. - `walletDelete.js`: removing a wallet or an address from the profile.
### Wallets, keys and encryption ### Wallets, keys and encryption
@@ -301,14 +303,18 @@ A wallet is one of three types:
- An xprv wallet (`type: "xprv"`) comes from an extended private key. It behaves - An xprv wallet (`type: "xprv"`) comes from an extended private key. It behaves
like an HD wallet but has no recovery phrase. like an HD wallet but has no recovery phrase.
All key handling goes through `ethers`. Addresses are derived on the path All key derivation and signing goes through `ethers`. Addresses are derived on
`m/44'/60'/0'/0/n`, with no BIP-39 passphrase (the optional extra word some the path `m/44'/60'/0'/0/n`, with no BIP-39 passphrase (the optional extra word
wallets add to a phrase). A new recovery phrase has 12 words. An extended some wallets add to a phrase). A new recovery phrase has 12 words. An extended
private key must be a master key, the root of the key tree. AddWallet refuses a private key must be a master key, the root of the key tree. AddWallet refuses a
child key, one derived below the root. Each HD or xprv address record stores its child key, one derived below the root. Each HD or xprv address record stores its
`index`, and signing derives the key at that index. `getSignerForAddress()` in `index`, and signing derives the key at that index. `getSignerForAddress()` in
`src/shared/wallet.js` refuses a key that does not belong to the address. `src/shared/wallet.js` refuses a key that does not belong to the address.
An xprv wallet imported from a key that was not a master key carries a notice in
the wallet list (`src/shared/walletDefects.js`). Such a wallet shows no "+"
button, and Send, site signing and private key export refuse it.
The secret of each wallet is encrypted with that wallet's password The secret of each wallet is encrypted with that wallet's password
(`src/shared/vault.js`): (`src/shared/vault.js`):
@@ -322,12 +328,14 @@ The password is not part of address derivation. Anyone holding the recovery
phrase can restore the wallet elsewhere without it. The password protects the phrase can restore the wallet elsewhere without it. The password protects the
copy stored in this browser and nothing else. copy stored in this browser and nothing else.
Public data is stored unencrypted: Only the wallet secrets are encrypted. Everything else in the profile is stored
as plain data, among it:
- the `xpub` of each HD or xprv wallet, the extended public key that its - the `xpub` of each HD or xprv wallet, the extended public key that its
addresses are derived from; addresses are derived from;
- the addresses and their balances; - the addresses and their balances;
- the tracked tokens; - the tracked tokens;
- the site lists;
- the settings. - the settings.
The popup shows all of that without a password. The password is required to: The popup shows all of that without a password. The password is required to:
@@ -350,12 +358,12 @@ which stays loaded. Both take the same code paths.
Three rules follow from the worker being stopped: Three rules follow from the worker being stopped:
- Nothing is kept in module variables between events. The worker reads the - The profile is never kept in module variables. The worker reads it from
profile from storage on every call (`getState()` in `src/background/state.js`) storage on every call (`getState()` in `src/background/state.js`) and writes
and writes it back under the lock (`updateState()`). it back under the lock (`updateState()`).
- The recurring balance refresh runs off an alarm (`src/shared/alarms.js`), not - The recurring balance refresh runs off an alarm (`src/shared/alarms.js`), not
a timer. The browser holds the schedule and wakes the worker. The period is a timer. The browser holds the schedule and wakes the worker. The period is
one minute. The refresh is skipped when the last refresh finished less than 30 one minute. The refresh is skipped when the last refresh started less than 30
seconds ago. An open popup refreshes every 10 seconds, so while the popup is seconds ago. An open popup refreshes every 10 seconds, so while the popup is
open the background stays quiet. open the background stays quiet.
- Connections allowed without "Remember my choice for this site" are kept in the - Connections allowed without "Remember my choice for this site" are kept in the
@@ -363,11 +371,12 @@ Three rules follow from the worker being stopped:
worker stopping. Such a connection ends when the browser closes, when the worker stopping. Such a connection ends when the browser closes, when the
active address changes, or when the site is removed in Settings. active address changes, or when the site is removed in Settings.
Pending approvals are the exception. An approval, and the site's request waiting Pending approvals and the record of broadcast nonces are the exceptions. Both
on it, live in the worker's memory. While any approval is pending, the worker live in the worker's memory, and both are lost when the worker stops. An
calls `runtime.getPlatformInfo()` every 20 seconds. Chrome counts that call as approval, and the site's request waiting on it, exist nowhere else. So while any
activity and keeps the worker running. The timer stops itself once nothing is approval is pending, the worker calls `runtime.getPlatformInfo()` every 20
pending. seconds. Chrome counts that call as activity and keeps the worker running. The
timer stops itself once nothing is pending.
Alarms the code no longer handles are listed in `OBSOLETE_ALARMS` and cleared on Alarms the code no longer handles are listed in `OBSOLETE_ALARMS` and cleared on
every start. The worker fetches nothing when it starts. every start. The worker fetches nothing when it starts.
@@ -431,36 +440,40 @@ Home otherwise. The Settings gear in the title bar is the only global control.
The popup saves the current screen. Reopening the popup lands on that screen The popup saves the current screen. Reopening the popup lands on that screen
when the screen is in `RESTORABLE_VIEWS` (`src/shared/restorableViews.js`). when the screen is in `RESTORABLE_VIEWS` (`src/shared/restorableViews.js`).
Every other screen falls back to Home. The two screens that show a secret, Every other screen falls back to Home. A restorable screen whose data is gone
ExportPrivKey and ShowRecoveryPhrase, are not restorable. Neither are the two falls back to Home too. The two screens that show a secret, ExportPrivKey and
that erase a wallet, DeleteWallet and DeleteWalletLostPassword. ShowRecoveryPhrase, are not restorable. Neither are the two that erase a wallet,
DeleteWallet and DeleteWalletLostPassword.
A reopened popup renders only the wallet list and the screen that it lands on. A reopened popup draws only the wallet list and the screen that it lands on.
"Back" onto a screen that this page load has not rendered renders that screen When "Back" returns to a screen that this page load has not drawn yet,
first (`src/popup/viewRouter.js`). When the data for that screen is gone, Home `src/popup/viewRouter.js` draws it first. When the data for that screen is gone,
is shown instead. "Back" onto a screen already on the page only unhides it, so Home is shown instead. "Back" onto a screen already drawn only unhides it, so
unsaved input in Settings survives. Home is rendered again on every "Back". unsaved input in Settings survives. Home is drawn again on every "Back".
A network switch, or a change to "Use Blockscout", cuts the stack at the first A network switch, or a change to "Use Blockscout", cuts the stack at the first
screen that cannot be rendered from saved state. Every screen then counts as not screen that cannot be drawn from saved state. The cut also drops AddressToken
rendered. Settings on top is redrawn in place. Any other screen is redrawn as a unless it shows ETH. After either change, and after a change to "Check
reopened popup would draw it. Five screens are drawn for the network of the recipients on Etherscan", every screen but Settings counts as not drawn.
transaction that they show, whichever network is active: Settings on top is redrawn in place. Any other screen is redrawn as a reopened
popup would draw it. Six screens are drawn for the network of the transaction
that they show, whichever network is active:
- ConfirmTx; - ConfirmTx;
- WaitTx; - WaitTx;
- SuccessTx; - SuccessTx;
- ErrorTx; - ErrorTx;
- TransactionDetail. - TransactionDetail;
- TxApproval.
A screen that holds a secret in the page registers a cleanup with A screen that holds a secret in the page registers a cleanup with
`onViewLeave()`. `showView()` runs the cleanup on every exit from that screen. `onViewLeave()`. `showView()` runs the cleanup on every exit from that screen.
So a revealed key or phrase, or a typed password, never survives in a hidden So a revealed key or phrase, or a typed password, never survives in a hidden
screen. screen.
Two things are present on every screen but StateRecovery: the title bar with the Every screen has the title bar and the flash message line under it. A flash
Settings gear, and the flash message line under it. The flash line holds one message stays two seconds unless the screen sets another time. StateRecovery
line of text for two seconds. hides the Settings gear in the title bar.
On Sepolia, or with debug on, a red banner sits at the top of the page. It reads On Sepolia, or with debug on, a red banner sits at the top of the page. It reads
`[TESTNET]` on Sepolia, `DEBUG / INSECURE` with debug on, and `[TESTNET]` on Sepolia, `DEBUG / INSECURE` with debug on, and
@@ -493,10 +506,14 @@ Ticking a checkbox saves the setting at once. "Add wallet" opens AddWallet.
Shown while at least one wallet exists. The screen holds: Shown while at least one wallet exists. The screen holds:
- the active address's ETH balance, with its USD value in parentheses; - the active address's ETH balance, with its USD value in parentheses when a
price is known;
- a "Total:" line with the USD value of the address's holdings (see Partial - a "Total:" line with the USD value of the address's holdings (see Partial
totals below); totals below);
- the active address: colour dot, full address, explorer link, tap to copy; - the active address:
- its colour dot;
- the full address. Tapping it copies it;
- an explorer link;
- "Send" and "Receive" buttons for the active address; - "Send" and "Receive" buttons for the active address;
- the ETH/USD price, on mainnet; - the ETH/USD price, on mainnet;
- the wallet list; - the wallet list;
@@ -506,8 +523,9 @@ Shown while at least one wallet exists. The screen holds:
- an "Add additional wallet..." link. - an "Add additional wallet..." link.
Each wallet in the list shows its name. Tapping the name renames it in place. HD Each wallet in the list shows its name. Tapping the name renames it in place. HD
and xprv wallets also show a "+" button that derives the next address. Each and xprv wallets also show a "+" button that derives the next address, unless
address of a wallet is a block: the wallet carries the notice from `src/shared/walletDefects.js`. Each address
of a wallet is a block:
- a row with the colour dot and "Address N", bold when the address is active; - a row with the colour dot and "Address N", bold when the address is active;
- on that row, an `[info]` button and, on HD and xprv wallets holding more than - on that row, an `[info]` button and, on HD and xprv wallets holding more than
@@ -539,7 +557,7 @@ Transitions:
- `[info]`: AddressDetail. - `[info]`: AddressDetail.
- `[x]`: DeleteAddress. - `[x]`: DeleteAddress.
- "Send": Send. Refused with a flash message when the active address has a zero - "Send": Send. Refused with a flash message when the active address has a zero
ETH balance. ETH balance or belongs to a wallet carrying the `walletDefects.js` notice.
- "Receive": Receive. - "Receive": Receive.
- Tap a transaction row: TransactionDetail. - Tap a transaction row: TransactionDetail.
- "Add additional wallet...": AddWallet. - "Add additional wallet...": AddWallet.
@@ -572,11 +590,12 @@ the RPC endpoint, five at a time. The scan stops after five unused addresses in
a row. When the scan finds two or more used addresses, they replace the wallet's a row. When the scan finds two or more used addresses, they replace the wallet's
list. The flash line reports the count. list. The flash line reports the count.
Refusals: A missing, short or mismatched password gets a full sentence on the error line.
A flash message refuses:
- a missing, short or mismatched password: a full sentence on the error line; - an invalid phrase or key;
- an invalid phrase or key, an extended private key that is not a master key, or - an extended private key that is not a master key;
a phrase or address already in a wallet: a flash message. - a phrase or address already in a wallet.
"Back" returns to the previous screen. "Back" returns to the previous screen.
@@ -587,7 +606,8 @@ Reached from `[info]` on Home. The screen holds:
- a "Back" button; - a "Back" button;
- the address's identicon; - the address's identicon;
- the title "Wallet Name — Address N"; - the title "Wallet Name — Address N";
- the full address with its ENS name, explorer link and tap to copy; - the full address, with its ENS name and an explorer link. Tapping the address
copies it;
- the address's USD total; - the address's USD total;
- the balance list: ETH, then each token shown for the address. Each row opens - the balance list: ETH, then each token shown for the address. Each row opens
AddressToken; AddressToken;
@@ -601,7 +621,7 @@ Transitions:
- a balance row: AddressToken; - a balance row: AddressToken;
- "Send": Send. Refused with a flash message when the address has a zero ETH - "Send": Send. Refused with a flash message when the address has a zero ETH
balance; balance or belongs to a wallet carrying the `walletDefects.js` notice;
- "Receive": Receive; - "Receive": Receive;
- "+ Token": AddToken; - "+ Token": AddToken;
- "Export Private Key": ExportPrivKey; - "Export Private Key": ExportPrivKey;
@@ -650,8 +670,9 @@ Reached by tapping a balance row on AddressDetail. The screen holds:
"Use Blockscout" is off. "Use Blockscout" is off.
"Send" opens Send with the token locked: the dropdown is replaced by the symbol "Send" opens Send with the token locked: the dropdown is replaced by the symbol
and contract address. "Receive" opens Receive with the ERC-20 warning. A and contract address. "Receive" opens Receive, with the ERC-20 warning for a
transaction row opens TransactionDetail. "Back" returns to AddressDetail. token. A transaction row opens TransactionDetail. "Back" returns to
AddressDetail.
#### Send (`send`) #### Send (`send`)
@@ -669,7 +690,7 @@ Reached from Home, AddressDetail or AddressToken. The screen holds:
The dropdown offers ETH and the tokens of the address's balance list. It leaves The dropdown offers ETH and the tokens of the address's balance list. It leaves
out: out:
- holdings below 0.000001; - token holdings below 0.000001;
- tokens with a spoofed symbol; - tokens with a spoofed symbol;
- tokens on the fraud contract blocklist; - tokens on the fraud contract blocklist;
- while "Hide tokens with fewer than 1,000 holders" is on, tokens with a - while "Hide tokens with fewer than 1,000 holders" is on, tokens with a
@@ -799,8 +820,8 @@ Reached from Home, AddressDetail or AddressToken. The screen holds:
- a QR code of the address; - a QR code of the address;
- the full address; - the full address;
- a "Copy address" button; - a "Copy address" button;
- when opened from a token's screen, a warning to send only that token on this - when opened from an ERC-20 token's screen, a warning to send only that token
network to this address. on this network to this address.
"Back" returns to the previous screen. "Back" returns to the previous screen.
@@ -848,7 +869,8 @@ Reached from "+ Token" on AddressDetail. The screen holds:
"Add" looks up `symbol()`, `decimals()` and `name()` on the contract through the "Add" looks up `symbol()`, `decimals()` and `name()` on the contract through the
RPC endpoint. On success it adds the token to the tracked list, pops the stack RPC endpoint. On success it adds the token to the tracked list, pops the stack
and shows AddressDetail. A flash message refuses: and shows AddressDetail. If another screen is showing by then, the screen does
not change. A flash message refuses:
- a token already tracked; - a token already tracked;
- an address on the scam list; - an address on the scam list;
@@ -862,8 +884,8 @@ says so.
Reached from the gear. The screen holds, under headings: Reached from the gear. The screen holds, under headings:
- Wallets: one row per wallet, with "+ Add wallet" under them. A row holds the - Wallets: one row per wallet, with "+ Add wallet" under them. A row holds the
name (tap to rename), a `[recovery phrase]` button on HD wallets, and an `[x]` name, a `[recovery phrase]` button on HD wallets, and an `[x]` button. Tapping
button. the name renames it in place.
- Passwords: the "Allow weak passwords" checkbox. - Passwords: the "Allow weak passwords" checkbox.
- Tracked Tokens: one row per token tracked on the active network with an `[x]` - Tracked Tokens: one row per token tracked on the active network with an `[x]`
button, and "+ Add token". button, and "+ Add token".
@@ -898,8 +920,9 @@ Transitions:
- Switching the network restores the RPC and Blockscout URLs last used on that - Switching the network restores the RPC and Blockscout URLs last used on that
network, or the network's defaults, and the tokens tracked there. It clears network, or the network's defaults, and the tokens tracked there. It clears
every address's balances. It tells every open tab the new chain id with every address's balances and the fraud contract blocklist. It tells every open
`chainChanged`. Choosing the network already active changes nothing. tab the new chain id with `chainChanged`. Choosing the network already active
changes nothing.
- Ticking or unticking "Use Blockscout" or "Check recipients on Etherscan" - Ticking or unticking "Use Blockscout" or "Check recipients on Etherscan"
stores the setting. From then on every screen and every refresh uses the new stores the setting. From then on every screen and every refresh uses the new
value. Unticking "Use Blockscout" also drops every untracked token from every value. Unticking "Use Blockscout" also drops every untracked token from every
@@ -1004,8 +1027,8 @@ separate window. The screen holds:
- "Allow" and "Deny" buttons. - "Allow" and "Deny" buttons.
"Allow" with "Remember" ticked adds the origin to Allowed Sites. "Allow" without "Allow" with "Remember" ticked adds the origin to Allowed Sites. "Allow" without
it connects the site until the browser closes. "Deny" with "Remember" ticked it adds the site to Connected Sites (see Settings). "Deny" with "Remember"
adds the origin to Denied Sites. Closing the popup without an answer is a ticked adds the origin to Denied Sites. Closing the popup without an answer is a
denial. denial.
#### TxApproval (`approve-tx`) #### TxApproval (`approve-tx`)
@@ -1061,11 +1084,11 @@ as a personal message.
Typed data is shown as its domain, the primary type that `ethers` will sign, and Typed data is shown as its domain, the primary type that `ethers` will sign, and
the message fields that this type declares, in the type's order. Keys that the the message fields that this type declares, in the type's order. Keys that the
type does not declare are listed under "Not part of the signature". Typed data type does not declare are listed under "Not part of the signature". A red
whose primary type is `Permit`, or one of the signature types of Permit2 warning heads the message when the primary type is `Permit` or a Permit2
(Uniswap's token allowance contract), gets a red warning at the top. The warning signature type. Permit2 is Uniswap's token allowance contract. The warning names
names the spender, and each token and amount that the signature lets the spender the spender, and each token and amount that the signature lets the spender take.
take. `Unlimited` marks the field's largest value. `Unknown` marks anything the `Unlimited` marks the field's largest value. `Unknown` marks anything the
declared fields do not give. declared fields do not give.
"Sign" is disabled, with the reason on the error line, when: "Sign" is disabled, with the reason on the error line, when:
@@ -1112,15 +1135,23 @@ popup closes.
### Amounts ### Amounts
The same data is formatted the same way on every screen. Token and ETH amounts These helpers write one kind of value on every screen that shows it:
show four decimal places. History rows cut amounts to four decimals and never
round up (`truncateAmount()` in `src/shared/amountDisplay.js`). The balance - `formatUsd()` in `src/shared/prices.js`: USD values;
lists and Home's ETH balance round to four places. The screens that state what - `isoDate()` and `timeAgo()` in `src/popup/views/helpers.js`: times;
is being authorized cut as the history does, and extend an amount below 0.0001 - `renderAddressHtml()` in the same file: most addresses.
to its first nonzero digit (`truncateAmountNeverZero()`). So a nonzero amount
never reads as zero there. Those screens are: Amounts are written by the two helpers below, or rounded by the balance lists.
Most screens show token and ETH amounts with four decimal places. History rows
cut amounts to four decimals and never round up (`truncateAmount()` in
`src/shared/amountDisplay.js`). The balance lists and Home's ETH balance round
to four places. The screens that state what is being authorized cut as the
history does, and extend an amount below 0.0001 to its first nonzero digit
(`truncateAmountNeverZero()`). So a nonzero amount never reads as zero there.
Those screens are:
- TxApproval; - TxApproval;
- SignApproval's Permit warning;
- ConfirmTx, and Send's current balance; - ConfirmTx, and Send's current balance;
- WaitTx, SuccessTx and ErrorTx. - WaitTx, SuccessTx and ErrorTx.
@@ -1149,7 +1180,8 @@ as a token's decimals. A token's `decimals` is stored only as a source reported
it, never guessed. A `null` balance means the decimals are unknown, never that it, never guessed. A `null` balance means the decimals are unknown, never that
the balance is zero. While "Use Blockscout" is off, the approval and status the balance is zero. While "Use Blockscout" is off, the approval and status
screens add a line under a token that neither the bundled list nor the tracked screens add a line under a token that neither the bundled list nor the tracked
tokens name. The line says that Blockscout's report is not used. tokens name or give decimals for. The line says that Blockscout's report is not
used.
The wallet's own ERC-20 send is encoded with the decimals the screen showed. At The wallet's own ERC-20 send is encoded with the decimals the screen showed. At
signing time the contract's `decimals()` is read only to be compared with those. signing time the contract's `decimals()` is read only to be compared with those.
@@ -1158,21 +1190,23 @@ A disagreement refuses the send (`src/shared/transferAmount.js`).
A native amount is labelled with the native currency of the transaction's chain: A native amount is labelled with the native currency of the transaction's chain:
`ETH` on mainnet, `SepoliaETH` on Sepolia (`nativeCurrencyByChainId()` in `ETH` on mainnet, `SepoliaETH` on Sepolia (`nativeCurrencyByChainId()` in
`src/shared/networks.js`). The balance lists and Send use the active network's. `src/shared/networks.js`). The balance lists and Send use the active network's.
The decoded swap lines call the native currency `ETH` on both networks.
A transaction's time is shown as an ISO datetime and a relative age, written by TransactionDetail shows the time as an ISO datetime and a relative age. A
`isoDate()` and `timeAgo()` in `src/popup/views/helpers.js`. With "UTC history row shows the age, with the datetime as its tooltip. With "UTC
Timestamps" on, the datetime is in UTC. Timestamps" on, the datetime is in UTC.
Every nonzero holding is stored exactly. A holding below 0.000001 is left out Every nonzero holding is stored exactly. A token holding below 0.000001 is left
of: out of:
- the balance lists; - the balance lists;
- the Send dropdown; - the Send dropdown;
- the address total; - the address total;
- the DeleteAddress warning. - the DeleteAddress warning.
A displayed token symbol is cut to 12 characters Most screens cut a token symbol to 12 characters
(`src/shared/symbolDisplay.js`). (`src/shared/symbolDisplay.js`). The approval screens, the decoded calldata
lines and Send's "Current balance" line show it in full.
#### Partial totals #### Partial totals
@@ -1189,12 +1223,11 @@ same way on every screen:
### Addresses ### Addresses
Every address is shown in full, on a row of its own that never wraps (the Every address is shown in full, on a row of its own that never wraps (the
`.am-address` class in `src/popup/styles/main.css`). No screen shortens an `.am-address` class in `src/popup/styles/main.css`). No caller shortens an
address shown as an address. `truncateMiddle()` in `src/popup/views/helpers.js` address. `truncateMiddle()` in `src/popup/views/helpers.js` removes at most 10
removes at most 10 characters, and `renderAddressHtml()` never shortens an characters, and `renderAddressHtml()` keeps at least 32. A token with no known
address below 32 characters, but no caller shortens one today. A token with no symbol is labelled by the first 10 characters of its contract address and `…`
known symbol is labelled by the first 10 characters of its contract address and (`resolveSymbol()` in `src/shared/tokenList.js`); Send and AddressToken show
`…` (`resolveSymbol()` in `src/shared/tokenList.js`); Send and AddressToken show
that label. that label.
On most screens, tapping an address copies it, and an explorer link sits beside On most screens, tapping an address copies it, and an explorer link sits beside
@@ -1203,12 +1236,13 @@ Each explorer link opens the block explorer of the address's network:
`etherscan.io` on mainnet and `sepolia.etherscan.io` on Sepolia. The links `etherscan.io` on mainnet and `sepolia.etherscan.io` on Sepolia. The links
contact nothing until clicked. contact nothing until clicked.
Each address gets a colour dot chosen from its first four hex digits, and an Each address gets a colour dot chosen from its first four hex digits. An
identicon on the screens where space allows. identicon appears on AddressDetail, AddressToken, ExportPrivKey, ConfirmTx and
TransactionDetail.
### Scam protection ### Scam protection
Five defenses are on by default. Six defenses are built in. Four of them are settings, on by default.
Known symbol spoofing. `KNOWN_SYMBOLS` in `src/shared/tokenList.js` maps each Known symbol spoofing. `KNOWN_SYMBOLS` in `src/shared/tokenList.js` maps each
symbol of the bundled list to the contracts that may bear it. `ETH` and every symbol of the bundled list to the contracts that may bear it. `ETH` and every
@@ -1232,8 +1266,9 @@ number written in digits counts as a count (`src/shared/holders.js`).
Fraud contract blocklist. Detecting a spoofed transfer adds its contract to Fraud contract blocklist. Detecting a spoofed transfer adds its contract to
`fraudContracts` in the profile. While "Hide transactions from detected fraud `fraudContracts` in the profile. While "Hide transactions from detected fraud
contracts" is on, the history hides transfers from those contracts. The Send contracts" is on, the history hides transfers from those contracts. The Send
dropdown always leaves them out. Turning the spoof setting off also stops new dropdown always leaves them out. Turning off "Hide fake tokens impersonating a
entries being learned. known symbol" also stops new entries being added. A network switch clears the
list.
Dust transactions. While "Hide dust transactions below N gwei" is on, the Dust transactions. While "Hide dust transactions below N gwei" is on, the
history hides native transfers below the threshold. Contract calls and token history hides native transfers below the threshold. Contract calls and token
@@ -1245,15 +1280,7 @@ has approved them). ConfirmTx warns before a send to one of them. AddToken and
SettingsAddToken refuse to track one. The list holds addresses involved in fraud SettingsAddToken refuse to track one. The list holds addresses involved in fraud
only. It is not a sanctions list. only. It is not a sanctions list.
ConfirmTx also warns when the recipient is: The other warnings on ConfirmTx are listed under that screen.
- a contract;
- a burn address;
- the sending address itself;
- an address that has never sent a transaction and holds none of the network's
native token;
- while "Check recipients on Etherscan" is on, an address Etherscan labels as
phishing, scam or exploiter.
#### Phishing domains #### Phishing domains
@@ -1292,17 +1319,18 @@ when both tokens are known. The lines are:
- `Protocol`: `Uniswap Universal Router`, with the router's address. - `Protocol`: `Uniswap Universal Router`, with the router's address.
- `Token In` and `Token Out`: the currencies the swap spends and buys. - `Token In` and `Token Out`: the currencies the swap spends and buys.
`ETH (native)` for ETH. `Unknown (not named in the calldata)` when no step `Token In` reads `ETH (native)` for ETH; `Token Out` reads `ETH`.
names one. `Unknown (could not be decoded)` when a V4 swap action could not be `Unknown (not named in the calldata)` when no step names one.
read or the transaction has an `EXECUTE_SUB_PLAN` step. `Unknown (could not be decoded)` when a V4 swap action could not be read or
the transaction has an `EXECUTE_SUB_PLAN` step.
- `Amount`: the total that the steps take of `Token In` from the user. `Up to` - `Amount`: the total that the steps take of `Token In` from the user. `Up to`
is prefixed when the transaction has a V2 or V3 exact-out step. `Unlimited` is prefixed when the transaction has a V2 or V3 exact-out step. `Unlimited`
marks an amount at or above the `uint160` maximum. marks an amount at or above the `uint160` maximum.
`All available (V4 open delta)` and `All available (V4 open delta)` and
`Whatever an earlier step sent to the pair (V2 already paid)` name the two `Whatever an earlier step sent to the pair (V2 already paid)` name the two
amounts that are not quantities. The line is left out when the decoder cannot amounts that are not quantities. The line is left out when the decoder cannot
bound what leaves the wallet; each amount that it did read then gets lines of tell the most that can leave the wallet; each amount that it did read then
its own. gets lines of its own.
- `Moved from the sender`, `Token moved` and `Moved to`: an amount taken from - `Moved from the sender`, `Token moved` and `Moved to`: an amount taken from
the sender that the `Amount` line does not include. The same three lines show the sender that the `Amount` line does not include. The same three lines show
what a `PERMIT2_TRANSFER_FROM` moves and where. what a `PERMIT2_TRANSFER_FROM` moves and where.
@@ -1323,10 +1351,11 @@ when both tokens are known. The lines are:
- no `Paid to` line is shown; - no `Paid to` line is shown;
- no allowance goes to a spender other than the router. - no allowance goes to a spender other than the router.
- `Steps`: each command by name, in order. A step that the router lets fail - `Steps`: each command by name, in order. A step that the router lets fail
without reverting is marked `(allowed to fail)`. A `BALANCE_CHECK_ERC20` step without reverting is marked `(allowed to fail)`. An `EXECUTE_SUB_PLAN` step is
that the decoder could not read, and a V4 step with a swap action that it marked `(its steps are not shown)`. Two kinds of step are marked
could not read, are marked `(could not be decoded)`. An `EXECUTE_SUB_PLAN` `(could not be decoded)`:
step is marked `(its steps are not shown)`. - a `BALANCE_CHECK_ERC20` step that the decoder could not read;
- a V4 step with a swap action that it could not read.
- `Deadline`: the router's deadline as a UTC date and time. A deadline past the - `Deadline`: the router's deadline as a UTC date and time. A deadline past the
range of a JavaScript date reads range of a JavaScript date reads
`After 275760-09-13 00:00:00 (no deadline in practice)`. `After 275760-09-13 00:00:00 (no deadline in practice)`.
@@ -1348,8 +1377,9 @@ decide what the router still holds after each step. Those rules decide what the
The inpage provider (`src/content/inpage.js`) puts a `window.ethereum` provider The inpage provider (`src/content/inpage.js`) puts a `window.ethereum` provider
on every page, unless another wallet already set one. It announces the provider on every page, unless another wallet already set one. It announces the provider
through EIP-6963 with a fresh random UUID per page load. The content script through EIP-6963, the standard that lets a page list the installed wallets, with
(`src/content/index.js`) relays each request to the background. a fresh random UUID per page load. The content script (`src/content/index.js`)
relays each request to the background.
A site is its full origin: scheme, host and port. A choice for A site is its full origin: scheme, host and port. A choice for
`https://app.example` says nothing about `http://app.example`. The background `https://app.example` says nothing about `http://app.example`. The background
@@ -1365,8 +1395,9 @@ The background answers these methods:
is allowed or connected, otherwise nothing. is allowed or connected, otherwise nothing.
- `eth_chainId` and `net_version`: the active network's values. - `eth_chainId` and `net_version`: the active network's values.
- `wallet_switchEthereumChain`: NetworkApproval, for a connected site naming a - `wallet_switchEthereumChain`: NetworkApproval, for a connected site naming a
supported network. An unsupported chain is refused with code `4902`. A site supported network other than the active one. A request naming the active
that is not connected is refused with code `4100`. network is answered with success at once. An unsupported chain is refused with
code `4902`. A site that is not connected is refused with code `4100`.
- `wallet_addEthereumChain`: success for a supported chain, code `4902` - `wallet_addEthereumChain`: success for a supported chain, code `4902`
otherwise. It never switches the network. otherwise. It never switches the network.
- `personal_sign`, `eth_sign`, `eth_signTypedData` and `eth_signTypedData_v4`: - `personal_sign`, `eth_sign`, `eth_signTypedData` and `eth_signTypedData_v4`:
@@ -1380,9 +1411,9 @@ The background answers these methods:
includes `eth_sendRawTransaction`. includes `eth_sendRawTransaction`.
- Anything else is refused with code `4200`. - Anything else is refused with code `4200`.
The background also answers messages from the extension's own pages, such as The background also answers messages from the extension's own pages. The types
`AUTISTMASK_GET_CONNECTED_SITES` and `AUTISTMASK_REMOVE_SITE`. It answers them in `POPUP_ONLY_TYPES` in `src/background/index.js` are answered only when the
only when the sender is one of those pages. sender is one of those pages.
One transaction approval exists at a time, wallet-wide, because filling in a One transaction approval exists at a time, wallet-wide, because filling in a
transaction fixes its nonce. A second `eth_sendTransaction` while one is transaction fixes its nonce. A second `eth_sendTransaction` while one is
@@ -1390,13 +1421,13 @@ unanswered is refused with code `-32002`. One site approval, one signature
approval and one network approval exist per site at a time, with the same code. approval and one network approval exist per site at a time, with the same code.
For `eth_sendTransaction`, the background fills in the transaction before any For `eth_sendTransaction`, the background fills in the transaction before any
window opens (`src/shared/approvalTx.js`). The nonce is the account's next nonce window opens (`src/shared/approvalTx.js`). The nonce is the sending address's
from the node. The fees are the site's when it gave them, otherwise the node's. next nonce, read from the node. The fees are the site's when it gave them,
The gas limit is the node's estimate unless the site gave one. A site's nonce is otherwise the node's. The gas limit is the node's estimate unless the site gave
ignored. When the node refuses the transaction or gives no answer within 20 one. A site's nonce is ignored. When the node refuses the transaction or gives
seconds, no window opens and the site gets the node's error. The popup signs no answer within 20 seconds, no window opens and the site gets an error. The
exactly the object that it was shown. The background then checks the signed popup signs exactly the object that it was shown. The background then checks the
transaction (`verifySignedTx()` in `src/shared/approvalVerify.js`): signed transaction (`verifySignedTx()` in `src/shared/approvalVerify.js`):
- the signer is the address that the approval was raised for; - the signer is the address that the approval was raised for;
- the type is 0, 1 or 2; - the type is 0, 1 or 2;
@@ -1408,18 +1439,21 @@ transaction (`verifySignedTx()` in `src/shared/approvalVerify.js`):
A mismatch spends the approval: the site gets an error, and the signed A mismatch spends the approval: the site gets an error, and the signed
transaction is not retried. A wrong password leaves the approval standing. After transaction is not retried. A wrong password leaves the approval standing. After
a broadcast the nonce is recorded, and a second signed transaction with that a broadcast the nonce is recorded, and the background refuses a second signed
nonce is refused. Signatures are checked the same way: the background recovers transaction with that nonce until the worker stops. Signatures are checked the
the signer from the signature and refuses any other address. same way: the background recovers the signer from the signature and refuses any
other address.
Switching the active address rejects every pending approval with code `4001` and Switching the active address rejects every pending approval with code `4001` and
closes its window. It ends every connection allowed without "Remember", and closes its window, except an approval already being signed and sent. It ends
sends `accountsChanged` to every tab with permission. every connection allowed without "Remember". It sends `accountsChanged` to every
tab: the active address to a site with permission, an empty list to the rest.
Approval windows open at 396 by 600 pixels, centred on the browser window last Approval windows open at 396 by 600 pixels, centred on the browser window last
in focus. The window widens itself by however far the screen would scroll in focus when that is a normal window. The window widens itself by however far
sideways. A transaction or signature approval whose window closes stays pending. the screen would scroll sideways. A transaction or signature approval whose
The toolbar button then opens the oldest such approval instead of the wallet. window closes stays pending. The toolbar button then opens the oldest such
approval instead of the wallet.
#### Ceilings #### Ceilings
@@ -1440,17 +1474,20 @@ Ethereum JSON-RPC endpoint, contacted by default:
- balance reads (`eth_getBalance`, `balanceOf` through `eth_call`) every 10 - balance reads (`eth_getBalance`, `balanceOf` through `eth_call`) every 10
seconds while the popup is open and every minute in the background; seconds while the popup is open and every minute in the background;
- ENS reverse lookups of the user's addresses on each refresh, and of history - ENS reverse lookups of the user's addresses on each refresh, and of history
counterparties when a list is shown (cached for 12 hours in the popup); counterparties when AddressDetail or AddressToken shows its list (cached for
12 hours in the popup);
- the ENS name lookup when Send resolves a name; - the ENS name lookup when Send resolves a name;
- on mainnet, Chainlink `latestRoundData()` reads for ETH and for each held or - on mainnet, Chainlink `latestRoundData()` reads for ETH and for each held or
tracked token with a feed. They go as one batched request, at most every 5 tracked token with a feed. They go as one batched request, at most every 5
minutes while the popup is open. A reading older than the feed's heartbeat minutes while the popup is open. A reading older than the feed's heartbeat
plus 30 minutes is no price; (the longest the feed goes without a new price) plus 30 minutes is no price;
- the address scan on import; - the address scan on import;
- the `symbol()`, `decimals()` and `name()` lookup when a token is added by - the `symbol()`, `decimals()` and `name()` lookup when a token is added by
contract address; contract address;
- the From address's balance each time ConfirmTx is shown;
- the recipient checks on ConfirmTx: `eth_getCode`, `eth_getTransactionCount` - the recipient checks on ConfirmTx: `eth_getCode`, `eth_getTransactionCount`
and `eth_getBalance`; and `eth_getBalance`;
- the token's `decimals()` when ConfirmTx signs an ERC-20 send;
- fee estimates, nonces, broadcasts and receipt lookups; - fee estimates, nonces, broadcasts and receipt lookups;
- `eth_chainId`, when "Save" beside the RPC URL in Settings checks the URL; - `eth_chainId`, when "Save" beside the RPC URL in Settings checks the URL;
- the methods in `PROXY_METHODS`, for any page that asks. - the methods in `PROXY_METHODS`, for any page that asks.
@@ -1536,8 +1573,8 @@ base-uri 'none'
- `connect-src https: http:` admits any RPC endpoint, a local node over - `connect-src https: http:` admits any RPC endpoint, a local node over
`http://127.0.0.1` included. What the wallet contacts is bounded by its code, `http://127.0.0.1` included. What the wallet contacts is bounded by its code,
not by this directive. not by this directive.
- `frame-src 'none'`, `form-action 'none'` and `base-uri 'none'` are named - `form-action` and `base-uri` are named because neither falls back to
because the last two do not fall back to `default-src`. `default-src`. `frame-src 'none'` is stricter than the fallback.
### Debug mode ### Debug mode
@@ -1567,21 +1604,23 @@ balances and ENS names, and each token contract looked up.
### Appearance ### Appearance
- Black text on a white background, or white on black in the dark theme. Colour - Black text on a white background, or white on black in the dark theme. Colour
marks errors, warnings, address dots and identicons. is used for errors, warnings, the red banner, address dots and identicons.
- Every piece of information is text in the system monospace font. The - Every piece of information is text in the system monospace font. The
identicons and the QR code on Receive are the only pictures. identicons and the QR code on Receive are the only pictures in the popup.
- Every control looks like a control. Buttons have a border, padding and a hover - Every control looks like a control. Buttons have a border, padding and a hover
state. Clickable text is underlined. There are no invisible hit targets. state. Clickable text is underlined. A clickable row changes its background on
- Nothing moves when a result arrives. Every element filled in later reserves hover. There are no invisible hit targets.
its space with `min-height` or a placeholder. `visibility: hidden` is - Every element filled in later reserves its space with `min-height` or a
placeholder, so a result arriving later moves nothing. `visibility: hidden` is
preferred over `display: none` where the space must stay. An empty USD value preferred over `display: none` where the space must stay. An empty USD value
is written as ` `. is written as ` `.
- The popup is 396 pixels wide (`body` in `src/popup/styles/main.css`) and up to - The popup is 396 pixels wide (`body` in `src/popup/styles/main.css`) and up to
600 pixels tall, the most a toolbar popup gets. 600 pixels tall, the most a toolbar popup gets.
- Styling is Tailwind utility classes in the markup. `main.css` adds four - Styling is Tailwind utility classes in the markup. `main.css` adds four
classes of its own: `.copy-flash-active` and `.copy-flash-fade` for the copy classes of its own:
feedback, `.am-address` for the rule that an address never wraps, and - `.copy-flash-active` and `.copy-flash-fade`, the copy feedback;
`.am-byte-order` for the signature screen's message. - `.am-address`, the rule that an address never wraps;
- `.am-byte-order`, the byte-order layout of the signature screen's message.
- No framework. The popup is plain JavaScript with one module per screen or - No framework. The popup is plain JavaScript with one module per screen or
group of screens. group of screens.
@@ -1595,15 +1634,15 @@ balances and ENS names, and each token contract looked up.
## Development ## Development
### Entry points ### Entrypoints
The repository follows The repository follows
[Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all). [Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all).
The `Makefile` targets are thin shims over `script/`: The `Makefile` targets are thin shims over `script/`:
- `script/bootstrap`: install the dependencies. Node and yarn come from the - `script/bootstrap`: install the dependencies. Node comes from the system or,
system or, failing that, from a pinned nvm install. Fails when node cannot failing that, from a pinned nvm install; yarn comes from corepack. Fails when
find a package named in `package.json`. node cannot find a package named in `package.json`.
- `script/setup`: `script/bootstrap`, then the git pre-commit hook. - `script/setup`: `script/bootstrap`, then the git pre-commit hook.
- `script/test`: build the `test` stage of the `Dockerfile` with `--no-cache`. - `script/test`: build the `test` stage of the `Dockerfile` with `--no-cache`.
The stage runs the jest suite, stopped at 90 seconds and rerun verbose on The stage runs the jest suite, stopped at 90 seconds and rerun verbose on
@@ -1625,8 +1664,9 @@ The `Makefile` targets are thin shims over `script/`:
- `script/projectname`: prints `autistmask`. - `script/projectname`: prints `autistmask`.
- `script/package`: `make build`, then the release archives (see Release - `script/package`: `make build`, then the release archives (see Release
archives). archives).
- `script/check-censored`: fail when the other wallet's name appears anywhere - `script/check-censored`: one competing wallet's name is barred from code and
but its allowed places. See Policies. documentation; fail when it appears anywhere but its allowed places (see
Policies).
- `script/vendor-blocklist`: refresh `src/shared/phishingBlocklist.json` from - `script/vendor-blocklist`: refresh `src/shared/phishingBlocklist.json` from
its upstream, pinned to a commit and to the sha256 of the fetched bytes. its upstream, pinned to a commit and to the sha256 of the fetched bytes.
- `script/verify-build --expect release|debug --receipt PATH`: check `dist/` - `script/verify-build --expect release|debug --receipt PATH`: check `dist/`
@@ -1675,6 +1715,7 @@ It copies into `dist/chrome/` and `dist/firefox/`:
It compiles into the bundles: It compiles into the bundles:
- the version; - the version;
- the license and the author;
- the commit hash, marked `-dirty` when the working tree is modified; - the commit hash, marked `-dirty` when the working tree is modified;
- the build date; - the build date;
- `DEBUG`. - `DEBUG`.
@@ -1729,17 +1770,20 @@ under a tag of its own, and removes the image afterwards. Neither is part of
`make check`. Run them after any change under `src/popup/views/`, before `make check`. Run them after any change under `src/popup/views/`, before
committing. committing.
Four rules hold for both suites: Two rules hold for both suites:
- Nothing under `tests/e2e/` is named `*.test.js`, so jest never picks the - Nothing under `tests/e2e/` is named `*.test.js`, so jest never picks the
suites up. suites up.
- Both suites share the test page and stub node in `tests/e2e/dapp.js` and the
fixtures in `tests/e2e/network.js`; the drivers are separate.
Two more hold for the Chrome suite:
- A test that provokes a `console.error` on purpose declares it with - A test that provokes a `console.error` on purpose declares it with
`errors.expect()` (`tests/e2e/harness.js`). An undeclared error fails the run, `errors.expect()` (`tests/e2e/harness.js`). An undeclared error fails the run,
and so does a declared one that never comes. and so does a declared one that never comes.
- `playwright-core` in `package.json` stays at the version of the container - `playwright-core` in `package.json` stays at the version of the container
image in `tests/e2e/Dockerfile`. image in `tests/e2e/Dockerfile`.
- Both suites share the test page and stub node in `tests/e2e/dapp.js` and the
fixtures in `tests/e2e/network.js`; the drivers are separate.
`make test-e2e` runs three programs in a pinned Playwright container: `make test-e2e` runs three programs in a pinned Playwright container:
@@ -1749,13 +1793,17 @@ Four rules hold for both suites:
uncaught page error or `console.error`. It covers: uncaught page error or `console.error`. It covers:
- WebAssembly compiling in the real popup under the shipped policy; - WebAssembly compiling in the real popup under the shipped policy;
- the Welcome settings, and what reaches Blockscout and Etherscan; - the Welcome settings, and what reaches Blockscout and Etherscan;
- wallet creation, Add Token and TransactionDetail; - wallet creation, AddToken and TransactionDetail;
- the recovery phrase and private key screens; - the recovery phrase and private key screens;
- address removal and Settings; - address removal and Settings;
- ConfirmTx's fee and balance checks, and the wallet's own ERC-20 send; - ConfirmTx's fee and balance checks, and the wallet's own ERC-20 send;
- the receipt wait and StateRecovery; - the receipt wait and StateRecovery;
- the site round trips: connect, `personal_sign`, `eth_signTypedData_v4`, - the site round trips:
`eth_sendTransaction` and `wallet_switchEthereumChain`. - connect;
- `personal_sign`;
- `eth_signTypedData_v4`;
- `eth_sendTransaction`;
- `wallet_switchEthereumChain`.
- `tests/e2e/storagePartition.js` loads the extension from two directories into - `tests/e2e/storagePartition.js` loads the extension from two directories into
one profile, with and without the manifest `key`. With the key, both loads get one profile, with and without the manifest `key`. With the key, both loads get
the pinned extension id and read the same storage. Without it, the second load the pinned extension id and read the same storage. Without it, the second load
@@ -1769,10 +1817,14 @@ Four rules hold for both suites:
geckodriver. The container has no network. The loopback server in geckodriver. The container has no network. The loopback server in
`tests/e2e/dapp.js` serves the test page and a stub node. `tests/e2e/dapp.js` serves the test page and a stub node.
- `tests/e2e/firefox/run.js` installs `dist/firefox/` as a temporary add-on. It - `tests/e2e/firefox/run.js` installs `dist/firefox/` as a temporary add-on.
covers popup load, StateRecovery, wallet creation, Add Token and the site Errors are read from Firefox's console service at each step boundary. Any
round trips. Errors are read from Firefox's console service at each step uncaught error from an extension source fails the run. It covers:
boundary. Any uncaught error from an extension source fails the run. - popup load;
- StateRecovery;
- wallet creation;
- AddToken;
- the site round trips.
- `tests/e2e/firefox/reinstall.js` installs the packaged `.xpi`, creates a - `tests/e2e/firefox/reinstall.js` installs the packaged `.xpi`, creates a
wallet, restarts the browser and adds the add-on again. It decrypts the stored wallet, restarts the browser and adds the add-on again. It decrypts the stored
secret through the real ShowRecoveryPhrase screen. It then uninstalls and secret through the real ShowRecoveryPhrase screen. It then uninstalls and
@@ -1784,9 +1836,15 @@ from the `check` workflow. `e2e-chrome` has a 40-minute cap and `e2e-firefox` a
`.gitea/workflows/check.yml` is a copy of the file in the `sneak/prompts` `.gitea/workflows/check.yml` is a copy of the file in the `sneak/prompts`
repository and is not edited here. repository and is not edited here.
## TODO
Planned work is on
[the issue tracker](https://git.eeqj.de/sneak/AutistMask/issues). `TODO.md`
holds the workflow, the status and the next step.
## Policies ## Policies
- The other wallet is not named in code or documentation. - One competing wallet's name is barred from code and documentation.
`script/check-censored` fails the lint stage and the build when the name `script/check-censored` fails the lint stage and the build when the name
appears outside its allowed places: appears outside its allowed places:
- the two provider flags in `src/content/inpage.js`. Many sites check them - the two provider flags in `src/content/inpage.js`. Many sites check them