From 61aeb07c936856813b2eba9196f1a7472980ba31 Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Mon, 13 Oct 2025 16:40:39 +0200 Subject: [PATCH 1/6] feat(core-backend): add AccountActivity and Websocket for TokenBalancesController --- .metamaskrc.dist | 9 + .vscode/launch.json | 12 + ...s-controllers-npm-81.0.0-706c32028b.patch} | 0 .../controller-init/bridge-controller-init.ts | 14 +- .../controller-init/controller-list.ts | 6 + .../account-activity-service-init.test.ts | 59 +++ .../account-activity-service-init.ts | 25 + .../backend-websocket-service-init.test.ts | 434 ++++++++++++++++++ .../backend-websocket-service-init.ts | 90 ++++ .../controller-init/core-backend/index.ts | 2 + ...account-activity-service-messenger.test.ts | 12 + .../account-activity-service-messenger.ts | 26 ++ ...ackend-websocket-service-messenger.test.ts | 26 ++ .../backend-websocket-service-messenger.ts | 46 ++ .../messengers/core-backend/index.ts | 10 + .../controller-init/messengers/index.ts | 13 + .../token-balances-controller-messenger.ts | 15 +- .../controllers/account-tracker-controller.ts | 14 +- app/scripts/metamask-controller.js | 14 + builds.yml | 10 + lavamoat/browserify/beta/policy.json | 25 + lavamoat/browserify/experimental/policy.json | 25 + lavamoat/browserify/flask/policy.json | 25 + lavamoat/browserify/main/policy.json | 25 + lavamoat/webpack/policy.json | 25 + package.json | 7 +- yarn.lock | 91 ++-- 27 files changed, 1010 insertions(+), 50 deletions(-) rename .yarn/patches/{@metamask-assets-controllers-npm-79.0.0-8b55992ea9.patch => @metamask-assets-controllers-npm-81.0.0-706c32028b.patch} (100%) create mode 100644 app/scripts/controller-init/core-backend/account-activity-service-init.test.ts create mode 100644 app/scripts/controller-init/core-backend/account-activity-service-init.ts create mode 100644 app/scripts/controller-init/core-backend/backend-websocket-service-init.test.ts create mode 100644 app/scripts/controller-init/core-backend/backend-websocket-service-init.ts create mode 100644 app/scripts/controller-init/core-backend/index.ts create mode 100644 app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.test.ts create mode 100644 app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.ts create mode 100644 app/scripts/controller-init/messengers/core-backend/backend-websocket-service-messenger.test.ts create mode 100644 app/scripts/controller-init/messengers/core-backend/backend-websocket-service-messenger.ts create mode 100644 app/scripts/controller-init/messengers/core-backend/index.ts diff --git a/.metamaskrc.dist b/.metamaskrc.dist index e0054b2e770a..5ab6dfafb9c9 100644 --- a/.metamaskrc.dist +++ b/.metamaskrc.dist @@ -10,6 +10,15 @@ SEEDLESS_ONBOARDING_ENABLED='false' ; Set this to `true` to enable Metamask Shield METAMASK_SHIELD_ENABLED='false' +; Backend WebSocket Connection Feature Flag +; Set to 'true' to enable WebSocket connection, 'false' to disable +; Comment out or set to empty to use remote feature flags +;MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED='true' + +; Backend WebSocket URL +; Uses production API endpoint by default (same as builds.yml) +;MM_BACKEND_WEBSOCKET_URL='wss://gateway.api.cx.metamask.io/v1' + ; These variables are required for OAuth Service GOOGLE_CLIENT_ID= APPLE_CLIENT_ID= diff --git a/.vscode/launch.json b/.vscode/launch.json index bb8b8618b403..077db9cf4521 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -49,6 +49,18 @@ ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" + }, + { + "name": "Launch Debug MetaMask Chrome", + "request": "launch", + "type": "chrome", + "runtimeArgs": [ + "--load-extension=${workspaceFolder}/dist/chrome", + "--remote-debugging-port=9222" + ], + "url": "chrome-extension://hebhblbkkdabgoldnojllkipeoacjioc/background.html", + "sourceMaps": true, + "webRoot": "${workspaceFolder}" } ], "inputs": [ diff --git a/.yarn/patches/@metamask-assets-controllers-npm-79.0.0-8b55992ea9.patch b/.yarn/patches/@metamask-assets-controllers-npm-81.0.0-706c32028b.patch similarity index 100% rename from .yarn/patches/@metamask-assets-controllers-npm-79.0.0-8b55992ea9.patch rename to .yarn/patches/@metamask-assets-controllers-npm-81.0.0-706c32028b.patch diff --git a/app/scripts/controller-init/bridge-controller-init.ts b/app/scripts/controller-init/bridge-controller-init.ts index cc44e190a0eb..c895dc34cef1 100644 --- a/app/scripts/controller-init/bridge-controller-init.ts +++ b/app/scripts/controller-init/bridge-controller-init.ts @@ -15,7 +15,7 @@ import { BridgeControllerMessenger, } from './messengers'; -type FetchWithCacheOptions = { +type FetchWithCacheOptions = RequestInit & { cacheOptions?: { cacheRefreshTime: number; }; @@ -49,14 +49,10 @@ export const BridgeControllerInit: ControllerInitFunction< getLayer1GasFee: (...args) => transactionController.getLayer1GasFee(...args), - fetchFn: async ( - url, - { - cacheOptions, - functionName, - ...requestOptions - }: FetchWithCacheOptions = {}, - ) => { + fetchFn: async (url, init?) => { + const { cacheOptions, functionName, ...requestOptions } = + (init as FetchWithCacheOptions) ?? {}; + if (functionName === 'fetchBridgeTokens') { return await fetchWithCache({ url: url.toString(), diff --git a/app/scripts/controller-init/controller-list.ts b/app/scripts/controller-init/controller-list.ts index 4142686b6ed0..3a4509b860eb 100644 --- a/app/scripts/controller-init/controller-list.ts +++ b/app/scripts/controller-init/controller-list.ts @@ -76,6 +76,10 @@ import { } from '@metamask/message-manager'; import { SignatureController } from '@metamask/signature-controller'; import { UserOperationController } from '@metamask/user-operation-controller'; +import { + AccountActivityService, + BackendWebSocketService, +} from '@metamask/core-backend'; import OnboardingController from '../controllers/onboarding'; import { PreferencesController } from '../controllers/preferences-controller'; import SwapsController from '../controllers/swaps'; @@ -181,6 +185,8 @@ export type Controller = | AssetsContractController | AccountTreeController | WebSocketService + | BackendWebSocketService + | AccountActivityService | MultichainAccountService | NetworkEnablementController; diff --git a/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts b/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts new file mode 100644 index 000000000000..30ac38fc087e --- /dev/null +++ b/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts @@ -0,0 +1,59 @@ +import { AccountActivityService } from '@metamask/core-backend'; +import { Messenger } from '@metamask/base-controller'; +import { ControllerInitRequest } from '../types'; +import { buildControllerInitRequestMock } from '../test/utils'; +import { + AccountActivityServiceMessenger, + getAccountActivityServiceMessenger, +} from '../messengers/core-backend'; +import { AccountActivityServiceInit } from './account-activity-service-init'; + +jest.mock('@metamask/core-backend'); + +function getInitRequestMock(): jest.Mocked< + ControllerInitRequest +> { + const baseMessenger = new Messenger(); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getAccountActivityServiceMessenger(baseMessenger), + initMessenger: undefined, + }; + + return requestMock; +} + +describe('AccountActivityServiceInit', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('initializes the controller', () => { + const { controller } = AccountActivityServiceInit(getInitRequestMock()); + expect(controller).toBeInstanceOf(AccountActivityService); + }); + + it('passes the messenger to the controller', () => { + AccountActivityServiceInit(getInitRequestMock()); + + const controllerMock = jest.mocked(AccountActivityService); + expect(controllerMock).toHaveBeenCalledWith({ + messenger: expect.any(Object), + }); + }); + + it('returns null for both state keys', () => { + const result = AccountActivityServiceInit(getInitRequestMock()); + + expect(result.memStateKey).toBeNull(); + expect(result.persistedStateKey).toBeNull(); + }); + + it('returns the controller instance', () => { + const { controller } = AccountActivityServiceInit(getInitRequestMock()); + + expect(controller).toBeDefined(); + expect(controller).toBeInstanceOf(AccountActivityService); + }); +}); diff --git a/app/scripts/controller-init/core-backend/account-activity-service-init.ts b/app/scripts/controller-init/core-backend/account-activity-service-init.ts new file mode 100644 index 000000000000..40a94771d3fc --- /dev/null +++ b/app/scripts/controller-init/core-backend/account-activity-service-init.ts @@ -0,0 +1,25 @@ +import { AccountActivityService } from '@metamask/core-backend'; +import { ControllerInitFunction } from '../types'; +import { AccountActivityServiceMessenger } from '../messengers/core-backend'; + +/** + * Initialize the Account Activity service. + * + * @param request - The request object. + * @param request.controllerMessenger - The messenger to use for the service. + * @returns The initialized service. + */ +export const AccountActivityServiceInit: ControllerInitFunction< + AccountActivityService, + AccountActivityServiceMessenger +> = ({ controllerMessenger }) => { + const controller = new AccountActivityService({ + messenger: controllerMessenger, + }); + + return { + memStateKey: null, + persistedStateKey: null, + controller, + }; +}; diff --git a/app/scripts/controller-init/core-backend/backend-websocket-service-init.test.ts b/app/scripts/controller-init/core-backend/backend-websocket-service-init.test.ts new file mode 100644 index 000000000000..06de8bfec46d --- /dev/null +++ b/app/scripts/controller-init/core-backend/backend-websocket-service-init.test.ts @@ -0,0 +1,434 @@ +import { BackendWebSocketService } from '@metamask/core-backend'; +import { Messenger, ActionConstraint } from '@metamask/base-controller'; +import { ControllerInitRequest } from '../types'; +import { buildControllerInitRequestMock } from '../test/utils'; +import { + BackendWebSocketServiceMessenger, + BackendWebSocketServiceInitMessenger, + getBackendWebSocketServiceMessenger, + getBackendWebSocketServiceInitMessenger, +} from '../messengers/core-backend'; +import { BackendWebSocketServiceInit } from './backend-websocket-service-init'; + +jest.mock('@metamask/core-backend'); + +function getInitRequestMock(): jest.Mocked< + ControllerInitRequest< + BackendWebSocketServiceMessenger, + BackendWebSocketServiceInitMessenger + > +> { + const baseMessenger = new Messenger(); + + // Mock RemoteFeatureFlagController:getState + baseMessenger.registerActionHandler( + 'RemoteFeatureFlagController:getState', + () => + ({ + remoteFeatureFlags: { + backendWebSocketConnection: { + value: false, + }, + }, + }) as never, + ); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), + initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), + }; + + return requestMock; +} + +describe('BackendWebSocketServiceInit', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('initializes the controller', () => { + const { controller } = BackendWebSocketServiceInit(getInitRequestMock()); + expect(controller).toBeInstanceOf(BackendWebSocketService); + }); + + it('passes the proper arguments to the controller', () => { + BackendWebSocketServiceInit(getInitRequestMock()); + + const controllerMock = jest.mocked(BackendWebSocketService); + expect(controllerMock).toHaveBeenCalledWith({ + messenger: expect.any(Object), + url: 'wss://gateway.api.cx.metamask.io/v1', + timeout: 15000, + reconnectDelay: 1000, + maxReconnectDelay: 30000, + requestTimeout: 20000, + traceFn: expect.any(Function), + isEnabled: expect.any(Function), + }); + }); + + it('returns null for both state keys', () => { + const result = BackendWebSocketServiceInit(getInitRequestMock()); + + expect(result.memStateKey).toBeNull(); + expect(result.persistedStateKey).toBeNull(); + }); + + it('uses environment variable for WebSocket URL when provided', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_URL; + process.env.MM_BACKEND_WEBSOCKET_URL = 'wss://custom-backend.example.com'; + + BackendWebSocketServiceInit(getInitRequestMock()); + + const controllerMock = jest.mocked(BackendWebSocketService); + expect(controllerMock).toHaveBeenCalledWith( + expect.objectContaining({ + url: 'wss://custom-backend.example.com', + }), + ); + + process.env.MM_BACKEND_WEBSOCKET_URL = originalEnv; + }); + + describe('isEnabled callback', () => { + it('returns false when feature flag is disabled and no env override', () => { + // Ensure env var is not set + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + + BackendWebSocketServiceInit(getInitRequestMock()); + + const { isEnabled } = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + + // When env var is undefined, the check envOverride != null is false, + // so it falls through to feature flag logic which returns false (feature flag is disabled in mock) + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(false); + + // Restore + if (originalEnv !== undefined) { + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + } + }); + + it('returns true when environment variable is set to true', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = 'true'; + + BackendWebSocketServiceInit(getInitRequestMock()); + + const { isEnabled } = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(true); + + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + }); + + it('returns false when environment variable is set to false', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = 'false'; + + BackendWebSocketServiceInit(getInitRequestMock()); + + const { isEnabled } = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(false); + + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + }); + + it('returns false when feature flag check fails', () => { + const baseMessenger = new Messenger(); + baseMessenger.registerActionHandler( + 'RemoteFeatureFlagController:getState', + () => { + throw new Error('Feature flag error'); + }, + ); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), + initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), + }; + + BackendWebSocketServiceInit(requestMock); + + const { isEnabled } = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(false); + }); + + it('returns true when environment variable is set to boolean true', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + // @ts-expect-error - Testing with boolean value + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = true; + + BackendWebSocketServiceInit(getInitRequestMock()); + + const constructorArgs = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + const { isEnabled } = constructorArgs; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(true); + + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + }); + + it('returns false when environment variable is any other value', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = 'something-else'; + + BackendWebSocketServiceInit(getInitRequestMock()); + + const { isEnabled } = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(false); + + // Cleanup - delete if original was undefined, otherwise restore + if (originalEnv === undefined) { + delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + } else { + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + } + }); + + it('returns true when feature flag is enabled and no env override', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + // Delete env var so it doesn't override the feature flag + delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + + const baseMessenger = new Messenger(); + baseMessenger.registerActionHandler( + 'RemoteFeatureFlagController:getState', + () => + ({ + remoteFeatureFlags: { + backendWebSocketConnection: { + value: true, + }, + }, + }) as never, + ); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), + initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), + }; + + BackendWebSocketServiceInit(requestMock); + + const constructorArgs = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + const { isEnabled } = constructorArgs; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(true); + + if (originalEnv !== undefined) { + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + } + }); + + it('returns false when feature flag is disabled and no env override', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + // Delete env var so it doesn't override the feature flag + delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + + const baseMessenger = new Messenger(); + baseMessenger.registerActionHandler( + 'RemoteFeatureFlagController:getState', + () => + ({ + remoteFeatureFlags: { + backendWebSocketConnection: { + value: false, + }, + }, + }) as never, + ); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), + initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), + }; + + BackendWebSocketServiceInit(requestMock); + + const constructorArgs = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + const { isEnabled } = constructorArgs; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(false); + + if (originalEnv !== undefined) { + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + } + }); + + it('returns false when feature flag object does not have value property', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + // Delete env var so it doesn't override the feature flag check + delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + + const baseMessenger = new Messenger(); + baseMessenger.registerActionHandler( + 'RemoteFeatureFlagController:getState', + () => + ({ + remoteFeatureFlags: { + backendWebSocketConnection: { + // Missing 'value' property + enabled: true, + }, + }, + }) as never, + ); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), + initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), + }; + + BackendWebSocketServiceInit(requestMock); + + const constructorArgs = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + const { isEnabled } = constructorArgs; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(false); + + if (originalEnv !== undefined) { + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + } + }); + + it('returns false when feature flag is not an object', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + // Delete env var so it doesn't override the feature flag check + delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + + const baseMessenger = new Messenger(); + baseMessenger.registerActionHandler( + 'RemoteFeatureFlagController:getState', + () => + ({ + remoteFeatureFlags: { + backendWebSocketConnection: 'enabled', // Not an object + }, + }) as never, + ); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), + initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), + }; + + BackendWebSocketServiceInit(requestMock); + + const constructorArgs = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + const { isEnabled } = constructorArgs; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(false); + + if (originalEnv !== undefined) { + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + } + }); + + it('returns false when remoteFeatureFlags is missing', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + // Delete env var so it doesn't override the feature flag check + delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + + const baseMessenger = new Messenger(); + baseMessenger.registerActionHandler( + 'RemoteFeatureFlagController:getState', + () => + ({ + // Missing remoteFeatureFlags + }) as never, + ); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), + initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), + }; + + BackendWebSocketServiceInit(requestMock); + + const constructorArgs = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + const { isEnabled } = constructorArgs; + + expect(isEnabled).toBeDefined(); + expect(isEnabled?.()).toBe(false); + + if (originalEnv !== undefined) { + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + } + }); + + it('logs warning when feature flag check fails', () => { + const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + // Delete env var so it doesn't short-circuit to env override path + delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; + + const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + + const baseMessenger = new Messenger(); + baseMessenger.registerActionHandler( + 'RemoteFeatureFlagController:getState', + () => { + throw new Error('Feature flag error'); + }, + ); + + const requestMock = { + ...buildControllerInitRequestMock(), + controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), + initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), + }; + + BackendWebSocketServiceInit(requestMock); + + const constructorArgs = jest.mocked(BackendWebSocketService).mock + .calls[0][0]; + const { isEnabled } = constructorArgs; + + expect(isEnabled).toBeDefined(); + isEnabled?.(); + + expect(consoleWarnSpy).toHaveBeenCalledWith( + '[BackendWebSocketService] Could not check feature flag, defaulting to NOT connect:', + expect.any(Error), + ); + + consoleWarnSpy.mockRestore(); + + if (originalEnv !== undefined) { + process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; + } + }); + }); +}); diff --git a/app/scripts/controller-init/core-backend/backend-websocket-service-init.ts b/app/scripts/controller-init/core-backend/backend-websocket-service-init.ts new file mode 100644 index 000000000000..484962be7d35 --- /dev/null +++ b/app/scripts/controller-init/core-backend/backend-websocket-service-init.ts @@ -0,0 +1,90 @@ +import { BackendWebSocketService } from '@metamask/core-backend'; +import type { TraceCallback } from '@metamask/controller-utils'; +import { ControllerInitFunction } from '../types'; +import { + BackendWebSocketServiceMessenger, + BackendWebSocketServiceInitMessenger, +} from '../messengers/core-backend'; +import { trace } from '../../../../shared/lib/trace'; + +/** + * Initialize the Backend Platform WebSocket service with authentication support. + * This provides WebSocket connectivity for backend platform services + * like AccountActivityService and other platform-level integrations. + * + * Authentication Flow (simplified with AuthenticationController): + * 1. Core WebSocketService: Controls WHETHER connections are allowed (AuthenticationController.isSignedIn = yes) + * 2. Browser/Extension lifecycle: Controls WHEN to connect/disconnect (close = disconnect, open = connect) + * 3. AuthenticationController.isSignedIn includes BOTH wallet unlock + identity provider authentication + * 4. Fresh bearer tokens retrieved on each connection attempt (getBearerToken checks wallet unlock internally) + * + * @param request - The request object. + * @param request.controllerMessenger - The messenger to use for the service. + * @param request.initMessenger - The messenger for accessing other controllers. + * @returns The initialized service. + */ +export const BackendWebSocketServiceInit: ControllerInitFunction< + BackendWebSocketService, + BackendWebSocketServiceMessenger, + BackendWebSocketServiceInitMessenger +> = ({ controllerMessenger, initMessenger }) => { + const controller = new BackendWebSocketService({ + messenger: controllerMessenger, + url: + process.env.MM_BACKEND_WEBSOCKET_URL || + 'wss://gateway.api.cx.metamask.io/v1', + // Backend Platform optimized configuration + timeout: 15000, // Longer timeout for backend operations + reconnectDelay: 1000, // Conservative reconnect strategy + maxReconnectDelay: 30000, // Allow longer delays for backend stability + requestTimeout: 20000, // Reasonable timeout for backend requests + // Inject the Sentry-backed trace function from extension platform + traceFn: trace as TraceCallback, + // Feature flag AND app lifecycle integration + // Service will check this callback before connecting/reconnecting + isEnabled: () => { + try { + // Check for local environment variable override first (for development) + const envOverride = process.env + .MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED as + | string + | boolean + | null + | undefined; + if (envOverride !== null && envOverride !== undefined) { + return envOverride === true || envOverride === 'true'; + } + + const remoteFeatureFlagState = initMessenger?.call( + 'RemoteFeatureFlagController:getState', + ); + const { backendWebSocketConnection } = + remoteFeatureFlagState?.remoteFeatureFlags || {}; + + const result = + backendWebSocketConnection && + typeof backendWebSocketConnection === 'object' && + 'value' in backendWebSocketConnection && + Boolean(backendWebSocketConnection.value); + + return Boolean(result); + } catch (error) { + // If feature flag check fails, default to NOT connecting for safer startup + console.warn( + '[BackendWebSocketService] Could not check feature flag, defaulting to NOT connect:', + error, + ); + return false; + } + }, + }); + + // Authentication and lock/unlock handling is now managed by the core WebSocket service + // Core service will automatically connect when wallet is unlocked (no manual connect() needed) + + return { + memStateKey: null, + persistedStateKey: null, + controller, + }; +}; diff --git a/app/scripts/controller-init/core-backend/index.ts b/app/scripts/controller-init/core-backend/index.ts new file mode 100644 index 000000000000..56fcf733d9d6 --- /dev/null +++ b/app/scripts/controller-init/core-backend/index.ts @@ -0,0 +1,2 @@ +export { BackendWebSocketServiceInit } from './backend-websocket-service-init'; +export { AccountActivityServiceInit } from './account-activity-service-init'; diff --git a/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.test.ts b/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.test.ts new file mode 100644 index 000000000000..955ae6f4be9b --- /dev/null +++ b/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.test.ts @@ -0,0 +1,12 @@ +import { Messenger, RestrictedMessenger } from '@metamask/base-controller'; +import { getAccountActivityServiceMessenger } from './account-activity-service-messenger'; + +describe('getAccountActivityServiceMessenger', () => { + it('returns a restricted controller messenger', () => { + const controllerMessenger = new Messenger(); + const messenger = getAccountActivityServiceMessenger(controllerMessenger); + + expect(messenger).toBeInstanceOf(RestrictedMessenger); + expect(messenger).toBeDefined(); + }); +}); diff --git a/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.ts b/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.ts new file mode 100644 index 000000000000..ee61d11a58d2 --- /dev/null +++ b/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.ts @@ -0,0 +1,26 @@ +import { + type AccountActivityServiceMessenger as BackendPlatformAccountActivityServiceMessenger, + ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS, + ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS, +} from '@metamask/core-backend'; + +export type AccountActivityServiceMessenger = + BackendPlatformAccountActivityServiceMessenger; + +/** + * Get a restricted messenger for the Account Activity service. This is scoped to the + * actions and events that the Account Activity service is allowed to handle. + * + * @param messenger - The main controller messenger. + * @returns The restricted messenger. + */ +export function getAccountActivityServiceMessenger( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + messenger: any, // Using any to avoid type conflicts with the main messenger +): BackendPlatformAccountActivityServiceMessenger { + return messenger.getRestricted({ + name: 'AccountActivityService', + allowedActions: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS], + allowedEvents: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS], + }); +} diff --git a/app/scripts/controller-init/messengers/core-backend/backend-websocket-service-messenger.test.ts b/app/scripts/controller-init/messengers/core-backend/backend-websocket-service-messenger.test.ts new file mode 100644 index 000000000000..acd9b6333f80 --- /dev/null +++ b/app/scripts/controller-init/messengers/core-backend/backend-websocket-service-messenger.test.ts @@ -0,0 +1,26 @@ +import { Messenger, RestrictedMessenger } from '@metamask/base-controller'; +import { + getBackendWebSocketServiceMessenger, + getBackendWebSocketServiceInitMessenger, +} from './backend-websocket-service-messenger'; + +describe('getBackendWebSocketServiceMessenger', () => { + it('returns a restricted controller messenger', () => { + const controllerMessenger = new Messenger(); + const messenger = getBackendWebSocketServiceMessenger(controllerMessenger); + + expect(messenger).toBeInstanceOf(RestrictedMessenger); + expect(messenger).toBeDefined(); + }); +}); + +describe('getBackendWebSocketServiceInitMessenger', () => { + it('returns a restricted controller messenger', () => { + const controllerMessenger = new Messenger(); + const messenger = + getBackendWebSocketServiceInitMessenger(controllerMessenger); + + expect(messenger).toBeInstanceOf(RestrictedMessenger); + expect(messenger).toBeDefined(); + }); +}); diff --git a/app/scripts/controller-init/messengers/core-backend/backend-websocket-service-messenger.ts b/app/scripts/controller-init/messengers/core-backend/backend-websocket-service-messenger.ts new file mode 100644 index 000000000000..875c6781316a --- /dev/null +++ b/app/scripts/controller-init/messengers/core-backend/backend-websocket-service-messenger.ts @@ -0,0 +1,46 @@ +import { BackendWebSocketServiceMessenger as BackendPlatformWebSocketServiceMessenger } from '@metamask/core-backend'; + +export type BackendWebSocketServiceMessenger = + BackendPlatformWebSocketServiceMessenger; + +/** + * Get a restricted messenger for the Backend Platform WebSocket service. + * This is scoped to backend platform operations and services. + * + * @param messenger - The main controller messenger. + * @returns The restricted messenger. + */ +export function getBackendWebSocketServiceMessenger( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + messenger: any, // Using any to avoid type conflicts with the main messenger +): BackendPlatformWebSocketServiceMessenger { + return messenger.getRestricted({ + name: 'BackendWebSocketService', + allowedActions: [ + 'AuthenticationController:getBearerToken', // Get auth token (includes wallet unlock check) + ], + allowedEvents: [ + 'AuthenticationController:stateChange', // Listen for authentication state (sign in/out) + 'KeyringController:lock', // Listen for wallet lock + 'KeyringController:unlock', // Listen for wallet unlock + ], + }); +} + +export type BackendWebSocketServiceInitMessenger = ReturnType< + typeof getBackendWebSocketServiceInitMessenger +>; + +export function getBackendWebSocketServiceInitMessenger( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + messenger: any, +) { + return messenger.getRestricted({ + name: 'BackendWebSocketServiceInit', + allowedEvents: [], + allowedActions: [ + 'RemoteFeatureFlagController:getState', + 'AuthenticationController:getBearerToken', + ], + }); +} diff --git a/app/scripts/controller-init/messengers/core-backend/index.ts b/app/scripts/controller-init/messengers/core-backend/index.ts new file mode 100644 index 000000000000..c624fd2c75e2 --- /dev/null +++ b/app/scripts/controller-init/messengers/core-backend/index.ts @@ -0,0 +1,10 @@ +export { + getBackendWebSocketServiceMessenger, + getBackendWebSocketServiceInitMessenger, +} from './backend-websocket-service-messenger'; +export type { + BackendWebSocketServiceMessenger, + BackendWebSocketServiceInitMessenger, +} from './backend-websocket-service-messenger'; +export { getAccountActivityServiceMessenger } from './account-activity-service-messenger'; +export type { AccountActivityServiceMessenger } from './account-activity-service-messenger'; diff --git a/app/scripts/controller-init/messengers/index.ts b/app/scripts/controller-init/messengers/index.ts index 50a6fe7218ee..e8250344b0c8 100644 --- a/app/scripts/controller-init/messengers/index.ts +++ b/app/scripts/controller-init/messengers/index.ts @@ -23,6 +23,11 @@ import { getTransactionControllerMessenger, getTransactionControllerInitMessenger, } from './transaction-controller-messenger'; +import { + getBackendWebSocketServiceMessenger, + getBackendWebSocketServiceInitMessenger, + getAccountActivityServiceMessenger, +} from './core-backend'; import { getMultichainBalancesControllerMessenger, getMultichainTransactionsControllerMessenger, @@ -682,6 +687,14 @@ export const CONTROLLER_MESSENGERS = { getMessenger: getWebSocketServiceMessenger, getInitMessenger: noop, }, + BackendWebSocketService: { + getMessenger: getBackendWebSocketServiceMessenger, + getInitMessenger: getBackendWebSocketServiceInitMessenger, + }, + AccountActivityService: { + getMessenger: getAccountActivityServiceMessenger, + getInitMessenger: noop, + }, SmartTransactionsController: { getMessenger: getSmartTransactionsControllerMessenger, getInitMessenger: getSmartTransactionsControllerInitMessenger, diff --git a/app/scripts/controller-init/messengers/token-balances-controller-messenger.ts b/app/scripts/controller-init/messengers/token-balances-controller-messenger.ts index 8bb7a98ce547..827528b30b55 100644 --- a/app/scripts/controller-init/messengers/token-balances-controller-messenger.ts +++ b/app/scripts/controller-init/messengers/token-balances-controller-messenger.ts @@ -19,6 +19,11 @@ import { } from '@metamask/assets-controllers'; import { KeyringControllerAccountRemovedEvent } from '@metamask/keyring-controller'; import { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller'; +import type { + AccountActivityServiceStatusChangedEvent, + AccountActivityServiceBalanceUpdatedEvent, +} from '@metamask/core-backend'; +import type { TokenDetectionControllerAddDetectedTokensViaWsAction } from '@metamask/assets-controllers'; import { AccountTrackerControllerGetStateAction } from '../../controllers/account-tracker-controller'; import { PreferencesControllerGetStateAction, @@ -45,13 +50,16 @@ type AllowedActions = | NetworkControllerGetNetworkClientByIdAction | NetworkControllerGetStateAction | PreferencesControllerGetStateAction - | TokensControllerGetStateAction; + | TokensControllerGetStateAction + | TokenDetectionControllerAddDetectedTokensViaWsAction; type AllowedEvents = | KeyringControllerAccountRemovedEvent | NetworkControllerStateChangeEvent | PreferencesControllerStateChangeEvent - | TokensControllerStateChangeEvent; + | TokensControllerStateChangeEvent + | AccountActivityServiceStatusChangedEvent + | AccountActivityServiceBalanceUpdatedEvent; export type TokenBalancesControllerMessenger = ReturnType< typeof getTokenBalancesControllerMessenger @@ -79,12 +87,15 @@ export function getTokenBalancesControllerMessenger( 'AccountTrackerController:getState', 'AccountTrackerController:updateNativeBalances', 'AccountTrackerController:updateStakedBalances', + 'TokenDetectionController:addDetectedTokensViaWs', ], allowedEvents: [ 'PreferencesController:stateChange', 'TokensController:stateChange', 'NetworkController:stateChange', 'KeyringController:accountRemoved', + 'AccountActivityService:statusChanged', + 'AccountActivityService:balanceUpdated', ], }); } diff --git a/app/scripts/controllers/account-tracker-controller.ts b/app/scripts/controllers/account-tracker-controller.ts index 288121dcbce5..07848381d1b0 100644 --- a/app/scripts/controllers/account-tracker-controller.ts +++ b/app/scripts/controllers/account-tracker-controller.ts @@ -1015,20 +1015,26 @@ export default class AccountTrackerController extends BaseController< ) { this.update((state) => { balances.forEach(({ address, chainId, balance }) => { + // Temporary until moving AccountTrackerController in core with an normalized format + // accountsByChainId works with lowercase addresses, this is just a safe guard in case address is in checksum format + // Convert address to lowercase to match extension's accountsByChainId format + const lowercaseAddress = address.toLowerCase(); + // Ensure the chainId exists in the state if (!state.accountsByChainId[chainId]) { state.accountsByChainId[chainId] = {}; } // Ensure the address exists for this chain - if (!state.accountsByChainId[chainId][address]) { - state.accountsByChainId[chainId][address] = { - address, + if (!state.accountsByChainId[chainId][lowercaseAddress]) { + state.accountsByChainId[chainId][lowercaseAddress] = { + address: lowercaseAddress, balance: '0x0', }; } // Update the balance if (balance) { - state.accountsByChainId[chainId][address].balance = toHex(balance); + state.accountsByChainId[chainId][lowercaseAddress].balance = + toHex(balance); } }); }); diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 56676183a7c1..f4fc65cba4e5 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -326,6 +326,10 @@ import { MultichainRouterInit, ///: END:ONLY_INCLUDE_IF } from './controller-init/snaps'; +import { + BackendWebSocketServiceInit, + AccountActivityServiceInit, +} from './controller-init/core-backend'; import { AuthenticationControllerInit } from './controller-init/identity/authentication-controller-init'; import { UserStorageControllerInit } from './controller-init/identity/user-storage-controller-init'; import { DeFiPositionsControllerInit } from './controller-init/defi-positions/defi-positions-controller-init'; @@ -551,6 +555,8 @@ export default class MetamaskController extends EventEmitter { SnapInsightsController: SnapInsightsControllerInit, SnapInterfaceController: SnapInterfaceControllerInit, WebSocketService: WebSocketServiceInit, + BackendWebSocketService: BackendWebSocketServiceInit, + AccountActivityService: AccountActivityServiceInit, PPOMController: PPOMControllerInit, PhishingController: PhishingControllerInit, OnboardingController: OnboardingControllerInit, @@ -665,6 +671,8 @@ export default class MetamaskController extends EventEmitter { this.swapsController = controllersByName.SwapsController; this.bridgeController = controllersByName.BridgeController; this.bridgeStatusController = controllersByName.BridgeStatusController; + this.backendWebSocketService = controllersByName.BackendWebSocketService; + this.accountActivityService = controllersByName.AccountActivityService; this.nftController = controllersByName.NftController; this.nftDetectionController = controllersByName.NftDetectionController; this.assetsContractController = controllersByName.AssetsContractController; @@ -7860,6 +7868,12 @@ export default class MetamaskController extends EventEmitter { // Notify Snaps that the client is open or closed. this.controllerMessenger.call('SnapController:setClientActive', open); + + if (open) { + this.controllerMessenger.call('BackendWebSocketService:connect'); + } else { + this.controllerMessenger.call('BackendWebSocketService:disconnect'); + } } /* eslint-enable accessor-pairs */ diff --git a/builds.yml b/builds.yml index 17228f8bd230..a26180e81e52 100644 --- a/builds.yml +++ b/builds.yml @@ -242,6 +242,16 @@ env: - STORYBOOK: false - INFURA_STORYBOOK_PROJECT_ID + ### + # Core Backend Services + ### + - MM_BACKEND_WEBSOCKET_URL: wss://gateway.api.cx.metamask.io/v1 + # Backend WebSocket Connection Feature Flag + # Set to "true" to enable, "false" to disable, "null" to use remote feature flags + # Local development: Set in .metamaskrc to override + # Production: Uses remote feature flags when null + - MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED: null + ### # Notifications Feature ### diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 24bd423ad27d..101da0ed84c7 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -899,6 +899,7 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, + "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -967,6 +968,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, @@ -2110,6 +2123,18 @@ "semver": true } }, + "@metamask/bridge-controller>@microsoft/fetch-event-source": { + "globals": { + "AbortController": true, + "TextDecoder": true, + "clearTimeout": true, + "document.addEventListener": true, + "document.hidden": true, + "document.removeEventListener": true, + "fetch": true, + "setTimeout": true + } + }, "@ngraveio/bc-ur": { "packages": { "@ngraveio/bc-ur>@keystonehq/alias-sampling": true, diff --git a/lavamoat/browserify/experimental/policy.json b/lavamoat/browserify/experimental/policy.json index 24bd423ad27d..101da0ed84c7 100644 --- a/lavamoat/browserify/experimental/policy.json +++ b/lavamoat/browserify/experimental/policy.json @@ -899,6 +899,7 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, + "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -967,6 +968,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, @@ -2110,6 +2123,18 @@ "semver": true } }, + "@metamask/bridge-controller>@microsoft/fetch-event-source": { + "globals": { + "AbortController": true, + "TextDecoder": true, + "clearTimeout": true, + "document.addEventListener": true, + "document.hidden": true, + "document.removeEventListener": true, + "fetch": true, + "setTimeout": true + } + }, "@ngraveio/bc-ur": { "packages": { "@ngraveio/bc-ur>@keystonehq/alias-sampling": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 24bd423ad27d..101da0ed84c7 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -899,6 +899,7 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, + "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -967,6 +968,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, @@ -2110,6 +2123,18 @@ "semver": true } }, + "@metamask/bridge-controller>@microsoft/fetch-event-source": { + "globals": { + "AbortController": true, + "TextDecoder": true, + "clearTimeout": true, + "document.addEventListener": true, + "document.hidden": true, + "document.removeEventListener": true, + "fetch": true, + "setTimeout": true + } + }, "@ngraveio/bc-ur": { "packages": { "@ngraveio/bc-ur>@keystonehq/alias-sampling": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 24bd423ad27d..101da0ed84c7 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -899,6 +899,7 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, + "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -967,6 +968,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, @@ -2110,6 +2123,18 @@ "semver": true } }, + "@metamask/bridge-controller>@microsoft/fetch-event-source": { + "globals": { + "AbortController": true, + "TextDecoder": true, + "clearTimeout": true, + "document.addEventListener": true, + "document.hidden": true, + "document.removeEventListener": true, + "fetch": true, + "setTimeout": true + } + }, "@ngraveio/bc-ur": { "packages": { "@ngraveio/bc-ur>@keystonehq/alias-sampling": true, diff --git a/lavamoat/webpack/policy.json b/lavamoat/webpack/policy.json index 6fff940ad9b1..5e822ef3da19 100644 --- a/lavamoat/webpack/policy.json +++ b/lavamoat/webpack/policy.json @@ -935,6 +935,7 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, + "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -1005,6 +1006,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, @@ -2177,6 +2190,18 @@ "semver": true } }, + "@metamask/bridge-controller>@microsoft/fetch-event-source": { + "globals": { + "AbortController": true, + "TextDecoder": true, + "clearTimeout": true, + "document.addEventListener": true, + "document.hidden": true, + "document.removeEventListener": true, + "fetch": true, + "setTimeout": true + } + }, "@ngraveio/bc-ur": { "globals": { "Buffer.alloc": true, diff --git a/package.json b/package.json index a8536d6313e7..d04377d9d74d 100644 --- a/package.json +++ b/package.json @@ -271,15 +271,16 @@ "@metamask/address-book-controller": "^6.1.0", "@metamask/announcement-controller": "^7.0.3", "@metamask/approval-controller": "^7.0.0", - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A79.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-79.0.0-8b55992ea9.patch", + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A81.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-81.0.0-706c32028b.patch", "@metamask/base-controller": "^8.3.0", "@metamask/bitcoin-wallet-snap": "^1.3.0", - "@metamask/bridge-controller": "^50.0.0", - "@metamask/bridge-status-controller": "^50.0.0", + "@metamask/bridge-controller": "^52.0.0", + "@metamask/bridge-status-controller": "^51.0.0", "@metamask/browser-passworder": "^4.3.0", "@metamask/chain-agnostic-permission": "^1.1.0", "@metamask/contract-metadata": "^2.5.0", "@metamask/controller-utils": "^11.14.0", + "@metamask/core-backend": "^2.0.0", "@metamask/delegation-controller": "^0.7.0", "@metamask/delegation-core": "^0.2.0-rc.1", "@metamask/delegation-deployments": "^0.11.0", diff --git a/yarn.lock b/yarn.lock index 683ca5af121f..69223bfbebc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5504,9 +5504,9 @@ __metadata: languageName: node linkType: hard -"@metamask/assets-controllers@npm:79.0.0": - version: 79.0.0 - resolution: "@metamask/assets-controllers@npm:79.0.0" +"@metamask/assets-controllers@npm:81.0.0": + version: 81.0.0 + resolution: "@metamask/assets-controllers@npm:81.0.0" dependencies: "@ethereumjs/util": "npm:^9.1.0" "@ethersproject/abi": "npm:^5.7.0" @@ -5515,13 +5515,13 @@ __metadata: "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" "@metamask/abi-utils": "npm:^2.0.3" - "@metamask/base-controller": "npm:^8.4.0" + "@metamask/base-controller": "npm:^8.4.1" "@metamask/contract-metadata": "npm:^2.4.0" - "@metamask/controller-utils": "npm:^11.14.0" + "@metamask/controller-utils": "npm:^11.14.1" "@metamask/eth-query": "npm:^4.0.0" "@metamask/keyring-api": "npm:^21.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/polling-controller": "npm:^14.0.0" + "@metamask/polling-controller": "npm:^14.0.1" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/snaps-sdk": "npm:^9.0.0" "@metamask/snaps-utils": "npm:^11.0.0" @@ -5541,6 +5541,7 @@ __metadata: "@metamask/account-tree-controller": ^1.0.0 "@metamask/accounts-controller": ^33.0.0 "@metamask/approval-controller": ^7.0.0 + "@metamask/core-backend": ^2.0.0 "@metamask/keyring-controller": ^23.0.0 "@metamask/network-controller": ^24.0.0 "@metamask/permission-controller": ^11.0.0 @@ -5550,13 +5551,13 @@ __metadata: "@metamask/snaps-controllers": ^14.0.0 "@metamask/transaction-controller": ^60.0.0 webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0 - checksum: 10/544257458c48bd99f444a9520a190cd559f2a08b2d9be3e9e48f7b2effd5f3514dac70febfbd009091d2938aec59fe7dfbd658ede1b4708b8c8656fd52e7239f + checksum: 10/62630d6ff66b19c701d90c5b127bf6c24af0825cf4396a4fb92c92e4e5f3b50104fccb6bc2dc7747f6e73605a0b0ec38544b243d28c7afe7b0c848abc2025e7b languageName: node linkType: hard -"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A79.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-79.0.0-8b55992ea9.patch": - version: 79.0.0 - resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A79.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-79.0.0-8b55992ea9.patch::version=79.0.0&hash=5bbfdf" +"@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A81.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-81.0.0-706c32028b.patch": + version: 81.0.0 + resolution: "@metamask/assets-controllers@patch:@metamask/assets-controllers@npm%3A81.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-81.0.0-706c32028b.patch::version=81.0.0&hash=5bbfdf" dependencies: "@ethereumjs/util": "npm:^9.1.0" "@ethersproject/abi": "npm:^5.7.0" @@ -5565,13 +5566,13 @@ __metadata: "@ethersproject/contracts": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.0" "@metamask/abi-utils": "npm:^2.0.3" - "@metamask/base-controller": "npm:^8.4.0" + "@metamask/base-controller": "npm:^8.4.1" "@metamask/contract-metadata": "npm:^2.4.0" - "@metamask/controller-utils": "npm:^11.14.0" + "@metamask/controller-utils": "npm:^11.14.1" "@metamask/eth-query": "npm:^4.0.0" "@metamask/keyring-api": "npm:^21.0.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/polling-controller": "npm:^14.0.0" + "@metamask/polling-controller": "npm:^14.0.1" "@metamask/rpc-errors": "npm:^7.0.2" "@metamask/snaps-sdk": "npm:^9.0.0" "@metamask/snaps-utils": "npm:^11.0.0" @@ -5591,6 +5592,7 @@ __metadata: "@metamask/account-tree-controller": ^1.0.0 "@metamask/accounts-controller": ^33.0.0 "@metamask/approval-controller": ^7.0.0 + "@metamask/core-backend": ^2.0.0 "@metamask/keyring-controller": ^23.0.0 "@metamask/network-controller": ^24.0.0 "@metamask/permission-controller": ^11.0.0 @@ -5600,7 +5602,7 @@ __metadata: "@metamask/snaps-controllers": ^14.0.0 "@metamask/transaction-controller": ^60.0.0 webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0 - checksum: 10/18f72ea2b98fc9440a1b30671961654ed9e15a1b5c2cfcd572db5f1cff76b8a0eec6892d13497c3962cf16c5bba06787f265145280c25ff2f3a6515289a72102 + checksum: 10/1f40cf1278e00be6e97da5f3e3b978c49cb528787dc9ea26a928bb4fc1c32913fb06bfc6f175d6c729ba048d19c728695d3d9595c4fdbc1c00505208214d7dcd languageName: node linkType: hard @@ -5671,9 +5673,9 @@ __metadata: languageName: node linkType: hard -"@metamask/bridge-controller@npm:^50.0.0": - version: 50.0.0 - resolution: "@metamask/bridge-controller@npm:50.0.0" +"@metamask/bridge-controller@npm:^52.0.0": + version: 52.0.0 + resolution: "@metamask/bridge-controller@npm:52.0.0" dependencies: "@ethersproject/address": "npm:^5.7.0" "@ethersproject/bignumber": "npm:^5.7.0" @@ -5688,23 +5690,24 @@ __metadata: "@metamask/multichain-network-controller": "npm:^1.0.1" "@metamask/polling-controller": "npm:^14.0.1" "@metamask/utils": "npm:^11.8.1" + "@microsoft/fetch-event-source": "npm:^2.0.1" bignumber.js: "npm:^9.1.2" reselect: "npm:^5.1.1" uuid: "npm:^8.3.2" peerDependencies: "@metamask/accounts-controller": ^33.0.0 - "@metamask/assets-controllers": ^80.0.0 + "@metamask/assets-controllers": ^81.0.0 "@metamask/network-controller": ^24.0.0 "@metamask/remote-feature-flag-controller": ^1.6.0 "@metamask/snaps-controllers": ^14.0.0 "@metamask/transaction-controller": ^60.0.0 - checksum: 10/3c3bae89d09ec7936a05c13472c68d3be796ef07d3bff597150f94bc9f8be0d3d7d7776145c3d97d5e7fa4e5cf77685c051be52843bae6ae54710f0f793b048f + checksum: 10/c701b697b2ce01ba0415c91c35b85a420c43dcaadcee4588d343ba3e589a636e6fed09a3ccd31c229cdb60a816f14e5d37b98030e500ec0134e8f0665990d728 languageName: node linkType: hard -"@metamask/bridge-status-controller@npm:^50.0.0": - version: 50.0.0 - resolution: "@metamask/bridge-status-controller@npm:50.0.0" +"@metamask/bridge-status-controller@npm:^51.0.0": + version: 51.0.0 + resolution: "@metamask/bridge-status-controller@npm:51.0.0" dependencies: "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" @@ -5715,12 +5718,12 @@ __metadata: uuid: "npm:^8.3.2" peerDependencies: "@metamask/accounts-controller": ^33.0.0 - "@metamask/bridge-controller": ^50.0.0 + "@metamask/bridge-controller": ^52.0.0 "@metamask/gas-fee-controller": ^24.0.0 "@metamask/network-controller": ^24.0.0 "@metamask/snaps-controllers": ^14.0.0 "@metamask/transaction-controller": ^60.0.0 - checksum: 10/ca16e53713a97295f33b86cffc811de22d139faab0132973b9293bd9df84b06175dc49e4512714f8a6db80144313e2429e611d9522d6dcbe3cf2640b7aa238b5 + checksum: 10/189c3ee572f995c09df987e2376f17f9628fa789f066e2ceac758c42c4b3f2e85e07ecd1c35314ba2608259524fb9563fa28ee805e4fee9a59aeaefeb150a8b4 languageName: node linkType: hard @@ -5786,6 +5789,22 @@ __metadata: languageName: node linkType: hard +"@metamask/core-backend@npm:^2.0.0": + version: 2.0.0 + resolution: "@metamask/core-backend@npm:2.0.0" + dependencies: + "@metamask/base-controller": "npm:^8.4.1" + "@metamask/controller-utils": "npm:^11.14.1" + "@metamask/profile-sync-controller": "npm:^25.1.1" + "@metamask/utils": "npm:^11.8.1" + uuid: "npm:^8.3.2" + peerDependencies: + "@metamask/accounts-controller": ^33.1.0 + "@metamask/keyring-controller": ^23.0.0 + checksum: 10/5cade0573623ab003ec409b6aaa1e4046914b121b0a565c50f5040dd831dc282816379639c7d8d9a4ed270821774359eaf83978c05b9cc5e43f2ae295772baa9 + languageName: node + linkType: hard + "@metamask/delegation-abis@npm:^0.9.0": version: 0.9.0 resolution: "@metamask/delegation-abis@npm:0.9.0" @@ -7446,11 +7465,11 @@ __metadata: languageName: node linkType: hard -"@metamask/profile-sync-controller@npm:^25.1.0": - version: 25.1.0 - resolution: "@metamask/profile-sync-controller@npm:25.1.0" +"@metamask/profile-sync-controller@npm:^25.1.0, @metamask/profile-sync-controller@npm:^25.1.1": + version: 25.1.1 + resolution: "@metamask/profile-sync-controller@npm:25.1.1" dependencies: - "@metamask/base-controller": "npm:^8.4.0" + "@metamask/base-controller": "npm:^8.4.1" "@metamask/snaps-sdk": "npm:^9.0.0" "@metamask/snaps-utils": "npm:^11.0.0" "@metamask/utils": "npm:^11.8.1" @@ -7465,7 +7484,7 @@ __metadata: "@metamask/providers": ^22.0.0 "@metamask/snaps-controllers": ^14.0.0 webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0 - checksum: 10/eb39da03198c21a161fa588a7e693592bee72df6f41b4320ec9f95406790ad05ddcd7d6a3fcd589b5f6854b7d351014a6fa39821bfe338dd23d106974bcd9ae7 + checksum: 10/50ec133a53af28c989ce13b93dbdc55f4c1b59dfabe6d113f15214cb2f9b4c8be9e23541d46ff23bcc1aa7c2091d9ac2566cfe19cf664f915912405e0f1d62dd languageName: node linkType: hard @@ -8194,6 +8213,13 @@ __metadata: languageName: node linkType: hard +"@microsoft/fetch-event-source@npm:^2.0.1": + version: 2.0.1 + resolution: "@microsoft/fetch-event-source@npm:2.0.1" + checksum: 10/c147055fafe83801efb9834136ba4b7944406a0bba3517afcea6ceeb56714b5ef78c2e89544bd9c1426ad1d2f964a087e3a719169ae04855836a23fb53269f8d + languageName: node + linkType: hard + "@mobily/ts-belt@npm:^3.13.1": version: 3.13.1 resolution: "@mobily/ts-belt@npm:3.13.1" @@ -31893,17 +31919,18 @@ __metadata: "@metamask/announcement-controller": "npm:^7.0.3" "@metamask/api-specs": "npm:^0.13.0" "@metamask/approval-controller": "npm:^7.0.0" - "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A79.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-79.0.0-8b55992ea9.patch" + "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A81.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-81.0.0-706c32028b.patch" "@metamask/auto-changelog": "npm:^5.1.0" "@metamask/base-controller": "npm:^8.3.0" "@metamask/bitcoin-wallet-snap": "npm:^1.3.0" - "@metamask/bridge-controller": "npm:^50.0.0" - "@metamask/bridge-status-controller": "npm:^50.0.0" + "@metamask/bridge-controller": "npm:^52.0.0" + "@metamask/bridge-status-controller": "npm:^51.0.0" "@metamask/browser-passworder": "npm:^4.3.0" "@metamask/build-utils": "npm:^3.0.0" "@metamask/chain-agnostic-permission": "npm:^1.1.0" "@metamask/contract-metadata": "npm:^2.5.0" "@metamask/controller-utils": "npm:^11.14.0" + "@metamask/core-backend": "npm:^2.0.0" "@metamask/delegation-controller": "npm:^0.7.0" "@metamask/delegation-core": "npm:^0.2.0-rc.1" "@metamask/delegation-deployments": "npm:^0.11.0" From f8266f8f15b9c5c13e153a959a5a33ff35b89e52 Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Wed, 15 Oct 2025 09:46:23 +0200 Subject: [PATCH 2/6] feat(core-backend): revert bridege bump --- .vscode/launch.json | 12 ------- .../controller-init/bridge-controller-init.ts | 14 +++++--- lavamoat/browserify/beta/policy.json | 13 -------- lavamoat/browserify/experimental/policy.json | 13 -------- lavamoat/browserify/flask/policy.json | 13 -------- lavamoat/browserify/main/policy.json | 13 -------- lavamoat/webpack/policy.json | 13 -------- package.json | 4 +-- yarn.lock | 32 +++++++------------ 9 files changed, 23 insertions(+), 104 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 077db9cf4521..bb8b8618b403 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -49,18 +49,6 @@ ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" - }, - { - "name": "Launch Debug MetaMask Chrome", - "request": "launch", - "type": "chrome", - "runtimeArgs": [ - "--load-extension=${workspaceFolder}/dist/chrome", - "--remote-debugging-port=9222" - ], - "url": "chrome-extension://hebhblbkkdabgoldnojllkipeoacjioc/background.html", - "sourceMaps": true, - "webRoot": "${workspaceFolder}" } ], "inputs": [ diff --git a/app/scripts/controller-init/bridge-controller-init.ts b/app/scripts/controller-init/bridge-controller-init.ts index c895dc34cef1..cc44e190a0eb 100644 --- a/app/scripts/controller-init/bridge-controller-init.ts +++ b/app/scripts/controller-init/bridge-controller-init.ts @@ -15,7 +15,7 @@ import { BridgeControllerMessenger, } from './messengers'; -type FetchWithCacheOptions = RequestInit & { +type FetchWithCacheOptions = { cacheOptions?: { cacheRefreshTime: number; }; @@ -49,10 +49,14 @@ export const BridgeControllerInit: ControllerInitFunction< getLayer1GasFee: (...args) => transactionController.getLayer1GasFee(...args), - fetchFn: async (url, init?) => { - const { cacheOptions, functionName, ...requestOptions } = - (init as FetchWithCacheOptions) ?? {}; - + fetchFn: async ( + url, + { + cacheOptions, + functionName, + ...requestOptions + }: FetchWithCacheOptions = {}, + ) => { if (functionName === 'fetchBridgeTokens') { return await fetchWithCache({ url: url.toString(), diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 101da0ed84c7..349e54aac24e 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -899,7 +899,6 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, - "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -2123,18 +2122,6 @@ "semver": true } }, - "@metamask/bridge-controller>@microsoft/fetch-event-source": { - "globals": { - "AbortController": true, - "TextDecoder": true, - "clearTimeout": true, - "document.addEventListener": true, - "document.hidden": true, - "document.removeEventListener": true, - "fetch": true, - "setTimeout": true - } - }, "@ngraveio/bc-ur": { "packages": { "@ngraveio/bc-ur>@keystonehq/alias-sampling": true, diff --git a/lavamoat/browserify/experimental/policy.json b/lavamoat/browserify/experimental/policy.json index 101da0ed84c7..349e54aac24e 100644 --- a/lavamoat/browserify/experimental/policy.json +++ b/lavamoat/browserify/experimental/policy.json @@ -899,7 +899,6 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, - "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -2123,18 +2122,6 @@ "semver": true } }, - "@metamask/bridge-controller>@microsoft/fetch-event-source": { - "globals": { - "AbortController": true, - "TextDecoder": true, - "clearTimeout": true, - "document.addEventListener": true, - "document.hidden": true, - "document.removeEventListener": true, - "fetch": true, - "setTimeout": true - } - }, "@ngraveio/bc-ur": { "packages": { "@ngraveio/bc-ur>@keystonehq/alias-sampling": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 101da0ed84c7..349e54aac24e 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -899,7 +899,6 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, - "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -2123,18 +2122,6 @@ "semver": true } }, - "@metamask/bridge-controller>@microsoft/fetch-event-source": { - "globals": { - "AbortController": true, - "TextDecoder": true, - "clearTimeout": true, - "document.addEventListener": true, - "document.hidden": true, - "document.removeEventListener": true, - "fetch": true, - "setTimeout": true - } - }, "@ngraveio/bc-ur": { "packages": { "@ngraveio/bc-ur>@keystonehq/alias-sampling": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 101da0ed84c7..349e54aac24e 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -899,7 +899,6 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, - "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -2123,18 +2122,6 @@ "semver": true } }, - "@metamask/bridge-controller>@microsoft/fetch-event-source": { - "globals": { - "AbortController": true, - "TextDecoder": true, - "clearTimeout": true, - "document.addEventListener": true, - "document.hidden": true, - "document.removeEventListener": true, - "fetch": true, - "setTimeout": true - } - }, "@ngraveio/bc-ur": { "packages": { "@ngraveio/bc-ur>@keystonehq/alias-sampling": true, diff --git a/lavamoat/webpack/policy.json b/lavamoat/webpack/policy.json index 5e822ef3da19..1112db09a340 100644 --- a/lavamoat/webpack/policy.json +++ b/lavamoat/webpack/policy.json @@ -935,7 +935,6 @@ "@metamask/bridge-controller>@metamask/polling-controller": true, "@metamask/superstruct": true, "@metamask/utils": true, - "@metamask/bridge-controller>@microsoft/fetch-event-source": true, "@metamask/bridge-controller>bignumber.js": true, "lodash": true, "reselect": true, @@ -2190,18 +2189,6 @@ "semver": true } }, - "@metamask/bridge-controller>@microsoft/fetch-event-source": { - "globals": { - "AbortController": true, - "TextDecoder": true, - "clearTimeout": true, - "document.addEventListener": true, - "document.hidden": true, - "document.removeEventListener": true, - "fetch": true, - "setTimeout": true - } - }, "@ngraveio/bc-ur": { "globals": { "Buffer.alloc": true, diff --git a/package.json b/package.json index d04377d9d74d..1211bf8df9b8 100644 --- a/package.json +++ b/package.json @@ -274,8 +274,8 @@ "@metamask/assets-controllers": "patch:@metamask/assets-controllers@npm%3A81.0.0#~/.yarn/patches/@metamask-assets-controllers-npm-81.0.0-706c32028b.patch", "@metamask/base-controller": "^8.3.0", "@metamask/bitcoin-wallet-snap": "^1.3.0", - "@metamask/bridge-controller": "^52.0.0", - "@metamask/bridge-status-controller": "^51.0.0", + "@metamask/bridge-controller": "^50.0.0", + "@metamask/bridge-status-controller": "^50.0.0", "@metamask/browser-passworder": "^4.3.0", "@metamask/chain-agnostic-permission": "^1.1.0", "@metamask/contract-metadata": "^2.5.0", diff --git a/yarn.lock b/yarn.lock index 69223bfbebc9..2e6464f71c33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5673,9 +5673,9 @@ __metadata: languageName: node linkType: hard -"@metamask/bridge-controller@npm:^52.0.0": - version: 52.0.0 - resolution: "@metamask/bridge-controller@npm:52.0.0" +"@metamask/bridge-controller@npm:^50.0.0": + version: 50.0.0 + resolution: "@metamask/bridge-controller@npm:50.0.0" dependencies: "@ethersproject/address": "npm:^5.7.0" "@ethersproject/bignumber": "npm:^5.7.0" @@ -5690,24 +5690,23 @@ __metadata: "@metamask/multichain-network-controller": "npm:^1.0.1" "@metamask/polling-controller": "npm:^14.0.1" "@metamask/utils": "npm:^11.8.1" - "@microsoft/fetch-event-source": "npm:^2.0.1" bignumber.js: "npm:^9.1.2" reselect: "npm:^5.1.1" uuid: "npm:^8.3.2" peerDependencies: "@metamask/accounts-controller": ^33.0.0 - "@metamask/assets-controllers": ^81.0.0 + "@metamask/assets-controllers": ^80.0.0 "@metamask/network-controller": ^24.0.0 "@metamask/remote-feature-flag-controller": ^1.6.0 "@metamask/snaps-controllers": ^14.0.0 "@metamask/transaction-controller": ^60.0.0 - checksum: 10/c701b697b2ce01ba0415c91c35b85a420c43dcaadcee4588d343ba3e589a636e6fed09a3ccd31c229cdb60a816f14e5d37b98030e500ec0134e8f0665990d728 + checksum: 10/3c3bae89d09ec7936a05c13472c68d3be796ef07d3bff597150f94bc9f8be0d3d7d7776145c3d97d5e7fa4e5cf77685c051be52843bae6ae54710f0f793b048f languageName: node linkType: hard -"@metamask/bridge-status-controller@npm:^51.0.0": - version: 51.0.0 - resolution: "@metamask/bridge-status-controller@npm:51.0.0" +"@metamask/bridge-status-controller@npm:^50.0.0": + version: 50.1.0 + resolution: "@metamask/bridge-status-controller@npm:50.1.0" dependencies: "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" @@ -5718,12 +5717,12 @@ __metadata: uuid: "npm:^8.3.2" peerDependencies: "@metamask/accounts-controller": ^33.0.0 - "@metamask/bridge-controller": ^52.0.0 + "@metamask/bridge-controller": ^51.0.0 "@metamask/gas-fee-controller": ^24.0.0 "@metamask/network-controller": ^24.0.0 "@metamask/snaps-controllers": ^14.0.0 "@metamask/transaction-controller": ^60.0.0 - checksum: 10/189c3ee572f995c09df987e2376f17f9628fa789f066e2ceac758c42c4b3f2e85e07ecd1c35314ba2608259524fb9563fa28ee805e4fee9a59aeaefeb150a8b4 + checksum: 10/b50f890b3a4f8214df2a36a41946269f85bbbe3becc986f752fea5babfb023cc5222b8718912f18adab12a028cf5636a482a6c471dfdc83708b66859a86d8515 languageName: node linkType: hard @@ -8213,13 +8212,6 @@ __metadata: languageName: node linkType: hard -"@microsoft/fetch-event-source@npm:^2.0.1": - version: 2.0.1 - resolution: "@microsoft/fetch-event-source@npm:2.0.1" - checksum: 10/c147055fafe83801efb9834136ba4b7944406a0bba3517afcea6ceeb56714b5ef78c2e89544bd9c1426ad1d2f964a087e3a719169ae04855836a23fb53269f8d - languageName: node - linkType: hard - "@mobily/ts-belt@npm:^3.13.1": version: 3.13.1 resolution: "@mobily/ts-belt@npm:3.13.1" @@ -31923,8 +31915,8 @@ __metadata: "@metamask/auto-changelog": "npm:^5.1.0" "@metamask/base-controller": "npm:^8.3.0" "@metamask/bitcoin-wallet-snap": "npm:^1.3.0" - "@metamask/bridge-controller": "npm:^52.0.0" - "@metamask/bridge-status-controller": "npm:^51.0.0" + "@metamask/bridge-controller": "npm:^50.0.0" + "@metamask/bridge-status-controller": "npm:^50.0.0" "@metamask/browser-passworder": "npm:^4.3.0" "@metamask/build-utils": "npm:^3.0.0" "@metamask/chain-agnostic-permission": "npm:^1.1.0" From 36504263687f70a9409448fe3708b785006e846b Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Wed, 15 Oct 2025 19:37:12 +0200 Subject: [PATCH 3/6] fix(core-backend): bump core-backend --- .../core-backend/account-activity-service-init.test.ts | 4 +++- .../core-backend/account-activity-service-init.ts | 3 +++ package.json | 2 +- yarn.lock | 10 +++++----- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts b/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts index 30ac38fc087e..fdef32f41b2d 100644 --- a/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts +++ b/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts @@ -9,6 +9,7 @@ import { import { AccountActivityServiceInit } from './account-activity-service-init'; jest.mock('@metamask/core-backend'); +jest.mock('../../../../shared/lib/trace'); function getInitRequestMock(): jest.Mocked< ControllerInitRequest @@ -34,12 +35,13 @@ describe('AccountActivityServiceInit', () => { expect(controller).toBeInstanceOf(AccountActivityService); }); - it('passes the messenger to the controller', () => { + it('passes the messenger and traceFn to the controller', () => { AccountActivityServiceInit(getInitRequestMock()); const controllerMock = jest.mocked(AccountActivityService); expect(controllerMock).toHaveBeenCalledWith({ messenger: expect.any(Object), + traceFn: expect.any(Function), }); }); diff --git a/app/scripts/controller-init/core-backend/account-activity-service-init.ts b/app/scripts/controller-init/core-backend/account-activity-service-init.ts index 40a94771d3fc..0cdb2d72f20d 100644 --- a/app/scripts/controller-init/core-backend/account-activity-service-init.ts +++ b/app/scripts/controller-init/core-backend/account-activity-service-init.ts @@ -1,4 +1,6 @@ import { AccountActivityService } from '@metamask/core-backend'; +import { TraceCallback } from '@metamask/controller-utils'; +import { trace } from '../../../../shared/lib/trace'; import { ControllerInitFunction } from '../types'; import { AccountActivityServiceMessenger } from '../messengers/core-backend'; @@ -15,6 +17,7 @@ export const AccountActivityServiceInit: ControllerInitFunction< > = ({ controllerMessenger }) => { const controller = new AccountActivityService({ messenger: controllerMessenger, + traceFn: trace as TraceCallback, }); return { diff --git a/package.json b/package.json index 1211bf8df9b8..2197b7230ed4 100644 --- a/package.json +++ b/package.json @@ -280,7 +280,7 @@ "@metamask/chain-agnostic-permission": "^1.1.0", "@metamask/contract-metadata": "^2.5.0", "@metamask/controller-utils": "^11.14.0", - "@metamask/core-backend": "^2.0.0", + "@metamask/core-backend": "^2.1.0", "@metamask/delegation-controller": "^0.7.0", "@metamask/delegation-core": "^0.2.0-rc.1", "@metamask/delegation-deployments": "^0.11.0", diff --git a/yarn.lock b/yarn.lock index 2e6464f71c33..70dc9287e9f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5788,9 +5788,9 @@ __metadata: languageName: node linkType: hard -"@metamask/core-backend@npm:^2.0.0": - version: 2.0.0 - resolution: "@metamask/core-backend@npm:2.0.0" +"@metamask/core-backend@npm:^2.1.0": + version: 2.1.0 + resolution: "@metamask/core-backend@npm:2.1.0" dependencies: "@metamask/base-controller": "npm:^8.4.1" "@metamask/controller-utils": "npm:^11.14.1" @@ -5800,7 +5800,7 @@ __metadata: peerDependencies: "@metamask/accounts-controller": ^33.1.0 "@metamask/keyring-controller": ^23.0.0 - checksum: 10/5cade0573623ab003ec409b6aaa1e4046914b121b0a565c50f5040dd831dc282816379639c7d8d9a4ed270821774359eaf83978c05b9cc5e43f2ae295772baa9 + checksum: 10/7dce39ab1d433013491c36737767edf1ff7e3fdf3793bc8612d47e8d8e4d454e9d921ec5e0cc6d79c809a31d0e0491560d45d245b2c3bd61f139b305a77e63a4 languageName: node linkType: hard @@ -31922,7 +31922,7 @@ __metadata: "@metamask/chain-agnostic-permission": "npm:^1.1.0" "@metamask/contract-metadata": "npm:^2.5.0" "@metamask/controller-utils": "npm:^11.14.0" - "@metamask/core-backend": "npm:^2.0.0" + "@metamask/core-backend": "npm:^2.1.0" "@metamask/delegation-controller": "npm:^0.7.0" "@metamask/delegation-core": "npm:^0.2.0-rc.1" "@metamask/delegation-deployments": "npm:^0.11.0" From df427a0a0f3da90cccfb50a08fbc48feab14c7f8 Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Thu, 16 Oct 2025 10:52:57 +0200 Subject: [PATCH 4/6] remove lavamoat policies for @metamaskbot update-policies --- lavamoat/browserify/beta/policy.json | 12 ------------ lavamoat/browserify/experimental/policy.json | 12 ------------ lavamoat/browserify/flask/policy.json | 12 ------------ lavamoat/browserify/main/policy.json | 12 ------------ lavamoat/webpack/policy.json | 12 ------------ 5 files changed, 60 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 349e54aac24e..24bd423ad27d 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -967,18 +967,6 @@ "lodash": true } }, - "@metamask/core-backend": { - "globals": { - "URL": true, - "WebSocket": true, - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@metamask/utils": true, - "uuid": true - } - }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, diff --git a/lavamoat/browserify/experimental/policy.json b/lavamoat/browserify/experimental/policy.json index 349e54aac24e..24bd423ad27d 100644 --- a/lavamoat/browserify/experimental/policy.json +++ b/lavamoat/browserify/experimental/policy.json @@ -967,18 +967,6 @@ "lodash": true } }, - "@metamask/core-backend": { - "globals": { - "URL": true, - "WebSocket": true, - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@metamask/utils": true, - "uuid": true - } - }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 349e54aac24e..24bd423ad27d 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -967,18 +967,6 @@ "lodash": true } }, - "@metamask/core-backend": { - "globals": { - "URL": true, - "WebSocket": true, - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@metamask/utils": true, - "uuid": true - } - }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 349e54aac24e..24bd423ad27d 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -967,18 +967,6 @@ "lodash": true } }, - "@metamask/core-backend": { - "globals": { - "URL": true, - "WebSocket": true, - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@metamask/utils": true, - "uuid": true - } - }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, diff --git a/lavamoat/webpack/policy.json b/lavamoat/webpack/policy.json index 1112db09a340..6fff940ad9b1 100644 --- a/lavamoat/webpack/policy.json +++ b/lavamoat/webpack/policy.json @@ -1005,18 +1005,6 @@ "lodash": true } }, - "@metamask/core-backend": { - "globals": { - "URL": true, - "WebSocket": true, - "clearTimeout": true, - "setTimeout": true - }, - "packages": { - "@metamask/utils": true, - "uuid": true - } - }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, From 545388b0eff5d79440bfaeb0675bef2ec1ad4c4e Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Thu, 16 Oct 2025 09:13:36 +0000 Subject: [PATCH 5/6] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 12 ++++++++++++ lavamoat/browserify/experimental/policy.json | 12 ++++++++++++ lavamoat/browserify/flask/policy.json | 12 ++++++++++++ lavamoat/browserify/main/policy.json | 12 ++++++++++++ lavamoat/webpack/policy.json | 12 ++++++++++++ 5 files changed, 60 insertions(+) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 24bd423ad27d..349e54aac24e 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -967,6 +967,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, diff --git a/lavamoat/browserify/experimental/policy.json b/lavamoat/browserify/experimental/policy.json index 24bd423ad27d..349e54aac24e 100644 --- a/lavamoat/browserify/experimental/policy.json +++ b/lavamoat/browserify/experimental/policy.json @@ -967,6 +967,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 24bd423ad27d..349e54aac24e 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -967,6 +967,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 24bd423ad27d..349e54aac24e 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -967,6 +967,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, diff --git a/lavamoat/webpack/policy.json b/lavamoat/webpack/policy.json index 6fff940ad9b1..1112db09a340 100644 --- a/lavamoat/webpack/policy.json +++ b/lavamoat/webpack/policy.json @@ -1005,6 +1005,18 @@ "lodash": true } }, + "@metamask/core-backend": { + "globals": { + "URL": true, + "WebSocket": true, + "clearTimeout": true, + "setTimeout": true + }, + "packages": { + "@metamask/utils": true, + "uuid": true + } + }, "@metamask/delegation-controller": { "packages": { "@metamask/base-controller": true, From 8d19837bed0b67ba43ba085c86d6d19706284f98 Mon Sep 17 00:00:00 2001 From: Kriys94 Date: Thu, 16 Oct 2025 20:36:58 +0200 Subject: [PATCH 6/6] fix(core-backend): clean code --- .metamaskrc.dist | 5 - .../account-activity-service-init.test.ts | 10 +- .../account-activity-service-init.ts | 10 +- .../backend-websocket-service-init.test.ts | 160 +----------------- .../backend-websocket-service-init.ts | 15 +- .../account-activity-service-messenger.ts | 33 ++-- .../messengers/core-backend/index.ts | 1 - builds.yml | 5 - 8 files changed, 35 insertions(+), 204 deletions(-) diff --git a/.metamaskrc.dist b/.metamaskrc.dist index 5ab6dfafb9c9..54d4fec455df 100644 --- a/.metamaskrc.dist +++ b/.metamaskrc.dist @@ -10,11 +10,6 @@ SEEDLESS_ONBOARDING_ENABLED='false' ; Set this to `true` to enable Metamask Shield METAMASK_SHIELD_ENABLED='false' -; Backend WebSocket Connection Feature Flag -; Set to 'true' to enable WebSocket connection, 'false' to disable -; Comment out or set to empty to use remote feature flags -;MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED='true' - ; Backend WebSocket URL ; Uses production API endpoint by default (same as builds.yml) ;MM_BACKEND_WEBSOCKET_URL='wss://gateway.api.cx.metamask.io/v1' diff --git a/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts b/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts index fdef32f41b2d..3d0abcd1ab47 100644 --- a/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts +++ b/app/scripts/controller-init/core-backend/account-activity-service-init.test.ts @@ -1,11 +1,11 @@ -import { AccountActivityService } from '@metamask/core-backend'; +import { + AccountActivityService, + AccountActivityServiceMessenger, +} from '@metamask/core-backend'; import { Messenger } from '@metamask/base-controller'; import { ControllerInitRequest } from '../types'; import { buildControllerInitRequestMock } from '../test/utils'; -import { - AccountActivityServiceMessenger, - getAccountActivityServiceMessenger, -} from '../messengers/core-backend'; +import { getAccountActivityServiceMessenger } from '../messengers/core-backend'; import { AccountActivityServiceInit } from './account-activity-service-init'; jest.mock('@metamask/core-backend'); diff --git a/app/scripts/controller-init/core-backend/account-activity-service-init.ts b/app/scripts/controller-init/core-backend/account-activity-service-init.ts index 0cdb2d72f20d..789620637c30 100644 --- a/app/scripts/controller-init/core-backend/account-activity-service-init.ts +++ b/app/scripts/controller-init/core-backend/account-activity-service-init.ts @@ -1,8 +1,9 @@ -import { AccountActivityService } from '@metamask/core-backend'; -import { TraceCallback } from '@metamask/controller-utils'; +import { + AccountActivityService, + AccountActivityServiceMessenger, +} from '@metamask/core-backend'; import { trace } from '../../../../shared/lib/trace'; import { ControllerInitFunction } from '../types'; -import { AccountActivityServiceMessenger } from '../messengers/core-backend'; /** * Initialize the Account Activity service. @@ -17,7 +18,8 @@ export const AccountActivityServiceInit: ControllerInitFunction< > = ({ controllerMessenger }) => { const controller = new AccountActivityService({ messenger: controllerMessenger, - traceFn: trace as TraceCallback, + // @ts-expect-error: Types of `TraceRequest` are not the same. + traceFn: trace, }); return { diff --git a/app/scripts/controller-init/core-backend/backend-websocket-service-init.test.ts b/app/scripts/controller-init/core-backend/backend-websocket-service-init.test.ts index 06de8bfec46d..cb8c10f9a5e2 100644 --- a/app/scripts/controller-init/core-backend/backend-websocket-service-init.test.ts +++ b/app/scripts/controller-init/core-backend/backend-websocket-service-init.test.ts @@ -92,46 +92,7 @@ describe('BackendWebSocketServiceInit', () => { }); describe('isEnabled callback', () => { - it('returns false when feature flag is disabled and no env override', () => { - // Ensure env var is not set - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - - BackendWebSocketServiceInit(getInitRequestMock()); - - const { isEnabled } = jest.mocked(BackendWebSocketService).mock - .calls[0][0]; - - // When env var is undefined, the check envOverride != null is false, - // so it falls through to feature flag logic which returns false (feature flag is disabled in mock) - expect(isEnabled).toBeDefined(); - expect(isEnabled?.()).toBe(false); - - // Restore - if (originalEnv !== undefined) { - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - } - }); - - it('returns true when environment variable is set to true', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = 'true'; - - BackendWebSocketServiceInit(getInitRequestMock()); - - const { isEnabled } = jest.mocked(BackendWebSocketService).mock - .calls[0][0]; - - expect(isEnabled).toBeDefined(); - expect(isEnabled?.()).toBe(true); - - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - }); - - it('returns false when environment variable is set to false', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = 'false'; - + it('returns false when feature flag is disabled', () => { BackendWebSocketServiceInit(getInitRequestMock()); const { isEnabled } = jest.mocked(BackendWebSocketService).mock @@ -139,8 +100,6 @@ describe('BackendWebSocketServiceInit', () => { expect(isEnabled).toBeDefined(); expect(isEnabled?.()).toBe(false); - - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; }); it('returns false when feature flag check fails', () => { @@ -167,48 +126,7 @@ describe('BackendWebSocketServiceInit', () => { expect(isEnabled?.()).toBe(false); }); - it('returns true when environment variable is set to boolean true', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - // @ts-expect-error - Testing with boolean value - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = true; - - BackendWebSocketServiceInit(getInitRequestMock()); - - const constructorArgs = jest.mocked(BackendWebSocketService).mock - .calls[0][0]; - const { isEnabled } = constructorArgs; - - expect(isEnabled).toBeDefined(); - expect(isEnabled?.()).toBe(true); - - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - }); - - it('returns false when environment variable is any other value', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = 'something-else'; - - BackendWebSocketServiceInit(getInitRequestMock()); - - const { isEnabled } = jest.mocked(BackendWebSocketService).mock - .calls[0][0]; - - expect(isEnabled).toBeDefined(); - expect(isEnabled?.()).toBe(false); - - // Cleanup - delete if original was undefined, otherwise restore - if (originalEnv === undefined) { - delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - } else { - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - } - }); - - it('returns true when feature flag is enabled and no env override', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - // Delete env var so it doesn't override the feature flag - delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - + it('returns true when feature flag is enabled', () => { const baseMessenger = new Messenger(); baseMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', @@ -236,55 +154,9 @@ describe('BackendWebSocketServiceInit', () => { expect(isEnabled).toBeDefined(); expect(isEnabled?.()).toBe(true); - - if (originalEnv !== undefined) { - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - } - }); - - it('returns false when feature flag is disabled and no env override', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - // Delete env var so it doesn't override the feature flag - delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - - const baseMessenger = new Messenger(); - baseMessenger.registerActionHandler( - 'RemoteFeatureFlagController:getState', - () => - ({ - remoteFeatureFlags: { - backendWebSocketConnection: { - value: false, - }, - }, - }) as never, - ); - - const requestMock = { - ...buildControllerInitRequestMock(), - controllerMessenger: getBackendWebSocketServiceMessenger(baseMessenger), - initMessenger: getBackendWebSocketServiceInitMessenger(baseMessenger), - }; - - BackendWebSocketServiceInit(requestMock); - - const constructorArgs = jest.mocked(BackendWebSocketService).mock - .calls[0][0]; - const { isEnabled } = constructorArgs; - - expect(isEnabled).toBeDefined(); - expect(isEnabled?.()).toBe(false); - - if (originalEnv !== undefined) { - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - } }); it('returns false when feature flag object does not have value property', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - // Delete env var so it doesn't override the feature flag check - delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - const baseMessenger = new Messenger(); baseMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', @@ -313,17 +185,9 @@ describe('BackendWebSocketServiceInit', () => { expect(isEnabled).toBeDefined(); expect(isEnabled?.()).toBe(false); - - if (originalEnv !== undefined) { - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - } }); it('returns false when feature flag is not an object', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - // Delete env var so it doesn't override the feature flag check - delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - const baseMessenger = new Messenger(); baseMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', @@ -349,17 +213,9 @@ describe('BackendWebSocketServiceInit', () => { expect(isEnabled).toBeDefined(); expect(isEnabled?.()).toBe(false); - - if (originalEnv !== undefined) { - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - } }); it('returns false when remoteFeatureFlags is missing', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - // Delete env var so it doesn't override the feature flag check - delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - const baseMessenger = new Messenger(); baseMessenger.registerActionHandler( 'RemoteFeatureFlagController:getState', @@ -383,17 +239,9 @@ describe('BackendWebSocketServiceInit', () => { expect(isEnabled).toBeDefined(); expect(isEnabled?.()).toBe(false); - - if (originalEnv !== undefined) { - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - } }); it('logs warning when feature flag check fails', () => { - const originalEnv = process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - // Delete env var so it doesn't short-circuit to env override path - delete process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED; - const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); const baseMessenger = new Messenger(); @@ -425,10 +273,6 @@ describe('BackendWebSocketServiceInit', () => { ); consoleWarnSpy.mockRestore(); - - if (originalEnv !== undefined) { - process.env.MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED = originalEnv; - } }); }); }); diff --git a/app/scripts/controller-init/core-backend/backend-websocket-service-init.ts b/app/scripts/controller-init/core-backend/backend-websocket-service-init.ts index 484962be7d35..5b775cb32c62 100644 --- a/app/scripts/controller-init/core-backend/backend-websocket-service-init.ts +++ b/app/scripts/controller-init/core-backend/backend-websocket-service-init.ts @@ -1,5 +1,4 @@ import { BackendWebSocketService } from '@metamask/core-backend'; -import type { TraceCallback } from '@metamask/controller-utils'; import { ControllerInitFunction } from '../types'; import { BackendWebSocketServiceMessenger, @@ -39,22 +38,12 @@ export const BackendWebSocketServiceInit: ControllerInitFunction< maxReconnectDelay: 30000, // Allow longer delays for backend stability requestTimeout: 20000, // Reasonable timeout for backend requests // Inject the Sentry-backed trace function from extension platform - traceFn: trace as TraceCallback, + // @ts-expect-error: Types of `TraceRequest` are not the same. + traceFn: trace, // Feature flag AND app lifecycle integration // Service will check this callback before connecting/reconnecting isEnabled: () => { try { - // Check for local environment variable override first (for development) - const envOverride = process.env - .MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED as - | string - | boolean - | null - | undefined; - if (envOverride !== null && envOverride !== undefined) { - return envOverride === true || envOverride === 'true'; - } - const remoteFeatureFlagState = initMessenger?.call( 'RemoteFeatureFlagController:getState', ); diff --git a/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.ts b/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.ts index ee61d11a58d2..badcb7380d5f 100644 --- a/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.ts +++ b/app/scripts/controller-init/messengers/core-backend/account-activity-service-messenger.ts @@ -1,11 +1,5 @@ -import { - type AccountActivityServiceMessenger as BackendPlatformAccountActivityServiceMessenger, - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS, - ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS, -} from '@metamask/core-backend'; - -export type AccountActivityServiceMessenger = - BackendPlatformAccountActivityServiceMessenger; +import { AccountActivityServiceMessenger } from '@metamask/core-backend'; +import { BaseControllerMessenger } from '../../types'; /** * Get a restricted messenger for the Account Activity service. This is scoped to the @@ -15,12 +9,25 @@ export type AccountActivityServiceMessenger = * @returns The restricted messenger. */ export function getAccountActivityServiceMessenger( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - messenger: any, // Using any to avoid type conflicts with the main messenger -): BackendPlatformAccountActivityServiceMessenger { + messenger: BaseControllerMessenger, +): AccountActivityServiceMessenger { return messenger.getRestricted({ name: 'AccountActivityService', - allowedActions: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_ACTIONS], - allowedEvents: [...ACCOUNT_ACTIVITY_SERVICE_ALLOWED_EVENTS], + allowedActions: [ + 'AccountsController:getSelectedAccount', + 'BackendWebSocketService:connect', + 'BackendWebSocketService:disconnect', + 'BackendWebSocketService:subscribe', + 'BackendWebSocketService:getConnectionInfo', + 'BackendWebSocketService:channelHasSubscription', + 'BackendWebSocketService:getSubscriptionsByChannel', + 'BackendWebSocketService:findSubscriptionsByChannelPrefix', + 'BackendWebSocketService:addChannelCallback', + 'BackendWebSocketService:removeChannelCallback', + ], + allowedEvents: [ + 'AccountsController:selectedAccountChange', + 'BackendWebSocketService:connectionStateChanged', + ], }); } diff --git a/app/scripts/controller-init/messengers/core-backend/index.ts b/app/scripts/controller-init/messengers/core-backend/index.ts index c624fd2c75e2..125ea53c8131 100644 --- a/app/scripts/controller-init/messengers/core-backend/index.ts +++ b/app/scripts/controller-init/messengers/core-backend/index.ts @@ -7,4 +7,3 @@ export type { BackendWebSocketServiceInitMessenger, } from './backend-websocket-service-messenger'; export { getAccountActivityServiceMessenger } from './account-activity-service-messenger'; -export type { AccountActivityServiceMessenger } from './account-activity-service-messenger'; diff --git a/builds.yml b/builds.yml index a26180e81e52..4201b70aeb27 100644 --- a/builds.yml +++ b/builds.yml @@ -246,11 +246,6 @@ env: # Core Backend Services ### - MM_BACKEND_WEBSOCKET_URL: wss://gateway.api.cx.metamask.io/v1 - # Backend WebSocket Connection Feature Flag - # Set to "true" to enable, "false" to disable, "null" to use remote feature flags - # Local development: Set in .metamaskrc to override - # Production: Uses remote feature flags when null - - MM_BACKEND_WEBSOCKET_CONNECTION_ENABLED: null ### # Notifications Feature