README: second rework after review
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:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user