Skip to content

Commit

Permalink
stash
Browse files Browse the repository at this point in the history
  • Loading branch information
MajorLift committed Jan 16, 2025
1 parent ee4ef64 commit 2b1e3ea
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 6 deletions.
129 changes: 125 additions & 4 deletions ui/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import {
getInternalAccountByAddress,
getSelectedInternalAccount,
getInternalAccounts,
oldestPendingConfirmationSelector,

Check failure on line 71 in ui/store/actions.ts

View workflow job for this annotation

GitHub Actions / Test lint / Test lint

'oldestPendingConfirmationSelector' is defined but never used
} from '../selectors';
import {
getSelectedNetworkClientId,
Expand Down Expand Up @@ -119,7 +120,7 @@ import {
import { ThemeType } from '../../shared/constants/preferences';
import { FirstTimeFlowType } from '../../shared/constants/onboarding';
import { getMethodDataAsync } from '../../shared/lib/four-byte';
import { BackgroundStateProxy } from '../../shared/types/metamask';
import { BackgroundStateProxy } from '../../shared/types/background';
import { DecodedTransactionDataResponse } from '../../shared/types/transaction-decode';
import { LastInteractedConfirmationInfo } from '../pages/confirmations/types/confirm';
import { EndTraceRequest } from '../../shared/lib/trace';
Expand Down Expand Up @@ -1615,7 +1616,124 @@ export function updateMetamaskState(
return currentState;
}

const newState = applyPatches<BackgroundStateProxy>(currentState, patches);
const newState = applyPatches<Record<string, unknown>>(
currentState,
patches,
true,
);
const { currentLocale } = currentState;
const currentInternalAccount = getSelectedInternalAccount(state);
const selectedAddress = currentInternalAccount?.address;
const { currentLocale: newLocale } = newState;
const newProviderConfig = getProviderConfig({ metamask: newState });
const newInternalAccount = getSelectedInternalAccount({
metamask: newState,
});
const newSelectedAddress = newInternalAccount?.address;

if (currentLocale && newLocale && currentLocale !== newLocale) {
dispatch(updateCurrentLocale(newLocale));
}

if (selectedAddress !== newSelectedAddress) {
dispatch({ type: actionConstants.SELECTED_ADDRESS_CHANGED });
}

const newAddressBook =
newState.addressBook?.[newProviderConfig?.chainId] ?? {};
const oldAddressBook =
currentState.addressBook?.[providerConfig?.chainId] ?? {};
// TODO: Replace `any` with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const newAccounts: { [address: string]: Record<string, any> } =
getMetaMaskAccounts({ metamask: newState });
// TODO: Replace `any` with type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const oldAccounts: { [address: string]: Record<string, any> } =
getMetaMaskAccounts({ metamask: currentState });
const newSelectedAccount = newAccounts[newSelectedAddress];
const oldSelectedAccount = newAccounts[selectedAddress];
// dispatch an ACCOUNT_CHANGED for any account whose balance or other
// properties changed in this update
Object.entries(oldAccounts).forEach(([address, oldAccount]) => {
if (!isEqual(oldAccount, newAccounts[address])) {
dispatch({
type: actionConstants.ACCOUNT_CHANGED,
payload: { account: newAccounts[address] },
});
}
});

// Also emit an event for the selected account changing, either due to a
// property update or if the entire account changes.
if (isEqual(oldSelectedAccount, newSelectedAccount) === false) {
dispatch({
type: actionConstants.SELECTED_ACCOUNT_CHANGED,
payload: { account: newSelectedAccount },
});
}
// We need to keep track of changing address book entries
if (isEqual(oldAddressBook, newAddressBook) === false) {
dispatch({
type: actionConstants.ADDRESS_BOOK_UPDATED,
payload: { addressBook: newAddressBook },
});
}

// track when gasFeeEstimates change
if (
isEqual(currentState.gasFeeEstimates, newState.gasFeeEstimates) === false
) {
dispatch({
type: actionConstants.GAS_FEE_ESTIMATES_UPDATED,
payload: {
gasFeeEstimates: newState.gasFeeEstimates,
gasEstimateType: newState.gasEstimateType,
},
});
}
dispatch({
type: actionConstants.UPDATE_METAMASK_STATE,
value: newState,
});
if (providerConfig.chainId !== newProviderConfig.chainId) {
dispatch({
type: actionConstants.CHAIN_CHANGED,
payload: newProviderConfig.chainId,
});
// We dispatch this action to ensure that the send state stays up to date
// after the chain changes. This async thunk will fail gracefully in the
// event that we are not yet on the send flow with a draftTransaction in
// progress.

dispatch(initializeSendState({ chainHasChanged: true }));
}

///: BEGIN:ONLY_INCLUDE_IF(build-mmi)
updateCustodyState(dispatch, newState, getState());
///: END:ONLY_INCLUDE_IF

return newState;
};
}

