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
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export const RemoteFeatureFlagControllerInit: ControllerInitFunction<
const onboardingState = initMessenger.call('OnboardingController:getState');
const preferencesState = initMessenger.call('PreferencesController:getState');
const { distribution, environment } = getConfigForRemoteFeatureFlagRequest();
const prevClientVersion =
persistedState?.AppMetadataController?.currentAppVersion;

let canUseExternalServices = preferencesState.useExternalServices === true;
let hasCompletedOnboarding = onboardingState.completedOnboarding === true;
Expand All @@ -91,6 +93,7 @@ export const RemoteFeatureFlagControllerInit: ControllerInitFunction<
getMetaMetricsId: () =>
initMessenger.call('MetaMetricsController:getMetaMetricsId'),
clientVersion: getBaseSemVerVersion(),
prevClientVersion,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New change from 4.1.0. See preview extension PR

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit test missing assertion for new prevClientVersion parameter

Medium Severity

The init function now extracts prevClientVersion from persistedState?.AppMetadataController?.currentAppVersion and passes it to the RemoteFeatureFlagController constructor, but the test named "passes the proper arguments to the controller" was not updated to assert prevClientVersion. Since the mock's persistedState is {}, prevClientVersion is undefined, and toHaveBeenCalledWith silently ignores undefined properties — so the test passes without verifying the new parameter. A regression removing prevClientVersion would go undetected.

Fix in Cursor Fix in Web

Triggered by project rule: MetaMask Extension - Cursor Rules

