The two node programs and the data file from f7a2437 regenerate the name and
url of each token in src/shared/tokenList.js. They are now CommonJS under
script/lib/ and read ethereum-lists at a pinned commit. The apply step also
rewrites an entry that already has a name and url, so it runs on the current
list, and stops on an address that matches no list entry. Exported as
applyEnrichment, it is run by tests/applyEnrichment.test.js, which fails when
the committed list differs from what it writes. Its table of websites now gives
FLOKI and TURBO a URL. make enrich-tokens and make apply-enrichment run the
programs. script/check-censored allows the MUSD token's on-chain name in the
data file.
Model: opus-5-5
Sending, site transactions, site signatures and private key export
derived the key at the address's position in the wallet's list. After
an import whose scan passed over an unused index, or after removing an
address that was not the last, that position named another address's
key.
Each HD and xprv address record now stores the index it was derived at
(at wallet creation, by the import scan, by "+"). getSignerForAddress()
derives at that index, refuses a record without one, and refuses any key
whose address differs from the record's. The confirmation screen signs
as the From address it shows, not the selected one. The export screen
reports a refusal instead of calling the password wrong.
Model: opus-5-5
The element holding the address on the connection prompt was a flex row,
so the title line and the address that renderAddressHtml() returns sat
side by side and the address got only the width left over, which hid its
last nine characters. It is now a plain block, as on the signature
screen, so the address has a row of its own. The Chrome browser suite's
connection test now measures that row with the check the other screens
already use.
Model: opus-5-5
ethers signs only the fields a type declares and drops any other key, but
the Message section listed every key the site sent, so an added key read
as signed. The Message section now follows the signed type, at every level
of nesting and in the type's order. Typed data with other keys is not
refused: those keys are listed after the message under "Not part of the
signature", by their place in the message. README SignApproval says so.
The operator/spender test now also checks the Message section, and new
tests cover an added key in a nested struct and in a list item.
Model: opus-5-5
The screen decoded a transaction's data against the history entry's `to`,
which for a token transfer row is the transfer's recipient. The decoded
Token line named the recipient, and the amount read "decimals unknown" for
a token the wallet tracks. It now decodes against the address the
explorer's answer for that transaction says it was sent to, which is the
right address for every kind of row. tests/tokenTransferDetail.test.js
opens a row made by the real history parser and checks the Token,
Recipient and Amount lines.
Model: opus-5-5
eth_sign is signed as an EIP-191 personal message over its data, which is
what the Ethereum JSON-RPC specification defines it to return. Its warning
on the signature prompt said the site was asking to sign a raw hash that
could drain funds, and the README said the same; neither was true. The
warning now says the site used the older eth_sign method and that the data
will be signed as a personal message, and the README's SignApproval section
says so too. Tests pin that the background reads eth_sign address first and
data second, and that the sign screen answers it with the personal message
signature over the data.
Model: opus-5-5
Settings is reachable from the confirmation screen, and Back from it only
unhides the screen as it was drawn. Sign & Send took the network at the
click, so a transfer reviewed on Sepolia went out on mainnet after a switch
in between. show() now records the chain id of the network it draws for.
Sign & Send refuses with a message on the screen when the active network
differs, and the send carries that chain id, so ethers will not populate it
for a provider on any other network.
Model: opus-5-5
Sites send the gas limit under its JSON-RPC name, `gas`. ethers only
reads `gasLimit`, so the field was dropped and the node's estimate was
shown and signed instead. The page's request now takes `gas` as the gas
limit, and the existing ceiling, display and comparison with the signed
transaction cover it as they already did `gasLimit`. A request that
gives both with different values is refused.
Model: opus-5-5
The notice told the user that these addresses are reachable by software
that repeats the same derivation, while the same wallet blocks Export
Private Key, so there was no way to take that route from here. The
second paragraph now names private key export among the disabled
controls and says the addresses can be reached outside this wallet with
the extended private key the user imported, if they kept a copy of it,
or with the master extended private key or recovery phrase it came
from. No behaviour changes. A test checks the new wording.
Model: opus-5-5
The blocklist check hashed the hostname as the page gave it, so
"myetheywallet.com." and every subdomain written that way hashed
differently from the listed "myetheywallet.com" and opened approval
prompts with no warning. The browser and DNS treat both spellings as
one host. hostnameVariants() now drops one trailing dot before
building the hostname and its parent domains, so the check sees the
name the list holds.
Model: opus-5-5
The approval port's disconnect handler leaves a transaction or signing
approval pending, and its comments said that was so the user could reopen
the toolbar popup. Those approvals open in their own window. When that
window closes, the windows.onRemoved listener rejects the approval, unless
an approved attempt is still running, in which case that attempt answers
the page. Both comments now say so. No code changed.
Model: opus-5-5
In a clone where script/bootstrap has not run, node_modules/ is empty and
yarn falls back to whatever prettier is on the PATH, which can format files
differently from the version yarn.lock pins. script/fmt and script/fmt-check
now stop with a message telling the user to run script/bootstrap when
node_modules/.bin/prettier is missing. After bootstrap they behave as
before. The README Entrypoints entries say so.
Model: opus-5-5
EIP-712 has a wallet refuse typed data whose domain chainId is not the
active chain, because the signature is valid on the chain it names. The
sign screen now shows such a request with the reason on its error line
and Sign disabled, the same way it refuses a mismatched primary type.
The background checks again when the signature comes back, against the
network active then, and answers the site with that refusal instead of
the signature. A chain id is read as ethers signs it, so 1, "1" and
"0x1" are one chain. Typed data with no chainId signs as before.
Model: opus-5-5
Chrome gives extension pages a stylesheet of its own that sets body to
75%, outside any cascade layer, so it beat Tailwind's text-sm and Chrome
drew the popup at 12px where Firefox draws 14px. body now carries
text-sm!, the important form, as it already carries font-mono!. Both
browser suites check that body computes to 14px.
At 14px the recovery phrase instruction on the add wallet screen takes a
third line, which pushed Import below the fold at 360x600. The die button
now shares the instruction's row instead of taking a row of its own.
Model: opus-5-5
Every change used to add an entry at the top of Completed Steps in its
own commit, so each merge into `next` left every other open PR
conflicting on that list. The Workflow section now says to add no
entry: what landed is recorded on the tracker and in `git log`, where
each commit title names the issue it closes. The step that moved Next
Step into Completed Steps and pulled the top of Future Steps into Next
Step goes with it. The entries already in Completed Steps stay.
Model: opus-5-5
The browser suites' wait for a screen passed as soon as the element laid
out. Until the page's stylesheet has applied, every view lays out, so an
approval test could read the prompt before the page's script had filled
it. visible() in the Chrome harness and waitVisible() in the Firefox
driver now also wait for the page to finish loading and for neither the
element nor anything around it to carry the hidden class that showView()
keeps on every view but the current one. Both also require the element's
computed visibility to be visible, as the browser library's own check in
Chrome did. No caller changed.
Model: opus-5-5
Once Settings has saved the new network it asks the background to send
chainChanged with the new chain id to every open tab, through the same
function an approved site switch request uses. Only the extension's own
pages can ask for that. Choosing the network already active changes
nothing and sends nothing.
Model: opus-5-5
A connected site's wallet_switchEthereumChain request for the other
supported network now opens a prompt in its own window, through the
existing approval machinery, naming the site and both networks. The
network, endpoints, balances and caches change, and chainChanged is
sent, only when the user approves it; rejecting or closing the prompt
answers 4001. One such prompt per site at a time; a request for the
active network needs none. The approval window no longer shows the
connection prompt while it waits for the approval's description,
since both prompts answer on the same port.
Model: opus-5-5
The README's 1.0 non-goal said Ethereum mainnet only, while Sepolia
ships as a selectable network. It now puts mainnet and Sepolia in scope
and other chains out of it.
The injected provider's flag named after the other wallet stays, as an
interface-compatibility flag many dApps check. It is now written down
as an exception to the rule against naming competitors, in the README's
Policies section and in RULES.md, and the comment beside it says so.
Neither document spells the name, so script/check-censored, which
already allows the flag only in src/content/inpage.js and its built
copies, is unchanged.
Model: opus-5-5
The polarity check now counts only `hostile` and `falsy` values, not a
`hostileRestore` value, which reaches only the views its entry names.
The stale index 5 moves into `hostile` for `selectedWallet` and
`selectedAddress`, as each field's one value still truthy after the
floor.
Each `hostileRestore` entry declares whether its boot lands on its view
or falls back to Home, and the test checks the one view on screen. A
value driven onto every restorable view lists only the views it falls
back on, so a view added later is driven by default.
The header and the README restate the remaining limits as built and say
which boots are held to where the popup lands.
Model: opus-5-5
The add wallet screen reported a missing, short or mismatched password in
the flash line at the top of the popup, and the private key export,
recovery phrase and delete wallet screens each wrote to a line of their own
above the password field. All four now use showError() and hideError() with
a fixed-height error line below the field, as the send confirmation and
approval screens do. On the add wallet screen the line sits beside the
Import button, which keeps its place at 360x600. The line clears when the
screen is shown again and when the password is tried again. Other add
wallet messages stay in the flash line.
Model: opus-5-5
The security review moves from Next Step to Completed Steps, saying what it
read (the tree at 99292b9), that its ten findings are filed and fixed on next,
which owner decisions it raised are still open, and what it did not cover.
Next Step takes the one Future Steps item, cutting 1.0.0 once the milestone is
empty. Status drops a dated gate result and links the current milestone PR.
Model: opus-5-5
Eighteen branches on origin that the issue classifies as landed, or
superseded by merged pull requests, were deleted; the evidence for each
is on the issue. Four whose work is not in next are kept: the three
issue 87 error-display branches, reference for issue 493, and
chore/token-list-enrichment, re-created at f7a2437, pending issue 495.
TODO.md records this.
The branch-pruning Future Step had already left TODO.md in the issue 191
rewrite, so only the Completed Steps entry is added.
Model: opus-5-5
The owner's ruling on #19 is
now in the README, under ExportPrivKey: copying the key leaves it on the
clipboard, because the clipboard is the user's, clearing it would go
against what they expect, and it could destroy something else they
copied since. ShowRecoveryPhrase copies the phrase the same way and
points back to it. Both describe the warning each password screen
shows on next, which does not mention the clipboard.
Model: opus-5-5
The one entry in ALLOWED_ERRORS in tests/e2e/firefox/run.js, Firefox
reporting a popup promise that settled after the page unloaded, lost its
cause when the site-connection buttons stopped sending with an unawaited
sendMessage before closing (#275). Left in place it would also hide the
same error from any other popup code that sends and then closes. The
entry goes, with the code that only printed and set aside tolerated
errors, and the README paragraph that described it.
Model: opus-5-5
script/lib/icons.js draws the mark from geometry read back out of the
committed PNGs, since the coordinates were never recorded, and writes each
PNG with node's own zlib. `make icons` runs it and leaves alone a file that
already holds the drawn image.
tests/icons.test.js requires each committed file to hold exactly that
image: the same IHDR and every pixel. The compressed bytes are not
compared, because node's bundled zlib does not compress as the stock zlib
that made the committed files did; the decision is recorded on the issue.
build.js copies the manifests from MANIFEST_SOURCES instead of naming the
two paths a second time.
Model: opus-5-5
The window.close override the issue named was removed with #275, so it
needs no entry. Every other place either suite changes or works around
the shipped extension is now listed: the popup loaded in a tab, Chrome's
wait before each site-connection request, its recording wrapper and click
listener, its clipboard grant, two layout tests that write into the page,
the forced leave during a decrypt, and Firefox's setting that forces the
site-connection prompt into a window. Firefox's tolerated error is
described as the leftover it is, with #487 to remove it; the phishing
blocklist is no longer listed as a failing fetch, and two stale figures
are corrected.
Model: opus-5-5
The recovery phrase and delete wallet screens take themselves off the
Back stack when left, so Settings, one of those screens, then the
settings gear leaves Settings under the Settings now showing. A reopened
popup restores the same stack, cut at the screen the gear left. Back
then showed Settings again and seemed to do nothing.
goBack() now skips any entry for the screen already showing before it
pops its target. Jest tests drive the gear and then Back for the
recovery phrase screen, once and twice over, for both delete screens,
and after a reopen.
Model: opus-5-5
With stderr closed, the wrapper's message write failed and set -e ended it
with status 2; with stderr a pipe whose reader had gone, the write killed it
with 141. The message is now written with SIGPIPE ignored and its failure
ignored, after the step has run. An interrupt while a step runs now exits with
130 once the step has ended, removing nothing: under bash, a step that caught
the interrupt and exited with a status, as check-censored does, used to get
dist/ removed. A failed check-censored --require-dist still removes dist/. The
header states both. Also the README bullet's missing period.
Model: opus-5-5
Leaving the delete wallet or lost-password screen drops its wallet
selection, but the settings gear had just pushed the screen onto the Back
stack, so Back from Settings showed a screen whose button could only
answer "No wallet selected for deletion." Each screen's leave handler now
also takes it off the top of the stack, as the private key export and
recovery phrase screens do since
#461. Back from Settings then
stays on Settings once, as it does for the recovery phrase screen.
Jest tests drive the gear and then Back on both screens, and the delete
screen's own Back.
Model: opus-5-5
yarn install exits 0 without touching node_modules whenever
node_modules/.yarn-integrity matches yarn.lock, so a package deleted from
node_modules stayed deleted while bootstrap printed "bootstrap complete".
After the install, bootstrap now asks node for the package.json of every
package listed in dependencies and devDependencies of package.json, and on
the first it cannot find it names the package and the fix:
rm -rf node_modules && make bootstrap. A package whose exports hides its
package.json (ethers, libsodium-wrappers-sumo) makes node throw
ERR_PACKAGE_PATH_NOT_EXPORTED, which it does only after finding the
package, so that error counts as found.
Model: opus-5-5
Leaving the private key export or recovery phrase screen drops the
selection it was showing, but the settings gear had just pushed the
screen onto the Back stack, so Back from Settings landed on a password
prompt that could only fail. Each screen's leave handler now also takes
it off the top of the stack, which is what a reopened popup already does
to these screens. Back from Settings goes to the address screen for the
export screen; for the recovery phrase screen, opened from Settings, it
stays on Settings once, as after a reopen.
Jest tests drive the gear and then Back, and each screen's own Back, for
both screens; leavePrivkeyScreen() in the e2e suite expects the address
screen.
Model: opus-5-5
lookupTokenInfo() cut the symbol at 12 and the name at 64 UTF-16 units,
so an emoji outside the Basic Multilingual Plane could be cut between its
two halves and the half left over stored and shown as U+FFFD. Both are
now cut on code points, as displaySymbol() does. A symbol already stored
broken is not repaired.
Model: opus-5-5
The transaction lists and ENS name lookups on the address and token
screens, the address scan after a wallet is created, the endpoint checks
in Settings, the wait screen's receipt check, the Send screen's Max fee
estimate and the token lookup on both add-token screens now check the
signal the popup aborts on pagehide before reporting a failed request.
scanForAddresses(), resolveEnsNames() and lookupTokenInfo() take the
signal.
End-to-end tests reload the popup on the address screen and during the
address scan with their requests held. Jest tests show each of these
reports a real failure and stays silent once the popup has closed. The
transaction detail and confirmation screens are left out: they discard
the popup context that carries the signal.
Model: opus-5-5
show() found the address line through the element inside it, then
replaced the line's contents with renderAddressHtml(), which deleted
that element, so the next show() in the same popup session threw
before it navigated. The line now carries the export-privkey-address
id itself and is looked up by it. No other view that renders an
address finds its container through a child.
The jest DOM stub now takes an element out of the document when its
parent's contents are replaced, and a new test opens the screen
twice. The #253 e2e case no longer reopens the popup before its
second open.
Model: opus-5-5
Copies .dockerignore, .gitignore, .prettierignore, check.yml and
REPO_POLICIES.md from sneak/prompts at dd4027b, keeping the repo's own
entries (dist/, release/, yarn files) after the canonical content.
The Dockerfile gets separate lint and test phases; its last stage
depends on both, checks the git describe version and runs make build.
script/lint, test, check, cibuild and docker are the canonical models.
check-censored moves into the lint phase and test-verify-build into the
test phase. fmt and fmt-check fall back to the nvm-installed node. The
e2e image builds are uncached. Comments citing the old test caps or what
runs a script are updated.
Model: opus-5-5
Chrome cancels a closing popup's open requests just after pagehide, and in
the page a cancelled fetch() fails with the same "Failed to fetch" as a
server that cannot be reached. The home screen's transaction list and the
balance refresh logged an error for each, and the e2e suite failed on them.
The popup now aborts an AbortController on pagehide, and those failure
reports check its signal first. End-to-end tests reload the popup with
Blockscout held and require nothing logged, and fail the transaction list
for real and require the failure reported; a unit test covers the balance
refresh. The address and token screens and the new-wallet address scan are
#475.
Model: opus-5-5
@tailwindcss/node 4.2.1, loaded by the Tailwind CLI, calls Node's
deprecated module.register() as it starts, so every make build on a
current Node printed the DEP0205 warning. 4.3.1 is the first release that
calls module.registerHooks() instead wherever Node has it.
tailwindcss and @tailwindcss/cli are pinned at exactly 4.3.1, and
yarn.lock carries the new versions of the packages they pull in. The
compiled CSS computes to the same values: it drops the unused .start and
.end rules and writes calc(var(--spacing) * 1) as var(--spacing).
Model: opus-5-5
make dev passed --watch to a build.js that read no arguments, so it
built once and exited. build.js --watch now builds, then builds again
after every change to a file under src/, manifest/ or icons/, until
interrupted; any other argument fails. Each directory gets its own
watcher, because Node's recursive watch on Linux loses a file that an
editor saves by renaming a new copy over it. A watch build writes no
build receipt and cannot be verified; README.md and the Makefile say
so and point to make build.
Model: opus-5-5
No workflow set timeout-minutes, so a hung build or browser held the
shared runner until the server's own limit, hours later. check now stops
at 10 minutes, e2e-firefox at 15 and e2e-chrome at 20: each is over two
and a half times the job's slowest cold-cache run. README "In CI" records
the measured times and the caps.
Model: opus-5-5
handleRpc answers both methods itself before it reaches its proxy branch, so
the two list entries were never used and the list named two methods that are
never sent to the RPC endpoint. No other entry is answered earlier.
PROXY_METHODS is now exported from the background script so that
tests/proxyMethods.test.js can send every listed method from a page and fail
on any that does not reach the RPC endpoint.
Model: opus-5-5
The popup's markup no longer carries style attributes. The 42 in
index.html and in the HTML the view helpers build are now Tailwind
classes, each computing to the value it replaced, so style-src is 'self'
in both manifests, pinned in tests/manifest.test.js.
The address dot's 16 colours are written out as whole classes, because
Tailwind builds only the classes it finds in the source. The Settings
debug well is shown and hidden with the hidden class, since clearing an
inline display no longer uncovers it. Two tests that found the colour dot
by its inline style now find it by its class. Script that sets
element.style is unaffected.
Model: opus-5-5
.prettierignore listed an AI vendor's tool directory, the only such
name in the tree. The directory is not tracked, so the line ignored
nothing and removing it changes no formatting result.
Model: opus-5-5
Two Chrome end-to-end cases send ETH and leave the wait for its receipt
running. In one, lookups answer "no receipt" until the 60-second deadline
ends the wait with the timeout message. In the other, a new fixture switch
makes every receipt lookup fail, and the sixth failure in a row ends the
wait with the message naming the unreachable network. Both check the exact
message and that Done returns to the address screen. Both wait in real
time: Playwright's clock would apply to every later test, and backdating
the stored broadcast time races the popup's own save.
Model: opus-5-5
Adding a token by its contract address is checked against the address
screen's balance list. TransactionDetail opened from the token screen is
checked on the persisted navigation stack, on arrival and after Back,
which is what tells it apart from the address screen's entry point. The
token contract row's explorer link is read off the anchor, not followed,
so it needs no network fixture.
The network stub now answers symbol() and name() for the stub token,
which Add Token reads; before, both decoded as empty strings. The token
stays tracked for the rest of the run.
Model: opus-5-5
Each control that leads to a signature or to the private key now has a
test that it refuses a defective wallet before decrypting anything: Send
on the main, address and token screens, Export Private Key, and both
approval screens, as drawn and as clicked.
Send on the confirmation screen had no such check. The Send buttons
stand in front of it, but the popup reopens onto it from a saved view,
so it now refuses the same way.
The comments that said the wallet's key cannot be derived now say that
getSignerForAddress refuses it, and the walletDefects module comment
names both earlier import paths.
Model: opus-5-5
The Chrome suite now drives the private key export screen as it drives the
recovery phrase screen: the correct password shows the key, leaving by the
settings gear empties the screen, and leaving while the password is still
being checked never puts the key on it. The cases use the imported key
wallet: leaving drops the address the screen was showing, so on an HD
wallet a late decrypt fails by itself and the liveness check would go
untested. Only the phrase screen's state reader now takes the screen's
name, and serves both; the wipe assertion takes the secret, as before. A
second open in one popup session throws (#460), so the cases reopen the
popup before it.
Model: opus-5-5
A stored record a newer build wrote opens the popup on the recovery
screen. Export Saved Data puts that record, exactly as stored, in the
text box; a near-miss confirmation phrase erases nothing; the exact
phrase erases it and reloads into Welcome. Chrome and Firefox run the
same four cases, each under its shipped CSP.
They run before any wallet exists: with no wallet nothing saves on a
timer, so no save can write a good record over the unreadable one, and
the erase leaves the popup on Welcome for wallet creation. If any of
them fails, the last one removes the record so later tests still start
from Welcome.
Model: opus-5-5
The token screen's decimals and holder count, the ETH price, every address
total and each balance row's USD value went into innerHTML unescaped, against
the rule at the top of src/popup/views/helpers.js. They are escaped now. None
could carry markup, but formatUsd() writes a value under a cent as "< $0.01".
displaySymbol() counts a symbol in code points, not UTF-16 units, so the cut
never leaves half of an emoji, which rendered as U+FFFD.
explorerLink() was already removed on next.
Model: opus-5-5
A test that turns a fixture switch on for itself alone turns it off in a
finally, so a failure no longer reddens the tests after it. The two tests
that drive the popup's own send also return it to the address screen,
reopening the popup to leave a wait for a receipt. The lying-decimals()
test asserts that nothing was broadcast as soon as the send ends, before
waiting for the failure screen. ethCallResult() answers an override of 0
instead of falling back to the explorer's scale.
Model: opus-5-5