1 Commits
Author SHA1 Message Date
sneak e0313de2ef fix: Blockscout and Etherscan lookups are off until the user turns them on (closes #668)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s
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
2026-10-26 12:44:24 +00:00
26 changed files with 203 additions and 183 deletions

No files matched your search

+69 -62
View File
@@ -15,19 +15,21 @@ 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
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.
Settings. The endpoint 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.
- a public CoinDesk API (no API key), for realtime prices. The API receives
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
- "Use Blockscout": a Blockscout block-explorer API, used for:
- transaction history
- the tokens the user has not tracked
- transaction details
- "Check recipients on Etherscan": a best-effort Etherscan label lookup of the
recipient during transaction confirmation
@@ -437,13 +439,14 @@ 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:
background worker. The background worker also makes requests while it starts up.
Those come earlier, so they are 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.
- With neither ticked, each of these screens must show the line saying what is
off: Home, AddressDetail, AddressToken and ConfirmTx.
- 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
@@ -460,7 +463,7 @@ comes earlier and is not recorded. The tests take these steps in order:
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.
unit tests cover that case.
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,
@@ -1165,20 +1168,21 @@ 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 these sources, in this order (`resolveTokenDecimals()` in
`src/shared/approvalAmount.js`):
The wallet takes the `decimals` 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`).
Those balances cover the active network only. This source is 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:
These screens show the line:
- TxApproval and SignApproval
- WaitTx, SuccessTx and ErrorTx, which follow TxApproval
@@ -1221,11 +1225,11 @@ refusal the wallet has any reason to make.
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.
contract. That token then has no scale on Send. Its stored balance is withdrawn
as well. 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
@@ -1456,9 +1460,10 @@ setting is off until the user turns it on. While it is off:
- 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.
Blockscout may answer just before the user turns the setting off. So the popup's
refresh and the background's both check the stored setting again when they save.
If the setting is off by then, 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
@@ -1657,9 +1662,9 @@ 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.
Any other open popup takes either change from the stored record, as it takes a
network switch (below). That popup then redraws its top screen as a reopened
popup would. 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
@@ -1702,8 +1707,9 @@ 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
- A well with a line about the user's data. The wallet sends that 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
@@ -1984,11 +1990,11 @@ view would leave a wallet one click from deletion.
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. 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
recipients on Etherscan" is on. While the setting 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
@@ -2100,9 +2106,8 @@ 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). 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.
calldata; the top-level Amount and To are hidden in that case). Each token
in the well gets TxApproval's line under it, in the same case.
- Amount + symbol
- To: color dot + full address + etherscan link, or for a contract creation
the same sentence as on WaitTx
@@ -2160,11 +2165,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. 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."
it, and its etherscan links open that network's explorer. While "Use
Blockscout" is off, Blockscout is not asked. One line then takes the place of
the block, network details, decoded details and raw data: "This screen shows
no block, decoded details, network details or raw data, 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
@@ -2249,17 +2254,17 @@ view would leave a wallet one click from deletion.
`eth_chainId` 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
- A sentence saying what the setting shows and what it sends. With the
setting on, the wallet 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
- A sentence saying the setting sends each recipient's address to
Etherscan, to check the address for a phishing or scam label
- Each of the two settings applies to every network. Welcome offers the same
two checkboxes
- Token Spam Protection:
@@ -2286,9 +2291,10 @@ view would leave a wallet one click from deletion.
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
stores the setting (no screen change). The next balance refresh uses the
new setting. So does every screen drawn after that. 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**
@@ -2830,9 +2836,9 @@ settings:
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.
Before each of these requests, the wallet reads its setting from storage. The
wallet reads the setting again when the answer lands. While the setting is off,
nothing is asked, and an answer that lands is dropped.
Contacted by default:
@@ -2841,10 +2847,10 @@ Contacted by default:
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. 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
receipts. Every ENS lookup is made through it alone. So a name does not
resolve when its resolver says the name is resolved off-chain. 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
@@ -2878,9 +2884,9 @@ Sent the user's data only while the user has turned the setting on:
- **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.
- **Saving the URL**: pressing "Save" beside the URL in Settings asks that
URL for `/stats`, whether or not the setting is on. 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
@@ -2888,9 +2894,10 @@ Sent the user's data only while the user has turned the setting on:
- **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.
time the screen is shown. The lookup 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
+9 -9
View File
@@ -106,7 +106,7 @@ addresses are necessarily visible to the RPC provider when querying balances.
`eth-sepolia.blockscout.com/api/v2` on Sepolia)
Blockscout is an open-source blockchain explorer. AutistMask sends it your data
only while "Use Blockscout" is on. It then asks Blockscout for:
only while "Use Blockscout" is on. AutistMask then asks Blockscout for:
- your recent transactions and token transfers
- the balances of the tokens you have not tracked, with the holder counts used
@@ -124,8 +124,8 @@ When it is contacted, while "Use Blockscout" is on:
- 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
Pressing "Save" beside the Blockscout URL in Settings asks that URL for `/stats`
to check it, whether or not the setting is on. That request sends none of your
data.
What gets sent: your Ethereum addresses (to look up balances and transactions)
@@ -147,7 +147,7 @@ address.
**Etherscan address labels** (`etherscan.io`; `sepolia.etherscan.io` on Sepolia)
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
public Etherscan address page when you review a send. AutistMask 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
@@ -252,14 +252,14 @@ always produce the same sequence of addresses.
### Adding ERC-20 Tokens
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
The wallet finds the tokens you hold by itself 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:
The wallet shows every token you track, whether or not "Use Blockscout" is on.
By default a tracked token 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".
+2 -2
View File
@@ -1392,8 +1392,8 @@ const RECENT_BALANCE_REFRESH_MS = Math.floor(BALANCE_REFRESH_PERIOD_MS / 2);
// 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.
// the save drops every token the user has not tracked if storage NOW holds "Use
// Blockscout" off.
async function backgroundRefresh() {
const s = await getState();
const now = Date.now();
+9 -9
View File
@@ -56,9 +56,9 @@
Use Blockscout
</label>
<p class="text-xs text-muted mb-2">
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
Shows transaction history, transaction details and the
tokens you have not tracked. Sends your addresses, and
the hash of each transaction you open, to the Blockscout
API.
</p>
<label
@@ -1072,10 +1072,10 @@
Use Blockscout
</label>
<p class="text-xs text-muted mb-2">
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.
Shows transaction history, transaction details and the
tokens you have not tracked. Sends your addresses, and
the hash of each transaction you open, to the Blockscout
API below.
</p>
<p class="text-xs text-muted mb-1">
Change this if you run your own Blockscout instance.
@@ -1608,8 +1608,8 @@
id="tx-detail-blockscout-note"
class="text-xs text-muted mx-1 mb-3 hidden"
>
No block, decoded details, network details or raw data are
shown, because "Use Blockscout" is off in Settings.
This screen shows no block, decoded details, network details
or raw data, because "Use Blockscout" is off in Settings.
</p>
<!-- ── Decoded details ── -->
+12 -11
View File
@@ -71,8 +71,9 @@ let refreshInFlight = false;
// the recovery screen: there is no profile left to refresh.
let refreshTimer = null;
// The refresh first takes any change to the network or to either setting saved
// elsewhere (followStoredSettings() below), so what it draws follows them.
// The refresh first takes the stored network, "Use Blockscout" and "Check
// recipients on Etherscan" (followStoredSettings() below). So it draws its
// screens under any change saved elsewhere.
//
// The refresh writes into a copy of the wallets. Inside the save, under the
// lock, its results are copied onto the address records in `state` only if the
@@ -85,8 +86,8 @@ let refreshTimer = null;
// (https://git.eeqj.de/sneak/AutistMask/issues/577). A dropped refresh is not
// recorded as done. Results are copied by address, so an address added or
// removed meanwhile is left as it is. Blockscout may have answered before the
// user turned "Use Blockscout" off. So if the stored setting is off when the
// refresh saves, the save drops every token the user has not tracked.
// user turned "Use Blockscout" off. So the save drops every token the user has
// not tracked if the stored setting is off by then.
async function doRefreshAndRender() {
if (refreshInFlight) return;
refreshInFlight = true;
@@ -239,9 +240,9 @@ function fallbackView() {
// here while this waits for the lock is made after it.
//
// Another page may change "Use Blockscout" or "Check recipients on Etherscan".
// This page takes either change the same way and draws its screens again. Its
// own changes to them are made under the lock too (views/settings.js,
// views/welcome.js).
// This page takes either change the same way and draws its screens again. This
// page's own changes to the two settings are made under the lock too
// (views/settings.js, views/welcome.js).
async function followStoredSettings() {
let changed = false;
// A failed save is reported by saveState() itself: see onSaveFailure().
@@ -413,10 +414,10 @@ async function init() {
settingsAddToken.init(ctx);
deleteAddress.init(ctx);
// Only a record whose network, "Use Blockscout" or "Check recipients on
// Etherscan" differs from this page's is followed. A record this page's
// own save wrote matches this page. A record naming no network is no
// switch.
// This page follows a record only when its network, "Use Blockscout" or
// "Check recipients on Etherscan" differs from this page's. A record
// written by this page's own save matches this page. A record naming no
// network is no switch.
onStorageChanged((changes) => {
const written = changes.autistmask && changes.autistmask.newValue;
if (!written || !written.networkId) return;
+2 -2
View File
@@ -46,8 +46,8 @@ function markViewRendered(view) {
// - after "Use Blockscout" or "Check recipients on Etherscan" changes, here
// or on another page, since those screens were drawn under the old setting
//
// The unit tests, which simulate several page loads against one module
// instance, ask for one too.
// The unit tests ask for one too. They simulate several page loads against one
// module instance.
function resetRenderedViews() {
renderedViews.clear();
}
+1 -1
View File
@@ -84,7 +84,7 @@ function show() {
ctx.showAddressToken();
});
});
// Tokens the user has not tracked are Blockscout's to report.
// Only Blockscout reports the tokens the user has not tracked.
$("address-tokens-note").classList.toggle("hidden", !!state.useBlockscout);
renderSendTokenSelect(addr);
$("tx-list").innerHTML =
+1 -1
View File
@@ -119,7 +119,7 @@ function show(walletIdx, addrIdx) {
$("delete-address-recovery").textContent = recoveryPathText(wallet);
$("delete-address-balance").innerHTML = balanceWarningHtml(addr);
// Tokens the user has not tracked are Blockscout's to report.
// Only Blockscout reports the tokens the user has not tracked.
$("delete-address-tokens-note").classList.toggle(
"hidden",
!!state.useBlockscout,
+9 -8
View File
@@ -326,12 +326,12 @@ function tokenSourcesByChainId(chainId) {
};
}
// The line shown under a token while "Use Blockscout" is off, when its name or
// decimals are missing from the bundled list and the tracked tokens. Otherwise
// "". The token is a contract address on the network with `chainId`, or on the
// active network when none is given. "ETH" and null are no contract, and get
// "". A token that is not text also gets "": WaitTx and ErrorTx pass a token
// restored from storage unchecked.
// The line shown under a token while "Use Blockscout" is off. The token gets
// the line only when the bundled list and the tracked tokens lack its name or
// decimals. Otherwise this returns "". The token is a contract address on the
// network with `chainId`, or on the active network when none is given. "ETH"
// and null are no contract, and get "". A token that is not text also gets "":
// WaitTx and ErrorTx pass a token restored from storage unchecked.
function blockscoutOffTokenNoteHtml(token, chainId) {
if (typeof token !== "string") return "";
if (state.useBlockscout || !token || token === "ETH") return "";
@@ -571,8 +571,9 @@ function addressTitle(address, wallets) {
const CONTRACT_CREATION_TEXT =
"This transaction creates a new contract. It has no recipient.";
// What the transaction lists on Home, AddressDetail and AddressToken say while
// the user has "Use Blockscout" off. Their history comes from Blockscout alone.
// The line shown in place of the transaction history on Home, AddressDetail and
// AddressToken while "Use Blockscout" is off. That history comes from
// Blockscout alone.
const HISTORY_OFF_TEXT =
'No transaction history is shown, because "Use Blockscout" is off in Settings.';
+1 -1
View File
@@ -305,7 +305,7 @@ function walletListHtml() {
function render(ctx) {
const container = $("wallet-list");
// Tokens the user has not tracked are Blockscout's to report.
// Only Blockscout reports the tokens the user has not tracked.
$("home-tokens-note").classList.toggle("hidden", !!state.useBlockscout);
if (state.wallets.length === 0) {
container.innerHTML =
+1 -1
View File
@@ -142,7 +142,7 @@ function updateToValidation() {
}
function renderSendTokenSelect(addr) {
// Tokens the user has not tracked are Blockscout's to report.
// Only Blockscout reports the tokens the user has not tracked.
$("send-tokens-note").classList.toggle("hidden", !!state.useBlockscout);
const sel = $("send-token");
sel.innerHTML = `<option value="ETH">${escapeHtml(nativeCurrency())}</option>`;
+1 -1
View File
@@ -311,7 +311,7 @@ function populateOnChainDetails(txData, chainId) {
// Reads the block, network details and calldata from Blockscout, and decodes
// the calldata. While the stored "Use Blockscout" is off, Blockscout is not
// asked, and an answer that lands after it went off is dropped
// asked. An answer that lands after the setting went off is dropped
// (src/shared/storedSettings.js). Either way none of them is shown.
async function loadFullTxDetails(txHash, chainId) {
if (!(await storedSettingIsOn("useBlockscout"))) return;
+5 -5
View File
@@ -3,7 +3,7 @@
// - ETH balances, and the balances of the tokens the user tracks, via RPC
// - the other ERC-20 token balances and the holder counts via Blockscout,
// while the user has "Use Blockscout" on. Those balances are dropped when
// it is off.
// the setting is off.
// - ENS reverse lookup via RPC
const {
@@ -96,10 +96,10 @@ function rawUnits(value) {
// Fetch token balances for a single address from Blockscout.
// Returns [{ address, name, symbol, decimals, balance, holders }].
//
// While the stored "Use Blockscout" is off, Blockscout is not asked, and an
// answer that lands after it went off is dropped (src/shared/storedSettings.js).
// Either way the answer is an empty list, so no token the user has not tracked
// is listed.
// While the stored "Use Blockscout" is off, Blockscout is not asked. An answer
// that lands after the setting went off is dropped
// (src/shared/storedSettings.js). Either way this returns an empty list, so
// only tracked tokens are listed.
//
// Filters out spam: only shows tokens that are in the known token list,
// explicitly tracked by the user, or have >= 1000 holders. A tracked token's
+1 -1
View File
@@ -80,7 +80,7 @@ function parseEtherscanPage(html) {
* Sepolia.
*
* While the stored "Check recipients on Etherscan" is off, Etherscan is not
* asked, and an answer that lands after it went off is dropped
* asked. An answer that lands after the setting went off is dropped
* (src/shared/storedSettings.js). Either way the answer is null.
*
* @param {string} address - Ethereum address to check.
+5 -4
View File
@@ -469,10 +469,11 @@ function mergeNetworkEndpoints(base, ours, theirs) {
// exists to remove, just delayed and confined to one page instead of two
// (caught by tests/txStatus.test.js). A page's live picture of a field it
// does not own goes on being whatever its last loadState() saw, same as
// before this fix; only the persisted record is guaranteed current. The
// network, "Use Blockscout" and "Check recipients on Etherscan" are the
// exceptions: the popup takes a change another context saved through
// `beforeWrite` (followStoredSettings() in src/popup/index.js).
// before this fix; only the persisted record is guaranteed current. Three
// fields are the exceptions: the network, "Use Blockscout" and "Check
// recipients on Etherscan". Through `beforeWrite`, the popup takes a change to
// any of them that another context saved (followStoredSettings() in
// src/popup/index.js).
async function saveStateOnce(beforeWrite) {
const result = await storageGet("autistmask");
// The record in storage right now is about to be merged into and written
+4 -3
View File
@@ -2,9 +2,10 @@
// profile.
//
// Before each request to Blockscout or Etherscan, 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. A page's own copy
// of the setting is not used, because another page may have just changed it.
// from storage. The wallet reads the setting again when the answer lands. While
// the setting is off, nothing is asked, and an answer that lands is dropped. A
// page's own copy of the setting is not used, because another page may have
// just changed it.
const { storageGet } = require("./browserApi");
const { normalizePersisted } = require("./persistedState");
+3 -3
View File
@@ -237,9 +237,9 @@ function mergeTransactions(txs, tokenTransfers) {
// carries it, and a native entry is labelled with that network's
// `nativeCurrency` from networks.js (`ETH`, `SepoliaETH`).
//
// While the stored "Use Blockscout" is off, Blockscout is not asked, and an
// answer that lands after it went off is dropped (src/shared/storedSettings.js).
// Either way the answer is an empty list.
// While the stored "Use Blockscout" is off, Blockscout is not asked. An answer
// that lands after the setting went off is dropped
// (src/shared/storedSettings.js). Either way the answer is an empty list.
async function fetchRecentTransactions(
address,
blockscoutUrl,
+7 -6
View File
@@ -1,9 +1,10 @@
// While "Use Blockscout" is off, the approval screens and the screens after
// TxApproval name a token, and scale its amount, from the bundled list and the
// tracked tokens alone (https://git.eeqj.de/sneak/AutistMask/issues/668). A
// token they lack gets a line saying Blockscout's report of the user's balances
// is not used to name it. While the setting is on, Blockscout's report of the
// user's holding names and scales the token, as before.
// While "Use Blockscout" is off, the approval screens, and the screens that
// follow TxApproval, take a token's name and scale from the bundled list and
// the tracked tokens alone (https://git.eeqj.de/sneak/AutistMask/issues/668).
// A token missing from both gets a line under it. The line says Blockscout's
// report of the user's balances is not used to name the token. While the
// setting is on, the token's name and scale come from Blockscout's report of
// the user's holding, as before.
//
// Booted through the real popup entry point, as the approval window or as a
// popup reopened onto SuccessTx, WaitTx or ErrorTx. The vault is mocked so that
+27 -22
View File
@@ -1,24 +1,24 @@
// Blockscout and Etherscan are sent none of the user's data until the user
// turns them on (https://git.eeqj.de/sneak/AutistMask/issues/668). A new
// profile has "Use Blockscout" and "Check recipients on Etherscan" both off.
// While a setting is off, the code that would ask its service asks nothing.
// While a setting is off, no code asks its service.
//
// The one request made with the setting off is the check behind "Save" beside
// the Blockscout URL in Settings. It asks that URL only for `/stats` and sends
// none of the user's data.
// Only one request is made with the setting off: the check behind "Save" beside
// the Blockscout URL in Settings. That check asks the URL only for `/stats` and
// sends none of the user's data.
//
// Before any other request to Blockscout or Etherscan, the wallet reads its
// setting from storage, not the page's copy. It reads it again when the answer
// lands. So a change another page saved applies at once. An open page also
// takes such a change and draws its screens again.
// setting from storage, not the page's copy. The wallet reads the setting again
// when the answer lands. So a change saved by another page applies at once. An
// open page also takes such a change and draws its screens again.
//
// Only Blockscout reports the tokens the user has not tracked. While "Use
// Blockscout" is off, no address keeps them and no screen lists them.
//
// The functions that make the requests are driven directly, against a fetch()
// that records every URL it is asked for. The screens are driven through the
// real popup over a stored profile (tests/support/popupBoot.js). Its fetch()
// stub records the same way.
// real popup over a stored profile (tests/support/popupBoot.js). The fetch()
// stub in popupBoot.js records the same way.
"use strict";
@@ -109,7 +109,7 @@ describe("a new profile", () => {
});
describe("where each request is made", () => {
// Every URL fetch() was asked for, in order.
// Every URL that fetch() was asked for, in order.
let asked;
// What Blockscout and Etherscan answer: USDC among ADDRESS's tokens,
@@ -208,8 +208,8 @@ describe("where each request is made", () => {
});
// Asks with `setting` on in storage. Once every request is out, storage
// holds `after` for it, as when the user changes it here or on another
// page. Only then does each request get its answer.
// holds `after` for the setting, as when the user changes it here or on
// another page. Only then does each request get its answer.
async function answeredAfter(setting, after, ask) {
storing({ [setting]: true });
let release;
@@ -232,8 +232,12 @@ describe("where each request is made", () => {
return result;
}
// Each request, the setting that gates it, how to make it, and its answer
// while that setting is off.
// One row per request, giving:
//
// - its name
// - the setting that gates it
// - how to make it
// - its answer while that setting is off
test.each([
[
"Blockscout's token balances",
@@ -290,8 +294,8 @@ describe("where each request is made", () => {
FetchRequest.registerGetUrl(FetchRequest.createGetUrlFunc());
});
// An address that was refreshed while Use Blockscout was on, and holds
// the USDC Blockscout reported then.
// An address refreshed while "Use Blockscout" was on. It holds the USDC
// that Blockscout reported then.
function addressWithUsdc() {
return {
address: ADDRESS,
@@ -539,9 +543,9 @@ describe("the popup", () => {
);
});
// A token the user tracks, and one Blockscout reported that the user has
// not tracked. Both are written in lower case, as the balance rows' and
// the dropdown's token ids are compared below.
// A token the user tracks, and an untracked one that Blockscout reported.
// Both are written in lower case, as the balance rows' and the dropdown's
// token ids are compared below.
const TRACKED = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
const UNTRACKED = TOKEN_ADDRESS.toLowerCase();
const SECOND_ADDRESS = "0x3333333333333333333333333333333333333333";
@@ -698,8 +702,8 @@ describe("the popup", () => {
}
// Turning the setting off dropped the untracked token. A refresh that
// started while it was on still saves its balances when it returns, but
// not that token. The same refresh in the background is in
// started while the setting was on still saves its balances when it
// returns, but not that token. The same refresh in the background is in
// tests/backgroundStateIsolation.test.js.
test("a refresh that returns after Use Blockscout was turned off saves its balances without the untracked token", async () => {
const page = await bootPopup(onAddressDetail(true));
@@ -794,7 +798,8 @@ describe("the popup", () => {
await page.settle();
// The page also takes the change and draws ConfirmTx again, which
// checks once more. So each URL asked for is counted once.
// checks once more. So the test counts each URL once, however often
// it was asked for.
const asked = new Set(
fetched().slice(from).filter(blockscoutOrEtherscan),
);
+1 -1
View File
@@ -247,7 +247,7 @@ async function openSend(token, tokenUnits = null) {
state.selectedWallet = 0;
state.selectedAddress = 0;
// Blockscout is asked only while the stored "Use Blockscout" is on. The
// screens save this page's copy of it over the stored one.
// screens save this page's copy of the setting over the stored one.
state.useBlockscout = true;
await refreshBalances(
state.wallets,
+3 -3
View File
@@ -656,9 +656,9 @@ function traceEnabled(raw) {
* @param {string} [opts.etherscanLabel] the label Etherscan's page gives the
* address asked about, such as "Fake_Phishing1"; none when unset. Read at
* request time.
* @param {object[]} [opts.requests] every request this handler sees, from the
* popup's pages and the background worker alike, appended in order as
* { method, url, fromWorker }.
* @param {object[]} [opts.requests] a list for the requests this handler
* sees. Each is appended in order as { method, url, fromWorker }, whether it
* came from a popup page or the background worker.
* @returns {Promise<{waitForServiceWorkerTraffic: (ms: number) =>
* Promise<string|null>}>}
*/
+22 -19
View File
@@ -274,24 +274,27 @@ test("the exact confirmation phrase erases the record and reloads into Welcome (
// A fresh install sends Blockscout and Etherscan none of the user's data until
// the user turns on "Use Blockscout" or "Check recipients on Etherscan". One
// request is made with the setting off: pressing "Save" beside the Blockscout
// URL asks that URL for `/stats`. It carries no user data.
// URL asks that URL for `/stats`. That request carries no user data.
//
// The route handler in tests/e2e/network.js records every request the
// extension makes in env.routeOpts.requests, from its pages and its background
// worker. Recording starts when launch() in tests/e2e/harness.js installs that
// handler, before the first popup page opens. A request the background worker
// makes while it starts up comes earlier and is not recorded.
// The route handler in tests/e2e/network.js records in env.routeOpts.requests
// every request the extension makes, from its pages and its background worker.
// Recording starts when launch() in tests/e2e/harness.js installs that handler,
// before the first popup page opens. The background worker also makes requests
// while it starts up. Those come earlier, so they are not recorded.
//
// Before either setting is changed, these tests drive Home, AddressDetail,
// AddressToken and ConfirmTx, and the background's own balance refresh. Then
// they turn each setting on and watch its requests start. "Use Blockscout" is
// also turned off once and on again, to see a token the user has not tracked
// leave every list. TransactionDetail, which also asks Blockscout, is left to
// the unit tests in tests/blockscoutEtherscanSettings.test.js.
// Before either setting is changed, these tests drive:
//
// - Home, AddressDetail, AddressToken and ConfirmTx
// - the background's own balance refresh
//
// Then they turn each setting on and watch its requests start. "Use Blockscout"
// is also turned off once and on again, to see an untracked token leave every
// list. TransactionDetail, which also asks Blockscout, is left to the unit
// tests in tests/blockscoutEtherscanSettings.test.js.
// What the three transaction lists say while Use Blockscout is off. Written
// out here rather than imported, so the assertion does not compare the popup
// with itself.
// The line shown in place of the three transaction histories while "Use
// Blockscout" is off. Written out here rather than imported, so the assertion
// does not compare the popup with itself.
const HISTORY_OFF =
'No transaction history is shown, because "Use Blockscout" is off in Settings.';
@@ -399,8 +402,8 @@ test("with neither setting changed, Home, AddressDetail, AddressToken and Confir
});
// The background refreshes balances on its alarm, once a minute. It skips a
// refresh when the stored record says one ran in the last 30 seconds, and the
// open popup's own refresh runs every 10. So the popup is closed first. The
// refresh when the stored record says one ran in the last 30 seconds. The open
// popup's own refresh runs every 10. So the popup is closed first. The
// background worker then writes that record back to say no refresh has run, as
// another extension page saving it would.
test("with neither setting changed, nothing has reached Blockscout or Etherscan, the background's refresh included (#668)", async (env) => {
@@ -435,7 +438,7 @@ test("with neither setting changed, nothing has reached Blockscout or Etherscan,
);
await sleep(250);
}
// Its RPC reads and any Blockscout request start together.
// The refresh's RPC reads and any Blockscout request start together.
await sleep(3000);
const seen = explorerRequests(env);
@@ -449,7 +452,7 @@ test("with neither setting changed, nothing has reached Blockscout or Etherscan,
env.routeOpts.requests.some((r) => !r.fromWorker),
"no request from the popup was recorded",
);
// It reopens on the address screen the test above left it on.
// The popup reopens on the address screen the test above left it on.
env.page = await openPopup(env.ctx, env.popupUrl);
await visible(env.page, "#view-address");
});
+5 -5
View File
@@ -1,14 +1,14 @@
// ENS lookups ask nothing but the RPC endpoint. A resolver can answer that a
// name is resolved off-chain, naming a web address to fetch the answer from.
// ethers fetches it unless the provider has `disableCcipRead` set. That request
// would carry the name to a server other than the RPC endpoint
// ethers fetches that address unless the provider has `disableCcipRead` set.
// That request would carry the name to a server other than the RPC endpoint
// (https://git.eeqj.de/sneak/AutistMask/issues/668). With the flag set, such a
// name does not resolve.
//
// The test builds its provider with getProvider() and runs it against a fake
// JSON-RPC node. Its answers go through ethers' own request hook, which also
// records the address of every request ethers makes, the off-chain fetch
// included.
// JSON-RPC node. The fake node answers through ethers' own request hook. The
// hook also records the address of every request ethers makes, the off-chain
// fetch included.
const { FetchRequest, Interface } = require("ethers");
const { getProvider } = require("../src/shared/balances");
+1 -1
View File
@@ -221,7 +221,7 @@ async function refreshWith(balanceWei, tokenItems = []) {
state.selectedWallet = 0;
state.selectedAddress = 0;
// Blockscout is asked only while the stored "Use Blockscout" is on. The
// screens save this page's copy of it over the stored one.
// screens save this page's copy of the setting over the stored one.
state.useBlockscout = true;
await refreshBalances(
state.wallets,
+1 -1
View File
@@ -217,7 +217,7 @@ async function refreshWith(balanceWei, tokenItems = []) {
state.selectedWallet = 0;
state.selectedAddress = 0;
// Blockscout is asked only while the stored "Use Blockscout" is on. The
// screens save this page's copy of it over the stored one.
// screens save this page's copy of the setting over the stored one.
state.useBlockscout = true;
await refreshBalances(
state.wallets,
+1 -1
View File
@@ -273,7 +273,7 @@ beforeEach(() => {
state.hideLowHolderTokens = false;
state.currentView = null;
// The screens save this page's copy of "Use Blockscout" over the stored
// one, so it is on here too.
// one. So the page's copy is turned on here too.
state.useBlockscout = true;
});