export function updateBackgroundState(
patches: Patch[],
): ThunkAction<void, MetaMaskReduxState, unknown, AnyAction> {
return (dispatch, getState) => {
const state = getState();
const providerConfig = getProviderConfig(state);
const { metamask: currentState } = state;

if (!patches?.length) {
return currentState;
}

const newState = applyPatches<BackgroundStateProxy>(
currentState,
patches,
true,
);
const {
PreferencesController: { currentLocale },
} = currentState;
Expand Down Expand Up @@ -6087,27 +6205,30 @@ export async function endBackgroundTrace(request: EndTraceRequest) {
*
* @param oldState - The current state.
* @param patches - The patches to apply.
* @param isFlattened - 'false' if the input state object is keyed by controller name.
* 'true' if it has been flattened so that controller state properties are at the top level.
* Only supports 'replace' operations with at most 2 path elements.
* Properties that are nested at deeper levels cannot be updated in isolation.
* @returns The new state.
*/
function applyPatches<State extends Record<string, unknown>>(
oldState: State,
patches: Patch[],
isFlattened: boolean = false,
): State {
const newState = { ...oldState };

for (const patch of patches) {
const { op, path, value } = patch;

if (op === 'replace') {
if (path.length === 2) {
if (!isFlattened && path.length === 2) {
const [controllerKey, key] = path;
if (!(controllerKey in newState)) {
newState[controllerKey] = {};
}
newState[controllerKey][key] = value;
} else if (path.length === 1) {
} else if (isFlattened || path.length === 1) {
newState[path[0]] = value;
}
} else {
Expand Down
60 changes: 58 additions & 2 deletions ui/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { configureStore as baseConfigureStore } from '@reduxjs/toolkit';
import devtoolsEnhancer from 'remote-redux-devtools';
import rootReducer from '../ducks';
import { AppSliceState } from '../ducks/app/app';
import { MetaMaskSliceState } from '../ducks/metamask/metamask';
import { BackgroundSliceState } from '../ducks/background/background';
import { BackgroundStateProxy } from '../../shared/types/background';

/**
* This interface is temporary and is copied from the message-manager.js file
Expand Down Expand Up @@ -34,6 +35,60 @@ export type MessagesIndexedById = {

type RootReducerReturnType = ReturnType<typeof rootReducer>;

type _$toIntersection2<T> = (
T extends unknown ? (x: T) => unknown : never
) extends (x: infer X) => void
? X
: never;

type _$toIntersection<T> = boolean extends T
? boolean & _$toIntersection2<Exclude<T, boolean>>
: _$toIntersection2<T>;

type _$toList<T, O extends unknown[] = []> = _$toIntersection<
T extends unknown ? (t: T) => T : never
> extends (x: never) => infer X
? _$toList<Exclude<T, X>, [X, ...O]>
: O;

export type _$cast<T, U> = [T] extends [U] ? T : U;

export type _$keys<T extends Record<string, unknown>> = _$toList<keyof T>;

type _PrependKeyToEntries<K extends PropertyKey, E extends unknown[][]> = {
[I in keyof E]: E[I] extends unknown[] ? [K, ...E[I]] : never;
};

type _DeepEntriesKey<
O extends Record<PropertyKey, unknown>,
K extends PropertyKey,
> = O[K] extends Record<PropertyKey, unknown>
? _PrependKeyToEntries<K, _$deepEntries<O[K]>>
: [[K, O[K]]];

export type _$deepEntries<
O extends Record<PropertyKey, unknown>,
Keys extends unknown[] = _$keys<O>,
Acc extends unknown[][] = [],
DEEP_KEYS = _DeepEntriesKey<O, _$cast<Keys[0], PropertyKey>>,
> = Keys extends [infer K, ...infer Rest]
? K extends keyof O
? _$deepEntries<O, Rest, [...Acc, ..._$cast<DEEP_KEYS, unknown[][]>]>
: never
: Acc;

type _$display<T extends Record<PropertyKey, unknown>> = {
[key in keyof T]: T[key];
};

export type _ = _DeepEntriesKey<
_$display<BackgroundStateProxy>,
keyof _$display<BackgroundStateProxy>
>;

type FlattenedBackgroundStateProxy =
FlattenObjectOneLevel<BackgroundStateProxy>;

/**
* `ReduxState` overrides incorrectly typed properties of `RootReducerReturnType`, and is only intended to be used as an input for `configureStore`.
* The `MetaMaskReduxState` type (derived from the returned output of `configureStore`) is to be used consistently as the single source-of-truth and representation of Redux state shape.
Expand All @@ -45,7 +100,8 @@ type ReduxState = {
activeTab: {
origin: string;
};
metamask: MetaMaskSliceState['metamask'];
metamask: FlattenedBackgroundStateProxy;
background: BackgroundSliceState['background'];
appState: AppSliceState['appState'];
} & Omit<RootReducerReturnType, 'activeTab' | 'metamask' | 'appState'>;

Expand Down

0 comments on commit 2b1e3ea

Please sign in to comment.