Skip to content
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

refactor: Add background Redux slice as proxy for unflattened background state #29694

Draft
wants to merge 62 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
52e818a
Remove `getFlatState` method from `ComposableObservableStore`
MajorLift Nov 6, 2024
2a1fee2
Define types for metamask controller store fields
MajorLift Nov 6, 2024
3beda4d
Refactor `MetamaskController` `store`, `memStore` fields so that they…
MajorLift Dec 6, 2024
7143084
Update `PatchStore` class to conform with new state types
MajorLift Nov 11, 2024
28ca845
Fix errors in `MetamaskController` store objects and types
MajorLift Nov 27, 2024
1f65a9f
Fix import paths
MajorLift Nov 27, 2024
899d855
Simply `PatchStore._generatePatches`
MajorLift Nov 27, 2024
0eb33d7
Update `App{State,Metadata}Controller` entries in store objects/types…
MajorLift Dec 4, 2024
cb7eecd
Move `metamask-controllers` types to `shared` directory
MajorLift Dec 4, 2024
55fe737
Remove `NotificationController` from `MetamaskController` and store
MajorLift Dec 2, 2024
eea4c34
Define `BackgroundStateProxy` type and replace `MemStoreControllersCo…
MajorLift Dec 4, 2024
e3be201
Add `eslint-disable` for `no-restricted-paths` in types file
MajorLift Dec 4, 2024
601ce9e
Optimize `PatchStore` to utilize `controllerKey` payload from `stateC…
MajorLift Dec 4, 2024
6e54d6a
Fix `sanitizeUIState` with `in` property checks
MajorLift Dec 4, 2024
5f88de6
Update and fix `store`, `memStore`
MajorLift Dec 5, 2024
2fc248a
Fix broken import for `BalancesControllerState`
MajorLift Dec 9, 2024
622a096
Convert `ComposableObservableStore` to TypeScript
MajorLift Dec 9, 2024
81e7756
Fix all type errors in `ComposableObservableStore`
MajorLift Dec 9, 2024
f94f6a2
Allow `PatchStore` to handle top-level keys that aren't controllerKey…
MajorLift Dec 9, 2024
474efaa
Fix implicit `any` in institutional store types
MajorLift Dec 10, 2024
1ccda39
refactor: `ComposableObservableStore` takes a controller map as input…
MajorLift Dec 10, 2024
11831bc
Fix `BridgeControllerState` import path
MajorLift Dec 11, 2024
df7fa87
Remove `ui/selectors/institutional/selectors.ts` from diff
MajorLift Dec 11, 2024
b74b346
Refactor `sanitizeUIState` so it can handle both the entire backgroun…
MajorLift Dec 12, 2024
d954084
Fix `PatchStore` to handle individual controller state only and not u…
MajorLift Dec 12, 2024
578a736
Fix Redux store actions `updateMetamaskState`, `applyPatches` to ensu…
MajorLift Dec 12, 2024
1b62855
Handle more edge cases in null check for `controller.store.subscribe`
MajorLift Dec 12, 2024
50ba81a
Add variable assignment
MajorLift Dec 12, 2024
398bc7c
Convert `ComposableObservableStore.test.{j,t}s`
MajorLift Dec 13, 2024
25cc18f
Remove incorrect return type on `sanitizeUIState`
MajorLift Dec 13, 2024
1eb1d70
Fix tests for `PatchStore`, `sanitizeUiState`
MajorLift Dec 13, 2024
d93a318
Fix tests for `ComposableObservableStore`
MajorLift Dec 13, 2024
bd57b45
Linter fixes
MajorLift Jan 6, 2025
83316a5
Temporary adjustment for `Bridge{,Status}ControllerState` types
MajorLift Jan 6, 2025
312898f
Move `initialMetamaskState` into `ui/ducks/metamask/constants.ts`
MajorLift Dec 11, 2024
46d9d90
Remove `TemporaryBackgroundState` type and merge into `initialMetamas…
MajorLift Jan 6, 2025
42607b3
Merge `MetamaskState` defined in `metametrics-controller` into `initi…
MajorLift Dec 2, 2024
ad32360
Convert to TypeScript: `combineReducer`, `metamask` slice
MajorLift Dec 2, 2024
161b882
Unflatten `metamask` slice and convert to TypeScript
MajorLift Dec 11, 2024
d05b4ec
Replace `CombinedBackgroundAndReduxState` type with fixed `MetaMaskRe…
MajorLift Dec 4, 2024
fdcc097
Remove unused import
MajorLift Dec 4, 2024
6b66633
Unflatten `SENTRY_UI_STATE` object
MajorLift Dec 5, 2024
cce8b65
Add missing `PreferencesController.preferences` properties to `SENTRY…
MajorLift Dec 7, 2024
578ccb6
Reduce diffs
MajorLift Dec 11, 2024
a06e900
Rename `MetamaskSliceState` to `MetaMaskSliceState`
MajorLift Dec 11, 2024
5f65555
Unflatten `metametrics-controller`
MajorLift Dec 11, 2024
7af2f6a
Update type in `updateMetamaskState`
MajorLift Dec 12, 2024
4a427ab
Rename: `shared/types/{metamask,background}.ts`
MajorLift Jan 14, 2025
ee4ef64
Revert `metamask` slice to flattened shape and create new unflattened…
MajorLift Jan 16, 2025
f1c8bf5
Define `FlattenedBackgroundStateProxy` to type `metamask` slice
MajorLift Jan 26, 2025
d7f3f66
Adjust `updateMetamaskState`, `applyPatches` methods to align with ne…
MajorLift Jan 26, 2025
0b007ee
Merge branch 'main' into jongsun/perf/redux/2501141-add-background-re…
MajorLift Jan 26, 2025
f62532a
Update background store,state types to align with new controller init…
MajorLift Jan 27, 2025
a8b5552
Revert flattening operations to be applied in later prs
MajorLift Jan 27, 2025
5fe95fd
Remove `build-mmi` feature flag-wrapped code
MajorLift Jan 29, 2025
a5e24f6
Remove `ObservableStore`, `BaseControllerV1` input handling logic fro…
MajorLift Jan 30, 2025
f651e8d
Merge branch 'main' into jongsun/perf/redux/2501141-add-background-re…
MajorLift Jan 30, 2025
21109fb
Remove unused import statements
MajorLift Jan 30, 2025
67dbeac
Linter fixes
MajorLift Jan 30, 2025
d934ba7
Merge branch 'main' into jongsun/perf/redux/2501141-add-background-re…
MajorLift Jan 31, 2025
7fe1094
Merge branch 'main' into jongsun/perf/redux/2501141-add-background-re…
MajorLift Feb 3, 2025
d3794e9
stash
MajorLift Feb 3, 2025
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
21 changes: 5 additions & 16 deletions app/scripts/constants/sentry-state.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
import { AllProperties } from '../../../shared/modules/object.utils';