clientConfigApiService: new ClientConfigApiService({
fetch: globalThis.fetch.bind(globalThis),
config: {
Expand Down
8 changes: 8 additions & 0 deletions app/scripts/metamask-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3510,6 +3510,8 @@ describe('MetaMaskController', () => {
expect(preferencesController.state.useExternalServices).toBe(false);
expect(remoteFeatureFlagController.state).toStrictEqual({
remoteFeatureFlags: {},
localOverrides: {},
rawRemoteFeatureFlags: {},
Comment on lines +3513 to +3514
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RemoteFeatureFlagController 4.0.0 has added these additional state properties.

cacheTimestamp: 0,
});
});
Expand All @@ -3529,6 +3531,8 @@ describe('MetaMaskController', () => {

expect(remoteFeatureFlagController.state).toStrictEqual({
remoteFeatureFlags: {},
localOverrides: {},
rawRemoteFeatureFlags: {},
cacheTimestamp: 0,
});
});
Expand All @@ -3553,6 +3557,8 @@ describe('MetaMaskController', () => {
// Verify the controller state remains unchanged after error
expect(remoteFeatureFlagController.state).toStrictEqual({
remoteFeatureFlags: {},
localOverrides: {},
rawRemoteFeatureFlags: {},
cacheTimestamp: 0,
});
});
Expand All @@ -3578,6 +3584,8 @@ describe('MetaMaskController', () => {
// Verify state is cleared
expect(remoteFeatureFlagController.state).toStrictEqual({
remoteFeatureFlags: {},
localOverrides: {},
rawRemoteFeatureFlags: {},
cacheTimestamp: 0,
});
});
Expand Down
3 changes: 3 additions & 0 deletions lavamoat/browserify/beta/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,9 @@
}
},
"@metamask/remote-feature-flag-controller": {
"globals": {
"TextEncoder": true
},
Comment on lines +1977 to +1979
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TextEncoded is now being used in this package version (code)

"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
Expand Down
3 changes: 3 additions & 0 deletions lavamoat/browserify/experimental/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,9 @@
}
},
"@metamask/remote-feature-flag-controller": {
"globals": {
"TextEncoder": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
Expand Down
3 changes: 3 additions & 0 deletions lavamoat/browserify/flask/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,9 @@
}
},
"@metamask/remote-feature-flag-controller": {
"globals": {
"TextEncoder": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
Expand Down
3 changes: 3 additions & 0 deletions lavamoat/browserify/main/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,9 @@
}
},
"@metamask/remote-feature-flag-controller": {
"globals": {
"TextEncoder": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
Expand Down
3 changes: 3 additions & 0 deletions lavamoat/webpack/mv2/beta/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,9 @@
}
},
"@metamask/remote-feature-flag-controller": {
"globals": {
"TextEncoder": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
Expand Down
3 changes: 3 additions & 0 deletions lavamoat/webpack/mv2/experimental/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,9 @@
}
},
"@metamask/remote-feature-flag-controller": {
"globals": {
"TextEncoder": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
Expand Down
3 changes: 3 additions & 0 deletions lavamoat/webpack/mv2/flask/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,9 @@
}
},
"@metamask/remote-feature-flag-controller": {
"globals": {
"TextEncoder": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
Expand Down
3 changes: 3 additions & 0 deletions lavamoat/webpack/mv2/main/policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,9 @@
}
},
"@metamask/remote-feature-flag-controller": {
"globals": {
"TextEncoder": true
},
"packages": {
"@metamask/base-controller": true,
"@metamask/controller-utils": true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@
"@metamask/profile-sync-controller": "^27.1.0",
"@metamask/providers": "^22.1.1",
"@metamask/rate-limit-controller": "^7.0.0",
"@metamask/remote-feature-flag-controller": "^3.0.0",
"@metamask/remote-feature-flag-controller": "^4.1.0",
"@metamask/rpc-errors": "^7.0.0",
"@metamask/safe-event-emitter": "^3.1.1",
"@metamask/scure-bip39": "^2.0.3",
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ export const MOCK_REMOTE_FEATURE_FLAGS_RESPONSE = {
feature1: true,
feature2: false,
feature3: {
name: 'groupC',
value: 'valueC',
name: 'groupA',
value: 'valueA',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Override test ineffective due to identical mock values

Low Severity

MOCK_REMOTE_FEATURE_FLAGS_RESPONSE.feature3 changed from groupC/valueC to groupA/valueA, which is now identical to MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS.feature3. The E2E override test in remote-feature-flag.spec.ts validates {...MOCK_REMOTE_FEATURE_FLAGS_RESPONSE, ...MOCK_CUSTOMIZED_REMOTE_FEATURE_FLAGS}, which now produces the same result as the response alone. This makes the manifest-override test a no-op — it passes regardless of whether the override mechanism actually works. The customized mock needs a distinct value (e.g., groupB/valueB) to meaningfully test override behavior.

Fix in Cursor Fix in Web

Comment on lines +171 to +172
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need some confirmation here.
RemoteFeatureFlagController 4.0.0 changes the threshold implementation/logic, hence the changes were needed to this response constant.

},
};

Expand Down
7 changes: 4 additions & 3 deletions test/e2e/fixtures/default-fixture.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@
"lastUpdatedAt": null,
"lastUpdatedFromVersion": null,
"lastViewedUserSurvey": null,
"musdConversionDismissedCtaKeys": [],
"musdConversionEducationSeen": false,
"newPrivacyPolicyToastClickedOrClosed": null,
"nftsDetectionNoticeDismissed": false,
"outdatedBrowserWarningLastShown": null,
Expand All @@ -211,9 +213,7 @@
"termsOfUseLastAgreed": 1770282497124,
"timeoutMinutes": 0,
"trezorModel": null,
"updateModalLastDismissedAt": null,
"musdConversionEducationSeen": false,
"musdConversionDismissedCtaKeys": []
"updateModalLastDismissedAt": null
},
"AuthenticationController": {},
"BridgeStatusController": {
Expand Down Expand Up @@ -982,6 +982,7 @@
},
"RemoteFeatureFlagController": {
"cacheTimestamp": 1770282502510,
"localOverrides": {},
"remoteFeatureFlags": {
"feature1": true,
"feature2": false,
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/fixtures/fixture-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ const getFixtureIgnoredKeys = (): string[] => [
'data.ProfileMetricsController.initialDelayEndTimestamp',
'data.RemoteFeatureFlagController.cacheTimestamp',
'data.RemoteFeatureFlagController.remoteFeatureFlags',
'data.RemoteFeatureFlagController.thresholdCache',
'data.RemoteFeatureFlagController.rawRemoteFeatureFlags',
Comment on lines +72 to +73
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excluding:

  1. thresholdCache uses the randomly generated address per run.
  2. rawRemoteFeatureFlags seems to be the actual raw feature flag results from our server, any changes in LD would invalidate this.

// Entire objects/controllers ignored (dynamic or impractical to validate)
'data.AccountsController.internalAccounts.accounts',
'data.AuthenticationController',
Expand Down
8 changes: 5 additions & 3 deletions test/e2e/fixtures/onboarding-fixture.json
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was modified by @metamaskbot to update fixtures.

Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
"lastUpdatedAt": null,
"lastUpdatedFromVersion": null,
"lastViewedUserSurvey": null,
"musdConversionDismissedCtaKeys": [],
"musdConversionEducationSeen": false,
"newPrivacyPolicyToastClickedOrClosed": null,
"newPrivacyPolicyToastShownDate": 1764061390419,
"nftsDetectionNoticeDismissed": false,
Expand All @@ -83,9 +85,7 @@
"surveyLinkLastClickedOrClosed": null,
"timeoutMinutes": 0,
"trezorModel": null,
"updateModalLastDismissedAt": null,
"musdConversionEducationSeen": false,
"musdConversionDismissedCtaKeys": []
"updateModalLastDismissedAt": null
},
"AuthenticationController": {
"isSignedIn": false
Expand Down Expand Up @@ -1969,6 +1969,8 @@
},
"RemoteFeatureFlagController": {
"cacheTimestamp": 0,
"localOverrides": {},
"rawRemoteFeatureFlags": {},
"remoteFeatureFlags": {}
},
"RewardsController": {
Expand Down
14 changes: 7 additions & 7 deletions test/e2e/tests/metrics/dapp-viewed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { loginWithBalanceValidation } from '../../page-objects/flows/login.flow'
import { connectAccountToTestDapp } from '../../page-objects/flows/test-dapp.flow';
import { Driver } from '../../webdriver/driver';

// Solana + EVM accounts (using the current E2E fixtures setup).
const ACCOUNTS_IN_MULTICHAIN_ACCOUNTS = 2;
// E2E Fixtures setup has 4 identities (1 EVM, 1 Solana, 1 Bitcoin, 1 Tron)
const METAMASK_IDENTITIES = 4;
Comment on lines +13 to +14
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This baffled me.... Looking at the underlying logic and the fixtures, the test points to the number of identities, not accounts.

I'm not entirely sure why now this e2e test is failing though.


async function waitForAccountsToBeAligned(driver: Driver) {
// Multichain accounts create non-EVM accounts asynchronously, thus we need to
Expand Down Expand Up @@ -152,7 +152,7 @@ describe('Dapp viewed Event', function () {
assert.equal(dappViewedEventProperties.is_first_visit, true);
assert.equal(
dappViewedEventProperties.number_of_accounts,
ACCOUNTS_IN_MULTICHAIN_ACCOUNTS,
METAMASK_IDENTITIES,
);
assert.equal(dappViewedEventProperties.number_of_accounts_connected, 1);
},
Expand Down Expand Up @@ -198,7 +198,7 @@ describe('Dapp viewed Event', function () {
assert.equal(dappViewedEventProperties.is_first_visit, false);
assert.equal(
dappViewedEventProperties.number_of_accounts,
ACCOUNTS_IN_MULTICHAIN_ACCOUNTS,
METAMASK_IDENTITIES,
);
assert.equal(dappViewedEventProperties.number_of_accounts_connected, 1);
},
Expand Down Expand Up @@ -245,7 +245,7 @@ describe('Dapp viewed Event', function () {
assert.equal(dappViewedEventProperties.is_first_visit, false);
assert.equal(
dappViewedEventProperties.number_of_accounts,
ACCOUNTS_IN_MULTICHAIN_ACCOUNTS,
METAMASK_IDENTITIES,
);
assert.equal(dappViewedEventProperties.number_of_accounts_connected, 1);
},
Expand Down Expand Up @@ -294,7 +294,7 @@ describe('Dapp viewed Event', function () {
assert.equal(dappViewedEventProperties.is_first_visit, false);
assert.equal(
dappViewedEventProperties.number_of_accounts,
ACCOUNTS_IN_MULTICHAIN_ACCOUNTS,
METAMASK_IDENTITIES,
);
assert.equal(dappViewedEventProperties.number_of_accounts_connected, 1);
},
Expand Down Expand Up @@ -346,7 +346,7 @@ describe('Dapp viewed Event', function () {
assert.equal(dappViewedEventProperties.is_first_visit, false);
assert.equal(
dappViewedEventProperties.number_of_accounts,
ACCOUNTS_IN_MULTICHAIN_ACCOUNTS,
METAMASK_IDENTITIES,
);
assert.equal(dappViewedEventProperties.number_of_accounts_connected, 1);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,14 @@
"ProfileMetricsController": "object",
"RemoteFeatureFlagController": {
"cacheTimestamp": "number",
"localOverrides": "object",
"rawRemoteFeatureFlags": "object",
"remoteFeatureFlags": {
"feature1": true,
"feature2": false,
"feature3": { "name": "groupC", "value": "valueC" }
}
"feature3": { "name": "groupA", "value": "valueA" }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need some confirmation here.
RemoteFeatureFlagController 4.0.0 changes the threshold implementation/logic, hence the changes to the state snapshots.

},
"thresholdCache": "object"
},
"RewardsController": {
"rewardsAccounts": "object",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@
"lastUpdatedAt": null,
"lastViewedUserSurvey": null,
"ledgerTransportType": "webhid",
"localOverrides": "object",
"logs": "object",
"lostIdentities": "object",
"manageInstitutionalWallets": "boolean",
Expand Down Expand Up @@ -312,13 +313,14 @@
"btc": { "conversionDate": 0, "conversionRate": 0 },
"sol": { "conversionDate": 0, "conversionRate": 0 }
},
"rawRemoteFeatureFlags": "object",
"recoveryPhraseReminderHasBeenShown": true,
"recoveryPhraseReminderLastShown": "number",
"referrals": "object",
"remoteFeatureFlags": {
"feature1": true,
"feature2": false,
"feature3": { "name": "groupC", "value": "valueC" }
"feature3": { "name": "groupA", "value": "valueA" }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need some confirmation here.
RemoteFeatureFlagController 4.0.0 changes the threshold implementation/logic, hence the changes to the state snapshots.

},
"rewardsAccounts": "object",
"rewardsActiveAccount": null,
Expand Down Expand Up @@ -400,6 +402,7 @@
"syncQueue": "object",
"termsOfUseLastAgreed": "number",
"theme": "light",
"thresholdCache": "object",
"throttledOrigins": "object",
"tokenBalances": "object",
"tokenScanCache": "object",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,14 @@
"ProfileMetricsController": "object",
"RemoteFeatureFlagController": {
"cacheTimestamp": "number",
"localOverrides": "object",
"rawRemoteFeatureFlags": "object",
"remoteFeatureFlags": {
"feature1": true,
"feature2": false,
"feature3": { "name": "groupC", "value": "valueC" }
}
"feature3": { "name": "groupA", "value": "valueA" }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need some confirmation here.
RemoteFeatureFlagController 4.0.0 changes the threshold implementation/logic, hence the changes to the state snapshots.

},
"thresholdCache": "object"
},
"RewardsController": {
"rewardsAccounts": "object",
Expand Down
8 changes: 0 additions & 8 deletions test/e2e/tests/remote-feature-flag/mock-data.ts

This file was deleted.

Loading
Loading