Files
AutistMask/tests/networkEndpoints.test.js
clawbot ba7c5d7d10
Some checks failed
check / check (push) Successful in 28s
e2e / e2e-chrome (push) Failing after 49s
e2e / e2e-firefox (push) Successful in 24s
fix: gate the chain switch and remember endpoints per network (closes #308)
wallet_switchEthereumChain was answered for any origin at all, with no
connection check and no prompt, so a page the user had never connected to
could move the active chain — clearing the [TESTNET] banner under someone
who believed they were on Sepolia. It now takes the same
allowedSites/connectedSites gate the signing methods take, ahead of the
same-chain and unsupported-chain answers, and refuses an unconnected origin
with 4100.

The switch also overwrote state.rpcUrl and state.blockscoutUrl with the
network defaults, so a user running their own node lost that url
permanently and silently to a public endpoint that then sees every address
they hold. Endpoints are now remembered per network in
state.networkEndpoints: the switch snapshots the network being left and
restores the network being entered, falling back to that network's
defaults. state.rpcUrl and state.blockscoutUrl remain the live endpoints of
the active network, so no reader changed; for the active network they are
authoritative and the map entry may be stale, and the snapshot is what
reconciles them. A profile written before the map existed has its stored
pair adopted for the network it was stored under, so nothing is lost on
first load.

Neither half held without loading state first. onChainSwitch() mutates the
module-level state singleton and persists every field of it, and
currentNetwork() reads the same singleton, but the service worker populates
nothing at module scope — a worker revived by the page's own message held
DEFAULT_STATE, so the same-chain check compared against the wrong network
and the save wrote empty wallets, empty allowedSites, no tracked tokens and
the default endpoints over the user's stored profile. The handler now
awaits loadState() after the gate, as the transaction path already does.

A stored networkEndpoints must now be an actual object. The previous guard
discarded only falsy values and arrays, so a stored primitive survived the
load, the seeding assignment silently no-opped on it, saveState()
re-persisted it, and every switch fell back to the public default in place
of the user's endpoint — permanently, with no self-healing.
2026-08-20 10:24:27 +00:00

7.4 KiB