Skip to content

chore: add remote feature flag for multichain accounts #33112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from 14 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
1 change: 1 addition & 0 deletions ui/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './accounts';
export * from './remote-feature-flags';
export * from './origin-throttling';
export * from './multichain/networks';
export * from './multichain-accounts';
48 changes: 48 additions & 0 deletions ui/selectors/multichain-accounts/feature-flags.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { type RemoteFeatureFlagsState } from '../remote-feature-flags';
import {
type MultichainAccountsFeatureFlag,
getIsMultichainAccountsState1Enabled,
getIsMultichainAccountsState2Enabled,
} from './feature-flags';

jest.mock('../../../package.json', () => ({
packageJson: {
version: '12.0.0',
},
}));

type TestState = RemoteFeatureFlagsState & {
metamask: {
remoteFeatureFlags: {
enableMultichainAccounts: MultichainAccountsFeatureFlag;
};
};
};

const disabledStateMock: MultichainAccountsFeatureFlag = {
enabled: false,
featureVersion: null,
minimumVersion: null,
};

const mockState: TestState = {
metamask: {
remoteFeatureFlags: {
enableMultichainAccounts: disabledStateMock,
},
},
};

describe('Multichain Accounts Feature Flags', () => {
describe('getIsMultichainAccountsState1Enabled', () => {
it('returns false for disabled state', () => {
expect(getIsMultichainAccountsState1Enabled(mockState)).toBe(false);
});
});

describe('getIsMultichainAccountsState2Enabled', () => {
it('returns false for disabled state', () => {
expect(getIsMultichainAccountsState2Enabled(mockState)).toBe(false);
});
});
});
96 changes: 96 additions & 0 deletions ui/selectors/multichain-accounts/feature-flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
// We can ignore the unused vars warning while the flag is not active

import {
Infer,
object,
boolean,
nullable,
string,
assert,
} from '@metamask/superstruct';
import semver from 'semver';
import packageJson from '../../../package.json';
import {
getRemoteFeatureFlags,
type RemoteFeatureFlagsState,
} from '../remote-feature-flags';
import { createDeepEqualSelector } from '../../../shared/modules/selectors/util';

/**
* Feature flag structure for multichain accounts features
*/
const MultichainAccountsFeatureFlag = object({
enabled: boolean(),
featureVersion: nullable(string()),
minimumVersion: nullable(string()),
});

/**
* Feature flag type for multichain accounts features
*/
export type MultichainAccountsFeatureFlag = Infer<
typeof MultichainAccountsFeatureFlag
>;

// TODO: Update the value to the decided version multichain accounts will be released
const APP_VERSION = packageJson.version;
const FEATURE_VERSION_1 = '1';
const FEATURE_VERSION_2 = '2';

/**
* Checks if the multichain accounts feature is enabled for a given state and feature version.
*
* @param state - The MetaMask state object
* @param featureVersion - The specific feature version to check
* @returns boolean - True if the feature is enabled for the given state and version, false otherwise.
*/
export const isMultichainAccountsFeatureEnabled = (
state: RemoteFeatureFlagsState,
featureVersion: string,
) => {
const { enableMultichainAccounts } = getRemoteFeatureFlags(state);
try {
assert(enableMultichainAccounts, MultichainAccountsFeatureFlag);
} catch (error) {
console.error(error);
return false;
}

const {
enabled,
featureVersion: currentFeatureVersion,
minimumVersion,
} = enableMultichainAccounts;
return (
enabled &&
currentFeatureVersion &&
minimumVersion &&
currentFeatureVersion === featureVersion &&
semver.gt(minimumVersion, APP_VERSION)
);
};

/**
* Selector to check if the multichain accounts feature is enabled for state 1.
*/
export const getIsMultichainAccountsState1Enabled = createDeepEqualSelector(
(state: RemoteFeatureFlagsState) => state,
(state) => {
return false;
// TODO: Uncomment this when the feature is ready for release
// return isMultichainAccountsFeatureEnabled(state, FEATURE_VERSION_1);
},
);

/**
* Selector to check if the multichain accounts feature is enabled for state 2.
*/
export const getIsMultichainAccountsState2Enabled = createDeepEqualSelector(
(state: RemoteFeatureFlagsState) => state,
(state) => {
return false;
// TODO: Uncomment this when the feature is ready for release
// return isMultichainAccountsFeatureEnabled(state, FEATURE_VERSION_2);
},
);
1 change: 1 addition & 0 deletions ui/selectors/multichain-accounts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './feature-flags';
Loading