From 3efffa749ccb4512a262296febfd344a5279090d Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 10:05:43 +0100 Subject: [PATCH 01/12] wip: Use FixtureExtensionStore --- app/scripts/background.js | 8 +-- app/scripts/lib/setup-initial-state-hooks.js | 4 +- .../lib/stores/fixture-extension-store.ts | 56 +++++++++++++++++++ 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 app/scripts/lib/stores/fixture-extension-store.ts diff --git a/app/scripts/background.js b/app/scripts/background.js index 9d13e7b13f3..ba3b730853b 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -60,7 +60,7 @@ import { PersistenceManager, } from './lib/stores/persistence-manager'; import ExtensionStore from './lib/stores/extension-store'; -import ReadOnlyNetworkStore from './lib/stores/read-only-network-store'; +import { FixtureExtensionStore } from './lib/stores/fixture-extension-store'; import migrations from './migrations'; import Migrator from './lib/migrator'; import { updateRemoteFeatureFlags } from './lib/update-remote-feature-flags'; @@ -114,10 +114,8 @@ const BADGE_COLOR_NOTIFICATION = '#D73847'; const BADGE_MAX_COUNT = 9; const inTest = process.env.IN_TEST; -const useReadOnlyNetworkStore = - inTest && getManifestFlags().testing?.forceExtensionStore !== true; -const localStore = useReadOnlyNetworkStore - ? new ReadOnlyNetworkStore() +const localStore = inTest + ? new FixtureExtensionStore() : new ExtensionStore(); const persistenceManager = new PersistenceManager({ localStore }); diff --git a/app/scripts/lib/setup-initial-state-hooks.js b/app/scripts/lib/setup-initial-state-hooks.js index 78fe7d53d92..e28a682d427 100644 --- a/app/scripts/lib/setup-initial-state-hooks.js +++ b/app/scripts/lib/setup-initial-state-hooks.js @@ -1,7 +1,7 @@ import { maskObject } from '../../../shared/modules/object.utils'; import ExtensionPlatform from '../platforms/extension'; import { SENTRY_BACKGROUND_STATE } from '../constants/sentry-state'; -import ReadOnlyNetworkStore from './stores/read-only-network-store'; +import { FixtureExtensionStore } from './stores/fixture-extension-store'; import ExtensionStore from './stores/extension-store'; import { PersistenceManager } from './stores/persistence-manager'; @@ -10,7 +10,7 @@ const platform = new ExtensionPlatform(); // This instance of `localStore` is used by Sentry to get the persisted state const sentryLocalStore = new PersistenceManager({ localStore: process.env.IN_TEST - ? new ReadOnlyNetworkStore() + ? new FixtureExtensionStore() : new ExtensionStore(), }); diff --git a/app/scripts/lib/stores/fixture-extension-store.ts b/app/scripts/lib/stores/fixture-extension-store.ts new file mode 100644 index 00000000000..993a536cf4a --- /dev/null +++ b/app/scripts/lib/stores/fixture-extension-store.ts @@ -0,0 +1,56 @@ +import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'; +import ExtensionStore from './extension-store'; + +const fetchWithTimeout = getFetchWithTimeout(); + +const FIXTURE_SERVER_HOST = 'localhost'; +const FIXTURE_SERVER_PORT = 12345; +const FIXTURE_SERVER_URL = `http://${FIXTURE_SERVER_HOST}:${FIXTURE_SERVER_PORT}/state.json`; + +export class FixtureExtensionStore extends ExtensionStore { + #initialized: boolean = false; + + #initializing?: Promise; + + constructor() { + super(); + this.#initializing = this.#init(); + } + + async #init() { + try { + const response = await fetchWithTimeout(FIXTURE_SERVER_URL); + + if (response.ok) { + this.#state = await response.json(); + } else { + log.debug( + `Received response with a status of ${response.status} ${response.statusText}`, + ); + } + } catch (error) { + console.log('error', error); + if (error instanceof Error) { + log.debug(`Error loading network state: '${error.message}'`); + } else { + log.debug(`Error loading network state: An unknown error occurred`); + } + } finally { + this.#initialized = true; + } + } + + async get(): Promise { + if (!this.#initialized) { + await this.#initializing; + } + return super.get(); + } + + async set(data: Required): Promise { + if (!this.#initialized) { + await this.#initializing; + } + return super.set(data); + } +} From 41b2e3280ccc3108c8503404d65f2f5367e47918 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 10:13:19 +0100 Subject: [PATCH 02/12] Allow disabling fixture store entirely --- app/scripts/background.js | 6 +++--- app/scripts/lib/stores/fixture-extension-store.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index ba3b730853b..3009742d468 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -114,9 +114,9 @@ const BADGE_COLOR_NOTIFICATION = '#D73847'; const BADGE_MAX_COUNT = 9; const inTest = process.env.IN_TEST; -const localStore = inTest - ? new FixtureExtensionStore() - : new ExtensionStore(); +const useFixtureStore = + inTest && getManifestFlags().testing?.forceExtensionStore !== true; +const localStore = useFixtureStore ? new FixtureExtensionStore() : new ExtensionStore(); const persistenceManager = new PersistenceManager({ localStore }); const { update, requestSafeReload } = getRequestSafeReload(persistenceManager); diff --git a/app/scripts/lib/stores/fixture-extension-store.ts b/app/scripts/lib/stores/fixture-extension-store.ts index 993a536cf4a..f427b729082 100644 --- a/app/scripts/lib/stores/fixture-extension-store.ts +++ b/app/scripts/lib/stores/fixture-extension-store.ts @@ -22,7 +22,8 @@ export class FixtureExtensionStore extends ExtensionStore { const response = await fetchWithTimeout(FIXTURE_SERVER_URL); if (response.ok) { - this.#state = await response.json(); + const state = await response.json(); + await super.set(state); } else { log.debug( `Received response with a status of ${response.status} ${response.statusText}`, From 4e554050c86ce3f6007f6e349e241cb8306db7df Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 10:39:26 +0100 Subject: [PATCH 03/12] Add reset method --- app/scripts/background.js | 4 +++- app/scripts/lib/stores/fixture-extension-store.ts | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 3009742d468..7e17b96d28f 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -116,7 +116,9 @@ const BADGE_MAX_COUNT = 9; const inTest = process.env.IN_TEST; const useFixtureStore = inTest && getManifestFlags().testing?.forceExtensionStore !== true; -const localStore = useFixtureStore ? new FixtureExtensionStore() : new ExtensionStore(); +const localStore = useFixtureStore + ? new FixtureExtensionStore() + : new ExtensionStore(); const persistenceManager = new PersistenceManager({ localStore }); const { update, requestSafeReload } = getRequestSafeReload(persistenceManager); diff --git a/app/scripts/lib/stores/fixture-extension-store.ts b/app/scripts/lib/stores/fixture-extension-store.ts index f427b729082..d310dc1550a 100644 --- a/app/scripts/lib/stores/fixture-extension-store.ts +++ b/app/scripts/lib/stores/fixture-extension-store.ts @@ -54,4 +54,11 @@ export class FixtureExtensionStore extends ExtensionStore { } return super.set(data); } + + async reset(): Promisea { + this.#initialized = false; + await super.reset(); + this.#initializing = this.#init(); + await this.#initializing; + } } From 3e97ac5fb8908478638e204c615b7d7f30357984 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 10:43:24 +0100 Subject: [PATCH 04/12] Fix typo and bad imports --- app/scripts/lib/stores/fixture-extension-store.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/scripts/lib/stores/fixture-extension-store.ts b/app/scripts/lib/stores/fixture-extension-store.ts index d310dc1550a..d23af3c6460 100644 --- a/app/scripts/lib/stores/fixture-extension-store.ts +++ b/app/scripts/lib/stores/fixture-extension-store.ts @@ -1,5 +1,7 @@ +import log from 'loglevel'; import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'; import ExtensionStore from './extension-store'; +import type { MetaMaskStorageStructure } from './base-store'; const fetchWithTimeout = getFetchWithTimeout(); @@ -55,7 +57,7 @@ export class FixtureExtensionStore extends ExtensionStore { return super.set(data); } - async reset(): Promisea { + async reset(): Promise { this.#initialized = false; await super.reset(); this.#initializing = this.#init(); From 22223d2094e4a39f6d5352b7c88bfa3d9129fcf5 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 11:19:50 +0100 Subject: [PATCH 05/12] Update UI state snapshot --- test/e2e/tests/metrics/errors.spec.ts | 1 - .../errors-before-init-opt-in-ui-state.json | 117 +++++++++--------- 2 files changed, 58 insertions(+), 60 deletions(-) diff --git a/test/e2e/tests/metrics/errors.spec.ts b/test/e2e/tests/metrics/errors.spec.ts index 314fcab2c6f..93c396b66ef 100644 --- a/test/e2e/tests/metrics/errors.spec.ts +++ b/test/e2e/tests/metrics/errors.spec.ts @@ -145,7 +145,6 @@ async function matchesSnapshot({ update?: boolean; }): Promise { const snapshotPath = resolve(__dirname, `./state-snapshots/${snapshot}.json`); - console.log('snapshotPath', snapshotPath); const rawSnapshotData = await fs.readFile(snapshotPath, { encoding: 'utf-8', }); diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json index 3d497689c46..b49c39ba131 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json @@ -1,16 +1,11 @@ { "data": { - "AuthenticationController": { "isSignedIn": "boolean" }, - "NotificationServicesController": { - "subscriptionAccountsSeen": "object", - "isFeatureAnnouncementsEnabled": "boolean", - "isNotificationServicesEnabled": "boolean", - "isMetamaskNotificationsFeatureSeen": "boolean", - "metamaskNotificationsList": "object", - "metamaskNotificationsReadList": "object" + "AccountOrderController": { + "hiddenAccountList": {}, + "pinnedAccountList": {} }, "AccountsController": { - "internalAccounts": { "selectedAccount": "string", "accounts": "object" } + "internalAccounts": { "accounts": "object", "selectedAccount": "string" } }, "AlertController": { "alertEnabledness": { "unconnectedAccount": true, "web3ShimUsage": true }, @@ -18,46 +13,33 @@ "web3ShimUsageOrigins": "object" }, "AnnouncementController": { "announcements": "object" }, - "NetworkOrderController": { - "orderedNetworkList": { - "0": "object", - "1": "object", - "2": "object" - } - }, - "NetworkEnablementController": { - "enabledNetworkMap": { "eip155": "object", "solana": "object" } - }, - "AccountOrderController": { - "pinnedAccountList": {}, - "hiddenAccountList": {} - }, "AppStateController": { + "appActiveTab": "object", "browserEnvironment": {}, - "nftsDropdownState": {}, "connectedStatusPopoverHasBeenShown": true, - "termsOfUseLastAgreed": "number", "defaultHomeActiveTabName": null, "fullScreenGasPollTokens": "object", + "hadAdvancedGasFeesSetPriorToMigration92_3": false, + "hasShownMultichainAccountsIntroModal": "boolean", + "isRampCardClosed": false, + "newPrivacyPolicyToastClickedOrClosed": "boolean", + "newPrivacyPolicyToastShownDate": "number", + "nftsDropdownState": {}, "notificationGasPollTokens": "object", + "pendingShieldCohort": null, + "pendingShieldCohortTxType": null, "popupGasPollTokens": "object", "recoveryPhraseReminderHasBeenShown": true, "recoveryPhraseReminderLastShown": "number", + "showShieldEntryModalOnce": "boolean", "showTestnetMessageInDropdown": true, - "trezorModel": null, - "isRampCardClosed": false, - "newPrivacyPolicyToastClickedOrClosed": "boolean", - "newPrivacyPolicyToastShownDate": "number", "snapsInstallPrivacyWarningShown": true, - "hasShownMultichainAccountsIntroModal": "boolean", - "showShieldEntryModalOnce": "boolean", - "pendingShieldCohort": null, - "pendingShieldCohortTxType": null, - "appActiveTab": "object" + "termsOfUseLastAgreed": "number", + "trezorModel": null }, + "AuthenticationController": { "isSignedIn": "boolean" }, "BridgeController": {}, "CurrencyController": { - "currentCurrency": "usd", "currencyRates": { "ETH": { "conversionDate": "number", @@ -69,7 +51,8 @@ "conversionRate": 0.2, "usdConversionRate": 0.2 } - } + }, + "currentCurrency": "usd" }, "GasFeeController": { "estimatedGasFeeTimeBounds": {}, @@ -78,25 +61,40 @@ }, "KeyringController": { "vault": "string" }, "MetaMetricsController": { + "dataCollectionForMarketing": "boolean", "eventsBeforeMetricsOptIn": "object", - "tracesBeforeMetricsOptIn": "object", "fragments": "object", "metaMetricsId": "0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420", "participateInMetaMetrics": true, - "dataCollectionForMarketing": "boolean", + "tracesBeforeMetricsOptIn": "object", "traits": "object" }, "MetaMetricsDataDeletionController": { "metaMetricsDataDeletionId": null, "metaMetricsDataDeletionTimestamp": 0 }, + "MultichainAccountService": "object", + "NameController": { "names": "object" }, "NetworkController": { - "selectedNetworkClientId": "string", - "networkConfigurations": "object", + "networkConfigurationsByChainId": "object", "networksMetadata": { - "networkConfigurationId": { "EIPS": {}, "status": "available" } + "networkConfigurationId": { "EIPS": {}, "status": "unknown" } }, - "providerConfig": "object" + "selectedNetworkClientId": "string" + }, + "NetworkEnablementController": { + "enabledNetworkMap": { "eip155": "object", "solana": "object" } + }, + "NetworkOrderController": { + "orderedNetworkList": { "0": "object", "1": "object", "2": "object" } + }, + "NotificationServicesController": { + "isFeatureAnnouncementsEnabled": "boolean", + "isMetamaskNotificationsFeatureSeen": "boolean", + "isNotificationServicesEnabled": "boolean", + "metamaskNotificationsList": "object", + "metamaskNotificationsReadList": "object", + "subscriptionAccountsSeen": "object" }, "OnboardingController": { "completedOnboarding": true, @@ -106,43 +104,44 @@ }, "PermissionController": { "subjects": "object" }, "PreferencesController": { - "advancedGasFee": null, + "advancedGasFee": {}, "currentLocale": "en", - "useExternalServices": "boolean", "dismissSeedBackUpReminder": true, - "overrideContentSecurityPolicyHeader": true, "featureFlags": {}, "forgottenPassword": false, "identities": "object", "ipfsGateway": "string", + "isMultiAccountBalancesEnabled": "boolean", "knownMethodData": "object", "ledgerTransportType": "webhid", "lostIdentities": "object", "openSeaEnabled": false, + "overrideContentSecurityPolicyHeader": true, "preferences": { "hideZeroBalanceTokens": false, + "shouldShowAggregatedBalancePopover": "boolean", "showExtensionInFullSizeView": false, "showFiatInTestnets": false, + "showMultiRpcModal": "boolean", + "showNativeTokenAsMainBalance": true, "showTestNetworks": false, + "smartAccountOptIn": "boolean", + "smartTransactionsMigrationApplied": "boolean", "smartTransactionsOptInStatus": true, - "showNativeTokenAsMainBalance": true, - "petnamesEnabled": "boolean", - "showMultiRpcModal": "boolean", - "showConfirmationAdvancedDetails": false, - "tokenSortConfig": "object", - "shouldShowAggregatedBalancePopover": "boolean", - "tokenNetworkFilter": {} + "tokenNetworkFilter": {}, + "tokenSortConfig": "object" }, + "referrals": "object", + "securityAlertsEnabled": "boolean", "selectedAddress": "string", "theme": "light", "useBlockie": false, - "useNftDetection": false, - "usePhishDetect": true, - "useTokenDetection": false, "useCurrencyRateCheck": true, + "useExternalServices": "boolean", "useMultiAccountBalanceChecker": true, - "isMultiAccountBalancesEnabled": "boolean", - "referrals": "object" + "useNftDetection": false, + "usePhishDetect": true, + "useTokenDetection": true }, "SelectedNetworkController": { "domains": "object" }, "SmartTransactionsController": { @@ -160,10 +159,10 @@ "allIgnoredTokens": {}, "allTokens": {} }, - "MultichainAccountService": "object", "TransactionController": { "transactions": "object" }, + "UserStorageController": { "isBackupAndSyncEnabled": true }, "config": "object", "firstTimeInfo": "object" }, - "meta": { "version": 74 } + "meta": { "version": 183 } } From fee3bfb601f8719f842acd95376ee61fdfee95b5 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 11:38:51 +0100 Subject: [PATCH 06/12] Delete ReadOnlyNetworkStore --- ...est.ts => fixture-extension-store.test.ts} | 70 +++++++------- .../lib/stores/fixture-extension-store.ts | 3 + .../lib/stores/read-only-network-store.ts | 94 ------------------- 3 files changed, 36 insertions(+), 131 deletions(-) rename app/scripts/lib/stores/{read-only-network-store.test.ts => fixture-extension-store.test.ts} (69%) delete mode 100644 app/scripts/lib/stores/read-only-network-store.ts diff --git a/app/scripts/lib/stores/read-only-network-store.test.ts b/app/scripts/lib/stores/fixture-extension-store.test.ts similarity index 69% rename from app/scripts/lib/stores/read-only-network-store.test.ts rename to app/scripts/lib/stores/fixture-extension-store.test.ts index b20778f3f20..e4158a07a0c 100644 --- a/app/scripts/lib/stores/read-only-network-store.test.ts +++ b/app/scripts/lib/stores/fixture-extension-store.test.ts @@ -1,6 +1,7 @@ import log from 'loglevel'; import nock from 'nock'; -import ReadOnlyNetworkStore from './read-only-network-store'; +import browser from 'webextension-polyfill'; +import { FixtureExtensionStore } from './fixture-extension-store'; const FIXTURE_SERVER_HOST = 'localhost'; const FIXTURE_SERVER_PORT = 12345; @@ -13,15 +14,28 @@ const DEFAULT_INITIAL_STATE = { const MOCK_STATE = { data: { config: { foo: 'bar' } }, meta: { version: 1 } }; -/** - * Initiatilizes a ReadOnlyNetworkStore for testing - * - * @returns store - a ReadOnlyNetworkStore - */ -function setupReadOnlyNetworkStore() { - const store = new ReadOnlyNetworkStore(); - return store; -} +jest.mock('webextension-polyfill', () => { + class MockBrowserStorage { + #state: unknown = null; + + async get() { + return this.#state; + } + + async set(value: unknown) { + this.#state = value; + } + + async clear() { + this.#state = null; + } + } + + return ({ + runtime: { lastError: null }, + storage: { local: new MockBrowserStorage() }, + }) +}); /** * Create a Nock scope for the fixture server response. @@ -43,16 +57,16 @@ function setMockFixtureServerReply( mockFixtureServerInterceptor().reply(200, state); } -describe('ReadOnlyNetworkStore', () => { - beforeEach(() => { - jest.resetModules(); +describe('FixtureExtensionStore', () => { + beforeEach(async () => { + await browser.storage.local.clear(); nock.cleanAll(); }); describe('constructor', () => { it('loads state from the network if fetch is successful and response is ok', async () => { setMockFixtureServerReply(MOCK_STATE); - const store = setupReadOnlyNetworkStore(); + const store = new FixtureExtensionStore(); const result = await store.get(); @@ -64,7 +78,7 @@ describe('ReadOnlyNetworkStore', () => { .spyOn(log, 'debug') .mockImplementation(() => undefined); mockFixtureServerInterceptor().reply(400); - const store = setupReadOnlyNetworkStore(); + const store = new FixtureExtensionStore(); const result = await store.get(); @@ -79,7 +93,7 @@ describe('ReadOnlyNetworkStore', () => { const logDebugSpy = jest .spyOn(log, 'debug') .mockImplementation(() => undefined); - const store = setupReadOnlyNetworkStore(); + const store = new FixtureExtensionStore(); const result = await store.get(); @@ -91,18 +105,9 @@ describe('ReadOnlyNetworkStore', () => { }); describe('get', () => { - it('returns null if #state is null', async () => { - mockFixtureServerInterceptor().reply(200); - const store = setupReadOnlyNetworkStore(); - - const result = await store.get(); - - expect(result).toBe(null); - }); - - it('returns null if state is null', async () => { + it('returns fixture state after waiting for init', async () => { setMockFixtureServerReply(MOCK_STATE); - const store = setupReadOnlyNetworkStore(); + const store = new FixtureExtensionStore(); const result = await store.get(); @@ -111,17 +116,8 @@ describe('ReadOnlyNetworkStore', () => { }); describe('set', () => { - it('throws if not passed a state parameter', async () => { - const store = setupReadOnlyNetworkStore(); - - await expect( - // @ts-expect-error Intentionally passing incorrect type - store.set(undefined), - ).rejects.toThrow('MetaMask - updated state is missing'); - }); - it('sets the state', async () => { - const store = setupReadOnlyNetworkStore(); + const store = new FixtureExtensionStore(); await store.set({ data: { appState: { test: true } }, diff --git a/app/scripts/lib/stores/fixture-extension-store.ts b/app/scripts/lib/stores/fixture-extension-store.ts index d23af3c6460..273022f1c97 100644 --- a/app/scripts/lib/stores/fixture-extension-store.ts +++ b/app/scripts/lib/stores/fixture-extension-store.ts @@ -9,6 +9,9 @@ const FIXTURE_SERVER_HOST = 'localhost'; const FIXTURE_SERVER_PORT = 12345; const FIXTURE_SERVER_URL = `http://${FIXTURE_SERVER_HOST}:${FIXTURE_SERVER_PORT}/state.json`; +/** + * Derived class of ExtensionStore that initializes the store using the fixture server. + */ export class FixtureExtensionStore extends ExtensionStore { #initialized: boolean = false; diff --git a/app/scripts/lib/stores/read-only-network-store.ts b/app/scripts/lib/stores/read-only-network-store.ts deleted file mode 100644 index b862e81308f..00000000000 --- a/app/scripts/lib/stores/read-only-network-store.ts +++ /dev/null @@ -1,94 +0,0 @@ -import log from 'loglevel'; -import getFetchWithTimeout from '../../../../shared/modules/fetch-with-timeout'; -import type { - MetaMaskStateType, - BaseStore, - MetaMaskStorageStructure, -} from './base-store'; - -const fetchWithTimeout = getFetchWithTimeout(); - -const FIXTURE_SERVER_HOST = 'localhost'; -const FIXTURE_SERVER_PORT = 12345; -const FIXTURE_SERVER_URL = `http://${FIXTURE_SERVER_HOST}:${FIXTURE_SERVER_PORT}/state.json`; - -/** - * A read-only network-based storage wrapper - */ -export default class ReadOnlyNetworkStore implements BaseStore { - #initialized: boolean = false; - - #initializing?: Promise; - - #state: MetaMaskStateType | null = null; - - constructor() { - this.#initializing = this.#init(); - } - - /** - * Declares this store as compatible with the current browser - */ - isSupported = true; - - /** - * Initializes by loading state from the network - */ - async #init() { - try { - const response = await fetchWithTimeout(FIXTURE_SERVER_URL); - - if (response.ok) { - this.#state = await response.json(); - } else { - log.debug( - `Received response with a status of ${response.status} ${response.statusText}`, - ); - } - } catch (error) { - console.log('error', error); - if (error instanceof Error) { - log.debug(`Error loading network state: '${error.message}'`); - } else { - log.debug(`Error loading network state: An unknown error occurred`); - } - } finally { - this.#initialized = true; - } - } - - /** - * Returns state - */ - async get() { - if (!this.#initialized) { - await this.#initializing; - } - return this.#state; - } - - /** - * Overwrite in-memory copy of state. - * - * @param data - The data to set - */ - async set(data: Required): Promise { - if (!data) { - throw new Error('MetaMask - updated state is missing'); - } - if (!this.#initialized) { - await this.#initializing; - } - this.#state = data; - } - - /** - * Resets data to its initial state. - */ - async reset(): Promise { - this.#initialized = false; - this.#state = null; - this.#initializing = this.#init(); - await this.#initializing; - } -} From 778459dbe48996a67bdc5140ba026808f7a0de1f Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 12:06:32 +0100 Subject: [PATCH 07/12] Revert "Update UI state snapshot" This reverts commit 6c29eeb3602f9cc336a142298eae36b42e858793. --- test/e2e/tests/metrics/errors.spec.ts | 1 + .../errors-before-init-opt-in-ui-state.json | 117 +++++++++--------- 2 files changed, 60 insertions(+), 58 deletions(-) diff --git a/test/e2e/tests/metrics/errors.spec.ts b/test/e2e/tests/metrics/errors.spec.ts index 93c396b66ef..314fcab2c6f 100644 --- a/test/e2e/tests/metrics/errors.spec.ts +++ b/test/e2e/tests/metrics/errors.spec.ts @@ -145,6 +145,7 @@ async function matchesSnapshot({ update?: boolean; }): Promise { const snapshotPath = resolve(__dirname, `./state-snapshots/${snapshot}.json`); + console.log('snapshotPath', snapshotPath); const rawSnapshotData = await fs.readFile(snapshotPath, { encoding: 'utf-8', }); diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json index b49c39ba131..3d497689c46 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json @@ -1,11 +1,16 @@ { "data": { - "AccountOrderController": { - "hiddenAccountList": {}, - "pinnedAccountList": {} + "AuthenticationController": { "isSignedIn": "boolean" }, + "NotificationServicesController": { + "subscriptionAccountsSeen": "object", + "isFeatureAnnouncementsEnabled": "boolean", + "isNotificationServicesEnabled": "boolean", + "isMetamaskNotificationsFeatureSeen": "boolean", + "metamaskNotificationsList": "object", + "metamaskNotificationsReadList": "object" }, "AccountsController": { - "internalAccounts": { "accounts": "object", "selectedAccount": "string" } + "internalAccounts": { "selectedAccount": "string", "accounts": "object" } }, "AlertController": { "alertEnabledness": { "unconnectedAccount": true, "web3ShimUsage": true }, @@ -13,33 +18,46 @@ "web3ShimUsageOrigins": "object" }, "AnnouncementController": { "announcements": "object" }, + "NetworkOrderController": { + "orderedNetworkList": { + "0": "object", + "1": "object", + "2": "object" + } + }, + "NetworkEnablementController": { + "enabledNetworkMap": { "eip155": "object", "solana": "object" } + }, + "AccountOrderController": { + "pinnedAccountList": {}, + "hiddenAccountList": {} + }, "AppStateController": { - "appActiveTab": "object", "browserEnvironment": {}, + "nftsDropdownState": {}, "connectedStatusPopoverHasBeenShown": true, + "termsOfUseLastAgreed": "number", "defaultHomeActiveTabName": null, "fullScreenGasPollTokens": "object", - "hadAdvancedGasFeesSetPriorToMigration92_3": false, - "hasShownMultichainAccountsIntroModal": "boolean", - "isRampCardClosed": false, - "newPrivacyPolicyToastClickedOrClosed": "boolean", - "newPrivacyPolicyToastShownDate": "number", - "nftsDropdownState": {}, "notificationGasPollTokens": "object", - "pendingShieldCohort": null, - "pendingShieldCohortTxType": null, "popupGasPollTokens": "object", "recoveryPhraseReminderHasBeenShown": true, "recoveryPhraseReminderLastShown": "number", - "showShieldEntryModalOnce": "boolean", "showTestnetMessageInDropdown": true, + "trezorModel": null, + "isRampCardClosed": false, + "newPrivacyPolicyToastClickedOrClosed": "boolean", + "newPrivacyPolicyToastShownDate": "number", "snapsInstallPrivacyWarningShown": true, - "termsOfUseLastAgreed": "number", - "trezorModel": null + "hasShownMultichainAccountsIntroModal": "boolean", + "showShieldEntryModalOnce": "boolean", + "pendingShieldCohort": null, + "pendingShieldCohortTxType": null, + "appActiveTab": "object" }, - "AuthenticationController": { "isSignedIn": "boolean" }, "BridgeController": {}, "CurrencyController": { + "currentCurrency": "usd", "currencyRates": { "ETH": { "conversionDate": "number", @@ -51,8 +69,7 @@ "conversionRate": 0.2, "usdConversionRate": 0.2 } - }, - "currentCurrency": "usd" + } }, "GasFeeController": { "estimatedGasFeeTimeBounds": {}, @@ -61,40 +78,25 @@ }, "KeyringController": { "vault": "string" }, "MetaMetricsController": { - "dataCollectionForMarketing": "boolean", "eventsBeforeMetricsOptIn": "object", + "tracesBeforeMetricsOptIn": "object", "fragments": "object", "metaMetricsId": "0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420", "participateInMetaMetrics": true, - "tracesBeforeMetricsOptIn": "object", + "dataCollectionForMarketing": "boolean", "traits": "object" }, "MetaMetricsDataDeletionController": { "metaMetricsDataDeletionId": null, "metaMetricsDataDeletionTimestamp": 0 }, - "MultichainAccountService": "object", - "NameController": { "names": "object" }, "NetworkController": { - "networkConfigurationsByChainId": "object", + "selectedNetworkClientId": "string", + "networkConfigurations": "object", "networksMetadata": { - "networkConfigurationId": { "EIPS": {}, "status": "unknown" } + "networkConfigurationId": { "EIPS": {}, "status": "available" } }, - "selectedNetworkClientId": "string" - }, - "NetworkEnablementController": { - "enabledNetworkMap": { "eip155": "object", "solana": "object" } - }, - "NetworkOrderController": { - "orderedNetworkList": { "0": "object", "1": "object", "2": "object" } - }, - "NotificationServicesController": { - "isFeatureAnnouncementsEnabled": "boolean", - "isMetamaskNotificationsFeatureSeen": "boolean", - "isNotificationServicesEnabled": "boolean", - "metamaskNotificationsList": "object", - "metamaskNotificationsReadList": "object", - "subscriptionAccountsSeen": "object" + "providerConfig": "object" }, "OnboardingController": { "completedOnboarding": true, @@ -104,44 +106,43 @@ }, "PermissionController": { "subjects": "object" }, "PreferencesController": { - "advancedGasFee": {}, + "advancedGasFee": null, "currentLocale": "en", + "useExternalServices": "boolean", "dismissSeedBackUpReminder": true, + "overrideContentSecurityPolicyHeader": true, "featureFlags": {}, "forgottenPassword": false, "identities": "object", "ipfsGateway": "string", - "isMultiAccountBalancesEnabled": "boolean", "knownMethodData": "object", "ledgerTransportType": "webhid", "lostIdentities": "object", "openSeaEnabled": false, - "overrideContentSecurityPolicyHeader": true, "preferences": { "hideZeroBalanceTokens": false, - "shouldShowAggregatedBalancePopover": "boolean", "showExtensionInFullSizeView": false, "showFiatInTestnets": false, - "showMultiRpcModal": "boolean", - "showNativeTokenAsMainBalance": true, "showTestNetworks": false, - "smartAccountOptIn": "boolean", - "smartTransactionsMigrationApplied": "boolean", "smartTransactionsOptInStatus": true, - "tokenNetworkFilter": {}, - "tokenSortConfig": "object" + "showNativeTokenAsMainBalance": true, + "petnamesEnabled": "boolean", + "showMultiRpcModal": "boolean", + "showConfirmationAdvancedDetails": false, + "tokenSortConfig": "object", + "shouldShowAggregatedBalancePopover": "boolean", + "tokenNetworkFilter": {} }, - "referrals": "object", - "securityAlertsEnabled": "boolean", "selectedAddress": "string", "theme": "light", "useBlockie": false, - "useCurrencyRateCheck": true, - "useExternalServices": "boolean", - "useMultiAccountBalanceChecker": true, "useNftDetection": false, "usePhishDetect": true, - "useTokenDetection": true + "useTokenDetection": false, + "useCurrencyRateCheck": true, + "useMultiAccountBalanceChecker": true, + "isMultiAccountBalancesEnabled": "boolean", + "referrals": "object" }, "SelectedNetworkController": { "domains": "object" }, "SmartTransactionsController": { @@ -159,10 +160,10 @@ "allIgnoredTokens": {}, "allTokens": {} }, + "MultichainAccountService": "object", "TransactionController": { "transactions": "object" }, - "UserStorageController": { "isBackupAndSyncEnabled": true }, "config": "object", "firstTimeInfo": "object" }, - "meta": { "version": 183 } + "meta": { "version": 74 } } From f64bfeb69d8f17bd71590960fcde2965971b7347 Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 12:42:41 +0100 Subject: [PATCH 08/12] Try breaking persistence --- test/e2e/tests/metrics/errors.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/e2e/tests/metrics/errors.spec.ts b/test/e2e/tests/metrics/errors.spec.ts index 314fcab2c6f..dc5a38ed55e 100644 --- a/test/e2e/tests/metrics/errors.spec.ts +++ b/test/e2e/tests/metrics/errors.spec.ts @@ -803,6 +803,12 @@ describe('Sentry errors', function () { async ({ driver, mockedEndpoint }) => { await driver.navigate(); await new LoginPage(driver).checkPageIsLoaded(); + + // Brick persistence to help simulate not initializing + await driver.executeScript( + `chrome.storage.local.get = () => { throw new Error("Failure") }`, + ); + // Erase `getSentryAppState` hook, simulating a "before initialization" state await driver.executeScript( 'window.stateHooks.getSentryAppState = undefined', From 17fc776c4f06cfb47d9e57854835e1f5a32ac32f Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 12:58:30 +0100 Subject: [PATCH 09/12] Fix lint and remove unused mock --- app/scripts/lib/stores/fixture-extension-store.test.ts | 4 ++-- app/scripts/lib/stores/persistence-manager.test.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/scripts/lib/stores/fixture-extension-store.test.ts b/app/scripts/lib/stores/fixture-extension-store.test.ts index e4158a07a0c..92cc7aab323 100644 --- a/app/scripts/lib/stores/fixture-extension-store.test.ts +++ b/app/scripts/lib/stores/fixture-extension-store.test.ts @@ -31,10 +31,10 @@ jest.mock('webextension-polyfill', () => { } } - return ({ + return { runtime: { lastError: null }, storage: { local: new MockBrowserStorage() }, - }) + }; }); /** diff --git a/app/scripts/lib/stores/persistence-manager.test.ts b/app/scripts/lib/stores/persistence-manager.test.ts index 0ee8619e583..f01d0131361 100644 --- a/app/scripts/lib/stores/persistence-manager.test.ts +++ b/app/scripts/lib/stores/persistence-manager.test.ts @@ -19,7 +19,6 @@ jest.mock('./extension-store', () => { return { set: mockStoreSet, get: mockStoreGet }; }); }); -jest.mock('./read-only-network-store'); jest.mock('loglevel', () => ({ error: jest.fn(), })); From ab882fb3d09131a1cfd5ad0963d11ffe6f0f80bd Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 13:52:12 +0100 Subject: [PATCH 10/12] Wait for state to settle --- test/e2e/tests/metrics/errors.spec.ts | 6 +- .../errors-before-init-opt-in-ui-state.json | 282 +++++++++++++----- 2 files changed, 212 insertions(+), 76 deletions(-) diff --git a/test/e2e/tests/metrics/errors.spec.ts b/test/e2e/tests/metrics/errors.spec.ts index dc5a38ed55e..19eb463c31e 100644 --- a/test/e2e/tests/metrics/errors.spec.ts +++ b/test/e2e/tests/metrics/errors.spec.ts @@ -804,10 +804,8 @@ describe('Sentry errors', function () { await driver.navigate(); await new LoginPage(driver).checkPageIsLoaded(); - // Brick persistence to help simulate not initializing - await driver.executeScript( - `chrome.storage.local.get = () => { throw new Error("Failure") }`, - ); + // Wait for state to settle + await driver.delay(5_000); // Erase `getSentryAppState` hook, simulating a "before initialization" state await driver.executeScript( diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json index 3d497689c46..8c510468010 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json @@ -1,63 +1,75 @@ { "data": { - "AuthenticationController": { "isSignedIn": "boolean" }, - "NotificationServicesController": { - "subscriptionAccountsSeen": "object", - "isFeatureAnnouncementsEnabled": "boolean", - "isNotificationServicesEnabled": "boolean", - "isMetamaskNotificationsFeatureSeen": "boolean", - "metamaskNotificationsList": "object", - "metamaskNotificationsReadList": "object" + "AccountOrderController": { + "hiddenAccountList": {}, + "pinnedAccountList": {} + }, + "AccountTracker": { "accountsByChainId": "object" }, + "AccountTreeController": { + "accountGroupsMetadata": "object", + "accountWalletsMetadata": "object", + "hasAccountTreeSyncingSyncedAtLeastOnce": "boolean" }, "AccountsController": { - "internalAccounts": { "selectedAccount": "string", "accounts": "object" } + "internalAccounts": { "accounts": "object", "selectedAccount": "string" } }, + "AddressBookController": { "addressBook": "object" }, "AlertController": { "alertEnabledness": { "unconnectedAccount": true, "web3ShimUsage": true }, "unconnectedAccountAlertShownOrigins": "object", "web3ShimUsageOrigins": "object" }, "AnnouncementController": { "announcements": "object" }, - "NetworkOrderController": { - "orderedNetworkList": { - "0": "object", - "1": "object", - "2": "object" - } - }, - "NetworkEnablementController": { - "enabledNetworkMap": { "eip155": "object", "solana": "object" } - }, - "AccountOrderController": { - "pinnedAccountList": {}, - "hiddenAccountList": {} + "AppMetadataController": { + "currentAppVersion": "string", + "currentMigrationVersion": "number", + "previousAppVersion": "", + "previousMigrationVersion": 0 }, "AppStateController": { - "browserEnvironment": {}, - "nftsDropdownState": {}, + "browserEnvironment": { "browser": "string", "os": "string" }, + "canTrackWalletFundsObtained": false, "connectedStatusPopoverHasBeenShown": true, - "termsOfUseLastAgreed": "number", "defaultHomeActiveTabName": null, - "fullScreenGasPollTokens": "object", - "notificationGasPollTokens": "object", - "popupGasPollTokens": "object", - "recoveryPhraseReminderHasBeenShown": true, - "recoveryPhraseReminderLastShown": "number", - "showTestnetMessageInDropdown": true, - "trezorModel": null, + "enableEnforcedSimulations": true, + "enforcedSimulationsSlippage": "number", + "hadAdvancedGasFeesSetPriorToMigration92_3": false, + "hasShownMultichainAccountsIntroModal": "boolean", "isRampCardClosed": false, + "isWalletResetInProgress": "boolean", + "lastUpdatedAt": null, + "lastViewedUserSurvey": null, "newPrivacyPolicyToastClickedOrClosed": "boolean", "newPrivacyPolicyToastShownDate": "number", - "snapsInstallPrivacyWarningShown": true, - "hasShownMultichainAccountsIntroModal": "boolean", - "showShieldEntryModalOnce": "boolean", - "pendingShieldCohort": null, + "nftsDetectionNoticeDismissed": false, + "onboardingDate": null, + "outdatedBrowserWarningLastShown": "object", "pendingShieldCohortTxType": null, - "appActiveTab": "object" + "productTour": "accountIcon", + "recoveryPhraseReminderHasBeenShown": true, + "recoveryPhraseReminderLastShown": "number", + "shieldEndingToastLastClickedOrClosed": "object", + "shieldPausedToastLastClickedOrClosed": "object", + "showAccountBanner": true, + "showBetaHeader": false, + "showDownloadMobileAppSlide": "boolean", + "showNetworkBanner": true, + "showPermissionsTour": true, + "showShieldEntryModalOnce": "boolean", + "showTestnetMessageInDropdown": true, + "slides": "object", + "snapsInstallPrivacyWarningShown": true, + "surveyLinkLastClickedOrClosed": "object", + "termsOfUseLastAgreed": "number", + "trezorModel": null, + "updateModalLastDismissedAt": null }, + "ApprovalController": {}, + "AuthenticationController": { "isSignedIn": "boolean" }, "BridgeController": {}, + "BridgeStatusController": { "txHistory": "object" }, + "ClaimsController": "object", "CurrencyController": { - "currentCurrency": "usd", "currencyRates": { "ETH": { "conversionDate": "number", @@ -69,101 +81,227 @@ "conversionRate": 0.2, "usdConversionRate": 0.2 } - } + }, + "currentCurrency": "usd" + }, + "DeFiPositionsController": "object", + "DecryptMessageController": {}, + "DelegationController": "object", + "EncryptionPublicKeyController": {}, + "EnsController": { + "ensEntries": "object", + "ensResolutionsByAddress": "object" }, "GasFeeController": { "estimatedGasFeeTimeBounds": {}, "gasEstimateType": "none", - "gasFeeEstimates": {} + "gasFeeEstimates": {}, + "gasFeeEstimatesByChainId": {}, + "nonRPCGasFeeApisDisabled": "boolean" }, + "GatorPermissionsController": "object", + "InstitutionalSnapController": "object", "KeyringController": { "vault": "string" }, + "LoggingController": { "logs": "object" }, "MetaMetricsController": { + "dataCollectionForMarketing": "boolean", "eventsBeforeMetricsOptIn": "object", - "tracesBeforeMetricsOptIn": "object", "fragments": "object", + "marketingCampaignCookieId": null, "metaMetricsId": "0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420", "participateInMetaMetrics": true, - "dataCollectionForMarketing": "boolean", + "segmentApiCalls": "object", + "tracesBeforeMetricsOptIn": "object", "traits": "object" }, "MetaMetricsDataDeletionController": { "metaMetricsDataDeletionId": null, "metaMetricsDataDeletionTimestamp": 0 }, + "MultichainAccountService": "object", + "MultichainAssetsController": { + "accountsAssets": "object", + "allIgnoredAssets": "object", + "assetsMetadata": "object" + }, + "MultichainAssetsRatesController": { "conversionRates": "object" }, + "MultichainBalancesController": { "balances": "object" }, + "MultichainNetworkController": "object", + "MultichainRatesController": { + "cryptocurrencies": ["btc", "sol"], + "fiatCurrency": "usd", + "rates": { + "btc": { "conversionDate": 0, "conversionRate": 0 }, + "sol": { "conversionDate": 0, "conversionRate": 0 } + } + }, + "MultichainTransactionsController": "object", + "NameController": { "nameSources": "object", "names": "object" }, "NetworkController": { - "selectedNetworkClientId": "string", - "networkConfigurations": "object", + "networkConfigurationsByChainId": "object", "networksMetadata": { - "networkConfigurationId": { "EIPS": {}, "status": "available" } + "networkConfigurationId": { "EIPS": {}, "status": "unknown" } }, - "providerConfig": "object" + "selectedNetworkClientId": "string" + }, + "NetworkEnablementController": { + "enabledNetworkMap": { "eip155": "object", "solana": "object" } + }, + "NetworkOrderController": { + "orderedNetworkList": { "0": "object", "1": "object", "2": "object" } + }, + "NftController": { + "allNftContracts": "object", + "allNfts": "object", + "ignoredNfts": "object" + }, + "NftDetectionController": "object", + "NotificationServicesController": { + "isFeatureAnnouncementsEnabled": "boolean", + "isMetamaskNotificationsFeatureSeen": "boolean", + "isNotificationServicesEnabled": "boolean", + "metamaskNotificationsList": "object", + "metamaskNotificationsReadList": "object", + "subscriptionAccountsSeen": "object" + }, + "NotificationServicesPushController": { + "fcmToken": "string", + "isPushEnabled": "boolean" }, "OnboardingController": { "completedOnboarding": true, "firstTimeFlowType": "import", - "onboardingTabs": "object", "seedPhraseBackedUp": true }, + "PPOMController": { "storageMetadata": {} }, "PermissionController": { "subjects": "object" }, + "PermissionLogController": { "permissionHistory": "object" }, + "PhishingController": { + "tokenScanCache": "object", + "urlScanCache": "object" + }, "PreferencesController": { - "advancedGasFee": null, + "addSnapAccountEnabled": "boolean", + "advancedGasFee": {}, "currentLocale": "en", - "useExternalServices": "boolean", "dismissSeedBackUpReminder": true, - "overrideContentSecurityPolicyHeader": true, + "enableMV3TimestampSave": true, "featureFlags": {}, "forgottenPassword": false, "identities": "object", "ipfsGateway": "string", + "isIpfsGatewayEnabled": "boolean", + "isMultiAccountBalancesEnabled": "boolean", "knownMethodData": "object", "ledgerTransportType": "webhid", "lostIdentities": "object", + "manageInstitutionalWallets": "boolean", "openSeaEnabled": false, + "overrideContentSecurityPolicyHeader": true, "preferences": { + "dismissSmartAccountSuggestionEnabled": "boolean", + "featureNotificationsEnabled": "boolean", "hideZeroBalanceTokens": false, + "petnamesEnabled": "boolean", + "privacyMode": "boolean", + "shouldShowAggregatedBalancePopover": "boolean", "showExtensionInFullSizeView": false, "showFiatInTestnets": false, + "showMultiRpcModal": "boolean", + "showNativeTokenAsMainBalance": true, "showTestNetworks": false, + "skipDeepLinkInterstitial": "boolean", + "smartAccountOptIn": "boolean", + "smartTransactionsMigrationApplied": "boolean", "smartTransactionsOptInStatus": true, - "showNativeTokenAsMainBalance": true, - "petnamesEnabled": "boolean", - "showMultiRpcModal": "boolean", - "showConfirmationAdvancedDetails": false, + "tokenNetworkFilter": {}, "tokenSortConfig": "object", - "shouldShowAggregatedBalancePopover": "boolean", - "tokenNetworkFilter": {} + "useNativeCurrencyAsPrimaryCurrency": "boolean", + "useSidePanelAsDefault": "boolean" }, + "referrals": "object", + "securityAlertsEnabled": "boolean", "selectedAddress": "string", + "snapRegistryList": "object", + "snapsAddSnapAccountModalDismissed": "boolean", "theme": "light", + "use4ByteResolution": true, + "useAddressBarEnsResolution": true, "useBlockie": false, - "useNftDetection": false, - "usePhishDetect": true, - "useTokenDetection": false, "useCurrencyRateCheck": true, + "useExternalNameSources": "boolean", + "useExternalServices": "boolean", "useMultiAccountBalanceChecker": true, - "isMultiAccountBalancesEnabled": "boolean", - "referrals": "object" + "useNftDetection": false, + "usePhishDetect": true, + "useSafeChainsListValidation": "boolean", + "useTokenDetection": true, + "useTransactionSimulations": true, + "watchEthereumAccountEnabled": "boolean" }, - "SelectedNetworkController": { "domains": "object" }, - "SmartTransactionsController": { - "smartTransactionsState": { - "fees": {}, - "feesByChainId": "object", - "liveness": true, - "livenessByChainId": "object", - "smartTransactions": "object" + "RemoteFeatureFlagController": { + "cacheTimestamp": "number", + "remoteFeatureFlags": { + "feature1": true, + "feature2": false, + "feature3": { "name": "groupC", "value": "valueC" }, + "sendRedesign": { "enabled": false } } }, + "RewardsController": { + "rewardsAccounts": "object", + "rewardsActiveAccount": null, + "rewardsSeasonStatuses": "object", + "rewardsSeasons": "object", + "rewardsSubscriptionTokens": "object", + "rewardsSubscriptions": "object" + }, + "SeedlessOnboardingController": "object", + "SelectedNetworkController": { "domains": "object" }, + "ShieldController": "object", + "SignatureController": {}, + "SmartTransactionsController": {}, + "SnapController": { + "snapStates": "object", + "snaps": "object", + "unencryptedSnapStates": "object" + }, + "SnapInsightsController": {}, + "SnapInterfaceController": { "interfaces": "object" }, + "SnapsRegistry": { + "database": "object", + "databaseUnavailable": "boolean", + "lastUpdated": "number" + }, "SubjectMetadataController": { "subjectMetadata": "object" }, + "SubscriptionController": "object", + "SwapsController": {}, + "TokenBalancesController": { "tokenBalances": "object" }, + "TokenListController": { + "preventPollingOnNetworkRestart": false, + "tokensChainsCache": {} + }, + "TokenRatesController": { "marketData": "object" }, "TokensController": { "allDetectedTokens": {}, "allIgnoredTokens": {}, "allTokens": {} }, - "MultichainAccountService": "object", - "TransactionController": { "transactions": "object" }, + "TransactionController": { + "lastFetchedBlockNumbers": "object", + "methodData": "object", + "submitHistory": "object", + "transactionBatches": "object", + "transactions": "object" + }, + "UserOperationController": { "userOperations": "object" }, + "UserStorageController": { + "isAccountSyncingEnabled": true, + "isBackupAndSyncEnabled": true, + "isContactSyncingEnabled": true + }, "config": "object", "firstTimeInfo": "object" }, - "meta": { "version": 74 } + "meta": { "version": 183 } } From cccca991ecabbfd865332c541d7e4d509f382fbe Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Fri, 21 Nov 2025 15:01:59 +0100 Subject: [PATCH 11/12] Update snapshot --- test/e2e/tests/metrics/errors.spec.ts | 2 - .../errors-before-init-opt-in-ui-state.json | 314 +++++++++--------- 2 files changed, 157 insertions(+), 159 deletions(-) diff --git a/test/e2e/tests/metrics/errors.spec.ts b/test/e2e/tests/metrics/errors.spec.ts index 19eb463c31e..0471ef2362c 100644 --- a/test/e2e/tests/metrics/errors.spec.ts +++ b/test/e2e/tests/metrics/errors.spec.ts @@ -145,7 +145,6 @@ async function matchesSnapshot({ update?: boolean; }): Promise { const snapshotPath = resolve(__dirname, `./state-snapshots/${snapshot}.json`); - console.log('snapshotPath', snapshotPath); const rawSnapshotData = await fs.readFile(snapshotPath, { encoding: 'utf-8', }); @@ -1152,7 +1151,6 @@ describe('Sentry errors', function () { const mockTextBody = (await mockedRequest.body.getText()).split('\n'); const mockJsonBody = JSON.parse(mockTextBody[2]); const appState = mockJsonBody?.extra?.appState; - console.log('mockJsonBody', mockJsonBody); const { extensionId, installType } = mockJsonBody.extra; assert.deepStrictEqual(Object.keys(appState), [ 'browser', diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json index 8c510468010..76bdc58a023 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json @@ -1,42 +1,42 @@ { "data": { - "AccountOrderController": { - "hiddenAccountList": {}, - "pinnedAccountList": {} - }, - "AccountTracker": { "accountsByChainId": "object" }, - "AccountTreeController": { - "accountGroupsMetadata": "object", - "accountWalletsMetadata": "object", - "hasAccountTreeSyncingSyncedAtLeastOnce": "boolean" + "AuthenticationController": { "isSignedIn": "boolean" }, + "NotificationServicesController": { + "subscriptionAccountsSeen": "object", + "isMetamaskNotificationsFeatureSeen": "boolean", + "isNotificationServicesEnabled": "boolean", + "isFeatureAnnouncementsEnabled": "boolean", + "metamaskNotificationsList": "object", + "metamaskNotificationsReadList": "object" }, "AccountsController": { "internalAccounts": { "accounts": "object", "selectedAccount": "string" } }, - "AddressBookController": { "addressBook": "object" }, "AlertController": { "alertEnabledness": { "unconnectedAccount": true, "web3ShimUsage": true }, "unconnectedAccountAlertShownOrigins": "object", "web3ShimUsageOrigins": "object" }, "AnnouncementController": { "announcements": "object" }, - "AppMetadataController": { - "currentAppVersion": "string", - "currentMigrationVersion": "number", - "previousAppVersion": "", - "previousMigrationVersion": 0 + "NetworkOrderController": { + "orderedNetworkList": { "0": "object", "1": "object", "2": "object" } + }, + "NetworkEnablementController": { + "enabledNetworkMap": { "eip155": "object", "solana": "object" } + }, + "AccountOrderController": { + "pinnedAccountList": {}, + "hiddenAccountList": {} }, "AppStateController": { - "browserEnvironment": { "browser": "string", "os": "string" }, - "canTrackWalletFundsObtained": false, + "browserEnvironment": { "os": "string", "browser": "string" }, "connectedStatusPopoverHasBeenShown": true, "defaultHomeActiveTabName": null, "enableEnforcedSimulations": true, "enforcedSimulationsSlippage": "number", "hadAdvancedGasFeesSetPriorToMigration92_3": false, - "hasShownMultichainAccountsIntroModal": "boolean", + "canTrackWalletFundsObtained": false, "isRampCardClosed": false, - "isWalletResetInProgress": "boolean", "lastUpdatedAt": null, "lastViewedUserSurvey": null, "newPrivacyPolicyToastClickedOrClosed": "boolean", @@ -44,32 +44,31 @@ "nftsDetectionNoticeDismissed": false, "onboardingDate": null, "outdatedBrowserWarningLastShown": "object", - "pendingShieldCohortTxType": null, "productTour": "accountIcon", "recoveryPhraseReminderHasBeenShown": true, "recoveryPhraseReminderLastShown": "number", - "shieldEndingToastLastClickedOrClosed": "object", - "shieldPausedToastLastClickedOrClosed": "object", "showAccountBanner": true, "showBetaHeader": false, "showDownloadMobileAppSlide": "boolean", "showNetworkBanner": true, "showPermissionsTour": true, - "showShieldEntryModalOnce": "boolean", "showTestnetMessageInDropdown": true, "slides": "object", - "snapsInstallPrivacyWarningShown": true, "surveyLinkLastClickedOrClosed": "object", - "termsOfUseLastAgreed": "number", + "shieldEndingToastLastClickedOrClosed": "object", + "shieldPausedToastLastClickedOrClosed": "object", "trezorModel": null, - "updateModalLastDismissedAt": null + "updateModalLastDismissedAt": null, + "hasShownMultichainAccountsIntroModal": "boolean", + "showShieldEntryModalOnce": "boolean", + "pendingShieldCohortTxType": null, + "isWalletResetInProgress": "boolean", + "termsOfUseLastAgreed": "number", + "snapsInstallPrivacyWarningShown": true }, - "ApprovalController": {}, - "AuthenticationController": { "isSignedIn": "boolean" }, "BridgeController": {}, - "BridgeStatusController": { "txHistory": "object" }, - "ClaimsController": "object", "CurrencyController": { + "currentCurrency": "usd", "currencyRates": { "ETH": { "conversionDate": "number", @@ -81,105 +80,44 @@ "conversionRate": 0.2, "usdConversionRate": 0.2 } - }, - "currentCurrency": "usd" - }, - "DeFiPositionsController": "object", - "DecryptMessageController": {}, - "DelegationController": "object", - "EncryptionPublicKeyController": {}, - "EnsController": { - "ensEntries": "object", - "ensResolutionsByAddress": "object" + } }, "GasFeeController": { + "gasFeeEstimatesByChainId": {}, + "gasFeeEstimates": {}, "estimatedGasFeeTimeBounds": {}, "gasEstimateType": "none", - "gasFeeEstimates": {}, - "gasFeeEstimatesByChainId": {}, "nonRPCGasFeeApisDisabled": "boolean" }, - "GatorPermissionsController": "object", - "InstitutionalSnapController": "object", "KeyringController": { "vault": "string" }, - "LoggingController": { "logs": "object" }, "MetaMetricsController": { + "participateInMetaMetrics": true, + "metaMetricsId": "0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420", "dataCollectionForMarketing": "boolean", - "eventsBeforeMetricsOptIn": "object", - "fragments": "object", "marketingCampaignCookieId": null, - "metaMetricsId": "0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420", - "participateInMetaMetrics": true, - "segmentApiCalls": "object", + "eventsBeforeMetricsOptIn": "object", "tracesBeforeMetricsOptIn": "object", - "traits": "object" + "traits": "object", + "fragments": "object", + "segmentApiCalls": "object" }, "MetaMetricsDataDeletionController": { "metaMetricsDataDeletionId": null, "metaMetricsDataDeletionTimestamp": 0 }, - "MultichainAccountService": "object", - "MultichainAssetsController": { - "accountsAssets": "object", - "allIgnoredAssets": "object", - "assetsMetadata": "object" - }, - "MultichainAssetsRatesController": { "conversionRates": "object" }, - "MultichainBalancesController": { "balances": "object" }, - "MultichainNetworkController": "object", - "MultichainRatesController": { - "cryptocurrencies": ["btc", "sol"], - "fiatCurrency": "usd", - "rates": { - "btc": { "conversionDate": 0, "conversionRate": 0 }, - "sol": { "conversionDate": 0, "conversionRate": 0 } - } - }, - "MultichainTransactionsController": "object", - "NameController": { "nameSources": "object", "names": "object" }, "NetworkController": { - "networkConfigurationsByChainId": "object", + "selectedNetworkClientId": "string", "networksMetadata": { "networkConfigurationId": { "EIPS": {}, "status": "unknown" } }, - "selectedNetworkClientId": "string" - }, - "NetworkEnablementController": { - "enabledNetworkMap": { "eip155": "object", "solana": "object" } - }, - "NetworkOrderController": { - "orderedNetworkList": { "0": "object", "1": "object", "2": "object" } - }, - "NftController": { - "allNftContracts": "object", - "allNfts": "object", - "ignoredNfts": "object" - }, - "NftDetectionController": "object", - "NotificationServicesController": { - "isFeatureAnnouncementsEnabled": "boolean", - "isMetamaskNotificationsFeatureSeen": "boolean", - "isNotificationServicesEnabled": "boolean", - "metamaskNotificationsList": "object", - "metamaskNotificationsReadList": "object", - "subscriptionAccountsSeen": "object" - }, - "NotificationServicesPushController": { - "fcmToken": "string", - "isPushEnabled": "boolean" + "networkConfigurationsByChainId": "object" }, "OnboardingController": { - "completedOnboarding": true, + "seedPhraseBackedUp": true, "firstTimeFlowType": "import", - "seedPhraseBackedUp": true + "completedOnboarding": true }, - "PPOMController": { "storageMetadata": {} }, "PermissionController": { "subjects": "object" }, - "PermissionLogController": { "permissionHistory": "object" }, - "PhishingController": { - "tokenScanCache": "object", - "urlScanCache": "object" - }, "PreferencesController": { "addSnapAccountEnabled": "boolean", "advancedGasFee": {}, @@ -191,7 +129,6 @@ "identities": "object", "ipfsGateway": "string", "isIpfsGatewayEnabled": "boolean", - "isMultiAccountBalancesEnabled": "boolean", "knownMethodData": "object", "ledgerTransportType": "webhid", "lostIdentities": "object", @@ -204,7 +141,6 @@ "hideZeroBalanceTokens": false, "petnamesEnabled": "boolean", "privacyMode": "boolean", - "shouldShowAggregatedBalancePopover": "boolean", "showExtensionInFullSizeView": false, "showFiatInTestnets": false, "showMultiRpcModal": "boolean", @@ -212,14 +148,14 @@ "showTestNetworks": false, "skipDeepLinkInterstitial": "boolean", "smartAccountOptIn": "boolean", - "smartTransactionsMigrationApplied": "boolean", "smartTransactionsOptInStatus": true, + "smartTransactionsMigrationApplied": "boolean", "tokenNetworkFilter": {}, "tokenSortConfig": "object", "useNativeCurrencyAsPrimaryCurrency": "boolean", - "useSidePanelAsDefault": "boolean" + "useSidePanelAsDefault": "boolean", + "shouldShowAggregatedBalancePopover": "boolean" }, - "referrals": "object", "securityAlertsEnabled": "boolean", "selectedAddress": "string", "snapRegistryList": "object", @@ -231,77 +167,141 @@ "useCurrencyRateCheck": true, "useExternalNameSources": "boolean", "useExternalServices": "boolean", + "isMultiAccountBalancesEnabled": "boolean", "useMultiAccountBalanceChecker": true, "useNftDetection": false, "usePhishDetect": true, "useSafeChainsListValidation": "boolean", "useTokenDetection": true, "useTransactionSimulations": true, - "watchEthereumAccountEnabled": "boolean" + "watchEthereumAccountEnabled": "boolean", + "referrals": "object" + }, + "SelectedNetworkController": { "domains": "object" }, + "SmartTransactionsController": {}, + "SubjectMetadataController": { "subjectMetadata": "object" }, + "TokensController": { + "allTokens": {}, + "allIgnoredTokens": {}, + "allDetectedTokens": {} + }, + "MultichainAccountService": "object", + "TransactionController": { + "methodData": "object", + "transactions": "object", + "transactionBatches": "object", + "lastFetchedBlockNumbers": "object", + "submitHistory": "object" + }, + "config": "object", + "firstTimeInfo": "object", + "NameController": { "names": "object", "nameSources": "object" }, + "UserStorageController": { + "isBackupAndSyncEnabled": true, + "isAccountSyncingEnabled": true, + "isContactSyncingEnabled": true + }, + "AppMetadataController": { + "currentAppVersion": "string", + "previousAppVersion": "", + "previousMigrationVersion": 0, + "currentMigrationVersion": "number" + }, + "AddressBookController": { "addressBook": "object" }, + "MultichainNetworkController": "object", + "SeedlessOnboardingController": "object", + "PermissionLogController": { "permissionHistory": "object" }, + "GatorPermissionsController": "object", + "TokenListController": { + "tokensChainsCache": {}, + "preventPollingOnNetworkRestart": false + }, + "TokenBalancesController": { "tokenBalances": "object" }, + "NftController": { + "allNftContracts": "object", + "allNfts": "object", + "ignoredNfts": "object" + }, + "PhishingController": { + "urlScanCache": "object", + "tokenScanCache": "object" + }, + "LoggingController": { "logs": "object" }, + "MultichainRatesController": { + "fiatCurrency": "usd", + "rates": { + "btc": { "conversionDate": 0, "conversionRate": 0 }, + "sol": { "conversionDate": 0, "conversionRate": 0 } + }, + "cryptocurrencies": ["btc", "sol"] + }, + "UserOperationController": { "userOperations": "object" }, + "NotificationServicesPushController": { + "isPushEnabled": "boolean", + "fcmToken": "string" }, "RemoteFeatureFlagController": { - "cacheTimestamp": "number", "remoteFeatureFlags": { "feature1": true, "feature2": false, "feature3": { "name": "groupC", "value": "valueC" }, "sendRedesign": { "enabled": false } - } - }, - "RewardsController": { - "rewardsAccounts": "object", - "rewardsActiveAccount": null, - "rewardsSeasonStatuses": "object", - "rewardsSeasons": "object", - "rewardsSubscriptionTokens": "object", - "rewardsSubscriptions": "object" + }, + "cacheTimestamp": "number" }, - "SeedlessOnboardingController": "object", - "SelectedNetworkController": { "domains": "object" }, - "ShieldController": "object", + "DeFiPositionsController": "object", + "AccountTracker": { "accountsByChainId": "object" }, + "TokenRatesController": { "marketData": "object" }, + "DecryptMessageController": {}, + "EncryptionPublicKeyController": {}, "SignatureController": {}, - "SmartTransactionsController": {}, + "SwapsController": {}, + "BridgeStatusController": { "txHistory": "object" }, + "EnsController": { + "ensEntries": "object", + "ensResolutionsByAddress": "object" + }, + "ApprovalController": {}, + "InstitutionalSnapController": "object", + "SnapsRegistry": { + "database": "object", + "lastUpdated": "number", + "databaseUnavailable": "boolean" + }, "SnapController": { - "snapStates": "object", "snaps": "object", + "snapStates": "object", "unencryptedSnapStates": "object" }, "SnapInsightsController": {}, "SnapInterfaceController": { "interfaces": "object" }, - "SnapsRegistry": { - "database": "object", - "databaseUnavailable": "boolean", - "lastUpdated": "number" - }, - "SubjectMetadataController": { "subjectMetadata": "object" }, - "SubscriptionController": "object", - "SwapsController": {}, - "TokenBalancesController": { "tokenBalances": "object" }, - "TokenListController": { - "preventPollingOnNetworkRestart": false, - "tokensChainsCache": {} - }, - "TokenRatesController": { "marketData": "object" }, - "TokensController": { - "allDetectedTokens": {}, - "allIgnoredTokens": {}, - "allTokens": {} - }, - "TransactionController": { - "lastFetchedBlockNumbers": "object", - "methodData": "object", - "submitHistory": "object", - "transactionBatches": "object", - "transactions": "object" + "PPOMController": { "storageMetadata": {} }, + "NftDetectionController": "object", + "AccountTreeController": { + "hasAccountTreeSyncingSyncedAtLeastOnce": "boolean", + "accountGroupsMetadata": "object", + "accountWalletsMetadata": "object" }, - "UserOperationController": { "userOperations": "object" }, - "UserStorageController": { - "isAccountSyncingEnabled": true, - "isBackupAndSyncEnabled": true, - "isContactSyncingEnabled": true + "MultichainAssetsController": { + "accountsAssets": "object", + "assetsMetadata": "object", + "allIgnoredAssets": "object" }, - "config": "object", - "firstTimeInfo": "object" + "MultichainAssetsRatesController": { "conversionRates": "object" }, + "MultichainBalancesController": { "balances": "object" }, + "MultichainTransactionsController": "object", + "DelegationController": "object", + "SubscriptionController": "object", + "ShieldController": "object", + "ClaimsController": "object", + "RewardsController": { + "rewardsActiveAccount": null, + "rewardsAccounts": "object", + "rewardsSubscriptions": "object", + "rewardsSeasons": "object", + "rewardsSeasonStatuses": "object", + "rewardsSubscriptionTokens": "object" + } }, "meta": { "version": 183 } } From 8b828059beff2a76cd5d780653b500731987cdaf Mon Sep 17 00:00:00 2001 From: Frederik Bolding Date: Mon, 24 Nov 2025 14:12:00 +0100 Subject: [PATCH 12/12] Update snapshot after rebase --- .../state-snapshots/errors-before-init-opt-in-ui-state.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json index 76bdc58a023..abe11e2d149 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-before-init-opt-in-ui-state.json @@ -262,7 +262,6 @@ "ensResolutionsByAddress": "object" }, "ApprovalController": {}, - "InstitutionalSnapController": "object", "SnapsRegistry": { "database": "object", "lastUpdated": "number", @@ -276,7 +275,6 @@ "SnapInsightsController": {}, "SnapInterfaceController": { "interfaces": "object" }, "PPOMController": { "storageMetadata": {} }, - "NftDetectionController": "object", "AccountTreeController": { "hasAccountTreeSyncingSyncedAtLeastOnce": "boolean", "accountGroupsMetadata": "object",