From a8e7c119ae804fe5cb8cb8fbbabcd49ba7977bfd Mon Sep 17 00:00:00 2001 From: sneak Date: Mon, 26 Oct 2026 07:47:54 +0000 Subject: [PATCH] fix: Blockscout and Etherscan lookups are off until the user turns them on (closes #668) Out of the box the wallet sent the user's addresses to Blockscout and each recipient to Etherscan. Two settings, "Use Blockscout" and "Check recipients on Etherscan", now gate those requests. Both are off in a new install and offered unticked on Welcome and in Settings. While one is off, the screens that relied on it say so in a plain line. Each gated request reads its setting from storage before it asks and when the answer lands, and drops a late answer. An open popup takes another page's change and draws its screens again. While "Use Blockscout" is off, no address keeps an untracked token, and no screen names a token from Blockscout's report. ENS lookups ask only the RPC endpoint. Model: opus-5-5 --- README.md | 387 +++++++--- docs/README.md | 108 ++- src/background/index.js | 13 +- src/popup/index.html | 122 +++- src/popup/index.js | 72 +- src/popup/viewRouter.js | 13 +- src/popup/views/addressDetail.js | 9 +- src/popup/views/addressToken.js | 7 +- src/popup/views/approval.js | 11 +- src/popup/views/confirmTx.js | 5 + src/popup/views/deleteAddress.js | 5 + src/popup/views/deleteWallet.js | 3 +- src/popup/views/helpers.js | 50 +- src/popup/views/home.js | 9 +- src/popup/views/send.js | 19 +- src/popup/views/settings.js | 84 ++- src/popup/views/transactionDetail.js | 12 + src/popup/views/txStatus.js | 18 +- src/popup/views/welcome.js | 38 +- src/shared/balances.js | 48 +- src/shared/chainSwitch.js | 4 +- src/shared/etherscanLabels.js | 11 + src/shared/persistedState.js | 12 + src/shared/state.js | 7 +- src/shared/storedSettings.js | 24 + src/shared/transactions.js | 7 + tests/alarms.test.js | 2 + tests/approvalAmount.test.js | 3 + tests/approvalUseBlockscoutOff.test.js | 307 ++++++++ tests/backgroundStateIsolation.test.js | 53 ++ tests/balanceRefreshCancelled.test.js | 6 + tests/blockscoutEtherscanSettings.test.js | 841 ++++++++++++++++++++++ tests/coldWorkerChainId.test.js | 2 + tests/confirmTxAmount.test.js | 8 +- tests/confirmTxLateResult.test.js | 8 +- tests/confirmTxNetworkSwitch.test.js | 10 +- tests/contractCreation.test.js | 2 + tests/e2e/network.js | 25 +- tests/e2e/run.js | 321 +++++++++ tests/ensOffchainLookup.test.js | 114 +++ tests/fabricatedDecimals.test.js | 8 +- tests/holders.test.js | 6 +- tests/nativeCurrency.test.js | 7 + tests/persistedEntryFloors.test.js | 6 +- tests/persistedFieldContract.test.js | 8 + tests/popupBoot.test.js | 6 +- tests/sendDisplayFloor.test.js | 5 +- tests/sendMax.test.js | 8 +- tests/sendTokenSelect.test.js | 6 +- tests/stateRecovery.test.js | 16 +- tests/support/popupBoot.js | 2 + tests/symbolSpoof.test.js | 14 +- tests/timestampDisplay.test.js | 2 + tests/tokenTransferDetail.test.js | 9 +- tests/trackedTokenBalances.test.js | 6 + tests/trackedTokensPerNetwork.test.js | 1 + tests/transactionListCancelled.test.js | 2 + tests/transactions.test.js | 2 + tests/uniswapRecipient.test.js | 9 +- tests/uniswapRouterAddress.test.js | 9 +- tests/uniswapUnknownDecimals.test.js | 3 + tests/unknownScaleSend.test.js | 9 +- 62 files changed, 2709 insertions(+), 235 deletions(-) create mode 100644 src/shared/storedSettings.js create mode 100644 tests/approvalUseBlockscoutOff.test.js create mode 100644 tests/blockscoutEtherscanSettings.test.js create mode 100644 tests/ensOffchainLookup.test.js diff --git a/README.md b/README.md index f4e8ae7..6266dba 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,27 @@ intercepts tx status links to their own site instead of going to Etherscan, etc. None of the common alternatives work on Firefox. Hence, a minimally viable ERC20 browser wallet/signer that works cross-platform. -Everything you need, nothing you don't. We import as few libraries as possible, -don't implement any crypto, and don't send user-specific data anywhere but a -(user-configurable) Ethereum RPC endpoint (which defaults to a public node). The -extension contacts three user-configurable services: the configured RPC node for -blockchain interactions, a public CoinDesk API (no API key) for realtime price -information, and a Blockscout block-explorer API for transaction history and -token balances. It also performs best-effort Etherscan address label lookups -during transaction confirmation. A community-maintained phishing domain -blocklist is built into the extension at build time and checked locally; nothing -is fetched for it at runtime. +Everything you need, nothing you don't. We import as few libraries as possible +and don't implement any crypto. By default the wallet sends user-specific data +only to the Ethereum RPC endpoint. The user can change that endpoint in +Settings. It defaults to a public node. + +Out of the box the extension contacts two services: + +- the configured RPC node, for blockchain interactions +- a public CoinDesk API (no API key), for realtime prices. It is sent token + symbols and nothing else. + +Two more services are sent none of the user's data until the user turns them on. +Each has a checkbox on the Welcome screen and in Settings: + +- "Use Blockscout": a Blockscout block-explorer API, for transaction history, + the tokens the user has not tracked, and transaction details +- "Check recipients on Etherscan": a best-effort Etherscan label lookup of the + recipient during transaction confirmation + +A community-maintained phishing domain blocklist is built into the extension at +build time and checked locally. Nothing is fetched for it at runtime. In the extension is a hardcoded list of the top ERC20 contract addresses. You can add any ERC20 contract by contract address if you wish, but the hardcoded @@ -422,6 +433,35 @@ It also covers the red banner on the Settings screen: switching the network there to Sepolia must put up the `[TESTNET]` banner, and switching back to mainnet must take it down, both while Settings is still on screen. +It also covers what a new install sends to Blockscout and Etherscan +([#668](https://git.eeqj.de/sneak/AutistMask/issues/668)). The harness installs +a route handler before the first popup page opens. From then on, the handler +records every request the extension makes, from its pages and from its +background worker. A request the background worker makes while it starts up +comes earlier and is not recorded. The tests take these steps in order: + +- Welcome must offer "Use Blockscout" and "Check recipients on Etherscan" + unticked. +- With neither ticked, Home, AddressDetail, AddressToken and ConfirmTx must each + show the line saying what is off. +- The popup is then closed until the background has run a balance refresh of its + own. No recorded request may have gone to Blockscout or Etherscan. +- "Use Blockscout" is ticked from AddressDetail's gear. On "Back", AddressDetail + must ask Blockscout for its history and no longer say the setting is off. + Blockscout must then be asked for the token balances. +- The stub Blockscout then reports a token the user has not tracked. Home lists + it. "Use Blockscout" is unticked from AddressDetail's gear. On "Back", the + token must be gone from AddressDetail, from Send's token dropdown and from + Home. Those three screens must each show the line saying the setting is off. + The setting is then ticked again. +- "Check recipients on Etherscan" is ticked from ConfirmTx's gear. On "Back", + ConfirmTx must ask Etherscan about the recipient and show the label the stub + page gives it. + +Both settings stay on for the rest of the run. TransactionDetail with "Use +Blockscout" off is not driven here, because no history row leads to it then. The +unit tests cover it. + It also covers the confirmation screen, for both a native ETH send and an ERC-20 send: Send disabled while the fee estimate is in flight, enabled once it lands, the fee block quoting the expected cost and the reserve separately, the distinct @@ -845,13 +885,16 @@ src/ views/ — one JS module per screen (home, send, approval, etc.) shared/ — modules used by both popup and background alarms.js — recurring background jobs (extension alarms API) - balances.js — ETH and tracked token balances via RPC, others via Blockscout + balances.js — ETH and tracked token balances via RPC, others via + Blockscout while "Use Blockscout" is on constants.js — chain IDs, default RPC endpoint, ERC-20 ABI ens.js — ENS forward/reverse resolution (popup only) holders.js — holder-count parsing and the low-holder rule prices.js — ETH/USD and token/USD via CoinDesk API scamlist.js — known fraud contract addresses state.js — persisted state (extension storage) + storedSettings.js — "Use Blockscout" and "Check recipients on Etherscan", + read from storage by the requests they gate symbolSpoof.js — the known-symbol spoof rule, shared by all surfaces tokenList.js — top ERC-20 tokens by market cap (hardcoded) transactions.js — tx history fetching + anti-poisoning filters @@ -1122,25 +1165,38 @@ with the same floor (`.5` reads `.5000`). **Specific Exception — unknown token scale:** Calldata carries base units and no scale, so every amount on the dApp approval screen needs the token's `decimals`. -It is resolved from the bundled token list, then from the tokens the user tracks -on the network the transaction is for, then, when that is the active network, -from what the block explorer reported for the contract in the wallet's balances, -which are the active network's only (`resolveTokenDecimals()` in -`src/shared/approvalAmount.js`). Where none of them answers, the amount is not -formatted: the line reads `5000000000 base units (decimals unknown)`. A guessed -scale is not an approximation but a different number — 1,000 units of a -6-decimal token formatted at 18 decimals reads `0.000000001` — on the screen -whose only job is to state what is being authorized. Both amount paths of that -screen take this rule: the ERC-20 `transfer`/`approve` line -(`src/popup/views/approval.js`) and the swap's `Amount`, `Min. received`, -`Moved from the sender` and `Permit2 allowance` lines and the `Paid to` line of -a move into the router (`src/shared/uniswap.js`). The token permission warning -on the signature screen takes the same rule for its amounts. An unbounded -allowance or permit needs no scale to describe and is still shown as -`Unlimited`. A source's answer counts only if it is a whole number from 0 to 80: -`decimals()` returns a `uint8`, but `formatUnits()` cannot format more than 80 -decimal places, so a token that reports 81 to 255 is shown as one whose scale -nothing knows. +It is resolved from these sources, in this order (`resolveTokenDecimals()` in +`src/shared/approvalAmount.js`): + +- the bundled token list +- the tokens the user tracks on the network the transaction is for +- what the block explorer reported for the contract in the wallet's balances. + These are the active network's only, and they are used only while "Use + Blockscout" is on (`tokenSourcesByChainId()` in `src/popup/views/helpers.js`). + +The token's name comes from the same sources. While "Use Blockscout" is off, a +token's name or decimals may be missing from the bundled list and the tracked +tokens. Then a line under the token says "Blockscout's report of your balances +is not used to name this token, because "Use Blockscout" is off in Settings." +These screens show it: + +- TxApproval and SignApproval +- WaitTx, SuccessTx and ErrorTx, which follow TxApproval + +Where none of the sources answers, the amount is not formatted: the line reads +`5000000000 base units (decimals unknown)`. A guessed scale is not an +approximation but a different number — 1,000 units of a 6-decimal token +formatted at 18 decimals reads `0.000000001` — on the screen whose only job is +to state what is being authorized. Both amount paths of that screen take this +rule: the ERC-20 `transfer`/`approve` line (`src/popup/views/approval.js`) and +the swap's `Amount`, `Min. received`, `Moved from the sender` and +`Permit2 allowance` lines and the `Paid to` line of a move into the router +(`src/shared/uniswap.js`). The token permission warning on the signature screen +takes the same rule for its amounts. An unbounded allowance or permit needs no +scale to describe and is still shown as `Unlimited`. A source's answer counts +only if it is a whole number from 0 to 80: `decimals()` returns a `uint8`, but +`formatUnits()` cannot format more than 80 decimal places, so a token that +reports 81 to 255 is shown as one whose scale nothing knows. The rule holds only if nothing invents a scale UPSTREAM of it. Those three sources are read as authoritative, so a value written into one of them cannot be @@ -1163,12 +1219,13 @@ and compare against. Reading the stored field directly instead answers `null` for a bundled or tracked token the explorer merely omitted, which is not a refusal the wallet has any reason to make. -The Send screen also consults every address's explorer reports, so a contract -two addresses report different `decimals` for has no scale there, and the stored -balance, formatted at one of those scales, is withdrawn with it. The Send -screen's `Current balance` and the confirmation screen's balance line then both -read `unknown (SYMBOL)`. The balance list formats each explorer row as it is -fetched, without that cross-address check, and shows the row's figure. +While "Use Blockscout" is on, the Send screen also consults every address's +explorer reports. Two addresses may report different `decimals` for one +contract. That token then has no scale on Send, and its stored balance is +withdrawn with it. The Send screen's `Current balance` and the confirmation +screen's balance line then both read `unknown (SYMBOL)`. The balance list +formats each explorer row as it is fetched, without that cross-address check, +and shows the row's figure. **Decoded amount lines on the transaction approval screen:** the `Amount` line of a decoded ERC-20 call, and the `Amount` and `Min. received` lines of a @@ -1388,23 +1445,38 @@ that token's previous entry, as a failed ETH balance keeps its figure, or takes Blockscout's entry from the same refresh when there is none. Every other token comes from the Blockscout `token-balances` response (`fetchTokenBalances()`), so tokens do appear without the user adding them, and they keep their previous -figures while Blockscout fails. A tracked token is shown when its balance is -nonzero, and any other ERC-20 when its balance is nonzero and it is in the -bundled known-token list or has 1,000 or more holders. A token claiming a symbol -from the bundled list from any other contract address is always dropped, tracked -or not, and so is any token claiming a symbol that belongs to the native asset -and therefore has no legitimate contract at all (`"ETH"`, and every network's -`nativeCurrency`, such as `"SepoliaETH"`, on every network). That filter is -unconditional — the "Hide tokens with fewer than 1,000 holders" setting governs -the transaction history and the send-screen token selector, not this list. A -token's holder count is unknown when the explorer reports none, or reports -anything other than a whole number written in digits alone, such as `1,000` or -`1e3` (`parseHoldersCount()` in `src/shared/holders.js`). This list does not -take an unknown count as 1,000 or more, so such a token is shown only when it is -on the bundled list or tracked. Every nonzero holding of a token shown is stored -exactly, however small, but a holding below 0.000001 is left out of the balance -lists, the send-screen token selector, the address total and the remove-address -warning (`isBelowOneMillionth()` in `src/shared/amountDisplay.js`). The Send and +figures while Blockscout fails. + +The refresh asks Blockscout only while the user has "Use Blockscout" on. The +setting is off until the user turns it on. While it is off: + +- Only tracked tokens are listed. Turning the setting off drops every other + token from every address at once. +- A tracked token's entry has no holder count and no `decimals` from Blockscout. +- A tracked token's balance uses the decimals from the bundled list, or else the + decimals saved when the token was tracked. + +Blockscout may answer just before the user turns the setting off. So if the +stored setting is off when the refresh saves, in the popup or the background, +the save drops every token the user has not tracked. + +A tracked token is shown when its balance is nonzero, and any other ERC-20 when +its balance is nonzero and it is in the bundled known-token list or has 1,000 or +more holders. A token claiming a symbol from the bundled list from any other +contract address is always dropped, tracked or not, and so is any token claiming +a symbol that belongs to the native asset and therefore has no legitimate +contract at all (`"ETH"`, and every network's `nativeCurrency`, such as +`"SepoliaETH"`, on every network). That filter is unconditional — the "Hide +tokens with fewer than 1,000 holders" setting governs the transaction history +and the send-screen token selector, not this list. A token's holder count is +unknown when the explorer reports none, or reports anything other than a whole +number written in digits alone, such as `1,000` or `1e3` (`parseHoldersCount()` +in `src/shared/holders.js`). This list does not take an unknown count as 1,000 +or more, so such a token is shown only when it is on the bundled list or +tracked. Every nonzero holding of a token shown is stored exactly, however +small, but a holding below 0.000001 is left out of the balance lists, the +send-screen token selector, the address total and the remove-address warning +(`isBelowOneMillionth()` in `src/shared/amountDisplay.js`). The Send and confirmation screens show it when its token is the one being sent. Tracked tokens with a zero balance are listed as well while "Show tracked tokens with zero balance" is on. @@ -1578,6 +1650,17 @@ such as an edit typed into Settings and not yet saved. Home is the one screen "Back" always re-renders, so the wallet list reflects anything that changed while the user was away from it. +Changing "Use Blockscout" or "Check recipients on Etherscan" in Settings makes +every screen but Settings count as not yet rendered. "Back" then renders again +any screen a reopened popup can render. A change to "Use Blockscout" also cuts +the stack, as a network switch does (below). The cut drops Send, whose token +dropdown was filled under the old setting. "Back" cannot render Send again. A +change to "Check recipients on Etherscan" leaves the stack as it is. + +Any other popup open meanwhile takes either change from the stored record, as it +takes a network switch (below). It then draws its screen on top again as a +reopened popup draws it, so Home takes the place of a screen such as Send. + A network switch leaves every screen on the page showing the previous network. The user makes it in Settings, or approves a site's request and the background saves it. Any other popup open meanwhile takes the switch from the stored @@ -1619,8 +1702,15 @@ view would leave a wallet one click from deletion. screen in that case. - **Elements**: - "Welcome! To get started, add a wallet." text + - A well with a line saying that the wallet sends the user's data only to + the Ethereum RPC endpoint unless one of the two checkboxes below is ticked + - In the well, the "Use Blockscout" and "Check recipients on Etherscan" + checkboxes, the same two as in Settings. Each has a sentence saying what + it sends and where. Both are unticked until the user ticks them - "Add wallet" button - **Transitions**: + - Ticking or unticking either checkbox → stores the setting, as Settings + does (no screen change) - "Add wallet" → **AddWallet** #### Home (`main`) @@ -1641,6 +1731,8 @@ view would leave a wallet one click from deletion. holding more than one address); the ENS name, if resolved, is below it; then the full address on a row of its own, followed by the address USD total and a balance line for ETH and for each token shown for that address + - While "Use Blockscout" is off, a line under the wallet list: "Only tracked + tokens are listed, because "Use Blockscout" is off in Settings." - "Recent Transactions": up to 25 transactions merged across every address of every wallet, deduplicated by hash and filtered. Each row is three lines: age and direction, then the counterparty's colour dot (with our own @@ -1648,7 +1740,11 @@ view would leave a wallet one click from deletion. counterparty's full address on a row of its own. A contract creation has no counterparty: its second line is the amount alone and its third line says "This transaction creates a new contract. It has no recipient." The - transaction lists on AddressDetail and AddressToken draw the same rows + transaction lists on AddressDetail and AddressToken draw the same rows. + The history comes from Blockscout. While "Use Blockscout" is off, + Blockscout is not asked. This list then shows only the line "No + transaction history is shown, because "Use Blockscout" is off in + Settings." - "Add additional wallet..." link at bottom - **Transitions**: - Tap address row → sets the active address and broadcasts @@ -1717,9 +1813,13 @@ view would leave a wallet one click from deletion. - USD total for address (see [Partial USD totals](#partial-usd-totals)) - Balance list: ETH + the ERC-20 tokens shown for this address (4 decimal places, USD inline). Each balance row is clickable → **AddressToken** + - While "Use Blockscout" is off, a line under the balance list: "Only + tracked tokens are listed, because "Use Blockscout" is off in Settings." - Send / Receive / + Token buttons and a "···" menu button - "···" dropdown containing a single "Export Private Key" entry - - Transaction list (with ENS resolution for counterparties) + - Transaction list (with ENS resolution for counterparties). While "Use + Blockscout" is off, it shows only the line "No transaction history is + shown, because "Use Blockscout" is off in Settings." - **Transitions**: - Tap balance row → **AddressToken** (for that token) - "Send" → **Send** (refuses with a flash message on a zero balance) @@ -1787,9 +1887,13 @@ view would leave a wallet one click from deletion. - Token contract well (ERC-20 only): full contract address (tap to copy, etherscan link) plus name, symbol, decimals, holder count and project website where known. The "Holders:" row is left out, not shown as 0, when - the token's balance-list entry has no holder count: the explorer did not - report a readable one, or the token is not in the balance list - - Token-filtered transaction list (only this token's transfers) + the token's balance-list entry has no holder count. That happens when: + - the explorer did not report a readable one + - "Use Blockscout" is off + - the token is not in the balance list + - Token-filtered transaction list (only this token's transfers). While "Use + Blockscout" is off, it shows only the line "No transaction history is + shown, because "Use Blockscout" is off in Settings." - **Transitions**: - "Send" → **Send** (token locked: the dropdown is replaced by a static symbol and contract address) @@ -1806,6 +1910,9 @@ view would leave a wallet one click from deletion. - From: address with color dot + etherscan link - What to send: token dropdown (or static display with contract address when locked from AddressToken) + - While "Use Blockscout" is off, a line under the token dropdown: "Only + tracked tokens are listed, because "Use Blockscout" is off in Settings." + The line is hidden with the dropdown when Send is opened from AddressToken - To: address or ENS name input, with an inline validation message - Amount input with current balance display, which reads `Current balance: unknown (SYMBOL)` for a token whose scale is unknown, as @@ -1825,13 +1932,14 @@ view would leave a wallet one click from deletion. - **Transitions**: - "Review" (valid inputs, ENS resolved) → **ConfirmTx** - "Review" with an unresolvable ENS name or an invalid amount → flash - message, no screen change. An amount is valid when it is a number above - zero, written as digits with at most one decimal point, with nothing but - zeros after the token's last decimal place. ETH has 18 places, and a token - with more, or with an unknown number, is held to 18. Zeros past those - places leave the amount unchanged, so `1.0000000` of a 6-place token - passes and is sent as exactly 1. ConfirmTx makes the same check before - anything can be sent + message, no screen change. A name with no address, or one resolved + off-chain, gets "That ENS name has no on-chain address." An amount is + valid when it is a number above zero, written as digits with at most one + decimal point, with nothing but zeros after the token's last decimal + place. ETH has 18 places, and a token with more, or with an unknown + number, is held to 18. Zeros past those places leave the amount unchanged, + so `1.0000000` of a 6-place token passes and is sent as exactly 1. + ConfirmTx makes the same check before anything can be sent - "Back" → previous screen (Home, AddressDetail, or AddressToken) #### ConfirmTx (`confirm-tx`) @@ -1875,7 +1983,12 @@ view would leave a wallet one click from deletion. recipient that has never sent a transaction and holds no ETH, recipient is a contract, burn address, and an Etherscan phishing/scam label. An address that has received ETH but never sent a transaction still holds that ETH, - so it gets no warning + so it gets no warning. The Etherscan label check is made only while "Check + recipients on Etherscan" is on. While it is off, the Etherscan box is left + out. A line in its place says "This address was not checked on Etherscan, + because "Check recipients on Etherscan" is off in Settings." The box or + the line is chosen when the screen is drawn, so neither moves anything + later - Errors (an invalid amount), plus five reserved error boxes, so the balance and the fee arriving later move nothing — the amount exceeds the balance (for a token whose scale is unknown it says the token's balance is @@ -1952,6 +2065,9 @@ view would leave a wallet one click from deletion. `Unknown (could not be decoded)` with no symbol, not the transaction's ETH value. When that swap's `Token In` is `ETH (native)`, the transaction's ETH value in the network's currency, as for any other transaction + - A line under the amount, worded as on TxApproval. It shows while "Use + Blockscout" is off and the token's name or decimals are missing from the + bundled list and the tracked tokens. - To: color dot + full address + etherscan link; for a contract creation, which has no recipient, "This transaction creates a new contract. It has no recipient." instead @@ -1984,7 +2100,9 @@ view would leave a wallet one click from deletion. - **Elements**: - "Transaction Confirmed" heading - Decoded action well (shown when the transaction carried recognized - calldata; the top-level Amount and To are hidden in that case) + calldata; the top-level Amount and To are hidden in that case). While "Use + Blockscout" is off, it has TxApproval's line under each token whose name + or decimals are missing from the bundled list and the tracked tokens. - Amount + symbol - To: color dot + full address + etherscan link, or for a contract creation the same sentence as on WaitTx @@ -2007,6 +2125,7 @@ view would leave a wallet one click from deletion. - Amount + symbol, or for a swap the approval screen showed no `Amount` for, the same as on WaitTx: `Unknown (could not be decoded)`, unless its `Token In` is `ETH (native)` + - While "Use Blockscout" is off, the same line under the amount as on WaitTx - To: color dot + full address + etherscan link, or for a contract creation the same sentence as on WaitTx - Error message (dashed border box) @@ -2041,7 +2160,11 @@ view would leave a wallet one click from deletion. AddressToken. The screen describes the transaction on the network it was sent on, whichever network is active: its block, network details, decoded details and raw data are read from that network's Blockscout, the one Settings has for - it, and its etherscan links open that network's explorer. + it, and its etherscan links open that network's explorer. The block, network + details, decoded details and raw data are read only while "Use Blockscout" is + on. While it is off, a line in their place says "No block, decoded details, + network details or raw data are shown, because "Use Blockscout" is off in + Settings." - **Elements** (grouped into logical blocks using light well containers; field labels are self-explanatory so groups have no headings): - "Transaction" heading, "Back" button @@ -2124,8 +2247,21 @@ view would leave a wallet one click from deletion. already active changes nothing and sends nothing - Ethereum RPC: endpoint URL input + "Save" button (validated against `eth_chainId` before being saved) - - Blockscout API: endpoint URL input + "Save" button (validated against - `/stats` before being saved) + - Blockscout API: + - "Use Blockscout" checkbox, unticked until the user ticks it + - A sentence saying what it shows and what it sends. It sends the + Blockscout API the user's addresses and the hash of each transaction + opened + - Endpoint URL input + "Save" button. "Save" checks the URL by asking it + for `/stats` before saving it. That request carries no user data. It + is made whether or not "Use Blockscout" is ticked. + - Etherscan: + - "Check recipients on Etherscan" checkbox, unticked until the user + ticks it + - A sentence saying it sends each recipient's address to Etherscan to + check it for a phishing or scam label + - Each of the two settings applies to every network. Welcome offers the same + two checkboxes - Token Spam Protection: - "Hide fake tokens impersonating a known symbol" checkbox - "Hide tokens with fewer than 1,000 holders" checkbox @@ -2149,6 +2285,10 @@ view would leave a wallet one click from deletion. - Debug: hidden until revealed, then an "Enable debug mode" checkbox that turns on the red banner and verbose logging - **Transitions**: + - Ticking or unticking "Use Blockscout" or "Check recipients on Etherscan" → + stores the setting (no screen change). The next balance refresh and every + screen drawn after that follow it. Unticking "Use Blockscout" also drops + every token the user has not tracked from every address - "+ Add wallet" → **AddWallet** - "+ Add token" → **SettingsAddToken** - `[recovery phrase]` on an HD wallet → **ShowRecoveryPhrase** @@ -2167,7 +2307,8 @@ view would leave a wallet one click from deletion. nothing and tells the background nothing - Ten clicks on the version → reveals the Debug well (no screen change) - "Back" (or Settings gear again) → previous screen (Home); after a network - switch, the stack is cut first (see the top of Screen Map) + switch or a change to "Use Blockscout", the stack is cut first (see the + top of Screen Map) #### ShowRecoveryPhrase (`show-phrase`) @@ -2324,6 +2465,8 @@ view would leave a wallet one click from deletion. money. The predicate is `addressHoldsFunds()` in `src/popup/views/helpers.js`, unrounded and token-aware. A balance is a warning, never a refusal. + - While "Use Blockscout" is off, a line under the warning: "Only tracked + tokens are listed, because "Use Blockscout" is off in Settings." - The rule that a wallet always keeps at least one address, and that removing the last one means deleting the wallet from Settings - Error line @@ -2456,6 +2599,12 @@ view would leave a wallet one click from deletion. a swap pays only the From address, a `Paid to` line for each other address it pays, or saying that a token stays in the router or who it pays is unknown (see Transaction Decoding) + - A line under each token of the decoded action: "Blockscout's report of + your balances is not used to name this token, because "Use Blockscout" is + off in Settings." It shows while "Use Blockscout" is off and the token's + name or decimals are missing from the bundled list and the tracked tokens. + Blockscout's report of the user's balances gives a token's name and + decimals only while the setting is on (see Unknown token scale). - From: color dot + full address + etherscan link - Contract: color dot + full address + etherscan link, token symbol label if known; for a contract creation, the same sentence as on WaitTx @@ -2535,7 +2684,10 @@ view would leave a wallet one click from deletion. the site puts in the message, except a `Permit`'s token, which is the domain's `verifyingContract`; any those fields do not give is shown as `Unknown`, and the domain, type and message lines still follow. Only typed - data that cannot be read at all is shown as raw text. + data that cannot be read at all is shown as raw text. A token gets the + same line under it as on TxApproval, in the same case. As there, + Blockscout's report of the user's balances gives a token's name and + decimals only while the setting is on. - Raw data (`personal_sign` and `eth_sign`): the message's hex exactly as the site sent it. The bytes it encodes are what is signed, as an EIP-191 personal message. @@ -2665,17 +2817,35 @@ view would leave a wallet one click from deletion. ### External Services -AutistMask is not a fully self-contained offline tool. It necessarily -communicates with three external services to function as a wallet: +AutistMask is not a fully self-contained offline tool. By default it sends the +user's data only to the Ethereum RPC endpoint. It also asks the CoinDesk price +API for prices, sending token symbols and nothing else. + +Two more services are sent none of the user's data until the user turns on their +settings: + +- Blockscout, with "Use Blockscout" +- Etherscan, with "Check recipients on Etherscan" + +Both settings are on the Welcome screen and in Settings. Both are off in a new +install. Each setting applies to every network. + +Before each of these requests, the wallet reads its setting from storage. It +reads it again when the answer lands. While the setting is off, nothing is +asked, and an answer that lands is dropped. + +Contacted by default: - **Ethereum JSON-RPC endpoint**: The extension needs an Ethereum node to query balances (`eth_getBalance`), read ERC-20 token contracts (`eth_call`), among them the balance of every tracked token at every address (`balanceOf`), estimate gas (`eth_estimateGas`), fetch nonces (`eth_getTransactionCount`), broadcast transactions (`eth_sendRawTransaction`), and check transaction - receipts. The default endpoint is a public RPC (configurable by the user to - any endpoint they prefer, including a local node). By default the extension - talks to `https://ethereum-rpc.publicnode.com`. + receipts. Every ENS lookup is made through it alone. A name whose resolver + says it is resolved off-chain therefore does not resolve. The default endpoint + is a public RPC (configurable by the user to any endpoint they prefer, + including a local node). By default the extension talks to + `https://ethereum-rpc.publicnode.com`. - **Data sent**: Ethereum addresses, transaction data, contract call parameters. The RPC endpoint can see all on-chain queries and submitted transactions. @@ -2687,13 +2857,44 @@ communicates with three external services to function as a wallet: - **Data sent**: Token symbol strings only (e.g. "ETH", "USDC"). No addresses or user-specific data. -- **Blockscout block-explorer API**: Used to fetch transaction history (normal - transactions and ERC-20 token transfers), ERC-20 token balances, and token - holder counts (for spam filtering). The default endpoint is +Sent the user's data only while the user has turned the setting on: + +- **Blockscout block-explorer API** ("Use Blockscout"): The default endpoint is `https://eth.blockscout.com/api/v2` (configurable by the user in Settings). - - **Data sent**: Ethereum addresses. Blockscout receives the user's - addresses to query their transaction history and token balances. No - private keys, passwords, or signing operations are sent. + - **Used for**: + - transaction history (normal transactions and ERC-20 token transfers), + for Home, AddressDetail and AddressToken + - the balances of the ERC-20 tokens the user has not tracked, for Home, + AddressDetail, Send and DeleteAddress + - the names and decimals of those tokens, for TxApproval, SignApproval, + WaitTx, SuccessTx and ErrorTx + - token holder counts, for spam filtering + - a transaction's block, network details and calldata, for + TransactionDetail + - **Contacted**, while the setting is on: + - on every balance refresh, in the popup and in the background + - when Home, AddressDetail or AddressToken loads its history + - when TransactionDetail opens + - **While the setting is off**: each of those screens shows a line saying + what it leaves out and that "Use Blockscout" is off. Only tracked tokens + are listed. + - **Saving the URL**: whether or not the setting is on, pressing "Save" + beside the URL in Settings asks that URL for `/stats`. This request + carries no user data. + - **Data sent**: Ethereum addresses, and the hash of each transaction the + user opens. Blockscout receives the user's addresses to query their + transaction history and token balances. No private keys, passwords, or + signing operations are sent. + +- **Etherscan address labels** ("Check recipients on Etherscan"): While the + setting is on, ConfirmTx looks up the recipient's address on Etherscan each + time it is shown. It checks for a phishing or scam label, best-effort. This is + a direct page fetch with no API key; the user's browser makes the request. + While the setting is off, ConfirmTx says that no Etherscan check was made. + - **Data sent**: the recipient's address and nothing else. + +The etherscan links on the screens open a page in the browser when the user +clicks one. They are not requests the wallet makes. What the extension does NOT do: @@ -2702,16 +2903,9 @@ What the extension does NOT do: - No Infura/Alchemy dependency (any JSON-RPC endpoint works) - No backend servers operated by the developer -In addition to the three user-configurable services above (RPC endpoint, -CoinDesk price API, and Blockscout API), AutistMask also contacts: - -- **Etherscan address labels**: When confirming a transaction, the extension - performs a best-effort lookup of the recipient address on Etherscan to check - for phishing/scam labels. This is a direct page fetch with no API key; the - user's browser makes the request. - -Users who want maximum privacy can point the RPC and Blockscout URLs at their -own self-hosted instances (price fetching can be disabled in a future version). +Users who want maximum privacy can point the RPC URL at their own node. With +"Use Blockscout" on, they can point the Blockscout URL at their own instance +too. ### Dependencies @@ -2893,8 +3087,11 @@ logged. - Add multiple addresses within an HD wallet - Manage multiple wallets simultaneously - View ETH balance per address -- View ERC-20 token balances (tokens on the bundled known-token list, tokens - with 1,000 or more holders, and tokens the user adds by contract address) +- View ERC-20 token balances: + - tokens the user adds by contract address + - while "Use Blockscout" is on, tokens on the bundled known-token list and + tokens with 1,000 or more holders +- View transaction history and transaction details, while "Use Blockscout" is on - Send ETH to an address - Send ERC-20 tokens to an address - Receive ETH/tokens (display address, copy to clipboard, QR code) diff --git a/docs/README.md b/docs/README.md index 424d7a2..07d4002 100644 --- a/docs/README.md +++ b/docs/README.md @@ -57,11 +57,12 @@ analytics, use a portfolio tracker. The wallet is not the place for any of that. - **No NFT galleries or portfolio views.** This is a wallet, not a dashboard. -- **No third-party token list APIs.** Token balances come from the same block - explorer you configure for transaction history, and the extension ships its - own hardcoded list of top ERC-20 contract addresses for symbol-spoofing - detection. Any token you want tracked across all your addresses, you add - yourself by contract address. +- **No third-party token list APIs.** The balances of the tokens you track are + read from your RPC endpoint. While "Use Blockscout" is on, the other tokens + come from the same block explorer you configure for transaction history. The + extension ships its own hardcoded list of top ERC-20 contract addresses for + symbol-spoofing detection. Any token you want tracked across all your + addresses, you add yourself by contract address. - **No backend servers operated by the developer.** Nothing is sent to any server run by AutistMask. Every network destination is listed below. @@ -69,8 +70,13 @@ analytics, use a portfolio tracker. The wallet is not the place for any of that. ## How It Works AutistMask is a browser extension that runs entirely in your browser. It does -not have a backend server. It communicates with five external destinations: -three you configure yourself, and two fixed ones used for scam detection. +not have a backend server. Out of the box it sends your data only to the +Ethereum RPC endpoint. It also asks the CoinDesk price API for prices, sending +token symbols and nothing else. + +Blockscout and Etherscan are sent none of your data until you turn them on. +Their settings are "Use Blockscout" and "Check recipients on Etherscan". Both +are on the Welcome screen and in Settings. Both are off in a new install. ### External Services @@ -83,11 +89,14 @@ verify confirmations. The default is a free public RPC endpoint. You can change this in Settings to any Ethereum JSON-RPC endpoint, including your own local node. -When it is contacted: on every balance refresh (every 10 seconds while the popup -is open, every 60 seconds in the background), when you type an ENS name into the -Send screen, when a send is prepared and broadcast, while a pending transaction -is polled for its receipt, and for the reverse ENS lookups used to label -addresses (cached for 12 hours). +When it is contacted: + +- on every balance refresh (every 10 seconds while the popup is open, every 60 + seconds in the background) +- when you review a send to an ENS name +- when a send is prepared and broadcast +- while a pending transaction is polled for its receipt +- for the reverse ENS lookups used to label addresses (cached for 12 hours) What gets sent: standard Ethereum JSON-RPC requests (balance queries, transaction broadcasts, gas estimates, ENS lookups, contract-code checks). Your @@ -96,16 +105,31 @@ addresses are necessarily visible to the RPC provider when querying balances. **Blockscout API** (default: `eth.blockscout.com/api/v2`; `eth-sepolia.blockscout.com/api/v2` on Sepolia) -Used to fetch token balances and transaction history. Blockscout is an -open-source blockchain explorer. AutistMask queries it for your ERC-20 token -balances (including the holder counts used for spam filtering) and your recent -transactions and token transfers. You can change this in Settings to a -self-hosted Blockscout instance. +Blockscout is an open-source blockchain explorer. AutistMask sends it your data +only while "Use Blockscout" is on. It then asks Blockscout for: -When it is contacted: on every balance refresh, and whenever a screen showing -transaction history is opened. +- your recent transactions and token transfers +- the balances of the tokens you have not tracked, with the holder counts used + for spam filtering +- the block, fees and calldata of a transaction you open -What gets sent: your Ethereum addresses (to look up balances and transactions). +You can change this in Settings to a self-hosted Blockscout instance. While the +setting is off, each screen that would show Blockscout's data says so instead. +Only the tokens you track are listed. When you turn the setting off, every other +token leaves the lists at once. + +When it is contacted, while "Use Blockscout" is on: + +- on every balance refresh +- whenever a screen showing transaction history is opened +- when you open a transaction + +Whether or not the setting is on, pressing "Save" beside the Blockscout URL in +Settings asks that URL for `/stats` to check it. That request sends none of your +data. + +What gets sent: your Ethereum addresses (to look up balances and transactions) +and the hash of each transaction you open. **CoinDesk CADLI price API** (`data-api.coindesk.com`) @@ -122,16 +146,21 @@ address. **Etherscan address labels** (`etherscan.io`; `sepolia.etherscan.io` on Sepolia) -When you review a send, AutistMask fetches the recipient's public Etherscan -address page and looks for a "Fake_Phishing"/"Phish/Hack" label or a scam -warning, and shows a red warning if it finds one. This is a plain page fetch -with no API key, made by your browser. It is best-effort: if it fails, it is -silently ignored. This endpoint is not user-configurable. +While "Check recipients on Etherscan" is on, AutistMask fetches the recipient's +public Etherscan address page when you review a send. It looks for a +"Fake_Phishing"/"Phish/Hack" label or a scam warning, and shows a red warning if +it finds one. This is a plain page fetch with no API key, made by your browser. +It is best-effort: if it fails, it is silently ignored. This endpoint is not +user-configurable. -When it is contacted: each time you reach the send confirmation screen. +The setting is off in a new install. While it is off, the confirmation screen +says that no Etherscan check was made. -What gets sent: the recipient address you are about to send to, and your IP -address. Your own addresses are not sent. +When it is contacted: only while "Check recipients on Etherscan" is on, each +time you reach the send confirmation screen. + +What gets sent: the recipient's address and nothing else. As with any request, +Etherscan sees your IP address. Etherscan links shown elsewhere in the UI (on addresses, transactions, and token contracts) are ordinary links. They contact nothing until you click them. @@ -185,6 +214,9 @@ password. 1. Click the AutistMask icon in your browser toolbar. 2. Click "Add wallet" (on first use), or open Settings and click "+ Add wallet". + On first use, the screen with "Add wallet" also offers "Use Blockscout" and + "Check recipients on Etherscan", both off. Tick either if you want what it + does (see External Services). You can change both later in Settings. 3. On the "From Phrase" tab, click the die button to generate a random 12-word recovery phrase. 4. **Write down the recovery phrase and store it safely.** Anyone with these @@ -220,10 +252,14 @@ always produce the same sequence of addresses. ### Adding ERC-20 Tokens -Tokens you hold show up automatically only if they are in the extension's -bundled list of well-known tokens or have at least 1,000 holders; everything -else is treated as spam and hidden. To track a token explicitly (which also -shows it at zero balance), add it by contract address: +Tokens you hold show up automatically only while "Use Blockscout" is on. Even +then, a token shows up only if it is in the extension's bundled list of +well-known tokens or has at least 1,000 holders. Everything else is treated as +spam and hidden. + +A token you track is shown whether or not "Use Blockscout" is on. By default it +is shown at zero balance too (see Display in Settings). To track a token, add it +by contract address: 1. Go to an address detail view (click `[info]` on any address). 2. Click "+ Token". @@ -239,7 +275,8 @@ screen. Tokens can also be added from Settings, under "Tracked Tokens". 1. Click "Send" from the home screen or an address detail view. 2. Select what to send (ETH, or any ERC-20 token with a balance on this address that survives the spam filters). -3. Enter the recipient address or ENS name (e.g. `vitalik.eth`). +3. Enter the recipient address or ENS name (e.g. `vitalik.eth`). AutistMask does + not look up a name resolved off-chain, so enter its address instead. 4. Enter the amount, or click "Max" to fill it in: a token's balance, cut to 18 decimal places, or your ETH balance minus the amount reserved for the network fee. @@ -389,8 +426,9 @@ Click the gear icon on the home screen to access settings: that network's defaults if you have not used it before. - **Ethereum RPC**: Change the Ethereum node endpoint. Default is a public RPC. You can use your own node for maximum privacy. -- **Blockscout API**: Change the Blockscout instance used for token balances and - transaction history. You can use a self-hosted instance. +- **Blockscout API**: the "Use Blockscout" checkbox, off until you tick it, and + the URL of the Blockscout instance. You can use a self-hosted instance. +- **Etherscan**: "Check recipients on Etherscan", off until you tick it. - **Token Spam Protection**: Toggle individual scam filters and set the dust transaction threshold. - **Allowed Sites / Denied Sites**: View and manage web3 site permissions. diff --git a/src/background/index.js b/src/background/index.js index f9d6b9d..3e46b3c 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -17,7 +17,11 @@ const { applyChainSwitchFields } = require("../shared/chainSwitchFields"); // the same prohibition reported early, not the guarantee. const { getState, updateState } = require("./state"); const { StateUnusableError } = require("../shared/stateSchema"); -const { refreshBalances, getProvider } = require("../shared/balances"); +const { + refreshBalances, + dropUntrackedTokens, + getProvider, +} = require("../shared/balances"); const { debugFetch, log } = require("../shared/log"); const { verifySignedTx, @@ -1386,6 +1390,10 @@ const RECENT_BALANCE_REFRESH_MS = Math.floor(BALANCE_REFRESH_PERIOD_MS / 2); // back over them (https://git.eeqj.de/sneak/AutistMask/issues/577). // lastBalanceRefresh is left as the switch set it, so the next tick refreshes // the new network. +// +// Blockscout may have answered before the user turned "Use Blockscout" off. So +// when "Use Blockscout" stored NOW is off, the save drops every token the user +// has not tracked. async function backgroundRefresh() { const s = await getState(); const now = Date.now(); @@ -1423,6 +1431,9 @@ async function backgroundRefresh() { } } } + if (!fresh.useBlockscout) { + dropUntrackedTokens(fresh.wallets, fresh.trackedTokens); + } fresh.lastBalanceRefresh = now; }); } diff --git a/src/popup/index.html b/src/popup/index.html index b41cf9e..e177aea 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -43,6 +43,38 @@
- + +
+ + +
@@ -660,6 +714,13 @@ phishing/scam. Do not send funds to this address.
+
To
@@ -796,6 +858,7 @@
Amount
+
To
@@ -1002,8 +1065,19 @@

Blockscout API

+ +

+ Shows transaction history, tokens you have not tracked + and transaction details. Sends your addresses, and the + hash of each transaction you open, to the Blockscout API + below. +

- Used to fetch token balances and transaction history. Change this if you run your own Blockscout instance.

+
+

Etherscan

+ +

+ Sends each recipient's address to Etherscan to check it + for a phishing or scam label. +

+
+

Token Spam Protection

@@ -1291,6 +1382,13 @@ >  

+

A wallet always keeps at least one address. To remove the last one, delete the whole wallet from Settings instead. @@ -1504,6 +1602,16 @@

+ + +