Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e71480822b |
@@ -53,18 +53,31 @@ function clearPopupCaches() {
|
||||
// change. So the switch also resets these fields in the stored record:
|
||||
//
|
||||
// - every address's balance and token balances;
|
||||
// - the time of the last balance refresh.
|
||||
// - the time of the last balance refresh;
|
||||
// - the fraud contracts.
|
||||
//
|
||||
// A balance that the background or another page stored since this page's last
|
||||
// save is then not kept under the new network
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/653).
|
||||
// A balance or a fraud contract that the background or another page stored
|
||||
// since this page's last save is then not kept under the new network
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/653,
|
||||
// https://git.eeqj.de/sneak/AutistMask/issues/692).
|
||||
//
|
||||
// Before the switch, `state` takes the tracked tokens that another page stored
|
||||
// since this page's last save (takeStoredTrackedTokens() in state.js). The
|
||||
// switch then puts them away under the previous network, the network they were
|
||||
// added on. When the stored record is on another network, a switch saved
|
||||
// elsewhere has not reached this page yet. The stored record's tokens are then
|
||||
// that network's, and none are taken.
|
||||
async function onChainSwitch(newNetworkId) {
|
||||
const { state, saveState } = require("./state");
|
||||
const { state, saveState, takeStoredTrackedTokens } = require("./state");
|
||||
let net = null;
|
||||
await saveState((stored) => {
|
||||
if (state.networkId === newNetworkId) return false;
|
||||
if (stored.networkId === state.networkId) {
|
||||
takeStoredTrackedTokens(stored);
|
||||
}
|
||||
net = applyChainSwitchFields(state, newNetworkId);
|
||||
stored.lastBalanceRefresh = 0;
|
||||
stored.fraudContracts = [];
|
||||
for (const wallet of stored.wallets) {
|
||||
for (const addr of wallet.addresses) {
|
||||
addr.balance = "0";
|
||||
|
||||
+27
-4
@@ -126,10 +126,10 @@ function currentNetwork() {
|
||||
}
|
||||
|
||||
// The persisted fields as this page held them when its last loadState()
|
||||
// finished, or when its last successful saveState() began, with any network
|
||||
// switch saved elsewhere that this page has taken since (takeStoredNetwork()).
|
||||
// saveState() diffs the live state against this to find only the fields THIS
|
||||
// page changed since.
|
||||
// finished, or when its last successful saveState() began, with what this page
|
||||
// has taken from the stored record since (takeStoredNetwork() and
|
||||
// takeStoredTrackedTokens()). saveState() diffs the live state against this to
|
||||
// find only the fields THIS page changed since.
|
||||
//
|
||||
// Deep-cloned, not a reference: callers mutate persisted objects and arrays
|
||||
// in place (state.wallets.push(...)), and a reference baseline would mutate
|
||||
@@ -615,6 +615,28 @@ function takeStoredNetwork(stored) {
|
||||
copyChainSwitchFields(baseline, structuredClone(stored));
|
||||
}
|
||||
|
||||
// Give this page the tracked tokens of `stored`, a record on this page's
|
||||
// network that a save of this page has just read (onChainSwitch() in
|
||||
// src/shared/chainSwitch.js calls this from `beforeWrite`):
|
||||
//
|
||||
// - A token that another page tracked or removed since this page's last save
|
||||
// is tracked or removed in `state` too.
|
||||
// - A token that this page tracked or removed since then stays as this page
|
||||
// has it.
|
||||
//
|
||||
// `baseline` takes the stored list, so the save stores the list in `state`
|
||||
// whenever the two differ.
|
||||
function takeStoredTrackedTokens(stored) {
|
||||
rawState.trackedTokens = mergeListByIdentity(
|
||||
baseline.trackedTokens,
|
||||
rawState.trackedTokens,
|
||||
stored.trackedTokens,
|
||||
(token) => token.address.toLowerCase(),
|
||||
(base, ours) => ours,
|
||||
);
|
||||
baseline.trackedTokens = structuredClone(stored.trackedTokens);
|
||||
}
|
||||
|
||||
// Rejects with StateUnusableError for a stored record this build cannot make
|
||||
// sense of. Nothing is assigned and `loaded` stays false in that case, so a
|
||||
// caller that ignores the rejection gets StateNotLoadedError on the first
|
||||
@@ -661,6 +683,7 @@ module.exports = {
|
||||
saveState,
|
||||
onSaveFailure,
|
||||
takeStoredNetwork,
|
||||
takeStoredTrackedTokens,
|
||||
loadState,
|
||||
currentAddress,
|
||||
currentNetwork,
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
// a contract lookup that answers after one. Driven through the real popup over
|
||||
// a stored profile; the same switch made by the background is in
|
||||
// tests/coldWorkerChainSwitch.test.js.
|
||||
//
|
||||
// The tests of what another page stored before a switch in Settings cover a
|
||||
// fraud contract too (https://git.eeqj.de/sneak/AutistMask/issues/692).
|
||||
|
||||
const {
|
||||
bootPopup,
|
||||
@@ -178,9 +181,8 @@ test("a token added by contract address on Sepolia is tracked on Sepolia only",
|
||||
});
|
||||
|
||||
// Another popup page tracks DAI on Ethereum Mainnet after this page's last
|
||||
// save. The switch's save writes only the token lists that this page changed.
|
||||
// So DAI stays in `trackedTokens`, the active network's list.
|
||||
test("a token another page tracked before a switch in Settings stays in trackedTokens", async () => {
|
||||
// save. The switch to Sepolia puts DAI away under Ethereum Mainnet.
|
||||
test("a token another page tracked before a switch in Settings stays tracked on Ethereum Mainnet", async () => {
|
||||
const boot = await bootPopup(unversionedValidProfile());
|
||||
expect(boot.pageErrors).toEqual([]);
|
||||
boot.storage.write("autistmask", {
|
||||
@@ -193,10 +195,51 @@ test("a token another page tracked before a switch in Settings stays in trackedT
|
||||
|
||||
const stored = boot.storage.read("autistmask");
|
||||
expect(stored.networkId).toBe("sepolia");
|
||||
expect(stored.trackedTokens).toEqual([MAINNET_TOKEN]);
|
||||
expect(stored.trackedTokens).toEqual([]);
|
||||
expect(stored.networkTrackedTokens.mainnet).toEqual([MAINNET_TOKEN]);
|
||||
});
|
||||
|
||||
// Another popup page stops tracking DAI on Ethereum Mainnet after this page's
|
||||
// last save. This page still holds DAI. The switch to Sepolia does not store
|
||||
// DAI under Ethereum Mainnet again.
|
||||
test("a token another page removed before a switch in Settings stays removed", async () => {
|
||||
const boot = await bootPopup(
|
||||
unversionedValidProfile({ trackedTokens: [MAINNET_TOKEN] }),
|
||||
);
|
||||
expect(boot.pageErrors).toEqual([]);
|
||||
boot.storage.write("autistmask", {
|
||||
...boot.storage.read("autistmask"),
|
||||
trackedTokens: [],
|
||||
});
|
||||
|
||||
await boot.click("btn-settings");
|
||||
await chooseNetwork(boot, "sepolia");
|
||||
|
||||
const stored = boot.storage.read("autistmask");
|
||||
expect(stored.networkId).toBe("sepolia");
|
||||
expect(stored.trackedTokens).toEqual([]);
|
||||
expect(stored.networkTrackedTokens.mainnet).toEqual([]);
|
||||
});
|
||||
|
||||
// Another popup page records a fraud contract on Ethereum Mainnet after this
|
||||
// page's last save. The switch to Sepolia drops that fraud contract, as the
|
||||
// switch drops this page's own.
|
||||
test("a fraud contract another page recorded before a switch in Settings is not kept on Sepolia", async () => {
|
||||
const boot = await bootPopup(unversionedValidProfile());
|
||||
expect(boot.pageErrors).toEqual([]);
|
||||
boot.storage.write("autistmask", {
|
||||
...boot.storage.read("autistmask"),
|
||||
fraudContracts: [NEW_TOKEN_ADDRESS],
|
||||
});
|
||||
|
||||
await boot.click("btn-settings");
|
||||
await chooseNetwork(boot, "sepolia");
|
||||
|
||||
const stored = boot.storage.read("autistmask");
|
||||
expect(stored.networkId).toBe("sepolia");
|
||||
expect(stored.fraudContracts).toEqual([]);
|
||||
});
|
||||
|
||||
// The contract lookup on SettingsAddToken answers on Ethereum Mainnet while the
|
||||
// switch to Sepolia waits for the lock. The page adds the token to Ethereum
|
||||
// Mainnet's list. The switch then keeps that list for Ethereum Mainnet.
|
||||
|
||||
Reference in New Issue
Block a user