export const MMI_SENTRY_BACKGROUND_STATE = {
MMIController: {
opts: true,
},
CustodyController: {
store: true,
},
MmiConfigurationController: {
store: true,
configurationClient: true,
},
};

// This describes the subset of background controller state attached to errors
// sent to Sentry These properties have some potential to be useful for
// debugging, and they do not contain any identifiable information.
Expand Down Expand Up @@ -251,6 +238,11 @@ export const SENTRY_BACKGROUND_STATE = {
petnamesEnabled: true,
showConfirmationAdvancedDetails: true,
privacyMode: false,
useNativeCurrencyAsPrimaryCurrency: true,
featureNotificationsEnabled: true,
showMultiRpcModal: true,
tokenSortConfig: {},
shouldShowAggregatedBalancePopover: true,
},
useExternalServices: false,
selectedAddress: false,
Expand Down Expand Up @@ -396,9 +388,6 @@ export const SENTRY_BACKGROUND_STATE = {
hasAccountSyncingSyncedAtLeastOnce: false,
isAccountSyncingReadyToBeDispatched: false,
},
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
...MMI_SENTRY_BACKGROUND_STATE,
///: END:ONLY_INCLUDE_IF
};

const flattenedBackgroundStateMask = Object.values(
Expand Down
157 changes: 59 additions & 98 deletions app/scripts/controllers/metametrics-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@
} from 'lodash';
import { bufferToHex, keccak } from 'ethereumjs-util';
import { v4 as uuidv4 } from 'uuid';
import { NameControllerState, NameType } from '@metamask/name-controller';
import { AccountsControllerState } from '@metamask/accounts-controller';
import { AddressBookControllerState } from '@metamask/address-book-controller';
import {
Nft,
NftControllerState,
TokensControllerState,
} from '@metamask/assets-controllers';
import { NameControllerState, NameType } from '@metamask/name-controller';
import {
getErrorMessage,
Hex,
Expand All @@ -26,19 +32,13 @@
NetworkState,
} from '@metamask/network-controller';
import { Browser } from 'webextension-polyfill';
import {
Nft,
NftControllerState,
TokensControllerState,
} from '@metamask/assets-controllers';
import { captureException as sentryCaptureException } from '@sentry/browser';
import {
BaseController,
ControllerGetStateAction,
ControllerStateChangeEvent,
RestrictedControllerMessenger,
} from '@metamask/base-controller';
import { AddressBookControllerState } from '@metamask/address-book-controller';
import { ENVIRONMENT_TYPE_BACKGROUND } from '../../../shared/constants/app';
import {
METAMETRICS_ANONYMOUS_ID,
Expand Down Expand Up @@ -72,9 +72,10 @@
import { ENVIRONMENT } from '../../../development/build/constants';
///: END:ONLY_INCLUDE_IF

import { BackgroundStateProxy } from '../../../shared/types/background';
import type {
PreferencesControllerState,
PreferencesControllerGetStateAction,
PreferencesControllerState,
PreferencesControllerStateChangeEvent,
} from './preferences-controller';

Expand Down Expand Up @@ -168,13 +169,6 @@
privacyMode: PreferencesControllerState['preferences']['privacyMode'];
tokenNetworkFilter: string[];
};
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
custodyAccountDetails: {
[address: string]: {
custodianName: string;
};
};
///: END:ONLY_INCLUDE_IF
};

