Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 15 additions & 57 deletions app/scripts/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import ExtensionStore from './lib/stores/extension-store';
import ReadOnlyNetworkStore from './lib/stores/read-only-network-store';
import migrations from './migrations';
import Migrator from './lib/migrator';
import { updateRemoteFeatureFlags } from './lib/update-remote-feature-flags';
import ExtensionPlatform from './platforms/extension';
import { SENTRY_BACKGROUND_STATE } from './constants/sentry-state';

Expand All @@ -78,6 +79,7 @@ import { getPlatform, shouldEmitDappViewedEvent } from './lib/util';
import { createOffscreen } from './offscreen';
import { setupMultiplex } from './lib/stream-utils';
import rawFirstTimeState from './first-time-state';
import { onUpdate } from './on-update';

/* eslint-enable import/first */

Expand Down Expand Up @@ -118,6 +120,9 @@ const localStore = useReadOnlyNetworkStore
? new ReadOnlyNetworkStore()
: new ExtensionStore();
const persistenceManager = new PersistenceManager({ localStore });

const { update, requestSafeReload } = getRequestSafeReload(persistenceManager);

// Setup global hook for improved Sentry state snapshots during initialization
global.stateHooks.getMostRecentPersistedState = () =>
persistenceManager.mostRecentRetrievedState;
Expand Down Expand Up @@ -721,9 +726,6 @@ async function initialize(backup) {
const cronjobControllerStorageManager = new CronjobControllerStorageManager();
await cronjobControllerStorageManager.init();

const { update, requestSafeReload } =
getRequestSafeReload(persistenceManager);

setupController(
initState,
initLangCode,
Expand All @@ -732,7 +734,6 @@ async function initialize(backup) {
initData.meta,
offscreenPromise,
preinstalledSnaps,
requestSafeReload,
cronjobControllerStorageManager,
);

Expand Down Expand Up @@ -1102,7 +1103,6 @@ function trackAppOpened(environment) {
* @param {object} stateMetadata - Metadata about the initial state and migrations, including the most recent migration version
* @param {Promise<void>} offscreenPromise - A promise that resolves when the offscreen document has finished initialization.
* @param {Array} preinstalledSnaps - A list of preinstalled Snaps loaded from disk during boot.
* @param {() => Promise<void>)} requestSafeReload - A function that requests a safe reload of the extension.
* @param {CronjobControllerStorageManager} cronjobControllerStorageManager - A storage manager for the CronjobController.
*/
export function setupController(
Expand All @@ -1113,7 +1113,6 @@ export function setupController(
stateMetadata,
offscreenPromise,
preinstalledSnaps,
requestSafeReload,
cronjobControllerStorageManager,
) {
//
Expand Down Expand Up @@ -1241,7 +1240,8 @@ export function setupController(
controller.setupTrustedCommunication(portStream, remotePort.sender);
trackAppOpened(processName);

initializeRemoteFeatureFlags();
// lazily update the remote feature flags every time the UI is opened.
updateRemoteFeatureFlags(controller);

if (processName === ENVIRONMENT_TYPE_POPUP) {
openPopupCount += 1;
Expand Down Expand Up @@ -1480,22 +1480,6 @@ export function setupController(
}
}

/**
* Initializes remote feature flags by making a request to fetch them from the clientConfigApi.
* This function is called when MM is during internal process.
* If the request fails, the error will be logged but won't interrupt extension initialization.
*
* @returns {Promise<void>} A promise that resolves when the remote feature flags have been updated.
*/
async function initializeRemoteFeatureFlags() {
try {
// initialize the request to fetch remote feature flags
await controller.remoteFeatureFlagController.updateRemoteFeatureFlags();
} catch (error) {
log.error('Error initializing remote feature flags:', error);
}
}

function getPendingApprovalCount() {
try {
const pendingApprovalCount =
Expand Down Expand Up @@ -1650,15 +1634,16 @@ const addAppInstalledEvent = () => {
*
* @param {[chrome.runtime.InstalledDetails]} params - Array containing a single installation details object.
*/
function handleOnInstalled([details]) {
async function handleOnInstalled([details]) {
if (details.reason === 'install') {
onInstall();
} else if (
details.reason === 'update' &&
details.previousVersion &&
details.previousVersion !== platform.getVersion()
) {
onUpdate(details.previousVersion);
} else if (details.reason === 'update') {
const { previousVersion } = details;
if (!previousVersion || previousVersion === platform.getVersion()) {
return;
}
await isInitialized;
onUpdate(controller, platform, previousVersion, requestSafeReload);
}
}

Expand Down Expand Up @@ -1694,33 +1679,6 @@ if (
});
}

// // On first install, open a new tab with MetaMask
// async function onInstall() {
// const storeAlreadyExisted = Boolean(await localStore.get());
// // If the store doesn't exist, then this is the first time running this script,
// // and is therefore an install
// if (process.env.IN_TEST) {
// addAppInstalledEvent();
// } else if (!storeAlreadyExisted && !process.env.METAMASK_DEBUG) {
// addAppInstalledEvent();
// platform.openExtensionInBrowser();
// }
// }

/**
* Trigger actions that should happen only upon update installation
*
* @param previousVersion
*/
async function onUpdate(previousVersion) {
await isInitialized;
log.debug('Update installation detected');
controller.appStateController.setLastUpdatedAt(Date.now());
if (previousVersion) {
controller.appStateController.setLastUpdatedFromVersion(previousVersion);
}
}

/**
* Trigger actions that should happen only when an update is available
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import {
NetworkControllerRpcEndpointDegradedEvent,
NetworkControllerRpcEndpointUnavailableEvent,
} from '@metamask/network-controller';
import { RemoteFeatureFlagControllerState } from '@metamask/remote-feature-flag-controller';
import {
RemoteFeatureFlagControllerGetStateAction,
RemoteFeatureFlagControllerState,
} from '@metamask/remote-feature-flag-controller';
import {
MetaMetricsControllerGetMetaMetricsIdAction,
MetaMetricsControllerTrackEventAction,
Expand Down Expand Up @@ -45,7 +48,8 @@ export function getNetworkControllerMessenger(

type AllowedInitializationActions =
| MetaMetricsControllerGetMetaMetricsIdAction
| MetaMetricsControllerTrackEventAction;
| MetaMetricsControllerTrackEventAction
| RemoteFeatureFlagControllerGetStateAction;

type AllowedInitializationEvents =
| NetworkControllerRpcEndpointUnavailableEvent
Expand Down Expand Up @@ -86,6 +90,7 @@ export function getNetworkControllerInitMessenger(
actions: [
'MetaMetricsController:getMetaMetricsId',
'MetaMetricsController:trackEvent',
'RemoteFeatureFlagController:getState',
],
events: [
'NetworkController:rpcEndpointUnavailable',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
PreferencesControllerStateChangeEvent,
} from '../../controllers/preferences-controller';
import { RootMessenger } from '../../lib/messenger';
import type {
OnboardingControllerGetStateAction,
OnboardingControllerStateChangeEvent,
} from '../../controllers/onboarding';

export type RemoteFeatureFlagControllerMessenger = ReturnType<
typeof getRemoteFeatureFlagControllerMessenger
Expand Down Expand Up @@ -33,9 +37,12 @@ export function getRemoteFeatureFlagControllerMessenger(

type AllowedInitializationActions =
| MetaMetricsControllerGetMetaMetricsIdAction
| PreferencesControllerGetStateAction;
| PreferencesControllerGetStateAction
| OnboardingControllerGetStateAction;

type AllowedInitializationEvents = PreferencesControllerStateChangeEvent;
type AllowedInitializationEvents =
| PreferencesControllerStateChangeEvent
| OnboardingControllerStateChangeEvent;

export type RemoteFeatureFlagControllerInitMessenger = ReturnType<
typeof getRemoteFeatureFlagControllerInitMessenger
Expand Down Expand Up @@ -68,8 +75,12 @@ export function getRemoteFeatureFlagControllerInitMessenger(
actions: [
'MetaMetricsController:getMetaMetricsId',
'PreferencesController:getState',
'OnboardingController:getState',
],
events: [
'PreferencesController:stateChange',
'OnboardingController:stateChange',
],
events: ['PreferencesController:stateChange'],
});
return controllerInitMessenger;
}
34 changes: 27 additions & 7 deletions app/scripts/controller-init/network-controller-init.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { ControllerStateChangeEvent } from '@metamask/base-controller';
import {
ActionConstraint,
MOCK_ANY_NAMESPACE,
Messenger,
MockAnyNamespace,
} from '@metamask/messenger';
import { NetworkController } from '@metamask/network-controller';
import { RemoteFeatureFlagControllerState } from '@metamask/remote-feature-flag-controller';
import { getRootMessenger } from '../lib/messenger';
import {
RemoteFeatureFlagControllerGetStateAction,
RemoteFeatureFlagControllerState,
} from '@metamask/remote-feature-flag-controller';
import { ControllerInitRequest } from './types';
import { buildControllerInitRequestMock } from './test/utils';
import {
Expand Down Expand Up @@ -37,17 +40,33 @@ jest.mock('@metamask/network-controller', () => {
});

function getInitRequestMock(
baseMessenger = getRootMessenger<never, never>(),
messenger = new Messenger<
MockAnyNamespace,
RemoteFeatureFlagControllerGetStateAction | ActionConstraint,
ControllerStateChangeEvent<
'RemoteFeatureFlagController',
RemoteFeatureFlagControllerState
>
>({ namespace: MOCK_ANY_NAMESPACE }),
): jest.Mocked<
ControllerInitRequest<
NetworkControllerMessenger,
NetworkControllerInitMessenger
>
> {
messenger.registerActionHandler(
'RemoteFeatureFlagController:getState',
jest.fn().mockReturnValue({
remoteFeatureFlags: {
walletFrameworkRpcFailoverEnabled: true,
},
}),
);

const requestMock = {
...buildControllerInitRequestMock(),
controllerMessenger: getNetworkControllerMessenger(baseMessenger),
initMessenger: getNetworkControllerInitMessenger(baseMessenger),
controllerMessenger: getNetworkControllerMessenger(messenger),
initMessenger: getNetworkControllerInitMessenger(messenger),
};

return requestMock;
Expand Down Expand Up @@ -78,6 +97,7 @@ describe('NetworkControllerInit', () => {
getBlockTrackerOptions: expect.any(Function),
getRpcServiceOptions: expect.any(Function),
infuraProjectId: undefined,
isRpcFailoverEnabled: true,
});
});

Expand Down Expand Up @@ -330,7 +350,7 @@ describe('NetworkControllerInit', () => {
it('enables RPC failover when the `walletFrameworkRpcFailoverEnabled` feature flag is enabled', () => {
const messenger = new Messenger<
MockAnyNamespace,
never,
RemoteFeatureFlagControllerGetStateAction,
ControllerStateChangeEvent<
'RemoteFeatureFlagController',
RemoteFeatureFlagControllerState
Expand Down Expand Up @@ -359,7 +379,7 @@ describe('NetworkControllerInit', () => {
it('disables RPC failover when the `walletFrameworkRpcFailoverEnabled` feature flag is disabled', () => {
const messenger = new Messenger<
MockAnyNamespace,
never,
RemoteFeatureFlagControllerGetStateAction,
ControllerStateChangeEvent<
'RemoteFeatureFlagController',
RemoteFeatureFlagControllerState
Expand Down
22 changes: 21 additions & 1 deletion app/scripts/controller-init/network-controller-init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ChainId,
} from '@metamask/controller-utils';
import { hasProperty } from '@metamask/utils';
import { RemoteFeatureFlagControllerState } from '@metamask/remote-feature-flag-controller';
import { SECOND } from '../../../shared/constants/time';
import { getIsQuicknodeEndpointUrl } from '../../../shared/lib/network-utils';
import {
Expand Down Expand Up @@ -167,8 +168,24 @@ export const NetworkControllerInit: ControllerInitFunction<
initMessenger,
persistedState,
}) => {
const remoteFeatureFlagsControllerState = initMessenger.call(
'RemoteFeatureFlagController:getState',
);
const initialState = getInitialState(persistedState.NetworkController);

/**
* Determines if RPC failover is enabled based on RemoteFeatureFlagController
* state.
*
* @param state - RemoteFeatureFlagControllerState
* @returns true if RPC failover is enabled, false otherwise
*/
const getIsRpcFailoverEnabled = (state: RemoteFeatureFlagControllerState) => {
const walletFrameworkRpcFailoverEnabled = state.remoteFeatureFlags
.walletFrameworkRpcFailoverEnabled as boolean | undefined;
return walletFrameworkRpcFailoverEnabled ?? false;
};

const getBlockTrackerOptions = () => {
return process.env.IN_TEST
? {}
Expand Down Expand Up @@ -233,6 +250,9 @@ export const NetworkControllerInit: ControllerInitFunction<
getBlockTrackerOptions,
getRpcServiceOptions,
additionalDefaultNetworks: ADDITIONAL_DEFAULT_NETWORKS,
isRpcFailoverEnabled: getIsRpcFailoverEnabled(
remoteFeatureFlagsControllerState,
),
});

initMessenger.subscribe(
Expand Down Expand Up @@ -284,7 +304,7 @@ export const NetworkControllerInit: ControllerInitFunction<
controller.disableRpcFailover();
}
},
(state) => state.remoteFeatureFlags.walletFrameworkRpcFailoverEnabled,
getIsRpcFailoverEnabled,
);

// Delay lookupNetwork until after onboarding to prevent network requests before the user can
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@metamask/messenger';
import { ENVIRONMENT } from '../../../development/build/constants';
import { PreferencesControllerGetStateAction } from '../controllers/preferences-controller';
import { OnboardingControllerGetStateAction } from '../controllers/onboarding';
import {
getConfigForRemoteFeatureFlagRequest,
RemoteFeatureFlagControllerInit,
Expand All @@ -33,7 +34,9 @@ function getInitRequestMock(): jest.Mocked<
> {
const baseMessenger = new Messenger<
MockAnyNamespace,
PreferencesControllerGetStateAction | ActionConstraint,
| PreferencesControllerGetStateAction
| OnboardingControllerGetStateAction
| ActionConstraint,
never
>({
namespace: MOCK_ANY_NAMESPACE,
Expand All @@ -45,6 +48,12 @@ function getInitRequestMock(): jest.Mocked<
useExternalServices: true,
}),
);
baseMessenger.registerActionHandler(
'OnboardingController:getState',
jest.fn().mockReturnValue({
completedOnboarding: true,
}),
);

const requestMock = {
...buildControllerInitRequestMock(),
Expand Down
Loading
Loading