import { StoreEnhancer } from 'redux'; import { configureStore as baseConfigureStore } from '@reduxjs/toolkit'; import devtoolsEnhancer from 'remote-redux-devtools'; import { ApprovalControllerState } from '@metamask/approval-controller'; import { GasEstimateType, GasFeeEstimates } from '@metamask/gas-fee-controller'; import rootReducer from '../ducks'; import { LedgerTransportTypes } from '../../shared/constants/hardware-wallets'; import { TransactionMeta } from '../../shared/constants/transaction'; import type { NetworkStatus } from '../../shared/constants/network'; /** * This interface is temporary and is copied from the message-manager.js file * and is the 'msgParams' key of the interface declared there. We should get a * universal Message type to use for this, the Message manager and all * the other types of messages. * * TODO: Replace this */ export interface TemporaryMessageDataType { id: number; type: string; msgParams: { metamaskId: number; data: string; }; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) custodyId?: string; status?: string; ///: END:ONLY_INCLUDE_IN } export interface MessagesIndexedById { [id: string]: TemporaryMessageDataType; } /** * This interface is a temporary interface to describe the state tree that is * sent from the background. Ideally we can build this using Types in the * backend when we compose the stores, then we can import it here and use it. * * Some of this is duplicated in the metamask redux duck. In *most* cases the * state received from the background takes precedence over anything in the * metamask reducer. */ interface TemporaryBackgroundState { addressBook: { [chainId: string]: { name: string; }[]; }; providerConfig: { chainId: string; }; currentNetworkTxList: TransactionMeta[]; selectedAddress: string; identities: { [address: string]: { balance: string; }; }; ledgerTransportType: LedgerTransportTypes; unapprovedDecryptMsgs: MessagesIndexedById; unapprovedTxs: { [transactionId: string]: TransactionMeta; }; unapprovedMsgs: MessagesIndexedById; unapprovedPersonalMsgs: MessagesIndexedById; unapprovedTypedMessages: MessagesIndexedById; networkId: string | null; networkStatus: NetworkStatus; pendingApprovals: ApprovalControllerState['pendingApprovals']; knownMethodData?: { [fourBytePrefix: string]: Record; }; gasFeeEstimates: GasFeeEstimates; gasEstimateType: GasEstimateType; ///: BEGIN:ONLY_INCLUDE_IN(build-mmi) custodyAccountDetails?: { [key: string]: any }; ///: END:ONLY_INCLUDE_IN } type RootReducerReturnType = ReturnType; export type CombinedBackgroundAndReduxState = RootReducerReturnType & { activeTab: { origin: string; }; metamask: RootReducerReturnType['metamask'] & TemporaryBackgroundState; }; export default function configureStore(preloadedState: any) { const debugModeEnabled = Boolean(process.env.METAMASK_DEBUG); const isDev = debugModeEnabled && !process.env.IN_TEST; const enhancers: StoreEnhancer[] = []; if (isDev) { enhancers.push( devtoolsEnhancer({ name: 'MetaMask', hostname: 'localhost', port: 8000, realtime: true, }) as StoreEnhancer, ); } return baseConfigureStore({ reducer: rootReducer as () => CombinedBackgroundAndReduxState, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ /** * We do not persist the redux tree for rehydration, so checking for * serializable state keys is not relevant for now. Any state that persists * is managed in the background. We may at some point want this, but we can * gradually implement by using the ignore options to ignore those actions * and state keys that are not serializable, preventing us from adding new * actions and state that would violate our ability to persist state keys. * NOTE: redux-thunk is included by default in the middleware below. */ serializableCheck: false, /** * immutableCheck controls whether we get warnings about mutation of * state, which will be true in dev. However in test lavamoat complains * about something the middleware is doing. It would be good to figure * that out and enable this in test environments so that mutation * causes E2E failures. */ immutableCheck: isDev ? { warnAfter: 100, } : false, }), devTools: false, enhancers, preloadedState, }); } type Store = ReturnType; export type MetaMaskReduxState = ReturnType; export type MetaMaskReduxDispatch = Store['dispatch'];