From 8a6691ce0f5ffaaab56da1131ac914f1bca090be Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 13:33:51 +0100 Subject: [PATCH 01/13] improv: use profile-sync-controller config for user storage paths + wait for _addAccountsWithBalance before dispatching the first account sync --- .../controllers/preferences-controller.ts | 8 + app/scripts/metamask-controller.js | 68 +++--- package.json | 2 +- .../userStorageMockttpController.test.ts | 221 ++++++++++++------ .../userStorageMockttpController.ts | 119 ++++++---- .../importing-private-key-account.spec.ts | 16 +- .../account-syncing/new-user-sync.spec.ts | 11 +- .../onboarding-with-opt-out.spec.ts | 21 +- .../sync-after-adding-account.spec.ts | 41 +++- .../sync-after-modifying-account-name.spec.ts | 16 +- .../sync-after-onboarding.spec.ts | 11 +- test/e2e/tests/notifications/mocks.ts | 34 ++- .../useProfileSyncing/profileSyncing.ts | 11 +- .../metamask-notifications/profile-syncing.ts | 21 +- ui/store/actions.test.js | 5 +- ui/store/actions.ts | 3 +- yarn.lock | 141 ++++++++++- 17 files changed, 559 insertions(+), 190 deletions(-) diff --git a/app/scripts/controllers/preferences-controller.ts b/app/scripts/controllers/preferences-controller.ts index dce2ef3d0512..7bb2102399bf 100644 --- a/app/scripts/controllers/preferences-controller.ts +++ b/app/scripts/controllers/preferences-controller.ts @@ -164,6 +164,7 @@ export type PreferencesControllerState = Omit< enableMV3TimestampSave: boolean; useExternalServices: boolean; textDirection?: string; + hasFinishedAddingAccountsWithBalance?: boolean; }; /** @@ -454,6 +455,7 @@ const controllerMetadata = { }, isMultiAccountBalancesEnabled: { persist: true, anonymous: true }, showIncomingTransactions: { persist: true, anonymous: true }, + hasFinishedAddingAccountsWithBalance: { persist: true, anonymous: true }, }; export class PreferencesController extends BaseController< @@ -1057,6 +1059,12 @@ export class PreferencesController extends BaseController< } ///: END:ONLY_INCLUDE_IF + setHasFinishedAddingAccountsWithBalance(value: boolean): void { + this.update((state) => { + state.hasFinishedAddingAccountsWithBalance = value; + }); + } + #handleAccountsControllerSync( newAccountsControllerState: AccountsControllerState, ): void { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 2f8a3a1fe807..b944a4f4a1ef 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1741,7 +1741,7 @@ export default class MetamaskController extends EventEmitter { if (!prevCompletedOnboarding && currCompletedOnboarding) { const { address } = this.accountsController.getSelectedAccount(); - this._addAccountsWithBalance(); + await this._addAccountsWithBalance(); this.postOnboardingInitialization(); this.triggerNetworkrequests(); @@ -4394,42 +4394,48 @@ export default class MetamaskController extends EventEmitter { } async _addAccountsWithBalance() { - // Scan accounts until we find an empty one - const chainId = getCurrentChainId({ - metamask: this.networkController.state, - }); - const ethQuery = new EthQuery(this.provider); - const accounts = await this.keyringController.getAccounts(); - let address = accounts[accounts.length - 1]; + try { + // Scan accounts until we find an empty one + const chainId = getCurrentChainId({ + metamask: this.networkController.state, + }); + const ethQuery = new EthQuery(this.provider); + const accounts = await this.keyringController.getAccounts(); + let address = accounts[accounts.length - 1]; - for (let count = accounts.length; ; count++) { - const balance = await this.getBalance(address, ethQuery); + for (let count = accounts.length; ; count++) { + const balance = await this.getBalance(address, ethQuery); - if (balance === '0x0') { - // This account has no balance, so check for tokens - await this.tokenDetectionController.detectTokens({ - selectedAddress: address, - }); + if (balance === '0x0') { + // This account has no balance, so check for tokens + await this.tokenDetectionController.detectTokens({ + selectedAddress: address, + }); - const tokens = - this.tokensController.state.allTokens?.[chainId]?.[address]; - const detectedTokens = - this.tokensController.state.allDetectedTokens?.[chainId]?.[address]; - - if ( - (tokens?.length ?? 0) === 0 && - (detectedTokens?.length ?? 0) === 0 - ) { - // This account has no balance or tokens - if (count !== 1) { - await this.removeAccount(address); + const tokens = + this.tokensController.state.allTokens?.[chainId]?.[address]; + const detectedTokens = + this.tokensController.state.allDetectedTokens?.[chainId]?.[address]; + + if ( + (tokens?.length ?? 0) === 0 && + (detectedTokens?.length ?? 0) === 0 + ) { + // This account has no balance or tokens + if (count !== 1) { + await this.removeAccount(address); + } + break; } - break; } - } - // This account has assets, so check the next one - address = await this.keyringController.addNewAccount(count); + // This account has assets, so check the next one + address = await this.keyringController.addNewAccount(count); + } + } catch (e) { + log.warn(`Failed to add accounts with balance. Error: ${e}`); + } finally { + this.preferencesController.setHasFinishedAddingAccountsWithBalance(true); } } diff --git a/package.json b/package.json index 5bd177799bb8..cde9b7a70986 100644 --- a/package.json +++ b/package.json @@ -337,7 +337,7 @@ "@metamask/post-message-stream": "^8.0.0", "@metamask/ppom-validator": "0.35.1", "@metamask/preinstalled-example-snap": "^0.2.0", - "@metamask/profile-sync-controller": "^0.9.7", + "@metamask/profile-sync-controller": "^1.0.1", "@metamask/providers": "^14.0.2", "@metamask/queued-request-controller": "^7.0.1", "@metamask/rate-limit-controller": "^6.0.0", diff --git a/test/e2e/helpers/user-storage/userStorageMockttpController.test.ts b/test/e2e/helpers/user-storage/userStorageMockttpController.test.ts index 1b6591899c0e..f4d8fdeb4e3d 100644 --- a/test/e2e/helpers/user-storage/userStorageMockttpController.test.ts +++ b/test/e2e/helpers/user-storage/userStorageMockttpController.test.ts @@ -1,4 +1,5 @@ import * as mockttp from 'mockttp'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { UserStorageMockttpController } from './userStorageMockttpController'; describe('UserStorageMockttpController', () => { @@ -12,11 +13,14 @@ describe('UserStorageMockttpController', () => { it('handles GET requests that have empty response', async () => { const controller = new UserStorageMockttpController(); - controller.setupPath('accounts', mockServer); + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer); - const request = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); + const request = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); expect(request.json).toEqual(null); }); @@ -36,13 +40,16 @@ describe('UserStorageMockttpController', () => { }, ]; - controller.setupPath('accounts', mockServer, { + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { getResponse: mockedData, }); - const request = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); + const request = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); expect(request.json).toEqual(mockedData); }); @@ -62,13 +69,16 @@ describe('UserStorageMockttpController', () => { }, ]; - controller.setupPath('accounts', mockServer, { + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { getResponse: mockedData, }); - const request = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); + const request = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); expect(request.json).toEqual(mockedData); }); @@ -88,13 +98,16 @@ describe('UserStorageMockttpController', () => { }, ]; - controller.setupPath('accounts', mockServer, { + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { getResponse: mockedData, }); - const request = await controller.onGet('accounts', { - path: `${baseUrl}/accounts/7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b`, - }); + const request = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}/7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b`, + }, + ); expect(request.json).toEqual(mockedData[0]); }); @@ -119,24 +132,30 @@ describe('UserStorageMockttpController', () => { Data: 'data3', }; - controller.setupPath('accounts', mockServer, { + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { getResponse: mockedData, }); - const putRequest = await controller.onPut('accounts', { - path: `${baseUrl}/accounts/6afbe024087495b4e0d56c4bdfc981c84eba44a7c284d4f455b5db4fcabc2173`, - body: { - getJson: async () => ({ - data: mockedAddedData.Data, - }), - } as unknown as mockttp.CompletedBody, - }); + const putRequest = await controller.onPut( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}/6afbe024087495b4e0d56c4bdfc981c84eba44a7c284d4f455b5db4fcabc2173`, + body: { + getJson: async () => ({ + data: mockedAddedData.Data, + }), + } as unknown as mockttp.CompletedBody, + }, + ); expect(putRequest.statusCode).toEqual(204); - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); + const getRequest = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); expect(getRequest.json).toEqual([...mockedData, mockedAddedData]); }); @@ -161,24 +180,30 @@ describe('UserStorageMockttpController', () => { Data: 'data3', }; - controller.setupPath('accounts', mockServer, { + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { getResponse: mockedData, }); - const putRequest = await controller.onPut('accounts', { - path: `${baseUrl}/accounts/c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468`, - body: { - getJson: async () => ({ - data: mockedUpdatedData.Data, - }), - } as unknown as mockttp.CompletedBody, - }); + const putRequest = await controller.onPut( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}/c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468`, + body: { + getJson: async () => ({ + data: mockedUpdatedData.Data, + }), + } as unknown as mockttp.CompletedBody, + }, + ); expect(putRequest.statusCode).toEqual(204); - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); + const getRequest = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); expect(getRequest.json).toEqual([mockedData[0], mockedUpdatedData]); }); @@ -210,7 +235,7 @@ describe('UserStorageMockttpController', () => { }, ]; - controller.setupPath('accounts', mockServer, { + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { getResponse: mockedData, }); @@ -219,20 +244,26 @@ describe('UserStorageMockttpController', () => { putData[entry.HashedKey] = entry.Data; }); - const putRequest = await controller.onPut('accounts', { - path: `${baseUrl}/accounts`, - body: { - getJson: async () => ({ - data: putData, - }), - } as unknown as mockttp.CompletedBody, - }); + const putRequest = await controller.onPut( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + body: { + getJson: async () => ({ + data: putData, + }), + } as unknown as mockttp.CompletedBody, + }, + ); expect(putRequest.statusCode).toEqual(204); - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); + const getRequest = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); expect(getRequest.json).toEqual(mockedUpdatedData); }); @@ -252,19 +283,25 @@ describe('UserStorageMockttpController', () => { }, ]; - controller.setupPath('accounts', mockServer, { + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { getResponse: mockedData, }); - const deleteRequest = await controller.onDelete('accounts', { - path: `${baseUrl}/accounts/c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468`, - }); + const deleteRequest = await controller.onDelete( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}/c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468`, + }, + ); expect(deleteRequest.statusCode).toEqual(204); - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, - }); + const getRequest = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); expect(getRequest.json).toEqual([mockedData[0]]); }); @@ -282,22 +319,76 @@ describe('UserStorageMockttpController', () => { 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', Data: 'data2', }, + { + HashedKey: + 'x236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', + Data: 'data3', + }, ]; - controller.setupPath('accounts', mockServer, { + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { getResponse: mockedData, }); - const deleteRequest = await controller.onDelete('accounts', { - path: `${baseUrl}/accounts`, - }); + const deleteRequest = await controller.onPut( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + body: { + getJson: async () => ({ + batch_delete: [mockedData[1].HashedKey, mockedData[2].HashedKey], + }), + } as unknown as mockttp.CompletedBody, + }, + ); expect(deleteRequest.statusCode).toEqual(204); - const getRequest = await controller.onGet('accounts', { - path: `${baseUrl}/accounts`, + const getRequest = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); + + expect(getRequest.json).toEqual([mockedData[0]]); + }); + + it('handles entire feature DELETE requests', async () => { + const controller = new UserStorageMockttpController(); + const mockedData = [ + { + HashedKey: + '7f8a7963423985c50f75f6ad42a6cf4e7eac43a6c55e3c6fcd49d73f01c1471b', + Data: 'data1', + }, + { + HashedKey: + 'c236b92ea7d513b2beda062cb546986961dfa7ca4334a2913f7837e43d050468', + Data: 'data2', + }, + ]; + + controller.setupPath(USER_STORAGE_FEATURE_NAMES.accounts, mockServer, { + getResponse: mockedData, }); + const deleteRequest = await controller.onDelete( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); + + expect(deleteRequest.statusCode).toEqual(204); + + const getRequest = await controller.onGet( + USER_STORAGE_FEATURE_NAMES.accounts, + { + path: `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + }, + ); + expect(getRequest.json).toEqual(null); }); }); diff --git a/test/e2e/helpers/user-storage/userStorageMockttpController.ts b/test/e2e/helpers/user-storage/userStorageMockttpController.ts index 970a10d11120..7337b6573a1a 100644 --- a/test/e2e/helpers/user-storage/userStorageMockttpController.ts +++ b/test/e2e/helpers/user-storage/userStorageMockttpController.ts @@ -1,13 +1,21 @@ import { CompletedRequest, Mockttp } from 'mockttp'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; + +const baseUrl = 'https://user-storage.api.cx.metamask.io/api/v1/userstorage'; -// TODO: Export user storage schema from @metamask/profile-sync-controller export const pathRegexps = { - accounts: - /https:\/\/user-storage\.api\.cx\.metamask\.io\/api\/v1\/userstorage\/accounts/u, - networks: - /https:\/\/user-storage\.api\.cx\.metamask\.io\/api\/v1\/userstorage\/networks/u, - notifications: - /https:\/\/user-storage\.api\.cx\.metamask\.io\/api\/v1\/userstorage\/notifications/u, + [USER_STORAGE_FEATURE_NAMES.accounts]: new RegExp( + `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.accounts}`, + 'u', + ), + [USER_STORAGE_FEATURE_NAMES.networks]: new RegExp( + `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.networks}`, + 'u', + ), + [USER_STORAGE_FEATURE_NAMES.notifications]: new RegExp( + `${baseUrl}/${USER_STORAGE_FEATURE_NAMES.notifications}`, + 'u', + ), }; type UserStorageResponseData = { HashedKey: string; Data: string }; @@ -70,50 +78,75 @@ export class UserStorageMockttpController { const isFeatureEntry = determineIfFeatureEntryFromURL(request.path); const data = (await request.body.getJson()) as { - data: string | { [key: string]: string }; + data?: string | Record; + batch_delete?: string[]; }; - const newOrUpdatedSingleOrBatchEntries = - isFeatureEntry && typeof data?.data === 'string' - ? [ - { - HashedKey: request.path.split('/').pop() as string, - Data: data?.data, - }, - ] - : Object.entries(data?.data).map(([key, value]) => ({ - HashedKey: key, - Data: value, - })); - - newOrUpdatedSingleOrBatchEntries.forEach((entry) => { + // We're handling batch delete inside the PUT method due to API limitations + if (data?.batch_delete) { + const keysToDelete = data.batch_delete; + const internalPathData = this.paths.get(path); if (!internalPathData) { - return; + return { + statusCode, + }; } - const doesThisEntryExist = internalPathData.response?.find( - (existingEntry) => existingEntry.HashedKey === entry.HashedKey, - ); + this.paths.set(path, { + ...internalPathData, + response: internalPathData.response.filter( + (entry) => !keysToDelete.includes(entry.HashedKey), + ), + }); + } - if (doesThisEntryExist) { - this.paths.set(path, { - ...internalPathData, - response: internalPathData.response.map((existingEntry) => - existingEntry.HashedKey === entry.HashedKey ? entry : existingEntry, - ), - }); - } else { - this.paths.set(path, { - ...internalPathData, - response: [ - ...(internalPathData?.response || []), - entry as { HashedKey: string; Data: string }, - ], - }); - } - }); + if (data?.data) { + const newOrUpdatedSingleOrBatchEntries = + isFeatureEntry && typeof data?.data === 'string' + ? [ + { + HashedKey: request.path.split('/').pop() as string, + Data: data?.data, + }, + ] + : Object.entries(data?.data).map(([key, value]) => ({ + HashedKey: key, + Data: value, + })); + + newOrUpdatedSingleOrBatchEntries.forEach((entry) => { + const internalPathData = this.paths.get(path); + + if (!internalPathData) { + return; + } + + const doesThisEntryExist = internalPathData.response?.find( + (existingEntry) => existingEntry.HashedKey === entry.HashedKey, + ); + + if (doesThisEntryExist) { + this.paths.set(path, { + ...internalPathData, + response: internalPathData.response.map((existingEntry) => + existingEntry.HashedKey === entry.HashedKey + ? entry + : existingEntry, + ), + }); + } else { + this.paths.set(path, { + ...internalPathData, + response: [ + ...(internalPathData?.response || []), + entry as { HashedKey: string; Data: string }, + ], + }); + } + }); + } return { statusCode, diff --git a/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts b/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts index 7b9e2378b058..8829bff24e07 100644 --- a/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts @@ -1,4 +1,5 @@ import { Mockttp } from 'mockttp'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { withFixtures } from '../../../helpers'; import FixtureBuilder from '../../../fixture-builder'; import { mockNotificationServices } from '../mocks'; @@ -28,9 +29,13 @@ describe('Account syncing - Import With Private Key @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + { + getResponse: accountsSyncMockResponse, + }, + ); return mockNotificationServices( server, @@ -75,7 +80,10 @@ describe('Account syncing - Import With Private Key @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); return mockNotificationServices( server, userStorageMockttpController, diff --git a/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts b/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts index 992027dd7840..b60c95b625bc 100644 --- a/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts @@ -1,4 +1,5 @@ import { Mockttp } from 'mockttp'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { withFixtures } from '../../../helpers'; import FixtureBuilder from '../../../fixture-builder'; import { mockNotificationServices } from '../mocks'; @@ -28,7 +29,10 @@ describe('Account syncing - New User @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); return mockNotificationServices( server, @@ -77,7 +81,10 @@ describe('Account syncing - New User @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); return mockNotificationServices( server, userStorageMockttpController, diff --git a/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts b/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts index f9574a27cb10..8ad354599850 100644 --- a/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts @@ -1,4 +1,5 @@ import { Mockttp } from 'mockttp'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { withFixtures } from '../../../helpers'; import FixtureBuilder from '../../../fixture-builder'; import { mockNotificationServices } from '../mocks'; @@ -35,9 +36,13 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { // Mocks are still set up to ensure that requests are not matched - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + { + getResponse: accountsSyncMockResponse, + }, + ); return mockNotificationServices( server, userStorageMockttpController, @@ -94,7 +99,10 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { // Mocks are still set up to ensure that requests are not matched - userStorageMockttpController.setupPath('accounts', server); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); return mockNotificationServices( server, userStorageMockttpController, @@ -146,7 +154,10 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { // Mocks are still set up to ensure that requests are not matched - userStorageMockttpController.setupPath('accounts', server); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); return mockNotificationServices( server, userStorageMockttpController, diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts index 31f92520f13e..d0fc7348e08e 100644 --- a/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts @@ -1,4 +1,5 @@ import { Mockttp } from 'mockttp'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { withFixtures } from '../../../helpers'; import FixtureBuilder from '../../../fixture-builder'; import { mockNotificationServices } from '../mocks'; @@ -27,9 +28,13 @@ describe('Account syncing - Add Account @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + { + getResponse: accountsSyncMockResponse, + }, + ); return mockNotificationServices( server, @@ -73,7 +78,10 @@ describe('Account syncing - Add Account @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); return mockNotificationServices( server, userStorageMockttpController, @@ -97,8 +105,9 @@ describe('Account syncing - Add Account @no-mmi', function () { const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - const accountSyncResponse = - userStorageMockttpController.paths.get('accounts')?.response; + const accountSyncResponse = userStorageMockttpController.paths.get( + USER_STORAGE_FEATURE_NAMES.accounts, + )?.response; await accountListPage.check_numberOfAvailableAccounts( accountSyncResponse?.length as number, @@ -124,9 +133,13 @@ describe('Account syncing - Add Account @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + { + getResponse: accountsSyncMockResponse, + }, + ); return mockNotificationServices( server, @@ -168,7 +181,10 @@ describe('Account syncing - Add Account @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); return mockNotificationServices( server, userStorageMockttpController, @@ -192,8 +208,9 @@ describe('Account syncing - Add Account @no-mmi', function () { const accountListPage = new AccountListPage(driver); await accountListPage.check_pageIsLoaded(); - const accountSyncResponse = - userStorageMockttpController.paths.get('accounts')?.response; + const accountSyncResponse = userStorageMockttpController.paths.get( + USER_STORAGE_FEATURE_NAMES.accounts, + )?.response; await accountListPage.check_numberOfAvailableAccounts( accountSyncResponse?.length as number, diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts index 45ee3ab23a85..db71e95681ef 100644 --- a/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts @@ -1,4 +1,5 @@ import { Mockttp } from 'mockttp'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { withFixtures } from '../../../helpers'; import FixtureBuilder from '../../../fixture-builder'; import { mockNotificationServices } from '../mocks'; @@ -27,9 +28,13 @@ describe('Account syncing - Rename Accounts @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + { + getResponse: accountsSyncMockResponse, + }, + ); return mockNotificationServices( server, @@ -72,7 +77,10 @@ describe('Account syncing - Rename Accounts @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); return mockNotificationServices( server, userStorageMockttpController, diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts index 5bebe7220e49..3f3e2bd4baae 100644 --- a/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts @@ -1,4 +1,5 @@ import { Mockttp } from 'mockttp'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { withFixtures } from '../../../helpers'; import FixtureBuilder from '../../../fixture-builder'; import { mockNotificationServices } from '../mocks'; @@ -27,9 +28,13 @@ describe('Account syncing - Onboarding @no-mmi', function () { fixtures: new FixtureBuilder({ onboarding: true }).build(), title: this.test?.fullTitle(), testSpecificMock: (server: Mockttp) => { - userStorageMockttpController.setupPath('accounts', server, { - getResponse: accountsSyncMockResponse, - }); + userStorageMockttpController.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + { + getResponse: accountsSyncMockResponse, + }, + ); return mockNotificationServices( server, userStorageMockttpController, diff --git a/test/e2e/tests/notifications/mocks.ts b/test/e2e/tests/notifications/mocks.ts index ce2ced3df210..748084918272 100644 --- a/test/e2e/tests/notifications/mocks.ts +++ b/test/e2e/tests/notifications/mocks.ts @@ -4,6 +4,7 @@ import { NotificationServicesController, NotificationServicesPushController, } from '@metamask/notification-services-controller'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { UserStorageMockttpController } from '../../helpers/user-storage/userStorageMockttpController'; const AuthMocks = AuthenticationController.Mocks; @@ -32,14 +33,35 @@ export async function mockNotificationServices( mockAPICall(server, AuthMocks.getMockAuthAccessTokenResponse()); // Storage - if (!userStorageMockttpControllerInstance?.paths.get('accounts')) { - userStorageMockttpControllerInstance.setupPath('accounts', server); + if ( + !userStorageMockttpControllerInstance?.paths.get( + USER_STORAGE_FEATURE_NAMES.accounts, + ) + ) { + userStorageMockttpControllerInstance.setupPath( + USER_STORAGE_FEATURE_NAMES.accounts, + server, + ); } - if (!userStorageMockttpControllerInstance?.paths.get('networks')) { - userStorageMockttpControllerInstance.setupPath('networks', server); + if ( + !userStorageMockttpControllerInstance?.paths.get( + USER_STORAGE_FEATURE_NAMES.networks, + ) + ) { + userStorageMockttpControllerInstance.setupPath( + USER_STORAGE_FEATURE_NAMES.networks, + server, + ); } - if (!userStorageMockttpControllerInstance?.paths.get('notifications')) { - userStorageMockttpControllerInstance.setupPath('notifications', server); + if ( + !userStorageMockttpControllerInstance?.paths.get( + USER_STORAGE_FEATURE_NAMES.notifications, + ) + ) { + userStorageMockttpControllerInstance.setupPath( + USER_STORAGE_FEATURE_NAMES.notifications, + server, + ); } // Notifications diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts index 5c073fdf6d94..9f6ae6532b70 100644 --- a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts +++ b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts @@ -10,7 +10,10 @@ import { } from '../../../store/actions'; import { selectIsSignedIn } from '../../../selectors/metamask-notifications/authentication'; -import { selectIsProfileSyncingEnabled } from '../../../selectors/metamask-notifications/profile-syncing'; +import { + selectHasFinishedAddingAccountsWithBalance, + selectIsProfileSyncingEnabled, +} from '../../../selectors/metamask-notifications/profile-syncing'; import { getUseExternalServices } from '../../../selectors'; import { getIsUnlocked, @@ -120,6 +123,9 @@ export function useSetIsProfileSyncingEnabled(): { * @returns a boolean if internally we can perform syncing features or not. */ export const useShouldDispatchProfileSyncing = () => { + const hasFinishedAddingAccountsWithBalance = useSelector( + selectHasFinishedAddingAccountsWithBalance, + ); const isProfileSyncingEnabled = useSelector(selectIsProfileSyncingEnabled); const basicFunctionality: boolean | undefined = useSelector( getUseExternalServices, @@ -135,7 +141,8 @@ export const useShouldDispatchProfileSyncing = () => { isProfileSyncingEnabled && isUnlocked && isSignedIn && - completedOnboarding, + completedOnboarding && + hasFinishedAddingAccountsWithBalance, ); return shouldDispatchProfileSyncing; diff --git a/ui/selectors/metamask-notifications/profile-syncing.ts b/ui/selectors/metamask-notifications/profile-syncing.ts index 8b9b8f4997d6..83e69d45e857 100644 --- a/ui/selectors/metamask-notifications/profile-syncing.ts +++ b/ui/selectors/metamask-notifications/profile-syncing.ts @@ -2,7 +2,9 @@ import { createSelector } from 'reselect'; import type { UserStorageController } from '@metamask/profile-sync-controller'; type AppState = { - metamask: UserStorageController.UserStorageControllerState; + metamask: UserStorageController.UserStorageControllerState & { + hasFinishedAddingAccountsWithBalance: boolean; + }; }; const getMetamask = (state: AppState) => state.metamask; @@ -36,3 +38,20 @@ export const selectIsProfileSyncingUpdateLoading = createSelector( return metamask.isProfileSyncingUpdateLoading; }, ); + +/** + * Selector to determine if the method _addAccountsWithBalance has finished adding accounts after onboarding. + * This is needed for account syncing in order to prevent conflicts with accounts that are being added by the above method during onboarding. + * + * This selector uses the `createSelector` function from 'reselect' to compute whether the update process for profile syncing is currently in a loading state, + * based on the `hasFinishedAddingAccountsWithBalance` property of the `metamask` object in the Redux store. + * + * @param {AppState} state - The current state of the Redux store. + * @returns {boolean} Returns true if the profile syncing update is loading, false otherwise. + */ +export const selectHasFinishedAddingAccountsWithBalance = createSelector( + [getMetamask], + (metamask) => { + return metamask.hasFinishedAddingAccountsWithBalance; + }, +); diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js index 22e8db2fa281..10391adad3df 100644 --- a/ui/store/actions.test.js +++ b/ui/store/actions.test.js @@ -4,6 +4,7 @@ import thunk from 'redux-thunk'; import { EthAccountType } from '@metamask/keyring-api'; import { TransactionStatus } from '@metamask/transaction-controller'; import { NotificationServicesController } from '@metamask/notification-services-controller'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; // TODO: Remove restricted import // eslint-disable-next-line import/no-restricted-paths import enLocale from '../../app/_locales/en/messages.json'; @@ -2605,7 +2606,9 @@ describe('Actions', () => { await store.dispatch(actions.deleteAccountSyncingDataFromUserStorage()); expect( - deleteAccountSyncingDataFromUserStorageStub.calledOnceWith('accounts'), + deleteAccountSyncingDataFromUserStorageStub.calledOnceWith( + USER_STORAGE_FEATURE_NAMES.accounts, + ), ).toBe(true); }); }); diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 67de497817c8..200309554833 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -42,6 +42,7 @@ import { import { InterfaceState } from '@metamask/snaps-sdk'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { NotificationServicesController } from '@metamask/notification-services-controller'; +import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; import { Patch } from 'immer'; import { HandlerType } from '@metamask/snaps-utils'; import switchDirection from '../../shared/lib/switch-direction'; @@ -5549,7 +5550,7 @@ export function deleteAccountSyncingDataFromUserStorage(): ThunkAction< try { const response = await submitRequestToBackground( 'deleteAccountSyncingDataFromUserStorage', - ['accounts'], + [USER_STORAGE_FEATURE_NAMES.accounts], ); return response; } catch (error) { diff --git a/yarn.lock b/yarn.lock index b7afd1eb5884..bbc6a6539187 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5105,6 +5105,24 @@ __metadata: languageName: node linkType: hard +"@metamask/controller-utils@npm:^11.4.3": + version: 11.4.3 + resolution: "@metamask/controller-utils@npm:11.4.3" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/ethjs-unit": "npm:^0.3.0" + "@metamask/utils": "npm:^10.0.0" + "@spruceid/siwe-parser": "npm:2.1.0" + "@types/bn.js": "npm:^5.1.5" + bignumber.js: "npm:^9.1.2" + bn.js: "npm:^5.2.1" + eth-ens-namehash: "npm:^2.0.8" + fast-deep-equal: "npm:^3.1.3" + checksum: 10/5703b0721daf679cf44affc690f2b313e40893b64b0aafaf203e69ee51438197cc3634ef7094145f580a8a8aaadcb79026b2fbd4065c1bb4a8c26627a2c4c69a + languageName: node + linkType: hard + "@metamask/design-tokens@npm:^4.0.0": version: 4.0.0 resolution: "@metamask/design-tokens@npm:4.0.0" @@ -5230,6 +5248,19 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-block-tracker@npm:^11.0.2": + version: 11.0.2 + resolution: "@metamask/eth-block-tracker@npm:11.0.2" + dependencies: + "@metamask/eth-json-rpc-provider": "npm:^4.1.5" + "@metamask/safe-event-emitter": "npm:^3.1.1" + "@metamask/utils": "npm:^9.1.0" + json-rpc-random-id: "npm:^1.0.1" + pify: "npm:^5.0.0" + checksum: 10/11d22bd86056401aa41eff5a32e862f3644eaf03040d8aa54a95cb0c1dfd3e3ce7e650c25efabbe0954cc6ba5f92172c338b518df84f73c4601c4bbc960b588a + languageName: node + linkType: hard + "@metamask/eth-block-tracker@npm:^9.0.2": version: 9.0.3 resolution: "@metamask/eth-block-tracker@npm:9.0.3" @@ -5269,6 +5300,18 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-json-rpc-infura@npm:^10.0.0": + version: 10.0.0 + resolution: "@metamask/eth-json-rpc-infura@npm:10.0.0" + dependencies: + "@metamask/eth-json-rpc-provider": "npm:^4.1.5" + "@metamask/json-rpc-engine": "npm:^10.0.0" + "@metamask/rpc-errors": "npm:^7.0.0" + "@metamask/utils": "npm:^9.1.0" + checksum: 10/17e0147ff86c48107983035e9bda4d16fba321ee0e29733347e9338a4c795c506a2ffd643c44c9d5334886696412cf288f852d06311fed0d76edc8847ee6b8de + languageName: node + linkType: hard + "@metamask/eth-json-rpc-infura@npm:^9.1.0": version: 9.1.0 resolution: "@metamask/eth-json-rpc-infura@npm:9.1.0" @@ -5320,6 +5363,25 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-json-rpc-middleware@npm:^15.0.0": + version: 15.0.0 + resolution: "@metamask/eth-json-rpc-middleware@npm:15.0.0" + dependencies: + "@metamask/eth-block-tracker": "npm:^11.0.1" + "@metamask/eth-json-rpc-provider": "npm:^4.1.5" + "@metamask/eth-sig-util": "npm:^7.0.3" + "@metamask/json-rpc-engine": "npm:^10.0.0" + "@metamask/rpc-errors": "npm:^7.0.0" + "@metamask/utils": "npm:^9.1.0" + "@types/bn.js": "npm:^5.1.5" + bn.js: "npm:^5.2.1" + klona: "npm:^2.0.6" + pify: "npm:^5.0.0" + safe-stable-stringify: "npm:^2.4.3" + checksum: 10/3c48d34264c695535f2b4e819fb602d835b6ed37309116a06d04d1b706a7335e0205cd4ccdbf1d3e9dc15ebf40d88954a9a2dc18a91f223dcd6d6392e026a5e9 + languageName: node + linkType: hard + "@metamask/eth-json-rpc-middleware@patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch": version: 14.0.1 resolution: "@metamask/eth-json-rpc-middleware@patch:@metamask/eth-json-rpc-middleware@npm%3A14.0.1#~/.yarn/patches/@metamask-eth-json-rpc-middleware-npm-14.0.1-b6c2ccbe8c.patch::version=14.0.1&hash=96e7e0" @@ -5374,6 +5436,19 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-json-rpc-provider@npm:^4.1.5, @metamask/eth-json-rpc-provider@npm:^4.1.6": + version: 4.1.6 + resolution: "@metamask/eth-json-rpc-provider@npm:4.1.6" + dependencies: + "@metamask/json-rpc-engine": "npm:^10.0.1" + "@metamask/rpc-errors": "npm:^7.0.1" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^10.0.0" + uuid: "npm:^8.3.2" + checksum: 10/aeec2c362a5386357e9f8c707da9baa4326e83889633723656b6801b6461ea8ab8f020b0d9ed0bbc2d8fd6add4af4c99cc9c9a1cbedca267a033a9f19da41200 + languageName: node + linkType: hard + "@metamask/eth-ledger-bridge-keyring@npm:^5.0.1": version: 5.0.1 resolution: "@metamask/eth-ledger-bridge-keyring@npm:5.0.1" @@ -5776,6 +5851,27 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-controller@npm:^18.0.0": + version: 18.0.0 + resolution: "@metamask/keyring-controller@npm:18.0.0" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@keystonehq/metamask-airgapped-keyring": "npm:^0.14.1" + "@metamask/base-controller": "npm:^7.0.2" + "@metamask/browser-passworder": "npm:^4.3.0" + "@metamask/eth-hd-keyring": "npm:^7.0.4" + "@metamask/eth-sig-util": "npm:^8.0.0" + "@metamask/eth-simple-keyring": "npm:^6.0.5" + "@metamask/keyring-api": "npm:^8.1.3" + "@metamask/message-manager": "npm:^11.0.1" + "@metamask/utils": "npm:^10.0.0" + async-mutex: "npm:^0.5.0" + ethereumjs-wallet: "npm:^1.0.1" + immer: "npm:^9.0.6" + checksum: 10/c301e4e8b9ac9da914bfaa371a43342aa37f5bb8ad107bbbd92f1d21a13c22351619f8bd6176493b808f4194aa9934bce5618ff0aed12325933f4330cdfd308e + languageName: node + linkType: hard + "@metamask/logging-controller@npm:^6.0.0": version: 6.0.0 resolution: "@metamask/logging-controller@npm:6.0.0" @@ -5871,6 +5967,31 @@ __metadata: languageName: node linkType: hard +"@metamask/network-controller@npm:^22.0.2": + version: 22.0.2 + resolution: "@metamask/network-controller@npm:22.0.2" + dependencies: + "@metamask/base-controller": "npm:^7.0.2" + "@metamask/controller-utils": "npm:^11.4.3" + "@metamask/eth-block-tracker": "npm:^11.0.2" + "@metamask/eth-json-rpc-infura": "npm:^10.0.0" + "@metamask/eth-json-rpc-middleware": "npm:^15.0.0" + "@metamask/eth-json-rpc-provider": "npm:^4.1.6" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/json-rpc-engine": "npm:^10.0.1" + "@metamask/rpc-errors": "npm:^7.0.1" + "@metamask/swappable-obj-proxy": "npm:^2.2.0" + "@metamask/utils": "npm:^10.0.0" + async-mutex: "npm:^0.5.0" + immer: "npm:^9.0.6" + loglevel: "npm:^1.8.1" + reselect: "npm:^5.1.1" + uri-js: "npm:^4.4.1" + uuid: "npm:^8.3.2" + checksum: 10/9da27189a4263ef7fa4596ada2000d7f944bc3f4dea63a77cf6f8b2ea89412d499068cf0542785088d19437263bd0b3b3bb3299533f87439729ccd8ecee2b625 + languageName: node + linkType: hard + "@metamask/network-controller@patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch": version: 21.0.0 resolution: "@metamask/network-controller@patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch::version=21.0.0&hash=1a5039" @@ -6169,13 +6290,14 @@ __metadata: languageName: node linkType: hard -"@metamask/profile-sync-controller@npm:^0.9.7": - version: 0.9.7 - resolution: "@metamask/profile-sync-controller@npm:0.9.7" +"@metamask/profile-sync-controller@npm:^1.0.1": + version: 1.0.1 + resolution: "@metamask/profile-sync-controller@npm:1.0.1" dependencies: - "@metamask/base-controller": "npm:^7.0.1" + "@metamask/base-controller": "npm:^7.0.2" "@metamask/keyring-api": "npm:^8.1.3" - "@metamask/keyring-controller": "npm:^17.2.2" + "@metamask/keyring-controller": "npm:^18.0.0" + "@metamask/network-controller": "npm:^22.0.2" "@metamask/snaps-sdk": "npm:^6.5.0" "@metamask/snaps-utils": "npm:^8.1.1" "@noble/ciphers": "npm:^0.5.2" @@ -6184,10 +6306,11 @@ __metadata: loglevel: "npm:^1.8.1" siwe: "npm:^2.3.2" peerDependencies: - "@metamask/accounts-controller": ^18.1.1 - "@metamask/keyring-controller": ^17.2.0 + "@metamask/accounts-controller": ^19.0.0 + "@metamask/keyring-controller": ^18.0.0 + "@metamask/network-controller": ^22.0.0 "@metamask/snaps-controllers": ^9.7.0 - checksum: 10/e53888533b2aae937bbe4e385dca2617c324b34e3e60af218cd98c26d514fb725f4c67b649f126e055f6a50a554817b229d37488115b98d70e8aee7b3a910bde + checksum: 10/4a4b0e95e4a03d1ba45343dae38c2c2670ab4931f742f1b3eca0af3d8736204b84258621917b30987e8ab6e633d9b9c255acb989609b9c6d2d341441b73b4ade languageName: node linkType: hard @@ -26755,7 +26878,7 @@ __metadata: "@metamask/ppom-validator": "npm:0.35.1" "@metamask/preferences-controller": "npm:^13.0.2" "@metamask/preinstalled-example-snap": "npm:^0.2.0" - "@metamask/profile-sync-controller": "npm:^0.9.7" + "@metamask/profile-sync-controller": "npm:^1.0.1" "@metamask/providers": "npm:^14.0.2" "@metamask/queued-request-controller": "npm:^7.0.1" "@metamask/rate-limit-controller": "npm:^6.0.0" From 104219f6239cd97efda832f9e62874a7255cdef1 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 13:35:25 +0100 Subject: [PATCH 02/13] fix: yarn.lock --- yarn.lock | 48 ++---------------------------------------------- 1 file changed, 2 insertions(+), 46 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5cd5fb89c0f2..351b81c145c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5146,24 +5146,6 @@ __metadata: languageName: node linkType: hard -"@metamask/controller-utils@npm:^11.4.3": - version: 11.4.3 - resolution: "@metamask/controller-utils@npm:11.4.3" - dependencies: - "@ethereumjs/util": "npm:^8.1.0" - "@metamask/eth-query": "npm:^4.0.0" - "@metamask/ethjs-unit": "npm:^0.3.0" - "@metamask/utils": "npm:^10.0.0" - "@spruceid/siwe-parser": "npm:2.1.0" - "@types/bn.js": "npm:^5.1.5" - bignumber.js: "npm:^9.1.2" - bn.js: "npm:^5.2.1" - eth-ens-namehash: "npm:^2.0.8" - fast-deep-equal: "npm:^3.1.3" - checksum: 10/5703b0721daf679cf44affc690f2b313e40893b64b0aafaf203e69ee51438197cc3634ef7094145f580a8a8aaadcb79026b2fbd4065c1bb4a8c26627a2c4c69a - languageName: node - linkType: hard - "@metamask/design-tokens@npm:^4.0.0": version: 4.0.0 resolution: "@metamask/design-tokens@npm:4.0.0" @@ -5276,20 +5258,7 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-block-tracker@npm:^11.0.1": - version: 11.0.1 - resolution: "@metamask/eth-block-tracker@npm:11.0.1" - dependencies: - "@metamask/eth-json-rpc-provider": "npm:^4.1.1" - "@metamask/safe-event-emitter": "npm:^3.1.1" - "@metamask/utils": "npm:^9.1.0" - json-rpc-random-id: "npm:^1.0.1" - pify: "npm:^5.0.0" - checksum: 10/6a5143dcd20ea87cd674efb25870275d97d4ffe921e843391a5b85876ebe074e5a587a128c268d27520904c74c9feecf91218ea086bd65cc6096f8501bdf8f32 - languageName: node - linkType: hard - -"@metamask/eth-block-tracker@npm:^11.0.2": +"@metamask/eth-block-tracker@npm:^11.0.1, @metamask/eth-block-tracker@npm:^11.0.2": version: 11.0.2 resolution: "@metamask/eth-block-tracker@npm:11.0.2" dependencies: @@ -5464,20 +5433,7 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-json-rpc-provider@npm:^4.0.0, @metamask/eth-json-rpc-provider@npm:^4.1.0, @metamask/eth-json-rpc-provider@npm:^4.1.1, @metamask/eth-json-rpc-provider@npm:^4.1.3": - version: 4.1.3 - resolution: "@metamask/eth-json-rpc-provider@npm:4.1.3" - dependencies: - "@metamask/json-rpc-engine": "npm:^9.0.2" - "@metamask/rpc-errors": "npm:^6.3.1" - "@metamask/safe-event-emitter": "npm:^3.0.0" - "@metamask/utils": "npm:^9.1.0" - uuid: "npm:^8.3.2" - checksum: 10/d581cc0f6485783ed59ac9517aa7f0eb37ee6a0674409eeaba1bbda4b54fcc5f633cc8ace66207871e2c2fac33195982969f4e61c18b04faf4656cccf79d8d3d - languageName: node - linkType: hard - -"@metamask/eth-json-rpc-provider@npm:^4.1.5, @metamask/eth-json-rpc-provider@npm:^4.1.6": +"@metamask/eth-json-rpc-provider@npm:^4.0.0, @metamask/eth-json-rpc-provider@npm:^4.1.0, @metamask/eth-json-rpc-provider@npm:^4.1.1, @metamask/eth-json-rpc-provider@npm:^4.1.3, @metamask/eth-json-rpc-provider@npm:^4.1.5, @metamask/eth-json-rpc-provider@npm:^4.1.6": version: 4.1.6 resolution: "@metamask/eth-json-rpc-provider@npm:4.1.6" dependencies: From 0d8ecbdf901d280790edb55e556eb47bc7eb9d27 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 13:52:06 +0100 Subject: [PATCH 03/13] fix: regex escaping --- test/e2e/helpers/user-storage/userStorageMockttpController.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/helpers/user-storage/userStorageMockttpController.ts b/test/e2e/helpers/user-storage/userStorageMockttpController.ts index 7337b6573a1a..ce8583b9adcd 100644 --- a/test/e2e/helpers/user-storage/userStorageMockttpController.ts +++ b/test/e2e/helpers/user-storage/userStorageMockttpController.ts @@ -1,7 +1,8 @@ import { CompletedRequest, Mockttp } from 'mockttp'; import { USER_STORAGE_FEATURE_NAMES } from '@metamask/profile-sync-controller/sdk'; -const baseUrl = 'https://user-storage.api.cx.metamask.io/api/v1/userstorage'; +const baseUrl = + 'https://user-storage\\.api\\.cx\\.metamask\\.io\\/api\\/v1\\/userstorage'; export const pathRegexps = { [USER_STORAGE_FEATURE_NAMES.accounts]: new RegExp( From c7dc7f7ad9afa0c5d1e01787f6d901dccfca1e4f Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Tue, 19 Nov 2024 13:05:36 +0000 Subject: [PATCH 04/13] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 32 ++------------------------- lavamoat/browserify/flask/policy.json | 32 ++------------------------- lavamoat/browserify/main/policy.json | 32 ++------------------------- lavamoat/browserify/mmi/policy.json | 32 ++------------------------- 4 files changed, 8 insertions(+), 120 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 54659cd66695..f87946a09c0f 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1919,40 +1919,12 @@ }, "@metamask/network-controller>@metamask/eth-json-rpc-provider": { "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": true, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": true, + "@metamask/json-rpc-engine": true, + "@metamask/rpc-errors": true, "@metamask/safe-event-emitter": true, "uuid": true } }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": true, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/network-controller>@metamask/json-rpc-engine": { "packages": { "@metamask/network-controller>@metamask/rpc-errors": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 54659cd66695..f87946a09c0f 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1919,40 +1919,12 @@ }, "@metamask/network-controller>@metamask/eth-json-rpc-provider": { "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": true, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": true, + "@metamask/json-rpc-engine": true, + "@metamask/rpc-errors": true, "@metamask/safe-event-emitter": true, "uuid": true } }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": true, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/network-controller>@metamask/json-rpc-engine": { "packages": { "@metamask/network-controller>@metamask/rpc-errors": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 54659cd66695..f87946a09c0f 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1919,40 +1919,12 @@ }, "@metamask/network-controller>@metamask/eth-json-rpc-provider": { "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": true, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": true, + "@metamask/json-rpc-engine": true, + "@metamask/rpc-errors": true, "@metamask/safe-event-emitter": true, "uuid": true } }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": true, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/network-controller>@metamask/json-rpc-engine": { "packages": { "@metamask/network-controller>@metamask/rpc-errors": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 38442fe5eb85..1bad9f3288a2 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2011,40 +2011,12 @@ }, "@metamask/network-controller>@metamask/eth-json-rpc-provider": { "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": true, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": true, + "@metamask/json-rpc-engine": true, + "@metamask/rpc-errors": true, "@metamask/safe-event-emitter": true, "uuid": true } }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/json-rpc-engine": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": true, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/rpc-errors": { - "packages": { - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/network-controller>@metamask/eth-json-rpc-provider>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/network-controller>@metamask/json-rpc-engine": { "packages": { "@metamask/network-controller>@metamask/rpc-errors": true, From b5b2f334b5ef87916bd9a63bb294a62e113b4da5 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 15:27:56 +0100 Subject: [PATCH 05/13] fix: unit test --- .../useProfileSyncing/profileSyncing.test.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx index 99d3064085ea..b10582b73106 100644 --- a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx +++ b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx @@ -14,6 +14,7 @@ type ArrangeMocksMetamaskStateOverrides = { isUnlocked?: boolean; useExternalServices?: boolean; completedOnboarding?: boolean; + hasFinishedAddingAccountsWithBalance?: boolean; }; const initialMetamaskState: ArrangeMocksMetamaskStateOverrides = { @@ -22,6 +23,7 @@ const initialMetamaskState: ArrangeMocksMetamaskStateOverrides = { isUnlocked: true, useExternalServices: true, completedOnboarding: true, + hasFinishedAddingAccountsWithBalance: true, }; const arrangeMockState = ( @@ -89,6 +91,7 @@ describe('useShouldDispatchProfileSyncing()', () => { 'isUnlocked', 'useExternalServices', 'completedOnboarding', + 'hasFinishedAddingAccountsWithBalance', ] as const; const baseState = { isSignedIn: true, @@ -96,6 +99,7 @@ describe('useShouldDispatchProfileSyncing()', () => { isUnlocked: true, useExternalServices: true, completedOnboarding: true, + hasFinishedAddingAccountsWithBalance: true, }; const failureStateCases: { From b4eab670d112a8ba695aa68558898ec1ebd484a5 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 16:03:20 +0100 Subject: [PATCH 06/13] fix: tests --- ui/selectors/metamask-notifications/profile-syncing.test.ts | 1 + ui/selectors/metamask-notifications/profile-syncing.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/selectors/metamask-notifications/profile-syncing.test.ts b/ui/selectors/metamask-notifications/profile-syncing.test.ts index 946ffd3f0b5f..6854237050b5 100644 --- a/ui/selectors/metamask-notifications/profile-syncing.test.ts +++ b/ui/selectors/metamask-notifications/profile-syncing.test.ts @@ -5,6 +5,7 @@ describe('Profile Syncing Selectors', () => { metamask: { isProfileSyncingEnabled: true, isProfileSyncingUpdateLoading: false, + hasFinishedAddingAccountsWithBalance: true, }, }; diff --git a/ui/selectors/metamask-notifications/profile-syncing.ts b/ui/selectors/metamask-notifications/profile-syncing.ts index 83e69d45e857..10245db67b44 100644 --- a/ui/selectors/metamask-notifications/profile-syncing.ts +++ b/ui/selectors/metamask-notifications/profile-syncing.ts @@ -3,7 +3,7 @@ import type { UserStorageController } from '@metamask/profile-sync-controller'; type AppState = { metamask: UserStorageController.UserStorageControllerState & { - hasFinishedAddingAccountsWithBalance: boolean; + hasFinishedAddingAccountsWithBalance?: boolean; }; }; From 8560b400bf0c1418b6ed1ffe35231ab28ac1b3ad Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 22:07:10 +0100 Subject: [PATCH 07/13] fix: bump profile-sync-controller and update code accordingly --- app/scripts/controllers/preferences-controller.ts | 8 -------- app/scripts/metamask-controller.js | 5 ++++- package.json | 2 +- test/e2e/page-objects/pages/homepage.ts | 12 ++++++++++++ .../importing-private-key-account.spec.ts | 2 ++ .../account-syncing/new-user-sync.spec.ts | 2 ++ .../account-syncing/onboarding-with-opt-out.spec.ts | 3 +++ .../sync-after-adding-account.spec.ts | 3 +++ .../sync-after-modifying-account-name.spec.ts | 2 ++ .../account-syncing/sync-after-onboarding.spec.ts | 1 + .../useProfileSyncing/profileSyncing.ts | 8 ++++---- .../metamask-notifications/profile-syncing.test.ts | 3 ++- .../metamask-notifications/profile-syncing.ts | 6 +++--- yarn.lock | 10 +++++----- 14 files changed, 44 insertions(+), 23 deletions(-) diff --git a/app/scripts/controllers/preferences-controller.ts b/app/scripts/controllers/preferences-controller.ts index 7bb2102399bf..dce2ef3d0512 100644 --- a/app/scripts/controllers/preferences-controller.ts +++ b/app/scripts/controllers/preferences-controller.ts @@ -164,7 +164,6 @@ export type PreferencesControllerState = Omit< enableMV3TimestampSave: boolean; useExternalServices: boolean; textDirection?: string; - hasFinishedAddingAccountsWithBalance?: boolean; }; /** @@ -455,7 +454,6 @@ const controllerMetadata = { }, isMultiAccountBalancesEnabled: { persist: true, anonymous: true }, showIncomingTransactions: { persist: true, anonymous: true }, - hasFinishedAddingAccountsWithBalance: { persist: true, anonymous: true }, }; export class PreferencesController extends BaseController< @@ -1059,12 +1057,6 @@ export class PreferencesController extends BaseController< } ///: END:ONLY_INCLUDE_IF - setHasFinishedAddingAccountsWithBalance(value: boolean): void { - this.update((state) => { - state.hasFinishedAddingAccountsWithBalance = value; - }); - } - #handleAccountsControllerSync( newAccountsControllerState: AccountsControllerState, ): void { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 30538f2a7c9b..000300160472 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1769,6 +1769,7 @@ export default class MetamaskController extends EventEmitter { this.postOnboardingInitialization(); this.triggerNetworkrequests(); + // execute once the token detection on the post-onboarding await this.tokenDetectionController.detectTokens({ selectedAddress: address, @@ -4473,7 +4474,9 @@ export default class MetamaskController extends EventEmitter { } catch (e) { log.warn(`Failed to add accounts with balance. Error: ${e}`); } finally { - this.preferencesController.setHasFinishedAddingAccountsWithBalance(true); + await this.userStorageController.setIsAccountSyncingReadyToBeDispatched( + true, + ); } } diff --git a/package.json b/package.json index cda24c42990f..6fada6a56dcf 100644 --- a/package.json +++ b/package.json @@ -338,7 +338,7 @@ "@metamask/post-message-stream": "^8.0.0", "@metamask/ppom-validator": "0.35.1", "@metamask/preinstalled-example-snap": "^0.2.0", - "@metamask/profile-sync-controller": "^1.0.1", + "@metamask/profile-sync-controller": "npm:@metamask-previews/profile-sync-controller@1.0.1-preview-913143f", "@metamask/providers": "^14.0.2", "@metamask/queued-request-controller": "^7.0.1", "@metamask/rate-limit-controller": "^6.0.0", diff --git a/test/e2e/page-objects/pages/homepage.ts b/test/e2e/page-objects/pages/homepage.ts index 89a32a550cc9..640cb8fed0aa 100644 --- a/test/e2e/page-objects/pages/homepage.ts +++ b/test/e2e/page-objects/pages/homepage.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import { Driver } from '../../webdriver/driver'; import { Ganache } from '../../seeder/ganache'; +import { getCleanAppState } from '../../helpers'; import HeaderNavbar from './header-navbar'; class HomePage { @@ -348,6 +349,17 @@ class HomePage { `Amount for transaction ${expectedNumber} is displayed as ${expectedAmount}`, ); } + + /** + * This function checks if account syncing has been successfully completed at least once. + */ + async check_hasAccountSyncingSyncedAtLeastOnce(): Promise { + console.log('Check if account syncing has synced at least once'); + await this.driver.wait(async () => { + const uiState = await getCleanAppState(); + return uiState.metamask.hasAccountSyncingSyncedAtLeastOnce === true; + }, 10000); + } } export default HomePage; diff --git a/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts b/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts index 8829bff24e07..1940d4bf3fd6 100644 --- a/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/importing-private-key-account.spec.ts @@ -52,6 +52,7 @@ describe('Account syncing - Import With Private Key @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); @@ -99,6 +100,7 @@ describe('Account syncing - Import With Private Key @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); diff --git a/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts b/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts index 15d547ca481a..8e2908682542 100644 --- a/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/new-user-sync.spec.ts @@ -49,6 +49,7 @@ describe('Account syncing - New User @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); // Open account menu and validate 1 account is shown const header = new HeaderNavbar(driver); @@ -101,6 +102,7 @@ describe('Account syncing - New User @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); // Open account menu and validate the 2 accounts have been retrieved const header = new HeaderNavbar(driver); diff --git a/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts b/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts index 209d3a51fdaf..432cf8f5b547 100644 --- a/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts @@ -69,6 +69,7 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); @@ -128,6 +129,7 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); @@ -173,6 +175,7 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts index d0fc7348e08e..23a5d1eaf47b 100644 --- a/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/sync-after-adding-account.spec.ts @@ -51,6 +51,7 @@ describe('Account syncing - Add Account @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); @@ -97,6 +98,7 @@ describe('Account syncing - Add Account @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); @@ -156,6 +158,7 @@ describe('Account syncing - Add Account @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts index db71e95681ef..22618d70f3c5 100644 --- a/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/sync-after-modifying-account-name.spec.ts @@ -51,6 +51,7 @@ describe('Account syncing - Rename Accounts @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); @@ -96,6 +97,7 @@ describe('Account syncing - Rename Accounts @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); diff --git a/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts b/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts index 3f3e2bd4baae..be2b2604633c 100644 --- a/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/sync-after-onboarding.spec.ts @@ -50,6 +50,7 @@ describe('Account syncing - Onboarding @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); + await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts index 9f6ae6532b70..57820d2e633b 100644 --- a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts +++ b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.ts @@ -11,7 +11,7 @@ import { import { selectIsSignedIn } from '../../../selectors/metamask-notifications/authentication'; import { - selectHasFinishedAddingAccountsWithBalance, + selectIsAccountSyncingReadyToBeDispatched, selectIsProfileSyncingEnabled, } from '../../../selectors/metamask-notifications/profile-syncing'; import { getUseExternalServices } from '../../../selectors'; @@ -123,8 +123,8 @@ export function useSetIsProfileSyncingEnabled(): { * @returns a boolean if internally we can perform syncing features or not. */ export const useShouldDispatchProfileSyncing = () => { - const hasFinishedAddingAccountsWithBalance = useSelector( - selectHasFinishedAddingAccountsWithBalance, + const isAccountSyncingReadyToBeDispatched = useSelector( + selectIsAccountSyncingReadyToBeDispatched, ); const isProfileSyncingEnabled = useSelector(selectIsProfileSyncingEnabled); const basicFunctionality: boolean | undefined = useSelector( @@ -142,7 +142,7 @@ export const useShouldDispatchProfileSyncing = () => { isUnlocked && isSignedIn && completedOnboarding && - hasFinishedAddingAccountsWithBalance, + isAccountSyncingReadyToBeDispatched, ); return shouldDispatchProfileSyncing; diff --git a/ui/selectors/metamask-notifications/profile-syncing.test.ts b/ui/selectors/metamask-notifications/profile-syncing.test.ts index 6854237050b5..d05512e59523 100644 --- a/ui/selectors/metamask-notifications/profile-syncing.test.ts +++ b/ui/selectors/metamask-notifications/profile-syncing.test.ts @@ -5,7 +5,8 @@ describe('Profile Syncing Selectors', () => { metamask: { isProfileSyncingEnabled: true, isProfileSyncingUpdateLoading: false, - hasFinishedAddingAccountsWithBalance: true, + isAccountSyncingReadyToBeDispatched: false, + hasAccountSyncingSyncedAtLeastOnce: false, }, }; diff --git a/ui/selectors/metamask-notifications/profile-syncing.ts b/ui/selectors/metamask-notifications/profile-syncing.ts index 10245db67b44..ae219f47be68 100644 --- a/ui/selectors/metamask-notifications/profile-syncing.ts +++ b/ui/selectors/metamask-notifications/profile-syncing.ts @@ -40,7 +40,7 @@ export const selectIsProfileSyncingUpdateLoading = createSelector( ); /** - * Selector to determine if the method _addAccountsWithBalance has finished adding accounts after onboarding. + * Selector to determine if account syncing is ready to be dispatched. This is set to true after all operations adding accounts are completed. * This is needed for account syncing in order to prevent conflicts with accounts that are being added by the above method during onboarding. * * This selector uses the `createSelector` function from 'reselect' to compute whether the update process for profile syncing is currently in a loading state, @@ -49,9 +49,9 @@ export const selectIsProfileSyncingUpdateLoading = createSelector( * @param {AppState} state - The current state of the Redux store. * @returns {boolean} Returns true if the profile syncing update is loading, false otherwise. */ -export const selectHasFinishedAddingAccountsWithBalance = createSelector( +export const selectIsAccountSyncingReadyToBeDispatched = createSelector( [getMetamask], (metamask) => { - return metamask.hasFinishedAddingAccountsWithBalance; + return metamask.isAccountSyncingReadyToBeDispatched; }, ); diff --git a/yarn.lock b/yarn.lock index 351b81c145c6..d95299763d78 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6287,9 +6287,9 @@ __metadata: languageName: node linkType: hard -"@metamask/profile-sync-controller@npm:^1.0.1": - version: 1.0.1 - resolution: "@metamask/profile-sync-controller@npm:1.0.1" +"@metamask/profile-sync-controller@npm:@metamask-previews/profile-sync-controller@1.0.1-preview-913143f": + version: 1.0.1-preview-913143f + resolution: "@metamask-previews/profile-sync-controller@npm:1.0.1-preview-913143f" dependencies: "@metamask/base-controller": "npm:^7.0.2" "@metamask/keyring-api": "npm:^8.1.3" @@ -6307,7 +6307,7 @@ __metadata: "@metamask/keyring-controller": ^18.0.0 "@metamask/network-controller": ^22.0.0 "@metamask/snaps-controllers": ^9.7.0 - checksum: 10/4a4b0e95e4a03d1ba45343dae38c2c2670ab4931f742f1b3eca0af3d8736204b84258621917b30987e8ab6e633d9b9c255acb989609b9c6d2d341441b73b4ade + checksum: 10/2bfe4be88a1d4c75470970a2606a01b7d60665b4b44e5ded7b4711c923cdc367a7ac28a0b188019d9bf2dd339bd7d1fff31d884819309a34efc32931dcadfe59 languageName: node linkType: hard @@ -26854,7 +26854,7 @@ __metadata: "@metamask/ppom-validator": "npm:0.35.1" "@metamask/preferences-controller": "npm:^13.0.2" "@metamask/preinstalled-example-snap": "npm:^0.2.0" - "@metamask/profile-sync-controller": "npm:^1.0.1" + "@metamask/profile-sync-controller": "npm:@metamask-previews/profile-sync-controller@1.0.1-preview-913143f" "@metamask/providers": "npm:^14.0.2" "@metamask/queued-request-controller": "npm:^7.0.1" "@metamask/rate-limit-controller": "npm:^6.0.0" From 61b6e70616b348c044d6a99089645bb18d1cea3f Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 22:10:16 +0100 Subject: [PATCH 08/13] fix: test helper --- test/e2e/page-objects/pages/homepage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/page-objects/pages/homepage.ts b/test/e2e/page-objects/pages/homepage.ts index 640cb8fed0aa..6a8a916d4349 100644 --- a/test/e2e/page-objects/pages/homepage.ts +++ b/test/e2e/page-objects/pages/homepage.ts @@ -356,7 +356,7 @@ class HomePage { async check_hasAccountSyncingSyncedAtLeastOnce(): Promise { console.log('Check if account syncing has synced at least once'); await this.driver.wait(async () => { - const uiState = await getCleanAppState(); + const uiState = await getCleanAppState(this.driver); return uiState.metamask.hasAccountSyncingSyncedAtLeastOnce === true; }, 10000); } From 4a1ddb219d058c41ef6135685a881e3d59a3f4f2 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 22:49:37 +0100 Subject: [PATCH 09/13] fix: test --- .../useProfileSyncing/profileSyncing.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx index b10582b73106..42e902e29401 100644 --- a/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx +++ b/ui/hooks/metamask-notifications/useProfileSyncing/profileSyncing.test.tsx @@ -14,7 +14,7 @@ type ArrangeMocksMetamaskStateOverrides = { isUnlocked?: boolean; useExternalServices?: boolean; completedOnboarding?: boolean; - hasFinishedAddingAccountsWithBalance?: boolean; + isAccountSyncingReadyToBeDispatched?: boolean; }; const initialMetamaskState: ArrangeMocksMetamaskStateOverrides = { @@ -23,7 +23,7 @@ const initialMetamaskState: ArrangeMocksMetamaskStateOverrides = { isUnlocked: true, useExternalServices: true, completedOnboarding: true, - hasFinishedAddingAccountsWithBalance: true, + isAccountSyncingReadyToBeDispatched: true, }; const arrangeMockState = ( @@ -91,7 +91,7 @@ describe('useShouldDispatchProfileSyncing()', () => { 'isUnlocked', 'useExternalServices', 'completedOnboarding', - 'hasFinishedAddingAccountsWithBalance', + 'isAccountSyncingReadyToBeDispatched', ] as const; const baseState = { isSignedIn: true, @@ -99,7 +99,7 @@ describe('useShouldDispatchProfileSyncing()', () => { isUnlocked: true, useExternalServices: true, completedOnboarding: true, - hasFinishedAddingAccountsWithBalance: true, + isAccountSyncingReadyToBeDispatched: true, }; const failureStateCases: { From af3ae625f08fcd5b53c538b884466cd6a6539f48 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Tue, 19 Nov 2024 23:56:42 +0100 Subject: [PATCH 10/13] fix: e2e & integration --- app/scripts/constants/sentry-state.ts | 2 ++ .../errors-after-init-opt-in-background-state.json | 4 +++- .../state-snapshots/errors-after-init-opt-in-ui-state.json | 2 ++ .../integration/notifications&auth/data/notification-state.ts | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/scripts/constants/sentry-state.ts b/app/scripts/constants/sentry-state.ts index 5146e38e8a41..bd953e72d49c 100644 --- a/app/scripts/constants/sentry-state.ts +++ b/app/scripts/constants/sentry-state.ts @@ -389,6 +389,8 @@ export const SENTRY_BACKGROUND_STATE = { UserStorageController: { isProfileSyncingEnabled: true, isProfileSyncingUpdateLoading: false, + hasAccountSyncingSyncedAtLeastOnce: false, + isAccountSyncingReadyToBeDispatched: false, }, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) ...MMI_SENTRY_BACKGROUND_STATE, diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json index e47cfcd806b9..97ad6dd2850f 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-background-state.json @@ -347,6 +347,8 @@ "UserOperationController": { "userOperations": "object" }, "UserStorageController": { "isProfileSyncingEnabled": null, - "isProfileSyncingUpdateLoading": "boolean" + "isProfileSyncingUpdateLoading": "boolean", + "hasAccountSyncingSyncedAtLeastOnce": "boolean", + "isAccountSyncingReadyToBeDispatched": "boolean" } } diff --git a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json index 7fd8501eb2b8..9f3c4755389a 100644 --- a/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/metrics/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -224,6 +224,8 @@ "isSignedIn": "boolean", "isProfileSyncingEnabled": null, "isProfileSyncingUpdateLoading": "boolean", + "hasAccountSyncingSyncedAtLeastOnce": "boolean", + "isAccountSyncingReadyToBeDispatched": "boolean", "subscriptionAccountsSeen": "object", "isMetamaskNotificationsFeatureSeen": "boolean", "isNotificationServicesEnabled": "boolean", diff --git a/test/integration/notifications&auth/data/notification-state.ts b/test/integration/notifications&auth/data/notification-state.ts index c58bf707f521..61d74d161671 100644 --- a/test/integration/notifications&auth/data/notification-state.ts +++ b/test/integration/notifications&auth/data/notification-state.ts @@ -38,6 +38,8 @@ export const getMockedNotificationsState = () => { ...mockMetaMaskState, isProfileSyncingEnabled: true, isProfileSyncingUpdateLoading: false, + hasAccountSyncingSyncedAtLeastOnce: false, + isAccountSyncingReadyToBeDispatched: false, isMetamaskNotificationsFeatureSeen: true, isNotificationServicesEnabled: true, isFeatureAnnouncementsEnabled: true, From d3081ac64c46ed1fa08a51b032c903491800fdc1 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Wed, 20 Nov 2024 00:27:11 +0100 Subject: [PATCH 11/13] fix: e2e test --- .../account-syncing/onboarding-with-opt-out.spec.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts b/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts index 432cf8f5b547..209d3a51fdaf 100644 --- a/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts +++ b/test/e2e/tests/notifications/account-syncing/onboarding-with-opt-out.spec.ts @@ -69,7 +69,6 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); - await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); @@ -129,7 +128,6 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); - await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); @@ -175,7 +173,6 @@ describe('Account syncing - Opt-out Profile Sync @no-mmi', function () { const homePage = new HomePage(driver); await homePage.check_pageIsLoaded(); await homePage.check_expectedBalanceIsDisplayed(); - await homePage.check_hasAccountSyncingSyncedAtLeastOnce(); const header = new HeaderNavbar(driver); await header.check_pageIsLoaded(); From 97e27872584d624d2d0112f8db29dbb42dbbaba7 Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Wed, 20 Nov 2024 11:52:15 +0100 Subject: [PATCH 12/13] fix: yarn.lock --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index a87410e73f1c..f4ea148d1fa6 100644 --- a/package.json +++ b/package.json @@ -338,7 +338,7 @@ "@metamask/post-message-stream": "^8.0.0", "@metamask/ppom-validator": "0.35.1", "@metamask/preinstalled-example-snap": "^0.2.0", - "@metamask/profile-sync-controller": "npm:@metamask-previews/profile-sync-controller@1.0.1-preview-913143f", + "@metamask/profile-sync-controller": "^1.0.2", "@metamask/providers": "^14.0.2", "@metamask/queued-request-controller": "^7.0.1", "@metamask/rate-limit-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index ebfb272ea023..0a76d871fdff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6287,9 +6287,9 @@ __metadata: languageName: node linkType: hard -"@metamask/profile-sync-controller@npm:@metamask-previews/profile-sync-controller@1.0.1-preview-913143f": - version: 1.0.1-preview-913143f - resolution: "@metamask-previews/profile-sync-controller@npm:1.0.1-preview-913143f" +"@metamask/profile-sync-controller@npm:^1.0.2": + version: 1.0.2 + resolution: "@metamask/profile-sync-controller@npm:1.0.2" dependencies: "@metamask/base-controller": "npm:^7.0.2" "@metamask/keyring-api": "npm:^8.1.3" @@ -6307,7 +6307,7 @@ __metadata: "@metamask/keyring-controller": ^18.0.0 "@metamask/network-controller": ^22.0.0 "@metamask/snaps-controllers": ^9.7.0 - checksum: 10/2bfe4be88a1d4c75470970a2606a01b7d60665b4b44e5ded7b4711c923cdc367a7ac28a0b188019d9bf2dd339bd7d1fff31d884819309a34efc32931dcadfe59 + checksum: 10/e8ce9cc5749746bea3f6fb9207bbd4e8e3956f92447f3a6b790e3ba7203747e38b9a819f7a4f1896022cf6e1a065e6136a3c82ee83a4ec0ee56b23de27e23f03 languageName: node linkType: hard @@ -26875,7 +26875,7 @@ __metadata: "@metamask/ppom-validator": "npm:0.35.1" "@metamask/preferences-controller": "npm:^13.0.2" "@metamask/preinstalled-example-snap": "npm:^0.2.0" - "@metamask/profile-sync-controller": "npm:@metamask-previews/profile-sync-controller@1.0.1-preview-913143f" + "@metamask/profile-sync-controller": "npm:^1.0.2" "@metamask/providers": "npm:^14.0.2" "@metamask/queued-request-controller": "npm:^7.0.1" "@metamask/rate-limit-controller": "npm:^6.0.0" From 570ee0a8626b6db350c6af5d114b6bf6e289d33a Mon Sep 17 00:00:00 2001 From: Mathieu Artu Date: Wed, 20 Nov 2024 15:23:58 +0100 Subject: [PATCH 13/13] feat: add analytics event --- app/scripts/metamask-controller.js | 10 ++++++++++ shared/constants/metametrics.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 775664faaa5e..15a44fa6747e 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1595,6 +1595,16 @@ export default class MetamaskController extends EventEmitter { }, }); }, + onAccountSyncErroneousSituation: (profileId, situationMessage) => { + this.metaMetricsController.trackEvent({ + category: MetaMetricsEventCategory.ProfileSyncing, + event: MetaMetricsEventName.AccountsSyncErroneousSituation, + properties: { + profile_id: profileId, + situation_message: situationMessage, + }, + }); + }, }, }, env: { diff --git a/shared/constants/metametrics.ts b/shared/constants/metametrics.ts index 3f1941a5c00e..8688b8cfa8ae 100644 --- a/shared/constants/metametrics.ts +++ b/shared/constants/metametrics.ts @@ -631,6 +631,7 @@ export enum MetaMetricsEventName { AccountRenamed = 'Account Renamed', AccountsSyncAdded = 'Accounts Sync Added', AccountsSyncNameUpdated = 'Accounts Sync Name Updated', + AccountsSyncErroneousSituation = 'Accounts Sync Erroneous Situation', ActivityDetailsOpened = 'Activity Details Opened', ActivityDetailsClosed = 'Activity Details Closed', AnalyticsPreferenceSelected = 'Analytics Preference Selected',