/**
Expand Down Expand Up @@ -358,10 +352,6 @@

#environment: MetaMetricsControllerOptions['environment'];

///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
#selectedAddress: PreferencesControllerState['selectedAddress'];
///: END:ONLY_INCLUDE_IF

#segment: MetaMetricsControllerOptions['segment'];

/**
Expand Down Expand Up @@ -412,10 +402,6 @@
this.#extension = extension;
this.#environment = environment;

///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
this.#selectedAddress = preferencesControllerState.selectedAddress;
///: END:ONLY_INCLUDE_IF

const abandonedFragments = omitBy(state.fragments, 'persist');

this.messagingSystem.subscribe(
Expand Down Expand Up @@ -584,6 +570,7 @@
: {};

this.update((state) => {
// @ts-expect-error TODO: Fix `Type instantiation is excessively deep and possibly infinite.ts(2589)`

Check failure on line 573 in app/scripts/controllers/metametrics-controller.ts

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

Unused '@ts-expect-error' directive.
state.fragments[id] = merge({}, additionalFragmentProps, fragment);
});

Expand Down Expand Up @@ -1004,7 +991,7 @@
}
}

handleMetaMaskStateUpdate(newState: MetaMaskState): void {
handleMetaMaskStateUpdate(newState: BackgroundStateProxy): void {
const userTraits = this._buildUserTraitsObject(newState);
if (userTraits) {
this.identify(userTraits);
Expand Down Expand Up @@ -1067,23 +1054,10 @@
referrer: MetaMetricsContext['referrer'],
page: MetaMetricsContext['page'] = METAMETRICS_BACKGROUND_PAGE_OBJECT,
): MetaMetricsContext {
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
const mmiProps: {
extensionId?: string;
} = {};

if (this.#extension?.runtime?.id) {
mmiProps.extensionId = this.#extension.runtime.id;
}
///: END:ONLY_INCLUDE_IF

return {
app: {
name: 'MetaMask Extension',
version: this.version,
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
...mmiProps,
///: END:ONLY_INCLUDE_IF
},
userAgent: window.navigator.userAgent,
page,
Expand Down Expand Up @@ -1115,21 +1089,6 @@
environmentType = ENVIRONMENT_TYPE_BACKGROUND,
} = rawPayload;

///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
const mmiProps: {
extensionId?: string;
accountAddress?: string;
} = {};

if (this.#extension?.runtime?.id) {
mmiProps.extensionId = this.#extension.runtime.id;
}

if (this.#selectedAddress) {
mmiProps.accountAddress = this.#selectedAddress;
}
///: END:ONLY_INCLUDE_IF

return {
event,
messageId: buildUniqueMessageId(rawPayload),
Expand All @@ -1153,9 +1112,6 @@
? properties.chain_id
: this.chainId,
environment_type: environmentType,
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
...mmiProps,
///: END:ONLY_INCLUDE_IF
},
context: this.#buildContext(referrer, page),
};
Expand All @@ -1169,73 +1125,73 @@
* @returns traits that have changed since last update
*/
_buildUserTraitsObject(
metamaskState: MetaMaskState,
metamaskState: BackgroundStateProxy,
): Partial<MetaMetricsUserTraits> | null {
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
const mmiAccountAddress =
metamaskState.custodyAccountDetails &&
Object.keys(metamaskState.custodyAccountDetails).length
? Object.keys(metamaskState.custodyAccountDetails)[0]
: null;
///: END:ONLY_INCLUDE_IF
const { traits, previousUserTraits } = this.state;

const currentTraits = {
[MetaMetricsUserTrait.AddressBookEntries]: sum(
Object.values(metamaskState.addressBook).map(size),
Object.values(metamaskState.AddressBookController.addressBook).map(
size,
),
),
[MetaMetricsUserTrait.InstallDateExt]:
traits[MetaMetricsUserTrait.InstallDateExt] || '',
[MetaMetricsUserTrait.LedgerConnectionType]:
metamaskState.ledgerTransportType,
metamaskState.PreferencesController.ledgerTransportType,
[MetaMetricsUserTrait.NetworksAdded]: Object.values(
metamaskState.networkConfigurationsByChainId,
metamaskState.NetworkController.networkConfigurationsByChainId,
).map((networkConfiguration) => networkConfiguration.chainId),
[MetaMetricsUserTrait.NetworksWithoutTicker]: Object.values(
metamaskState.networkConfigurationsByChainId,
metamaskState.NetworkController.networkConfigurationsByChainId,
)
.filter(({ nativeCurrency }) => !nativeCurrency)
.map(({ chainId }) => chainId),
[MetaMetricsUserTrait.NftAutodetectionEnabled]:
metamaskState.useNftDetection,
metamaskState.PreferencesController.useNftDetection,
[MetaMetricsUserTrait.NumberOfAccounts]: Object.values(
metamaskState.internalAccounts.accounts,
metamaskState.AccountsController.internalAccounts.accounts,
).length,
[MetaMetricsUserTrait.NumberOfNftCollections]:
this.#getAllUniqueNFTAddressesLength(metamaskState.allNfts),
this.#getAllUniqueNFTAddressesLength(
metamaskState.NftController.allNfts,
),
[MetaMetricsUserTrait.NumberOfNfts]: this.#getAllNFTsFlattened(
metamaskState.allNfts,
metamaskState.NftController.allNfts,
).length,
[MetaMetricsUserTrait.NumberOfTokens]: this.#getNumberOfTokens(
metamaskState.allTokens,
metamaskState.TokensController.allTokens,
),
[MetaMetricsUserTrait.OpenSeaApiEnabled]: metamaskState.openSeaEnabled,
[MetaMetricsUserTrait.OpenSeaApiEnabled]:
metamaskState.PreferencesController.openSeaEnabled,
[MetaMetricsUserTrait.ThreeBoxEnabled]: false, // deprecated, hard-coded as false
[MetaMetricsUserTrait.Theme]: metamaskState.theme || 'default',
[MetaMetricsUserTrait.Theme]:
metamaskState.PreferencesController.theme || 'default',
[MetaMetricsUserTrait.TokenDetectionEnabled]:
metamaskState.useTokenDetection,
metamaskState.PreferencesController.useTokenDetection,
[MetaMetricsUserTrait.ShowNativeTokenAsMainBalance]:
metamaskState.ShowNativeTokenAsMainBalance,
[MetaMetricsUserTrait.CurrentCurrency]: metamaskState.currentCurrency,
///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
[MetaMetricsUserTrait.MmiExtensionId]: this.#extension?.runtime?.id,
[MetaMetricsUserTrait.MmiAccountAddress]: mmiAccountAddress ?? null,
[MetaMetricsUserTrait.MmiIsCustodian]: Boolean(mmiAccountAddress),
///: END:ONLY_INCLUDE_IF
[MetaMetricsUserTrait.SecurityProviders]:
metamaskState.securityAlertsEnabled ? ['blockaid'] : [],
metamaskState.PreferencesController.preferences
?.showNativeTokenAsMainBalance,
[MetaMetricsUserTrait.CurrentCurrency]:
metamaskState.CurrencyController.currentCurrency,
[MetaMetricsUserTrait.SecurityProviders]: metamaskState
.PreferencesController.securityAlertsEnabled
? ['blockaid']
: [],
[MetaMetricsUserTrait.PetnameAddressCount]:
this.#getPetnameAddressCount(metamaskState),
[MetaMetricsUserTrait.IsMetricsOptedIn]:
metamaskState.participateInMetaMetrics,
metamaskState.MetaMetricsController.participateInMetaMetrics,
[MetaMetricsUserTrait.HasMarketingConsent]:
metamaskState.dataCollectionForMarketing,
metamaskState.MetaMetricsController.dataCollectionForMarketing,
[MetaMetricsUserTrait.TokenSortPreference]:
metamaskState.tokenSortConfig?.key || '',
metamaskState.PreferencesController.preferences?.tokenSortConfig?.key ||
'',
[MetaMetricsUserTrait.PrivacyModeEnabled]:
metamaskState.preferences.privacyMode,
metamaskState.PreferencesController.preferences?.privacyMode,
[MetaMetricsUserTrait.NetworkFilterPreference]: Object.keys(
metamaskState.preferences.tokenNetworkFilter || {},
metamaskState.PreferencesController.preferences?.tokenNetworkFilter ||
{},
),
};

