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.
This commit is contained in:
@@ -19,9 +19,26 @@ async function onChainSwitch(newNetworkId) {
|
||||
const net = networkById(newNetworkId);
|
||||
|
||||
// --- core identity ---
|
||||
// Endpoints are remembered per network rather than reset to the
|
||||
// defaults, because a user who points the wallet at their own node has
|
||||
// no way to get that URL back once it is gone: overwriting it moved
|
||||
// every address and every transaction onto a third-party endpoint
|
||||
// silently and permanently.
|
||||
//
|
||||
// state.rpcUrl / state.blockscoutUrl stay the live endpoints of the
|
||||
// active network, so nothing that reads them changes. The invariant is
|
||||
// that for the ACTIVE network those two fields are authoritative and
|
||||
// the map entry may be stale (Settings writes the fields directly);
|
||||
// for every other network the map is authoritative. Snapshotting the
|
||||
// outgoing network here, before the switch, is what reconciles them.
|
||||
state.networkEndpoints[state.networkId] = {
|
||||
rpcUrl: state.rpcUrl,
|
||||
blockscoutUrl: state.blockscoutUrl,
|
||||
};
|
||||
const remembered = state.networkEndpoints[net.id] || {};
|
||||
state.networkId = net.id;
|
||||
state.rpcUrl = net.defaultRpcUrl;
|
||||
state.blockscoutUrl = net.defaultBlockscoutUrl;
|
||||
state.rpcUrl = remembered.rpcUrl || net.defaultRpcUrl;
|
||||
state.blockscoutUrl = remembered.blockscoutUrl || net.defaultBlockscoutUrl;
|
||||
|
||||
// --- price cache ---
|
||||
// Prices are chain-specific (testnet tokens are worthless,
|
||||
|
||||
Reference in New Issue
Block a user