Expand Down Expand Up @@ -1299,11 +1255,13 @@
*
* @param allNfts
*/
#getAllNFTsFlattened = memoize((allNfts: MetaMaskState['allNfts'] = {}) => {
return Object.values(allNfts).reduce((result: Nft[], chainNFTs) => {
return result.concat(...Object.values(chainNFTs));
}, []);
});
#getAllNFTsFlattened = memoize(
(allNfts: BackgroundStateProxy['NftController']['allNfts'] = {}) => {
return Object.values(allNfts).reduce((result: Nft[], chainNFTs) => {
return result.concat(...Object.values(chainNFTs));
}, []);
},
);

/**
* Returns the number of unique NFT addresses the user
Expand All @@ -1312,7 +1270,7 @@
* @param allNfts
*/
#getAllUniqueNFTAddressesLength(
allNfts: MetaMaskState['allNfts'] = {},
allNfts: BackgroundStateProxy['NftController']['allNfts'] = {},
): number {
const allNFTAddresses = this.#getAllNFTsFlattened(allNfts).map(
(nft) => nft.address,
Expand All @@ -1325,7 +1283,9 @@
* @param allTokens
* @returns number of unique token addresses
*/
#getNumberOfTokens(allTokens: MetaMaskState['allTokens']): number {
#getNumberOfTokens(
allTokens: BackgroundStateProxy['TokensController']['allTokens'],
): number {
return Object.values(allTokens).reduce((result, accountsByChain) => {
return result + sum(Object.values(accountsByChain).map(size));
}, 0);
Expand Down Expand Up @@ -1549,8 +1509,9 @@
*
* @param metamaskState
*/
#getPetnameAddressCount(metamaskState: MetaMaskState): number {
const addressNames = metamaskState.names?.[NameType.ETHEREUM_ADDRESS] ?? {};
#getPetnameAddressCount(metamaskState: BackgroundStateProxy): number {
const addressNames =
metamaskState.NameController.names?.[NameType.ETHEREUM_ADDRESS] ?? {};

return Object.keys(addressNames).reduce((totalCount, address) => {
const addressEntry = addressNames[address];
Expand Down
Loading
Loading