1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 09:23:21 +01:00

Use transactions array in frontend (#20523)

Send the entire transactions array to the frontend rather than currentNetworkTxList and unapprovedTxs.
This commit is contained in:
Matthew Walsh 2023-09-04 16:48:25 +01:00 committed by GitHub
parent 6ee90ac8b2
commit 07adba5926
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 416 additions and 376 deletions

View File

@ -27,7 +27,7 @@ const state = {
image: {
src: 'images/global-menu-block-explorer.svg',
},
}
},
},
tokenList: {
'0x514910771af9ca656af840dff83e8264ecf986ca': {
@ -314,8 +314,8 @@ const state = {
address: '0x9d0ba4ddac06032527b140912ec808ab9451b788',
},
},
unapprovedTxs: {
3111025347726181: {
transactions: [
{
id: 3111025347726181,
time: 1620710815484,
status: 'unapproved',
@ -365,7 +365,7 @@ const state = {
],
],
},
},
],
addressBook: {
undefined: {
0: {
@ -574,7 +574,7 @@ const state = {
},
},
currentBlockGasLimit: '0x793af4',
currentNetworkTxList: [
transactions: [
{
chainId: '0x38',
dappSuggestedGasFees: null,

View File

@ -218,7 +218,6 @@ browser.runtime.onConnectExternal.addListener(async (...args) => {
* @property {boolean} isAccountMenuOpen - Represents whether the main account selection UI is currently displayed.
* @property {boolean} isNetworkMenuOpen - Represents whether the main network selection UI is currently displayed.
* @property {object} identities - An object matching lower-case hex addresses to Identity objects with "address" and "name" (nickname) keys.
* @property {object} unapprovedTxs - An object mapping transaction hashes to unapproved transactions.
* @property {object} networkConfigurations - A list of network configurations, containing RPC provider details (eg chainId, rpcUrl, rpcPreferences).
* @property {Array} addressBook - A list of previously sent to addresses.
* @property {object} contractExchangeRates - Info about current token prices.
@ -235,7 +234,6 @@ browser.runtime.onConnectExternal.addListener(async (...args) => {
* @property {string} networkStatus - Either "unknown", "available", "unavailable", or "blocked", depending on the status of the currently selected network.
* @property {object} accounts - An object mapping lower-case hex addresses to objects with "balance" and "address" keys, both storing hex string values.
* @property {hex} currentBlockGasLimit - The most recently seen block gas limit, in a lower case hex prefixed string.
* @property {TransactionMeta[]} currentNetworkTxList - An array of transactions associated with the currently selected network.
* @property {object} unapprovedMsgs - An object of messages pending approval, mapping a unique ID to the options.
* @property {number} unapprovedMsgCount - The number of messages in unapprovedMsgs.
* @property {object} unapprovedPersonalMsgs - An object of messages pending approval, mapping a unique ID to the options.

View File

@ -229,7 +229,7 @@ export default class TransactionController extends EventEmitter {
isEnabled: () =>
Boolean(
this.preferencesStore.getState().incomingTransactionsPreferences?.[
this._getChainId()
this._getCurrentChainId()
] && this._hasCompletedOnboarding(),
),
lastFetchedBlockNumbers: opts.initState?.lastFetchedBlockNumbers || {},
@ -2135,16 +2135,12 @@ export default class TransactionController extends EventEmitter {
* Updates the memStore in transaction controller
*/
_updateMemstore() {
const { transactions } = this.store.getState();
const unapprovedTxs = this.txStateManager.getUnapprovedTxList();
const currentNetworkTxList = this.txStateManager.getTransactions({
const transactions = this.getTransactions({
filterToCurrentNetwork: false,
limit: MAX_MEMSTORE_TX_LIST_SIZE,
});
this.memStore.updateState({
unapprovedTxs,
currentNetworkTxList,
transactions,
});
}

View File

@ -56,10 +56,10 @@ const TRANSACTION_META_MOCK = {
hash: '0x1',
id: 1,
status: TransactionStatus.confirmed,
transaction: {
time: 123456789,
txParams: {
from: VALID_ADDRESS,
},
time: 123456789,
};
async function flushPromises() {
@ -186,21 +186,10 @@ describe('Transaction Controller', function () {
it('should return a state object with the right keys and data types', function () {
const exposedState = txController.getState();
assert.ok(
'unapprovedTxs' in exposedState,
'state should have the key unapprovedTxs',
);
assert.ok(
'currentNetworkTxList' in exposedState,
'state should have the key currentNetworkTxList',
);
assert.ok(
typeof exposedState?.unapprovedTxs === 'object',
'should be an object',
);
assert.ok(
Array.isArray(exposedState.currentNetworkTxList),
'should be an array',
'transactions' in exposedState,
'state should have the key transactions',
);
assert.ok(Array.isArray(exposedState.transactions), 'should be an array');
});
});

View File

@ -283,14 +283,11 @@ export const SENTRY_BACKGROUND_STATE = {
tokens: false,
},
TransactionController: {
currentNetworkTxList: false,
transactions: false,
lastFetchedBlockNumbers: false,
},
TxController: {
currentNetworkTxList: false,
transactions: false,
unapprovedTxs: false,
},
};

View File

@ -180,57 +180,6 @@
}
},
"cachedBalances": {},
"incomingTransactions": {},
"unapprovedTxs": {
"8393540981007587": {
"id": 8393540981007587,
"time": 1536268017676,
"status": "unapproved",
"metamaskNetworkId": "4",
"loadingDefaults": false,
"txParams": {
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x3b9aca00"
},
"history": [
{
"id": 8393540981007587,
"time": 1536268017676,
"status": "unapproved",
"metamaskNetworkId": "4",
"loadingDefaults": true,
"txParams": {
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x3b9aca00"
}
},
[
{
"op": "replace",
"path": "/loadingDefaults",
"value": false,
"timestamp": 1536268017685
}
],
[
{
"op": "add",
"path": "/origin",
"value": "MetaMask",
"note": "#newUnapprovedTransaction - adding the origin",
"timestamp": 1536268017686
}
]
],
"origin": "MetaMask"
}
},
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"accounts": {
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
@ -347,7 +296,55 @@
"occurrences": 12
}
},
"currentNetworkTxList": [
"transactions": [
{
"id": 8393540981007587,
"time": 1536268017676,
"status": "unapproved",
"metamaskNetworkId": "4",
"loadingDefaults": false,
"txParams": {
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x3b9aca00"
},
"history": [
{
"id": 8393540981007587,
"time": 1536268017676,
"status": "unapproved",
"metamaskNetworkId": "4",
"loadingDefaults": true,
"txParams": {
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x3b9aca00"
}
},
[
{
"op": "replace",
"path": "/loadingDefaults",
"value": false,
"timestamp": 1536268017685
}
],
[
{
"op": "add",
"path": "/origin",
"value": "MetaMask",
"note": "#newUnapprovedTransaction - adding the origin",
"timestamp": 1536268017686
}
]
],
"origin": "MetaMask"
},
{
"id": 3387511061307736,
"time": 1528133130531,

View File

@ -227,59 +227,6 @@
"0xaa36a7": true,
"0xe704": true
},
"unapprovedTxs": {
"8393540981007587": {
"chainId": "0x5",
"id": 8393540981007587,
"time": 1536268017676,
"status": "unapproved",
"metamaskNetworkId": "4",
"loadingDefaults": false,
"txParams": {
"data": "0xa9059cbb000000000000000000000000b19ac54efa18cc3a14a5b821bfec73d284bf0c5e0000000000000000000000000000000000000000000000003782dace9d900000",
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x3b9aca00"
},
"history": [
{
"id": 8393540981007587,
"time": 1536268017676,
"status": "unapproved",
"metamaskNetworkId": "4",
"loadingDefaults": true,
"txParams": {
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x3b9aca00"
}
},
[
{
"op": "replace",
"path": "/loadingDefaults",
"value": false,
"timestamp": 1536268017685
}
],
[
{
"op": "add",
"path": "/origin",
"value": "MetaMask",
"note": "#newUnapprovedTransaction - adding the origin",
"timestamp": 1536268017686
}
]
],
"origin": "metamask"
}
},
"selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"accounts": {
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
@ -643,7 +590,57 @@
"occurrences": 12
}
},
"currentNetworkTxList": [
"transactions": [
{
"chainId": "0x5",
"id": 8393540981007587,
"time": 1536268017676,
"status": "unapproved",
"metamaskNetworkId": "4",
"loadingDefaults": false,
"txParams": {
"data": "0xa9059cbb000000000000000000000000b19ac54efa18cc3a14a5b821bfec73d284bf0c5e0000000000000000000000000000000000000000000000003782dace9d900000",
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x3b9aca00"
},
"history": [
{
"id": 8393540981007587,
"time": 1536268017676,
"status": "unapproved",
"metamaskNetworkId": "4",
"loadingDefaults": true,
"txParams": {
"from": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"to": "0xc42edfcc21ed14dda456aa0756c153f7985d8813",
"value": "0x0",
"gas": "0x5208",
"gasPrice": "0x3b9aca00"
}
},
[
{
"op": "replace",
"path": "/loadingDefaults",
"value": false,
"timestamp": 1536268017685
}
],
[
{
"op": "add",
"path": "/origin",
"value": "MetaMask",
"note": "#newUnapprovedTransaction - adding the origin",
"timestamp": 1536268017686
}
]
],
"origin": "metamask"
},
{
"id": 3387511061307736,
"time": 1528133130531,

View File

@ -218,8 +218,6 @@
"allDetectedTokens": {}
},
"TxController": {
"unapprovedTxs": "object",
"currentNetworkTxList": "object",
"transactions": "object"
}
}

View File

@ -13,7 +13,6 @@
"isAccountMenuOpen": false,
"isNetworkMenuOpen": false,
"identities": "object",
"unapprovedTxs": "object",
"networkConfigurations": "object",
"addressBook": "object",
"contractExchangeRates": "object",
@ -153,7 +152,6 @@
"lastUpdated": "object",
"notifications": "object",
"accounts": "object",
"currentNetworkTxList": "object",
"transactions": "object",
"unapprovedDecryptMsgs": "object",
"unapprovedDecryptMsgCount": 0,

View File

@ -154,7 +154,7 @@ export const createSwapsMockStore = () => {
showFiatInTestnets: true,
},
currentCurrency: 'ETH',
currentNetworkTxList: [
transactions: [
{
id: 6571648590592143,
time: 1667403993369,
@ -162,7 +162,7 @@ export const createSwapsMockStore = () => {
metamaskNetworkId: '5',
originalGasEstimate: '0x7548',
userEditedGasLimit: false,
chainId: '0x5',
chainId: CHAIN_IDS.MAINNET,
loadingDefaults: false,
dappSuggestedGasFees: null,
sendFlowHistory: null,

View File

@ -18,19 +18,6 @@ const mmState = {
balance: '0x1F4',
},
},
unapprovedTxs: {
8393540981007587: {
...mockState.metamask.unapprovedTxs[8393540981007587],
txParams: {
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
value: '0x0',
gas: '0x5208',
gasPrice: '0x3b9aca00',
type: '0x0',
},
},
},
preferences: {
useNativeCurrencyAsPrimaryCurrency: true,
},
@ -52,6 +39,15 @@ const mmState = {
},
};
mmState.metamask.transactions[0].txParams = {
from: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
value: '0x0',
gas: '0x5208',
gasPrice: '0x3b9aca00',
type: '0x0',
};
const render = ({ contextProps, state = mmState } = {}) => {
const store = configureStore({ ...state, ...contextProps });

View File

@ -177,12 +177,13 @@ describe('SignatureRequestSIWE (Sign in with Ethereum)', () => {
...mockStoreInitialState,
metamask: {
...mockStoreInitialState.metamask,
unapprovedTxs: {
...mockStoreInitialState.metamask.unapprovedTxs,
'0x12333': {
transactions: [
...mockStoreInitialState.metamask.transactions,
{
chainId: mockStoreInitialState.metamask.providerConfig.chainId,
status: 'unapproved',
},
},
],
unapprovedMsgCount: 2,
},
});

View File

@ -9,6 +9,7 @@ const render = () => {
const store = configureStore({
metamask: {
...mockState.metamask,
transactions: [],
},
});
return renderWithProvider(<TransactionList />, store);

View File

@ -44,6 +44,7 @@ import {
getSelectedIdentity,
getShowProductTour,
getTestNetworkBackgroundColor,
getUnapprovedTransactions,
getSelectedAddress,
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getTheme,
@ -136,9 +137,10 @@ export const AppHeader = ({ location }) => {
matchPath(location.pathname, { path: BUILD_QUOTE_ROUTE, exact: false }),
);
const hasUnapprovedTransactions = useSelector(
(state) => Object.keys(state.metamask.unapprovedTxs).length > 0,
);
const unapprovedTransactions = useSelector(getUnapprovedTransactions);
const hasUnapprovedTransactions =
Object.keys(unapprovedTransactions).length > 0;
const disableAccountPicker =
isConfirmationPage || (isSwapsPage && !isSwapsBuildQuotePage);

View File

@ -44,6 +44,7 @@ import {
getMetaMetricsId,
///: END:ONLY_INCLUDE_IN(build-mmi)
getSelectedAddress,
getUnapprovedTransactions,
///: BEGIN:ONLY_INCLUDE_IN(snaps)
getUnreadNotificationsCount,
///: END:ONLY_INCLUDE_IN
@ -69,10 +70,11 @@ export const GlobalMenu = ({ closeMenu, anchorElement }) => {
const trackEvent = useContext(MetaMetricsContext);
const history = useHistory();
const address = useSelector(getSelectedAddress);
const unapprovedTransactons = useSelector(getUnapprovedTransactions);
const hasUnapprovedTransactions =
Object.keys(unapprovedTransactons).length > 0;
const hasUnapprovedTransactions = useSelector(
(state) => Object.keys(state.metamask.unapprovedTxs).length > 0,
);
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const metaMetricsId = useSelector(getMetaMetricsId);
const mmiPortfolioUrl = useSelector(getMmiPortfolioUrl);

View File

@ -51,7 +51,7 @@ describe('AccountListItem', () => {
});
it('enables the settings item when there is no active transaction', async () => {
const { getByTestId } = render({ unapprovedTxs: {} });
const { getByTestId } = render({ transactions: [] });
await waitFor(() => {
expect(getByTestId('global-menu-settings')).toBeEnabled();
});
@ -65,7 +65,7 @@ describe('AccountListItem', () => {
});
it('enables the connected sites item when there is no active transaction', async () => {
const { getByTestId } = render({ unapprovedTxs: {} });
const { getByTestId } = render({ transactions: [] });
await waitFor(() => {
expect(getByTestId('global-menu-connected-sites')).toBeEnabled();
});

View File

@ -152,14 +152,14 @@ describe('App State', () => {
it('shows confirm tx page', () => {
const txs = {
unapprovedTxs: {
1: {
transactions: [
{
id: 1,
},
2: {
{
id: 2,
},
},
],
};
const oldState = { ...metamaskState, ...txs };
const state = reduceApp(oldState, {
@ -174,14 +174,14 @@ describe('App State', () => {
it('completes tx continues to show pending txs current view context', () => {
const txs = {
unapprovedTxs: {
1: {
transactions: [
{
id: 1,
},
2: {
{
id: 2,
},
},
],
};
const oldState = { ...metamaskState, ...txs };

View File

@ -356,8 +356,8 @@ describe('Confirm Transaction Duck', () => {
providerConfig: {
chainId: '0x5',
},
unapprovedTxs: {
2603411941761054: {
transactions: [
{
history: [],
id: 2603411941761054,
loadingDefaults: false,
@ -373,7 +373,7 @@ describe('Confirm Transaction Duck', () => {
value: '0xde0b6b3a7640000',
},
},
},
],
},
confirmTransaction: {},
};

View File

@ -27,7 +27,7 @@ const initialState = {
isAccountMenuOpen: false,
isNetworkMenuOpen: false,
identities: {},
unapprovedTxs: {},
transactions: [],
networkConfigurations: {},
addressBook: [],
contractExchangeRates: {},
@ -111,8 +111,8 @@ export default function reduceMetamask(state = initialState, action) {
case actionConstants.UPDATE_TRANSACTION_PARAMS: {
const { id: txId, value } = action;
let { currentNetworkTxList } = metamaskState;
currentNetworkTxList = currentNetworkTxList.map((tx) => {
let { transactions } = metamaskState;
transactions = transactions.map((tx) => {
if (tx.id === txId) {
const newTx = { ...tx };
newTx.txParams = value;
@ -123,7 +123,7 @@ export default function reduceMetamask(state = initialState, action) {
return {
...metamaskState,
currentNetworkTxList,
transactions,
};
}
@ -300,10 +300,6 @@ export function getSendToAccounts(state) {
return [...fromAccounts, ...addressBookAccounts];
}
export function getUnapprovedTxs(state) {
return state.metamask.unapprovedTxs;
}
/**
* Function returns true if network details are fetched and it is found to not support EIP-1559
*

View File

@ -9,7 +9,6 @@ import reduceMetamask, {
getNativeCurrency,
getSendHexDataFeatureFlagState,
getSendToAccounts,
getUnapprovedTxs,
isNotEIP1559Network,
} from './metamask';
@ -91,8 +90,8 @@ describe('MetaMask Reducers', () => {
},
},
},
unapprovedTxs: {
4768706228115573: {
transactions: [
{
id: 4768706228115573,
time: 1487363153561,
status: TransactionStatus.unapproved,
@ -111,7 +110,7 @@ describe('MetaMask Reducers', () => {
maxCost: 'de234b52e4a0800',
gasPrice: '4a817c800',
},
},
],
},
{},
),
@ -175,7 +174,7 @@ describe('MetaMask Reducers', () => {
it('updates value of tx by id', () => {
const oldState = {
currentNetworkTxList: [
transactions: [
{
id: 1,
txParams: 'foo',
@ -189,7 +188,7 @@ describe('MetaMask Reducers', () => {
value: 'bar',
});
expect(state.currentNetworkTxList[0].txParams).toStrictEqual('bar');
expect(state.transactions[0].txParams).toStrictEqual('bar');
});
it('close welcome screen', () => {
@ -326,30 +325,6 @@ describe('MetaMask Reducers', () => {
]);
});
});
it('should return the unapproved txs', () => {
expect(getUnapprovedTxs(mockState)).toStrictEqual({
4768706228115573: {
id: 4768706228115573,
time: 1487363153561,
status: TransactionStatus.unapproved,
gasMultiplier: 1,
metamaskNetworkId: '5',
txParams: {
from: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb',
to: '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761',
value: '0xde0b6b3a7640000',
metamaskId: 4768706228115573,
metamaskNetworkId: '5',
gas: '0x5209',
},
txFee: '17e0186e60800',
txValue: 'de0b6b3a7640000',
maxCost: 'de234b52e4a0800',
gasPrice: '4a817c800',
},
});
});
});
describe('isNotEIP1559Network()', () => {

View File

@ -39,6 +39,7 @@ import {
getEnsResolutionByAddress,
getSelectedAccount,
getSelectedAddress,
getUnapprovedTransactions,
} from '../../selectors';
import {
disconnectGasFeeEstimatePoller,
@ -80,7 +81,6 @@ import {
getGasEstimateType,
getProviderConfig,
getTokens,
getUnapprovedTxs,
} from '../metamask/metamask';
import { resetDomainResolution } from '../domains';
@ -499,7 +499,7 @@ export const computeEstimatedGasLimit = createAsyncThunk(
const { send, metamask } = state;
const draftTransaction =
send.draftTransactions[send.currentTransactionUUID];
const unapprovedTxs = getUnapprovedTxs(state);
const unapprovedTxs = getUnapprovedTransactions(state);
const isMultiLayerFeeNetwork = getIsMultiLayerFeeNetwork(state);
const transaction = unapprovedTxs[draftTransaction.id];
const isNonStandardEthChain = getIsNonStandardEthChain(state);
@ -1731,7 +1731,7 @@ export function editExistingTransaction(assetType, transactionId) {
return async (dispatch, getState) => {
await dispatch(actions.clearPreviousDrafts());
const state = getState();
const unapprovedTransactions = getUnapprovedTxs(state);
const unapprovedTransactions = getUnapprovedTransactions(state);
const transaction = unapprovedTransactions[transactionId];
const account = getTargetAccount(state, transaction.txParams.from);
@ -2033,7 +2033,7 @@ export function updateSendAsset(
getSelectedAddress(state);
const account = getTargetAccount(state, sendingAddress);
if (type === AssetType.native) {
const unapprovedTxs = getUnapprovedTxs(state);
const unapprovedTxs = getUnapprovedTransactions(state);
const unapprovedTx = unapprovedTxs?.[draftTransaction.id];
await dispatch(
@ -2274,7 +2274,7 @@ export function signTransaction() {
// We first must grab the previous transaction object from state and then
// merge in the modified txParams. Once the transaction has been modified
// we can send that to the background to update the transaction in state.
const unapprovedTxs = getUnapprovedTxs(state);
const unapprovedTxs = getUnapprovedTransactions(state);
const unapprovedTx = cloneDeep(unapprovedTxs[draftTransaction.id]);
// We only update the tx params that can be changed via the edit flow UX
const eip1559OnlyTxParamsToUpdate = {

View File

@ -1479,7 +1479,10 @@ describe('Send Slice', () => {
'send/computeEstimatedGasLimit/pending',
);
expect(actionResult[3].type).toStrictEqual(
'send/computeEstimatedGasLimit/rejected',
'metamask/gas/SET_CUSTOM_GAS_LIMIT',
);
expect(actionResult[4].type).toStrictEqual(
'send/computeEstimatedGasLimit/fulfilled',
);
});
@ -1531,7 +1534,10 @@ describe('Send Slice', () => {
'send/computeEstimatedGasLimit/pending',
);
expect(actionResult[3].type).toStrictEqual(
'send/computeEstimatedGasLimit/rejected',
'metamask/gas/SET_CUSTOM_GAS_LIMIT',
);
expect(actionResult[4].type).toStrictEqual(
'send/computeEstimatedGasLimit/fulfilled',
);
});
@ -1568,14 +1574,17 @@ describe('Send Slice', () => {
const actionResult = store.getActions();
expect(actionResult).toHaveLength(4);
expect(actionResult).toHaveLength(5);
expect(actionResult[0].type).toStrictEqual('send/addHistoryEntry');
expect(actionResult[1].type).toStrictEqual('send/updateSendAmount');
expect(actionResult[2].type).toStrictEqual(
'send/computeEstimatedGasLimit/pending',
);
expect(actionResult[3].type).toStrictEqual(
'send/computeEstimatedGasLimit/rejected',
'metamask/gas/SET_CUSTOM_GAS_LIMIT',
);
expect(actionResult[4].type).toStrictEqual(
'send/computeEstimatedGasLimit/fulfilled',
);
});
});
@ -1636,7 +1645,7 @@ describe('Send Slice', () => {
const actionResult = store.getActions();
expect(actionResult).toHaveLength(4);
expect(actionResult).toHaveLength(5);
expect(actionResult[0]).toMatchObject({
type: 'send/addHistoryEntry',
@ -1657,7 +1666,10 @@ describe('Send Slice', () => {
'send/computeEstimatedGasLimit/pending',
);
expect(actionResult[3].type).toStrictEqual(
'send/computeEstimatedGasLimit/rejected',
'metamask/gas/SET_CUSTOM_GAS_LIMIT',
);
expect(actionResult[4].type).toStrictEqual(
'send/computeEstimatedGasLimit/fulfilled',
);
});
@ -1692,7 +1704,7 @@ describe('Send Slice', () => {
const actionResult = store.getActions();
expect(actionResult).toHaveLength(6);
expect(actionResult).toHaveLength(7);
expect(actionResult[0].type).toStrictEqual('SHOW_LOADING_INDICATION');
expect(actionResult[1].type).toStrictEqual('HIDE_LOADING_INDICATION');
expect(actionResult[2]).toMatchObject({
@ -1719,7 +1731,10 @@ describe('Send Slice', () => {
'send/computeEstimatedGasLimit/pending',
);
expect(actionResult[5].type).toStrictEqual(
'send/computeEstimatedGasLimit/rejected',
'metamask/gas/SET_CUSTOM_GAS_LIMIT',
);
expect(actionResult[6].type).toStrictEqual(
'send/computeEstimatedGasLimit/fulfilled',
);
});
@ -2092,7 +2107,7 @@ describe('Send Slice', () => {
await store.dispatch(resetRecipientInput());
const actionResult = store.getActions();
expect(actionResult).toHaveLength(11);
expect(actionResult).toHaveLength(12);
expect(actionResult[0]).toMatchObject({
type: 'send/addHistoryEntry',
payload: 'sendFlow - user cleared recipient input',
@ -2118,10 +2133,15 @@ describe('Send Slice', () => {
'send/computeEstimatedGasLimit/pending',
);
expect(actionResult[8].type).toStrictEqual(
'send/computeEstimatedGasLimit/rejected',
'metamask/gas/SET_CUSTOM_GAS_LIMIT',
);
expect(actionResult[9].type).toStrictEqual(
'send/computeEstimatedGasLimit/fulfilled',
);
expect(actionResult[9].type).toStrictEqual('DNS/resetDomainResolution');
expect(actionResult[10].type).toStrictEqual(
'DNS/resetDomainResolution',
);
expect(actionResult[11].type).toStrictEqual(
'send/validateRecipientUserInput',
);
});
@ -2238,7 +2258,7 @@ describe('Send Slice', () => {
const actionResult = store.getActions();
expect(actionResult).toHaveLength(5);
expect(actionResult).toHaveLength(6);
expect(actionResult[0].type).toStrictEqual('send/updateAmountMode');
expect(actionResult[1].type).toStrictEqual('send/updateSendAmount');
expect(actionResult[2]).toMatchObject({
@ -2249,7 +2269,10 @@ describe('Send Slice', () => {
'send/computeEstimatedGasLimit/pending',
);
expect(actionResult[4].type).toStrictEqual(
'send/computeEstimatedGasLimit/rejected',
'metamask/gas/SET_CUSTOM_GAS_LIMIT',
);
expect(actionResult[5].type).toStrictEqual(
'send/computeEstimatedGasLimit/fulfilled',
);
});
});
@ -2287,14 +2310,19 @@ describe('Send Slice', () => {
it('should pass the correct transaction parameters to addTransactionAndRouteToConfirmationPage', async () => {
const tokenTransferTxState = {
metamask: {
unapprovedTxs: {
1: {
providerConfig: {
chainId: CHAIN_IDS.GOERLI,
},
transactions: [
{
id: 1,
chainId: CHAIN_IDS.GOERLI,
status: 'unapproved',
txParams: {
value: 'oldTxValue',
},
},
},
],
},
send: {
...getInitialSendStateWithExistingTxState({
@ -2339,14 +2367,19 @@ describe('Send Slice', () => {
it('should create actions for updateTransaction rejecting', async () => {
const editStageSignTxState = {
metamask: {
unapprovedTxs: {
1: {
providerConfig: {
chainId: '0x1',
},
transactions: [
{
id: 1,
chainId: '0x1',
status: 'unapproved',
txParams: {
value: 'oldTxValue',
},
},
},
],
},
send: {
...signTransactionState.send,
@ -2405,19 +2438,21 @@ describe('Send Slice', () => {
},
},
tokenList: {},
unapprovedTxs: {
1: {
transactions: [
{
id: 1,
chainId: CHAIN_IDS.GOERLI,
status: 'unapproved',
txParams: {
data: '',
from: mockAddress1,
to: '0xRecipientAddress',
gas: GAS_LIMITS.SIMPLE,
gasPrice: '0x3b9aca00', // 1000000000
value: '0xde0b6b3a7640000', // 1000000000000000000
value: '0xde0b6b3a7640000', // 1000000000000000000,
},
},
},
],
},
send: {
// We are going to remove this transaction as a part of the flow,
@ -2542,9 +2577,11 @@ describe('Send Slice', () => {
},
},
tokenList: {},
unapprovedTxs: {
1: {
transactions: [
{
id: 1,
chainId: CHAIN_IDS.GOERLI,
status: 'unapproved',
txParams: {
data: generateERC721TransferData({
toAddress: BURN_ADDRESS,
@ -2558,7 +2595,7 @@ describe('Send Slice', () => {
value: '0x0',
},
},
},
],
},
send: {
...getInitialSendStateWithExistingTxState({
@ -2626,7 +2663,7 @@ describe('Send Slice', () => {
status: SEND_STATUSES.VALID,
transactionType: '0x0',
userInputHexData:
editTransactionState.metamask.unapprovedTxs[1].txParams.data,
editTransactionState.metamask.transactions[0].txParams.data,
},
});
expect(actionResult[2].type).toStrictEqual('SHOW_LOADING_INDICATION');
@ -2726,9 +2763,11 @@ describe('Send Slice', () => {
[mockAddress1]: '0x0',
},
},
unapprovedTxs: {
1: {
transactions: [
{
id: 1,
chainId: CHAIN_IDS.GOERLI,
status: 'unapproved',
txParams: {
data: generateERC20TransferData({
toAddress: BURN_ADDRESS,
@ -2746,7 +2785,7 @@ describe('Send Slice', () => {
value: '0x0',
},
},
},
],
},
send: {
...getInitialSendStateWithExistingTxState({
@ -2819,7 +2858,7 @@ describe('Send Slice', () => {
status: SEND_STATUSES.VALID,
transactionType: '0x0',
userInputHexData:
editTransactionState.metamask.unapprovedTxs[1].txParams.data,
editTransactionState.metamask.transactions[0].txParams.data,
},
});
expect(actionResult[2].type).toStrictEqual('SHOW_LOADING_INDICATION');

View File

@ -17,6 +17,7 @@ import configureStore from './store/store';
import {
getPermittedAccountsForCurrentTab,
getSelectedAddress,
getUnapprovedTransactions,
} from './selectors';
import { ALERT_STATE } from './ducks/alerts';
import {
@ -152,9 +153,11 @@ async function startApp(metamaskState, backgroundConnection, opts) {
const store = configureStore(draftInitialState);
reduxStore = store;
const unapprovedTxs = getUnapprovedTransactions(metamaskState);
// if unconfirmed txs, start on txConf page
const unapprovedTxsAll = txHelper(
metamaskState.unapprovedTxs,
unapprovedTxs,
metamaskState.unapprovedMsgs,
metamaskState.unapprovedPersonalMsgs,
metamaskState.unapprovedDecryptMsgs,

View File

@ -4,7 +4,7 @@ import { text } from '@storybook/addon-knobs';
import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { updateMetamaskState } from '../../store/actions';
import { currentNetworkTxListSelector } from '../../selectors/transactions';
import { getCurrentNetworkTransactions } from '../../selectors/transactions';
import { store, getNewState } from '../../../.storybook/preview';
import { subjectMetadata } from '../../../.storybook/initial-states/approval-screens/token-approval';
@ -15,7 +15,7 @@ export default {
title: 'Pages/ConfirmApprove',
};
// transaction ID, maps to entry in state.metamask.currentNetworkTxList
// transaction ID, maps to entry in state.metamask.transactions
const txId = 7900715443136469;
const PageSet = ({ children }) => {
@ -25,7 +25,7 @@ const PageSet = ({ children }) => {
'https://metamask.github.io/test-dapp/metamask-fox.svg',
);
const state = store.getState();
const currentNetworkTxList = useSelector(currentNetworkTxListSelector);
const currentNetworkTxList = useSelector(getCurrentNetworkTransactions);
const transaction = currentNetworkTxList.find(({ id }) => id === txId);
useEffect(() => {
@ -33,7 +33,7 @@ const PageSet = ({ children }) => {
store.dispatch(
updateMetamaskState(
getNewState(state.metamask, {
currentNetworkTxList: [transaction],
transactions: [transaction],
}),
),
);

View File

@ -40,10 +40,12 @@ const sendEther = {
},
};
mockState.metamask.unapprovedTxs[sendEther.id] = sendEther;
mockState.metamask.transactions.push(sendEther);
mockState.confirmTransaction = {
txData: sendEther,
};
const store = configureStore(mockState);
export default {

View File

@ -50,10 +50,12 @@ const sendEther = {
},
};
mockState.metamask.unapprovedTxs[sendEther.id] = sendEther;
mockState.metamask.transactions.push(sendEther);
mockState.confirmTransaction = {
txData: sendEther,
};
const store = configureStore(mockState);
describe('ConfirmSendEther', () => {

View File

@ -18,6 +18,8 @@ import {
getSelectedAccount,
///: END:ONLY_INCLUDE_IN
getTargetSubjectMetadata,
getCurrentNetworkTransactions,
getUnapprovedTransactions,
} from '../../selectors';
import { MESSAGE_TYPE } from '../../../shared/constants/app';
import { TransactionStatus } from '../../../shared/constants/transaction';
@ -53,9 +55,7 @@ const ConfirmTxScreen = ({ match }) => {
);
const sendTo = useSelector(getSendTo);
const {
unapprovedTxs,
identities,
currentNetworkTxList,
currentCurrency,
unapprovedMsgs,
unapprovedPersonalMsgs,
@ -63,6 +63,8 @@ const ConfirmTxScreen = ({ match }) => {
networkId,
blockGasLimit,
} = useSelector((state) => state.metamask);
const unapprovedTxs = useSelector(getUnapprovedTransactions);
const currentNetworkTxList = useSelector(getCurrentNetworkTransactions);
const { chainId } = useSelector(getProviderConfig);
const { txId: index } = useSelector((state) => state.appState);

View File

@ -41,6 +41,7 @@ import {
getUnapprovedTransaction,
getFullTxData,
getUseCurrencyRateCheck,
getUnapprovedTransactions,
} from '../../selectors';
import { getMostRecentOverviewPage } from '../../ducks/history/history';
import {
@ -121,14 +122,9 @@ const mapStateToProps = (state, ownProps) => {
const gasLoadingAnimationIsShowing = getGasLoadingAnimationIsShowing(state);
const isBuyableChain = getIsBuyableChain(state);
const { confirmTransaction, metamask } = state;
const {
conversionRate,
identities,
addressBook,
networkId,
unapprovedTxs,
nextNonce,
} = metamask;
const { conversionRate, identities, addressBook, networkId, nextNonce } =
metamask;
const unapprovedTxs = getUnapprovedTransactions(state);
const { chainId } = getProviderConfig(state);
const { tokenData, txData, tokenProps, nonce } = confirmTransaction;
const { txParams = {}, id: transactionId, type } = txData;

View File

@ -66,13 +66,14 @@ const baseStore = {
},
history: { mostRecentOverviewPage: '/' },
metamask: {
unapprovedTxs: {
1: {
transactions: [
{
id: 1,
metamaskNetworkId: mockNetworkId,
txParams: { ...mockTxParams },
status: 'unapproved',
},
},
],
gasEstimateType: GasEstimateTypes.legacy,
gasFeeEstimates: {
low: '0',
@ -175,7 +176,7 @@ const baseStore = {
const mockedStore = jest.mocked(baseStore);
const mockedStoreWithConfirmTxParams = (_mockTxParams = mockTxParams) => {
mockedStore.metamask.unapprovedTxs[1].txParams = { ..._mockTxParams };
mockedStore.metamask.transactions[0].txParams = { ..._mockTxParams };
mockedStore.confirmTransaction.txData.txParams = { ..._mockTxParams };
};

View File

@ -1,11 +1,12 @@
import { connect } from 'react-redux';
import { unconfirmedTransactionsListSelector } from '../../selectors';
import {
getUnapprovedTransactions,
unconfirmedTransactionsListSelector,
} from '../../selectors';
import ConfirmTransactionSwitch from './confirm-transaction-switch.component';
const mapStateToProps = (state, ownProps) => {
const {
metamask: { unapprovedTxs },
} = state;
const unapprovedTxs = getUnapprovedTransactions(state);
const {
match: { params = {}, url },
} = ownProps;

View File

@ -22,7 +22,7 @@ import {
import ConfirmTransaction from '.';
const mockUnapprovedTx = Object.values(_mockState.metamask.unapprovedTxs)[0];
const mockUnapprovedTx = _mockState.metamask.transactions[0];
const middleware = [thunk];
@ -137,7 +137,7 @@ describe('Confirmation Transaction Page', () => {
...mockState,
metamask: {
...mockState.metamask,
unapprovedTxs: {},
transactions: [],
},
});
const { container } = renderWithProvider(<ConfirmTransaction />, mockStore);
@ -177,12 +177,12 @@ describe('Confirmation Transaction Page', () => {
...mockState,
metamask: {
...mockState.metamask,
unapprovedTxs: {
[mockUnapprovedTx.id]: {
transactions: [
{
...mockUnapprovedTx,
type: 'transfer',
},
},
],
},
});
const { container } = renderWithProvider(
@ -237,7 +237,7 @@ describe('Confirmation Transaction Page', () => {
it('should not call setTransactionToConfirm when transaction id is not provided', () => {
const mockStore = configureMockStore(middleware)({
...mockState,
metamask: { ...mockState.metamask, unapprovedTxs: {} },
metamask: { ...mockState.metamask, transactions: [] },
});
jest.spyOn(ReactRouterDOM, 'useParams').mockImplementation(() => {
return { id: null };
@ -272,7 +272,7 @@ describe('Confirmation Transaction Page', () => {
...mockState,
metamask: {
...mockState.metamask,
unapprovedTxs: {},
transactions: [],
},
});
const replaceSpy = jest.fn();

View File

@ -25,13 +25,13 @@ setBackgroundConnection({
describe('Confirm Transaction', () => {
const unapprovedTransactionId = Object.keys(
mockState.metamask.unapprovedTxs,
mockState.metamask.transactions,
)[0];
it('should render correct information for approve transaction with value', () => {
const store = configureMockStore(middleware)({
...mockState,
confirmTransaction: {
txData: mockState.metamask.unapprovedTxs[unapprovedTransactionId],
txData: mockState.metamask.transactions[0],
},
});
const { getByText, getByRole } = renderWithProvider(

View File

@ -51,6 +51,10 @@ describe('success template', () => {
type: ApprovalType.ResultSuccess,
},
},
providerConfig: {
chainId: '0x1',
},
transactions: [],
},
};
const store = configureMockStore(middleware)(testStore);

View File

@ -63,6 +63,7 @@ describe('switch-ethereum-chain confirmation', () => {
type: MESSAGE_TYPE.SWITCH_ETHEREUM_CHAIN,
},
},
transactions: [],
},
};
const store = configureMockStore(middleware)(testStore);
@ -83,11 +84,12 @@ describe('switch-ethereum-chain confirmation', () => {
type: MESSAGE_TYPE.SWITCH_ETHEREUM_CHAIN,
},
},
unapprovedTxs: {
1: {
transactions: [
{
id: 1,
status: 'unapproved',
},
},
],
},
};

View File

@ -27,6 +27,7 @@ import {
transactionFeeSelector,
getIsTestnet,
getUseCurrencyRateCheck,
getUnapprovedTransactions,
} from '../../../selectors';
import { INSUFFICIENT_TOKENS_ERROR } from '../send.constants';
@ -62,7 +63,7 @@ export default function GasDisplay({ gasError }) {
const useCurrencyRateCheck = useSelector(getUseCurrencyRateCheck);
const { showFiatInTestnets, useNativeCurrencyAsPrimaryCurrency } =
useSelector(getPreferences);
const { unapprovedTxs } = useSelector((state) => state.metamask);
const unapprovedTxs = useSelector(getUnapprovedTransactions);
const nativeCurrency = useSelector(getNativeCurrency);
const { chainId } = providerConfig;
const networkName = NETWORK_TO_NAME_MAP[chainId];

View File

@ -3,7 +3,7 @@ import {
getAddressBook,
getAddressBookEntry,
getMetaMaskAccountsOrdered,
currentNetworkTxListSelector,
getCurrentNetworkTransactions,
} from '../../../../selectors';
import {
@ -35,7 +35,7 @@ function mapStateToProps(state) {
const addressBook = getAddressBook(state);
const txList = [...currentNetworkTxListSelector(state)].reverse();
const txList = [...getCurrentNetworkTransactions(state)].reverse();
const nonContacts = addressBook
.filter(({ name }) => !name)

View File

@ -16,7 +16,7 @@ jest.mock('../../../../selectors', () => ({
{ name: `account1:mockState` },
{ name: `account2:mockState` },
],
currentNetworkTxListSelector: (s) => `currentNetworkTxListSelector:${s}`,
getCurrentNetworkTransactions: (s) => `getCurrentNetworkTransactions:${s}`,
}));
jest.mock('../../../../ducks/domains', () => ({

View File

@ -65,14 +65,14 @@ const baseStore = {
},
history: { mostRecentOverviewPage: 'activity' },
metamask: {
unapprovedTxs: {
1: {
transactions: [
{
id: 1,
txParams: {
value: 'oldTxValue',
},
},
},
],
gasEstimateType: GasEstimateTypes.legacy,
gasFeeEstimates: {
low: '0',
@ -108,7 +108,6 @@ const baseStore = {
addressBook: {
[CHAIN_IDS.GOERLI]: [],
},
currentNetworkTxList: [],
cachedBalances: {
[CHAIN_IDS.GOERLI]: {},
},

View File

@ -54,7 +54,7 @@ import {
} from '../../ducks/swaps/swaps';
import {
checkNetworkAndAccountSupports1559,
currentNetworkTxListSelector,
getCurrentNetworkTransactions,
} from '../../selectors';
import {
AWAITING_SIGNATURES_ROUTE,
@ -137,7 +137,7 @@ export default function Swap() {
const selectedAccount = useSelector(getSelectedAccount, shallowEqual);
const quotes = useSelector(getQuotes, isEqual);
const latestAddedTokenTo = useSelector(getLatestAddedTokenTo, isEqual);
const txList = useSelector(currentNetworkTxListSelector, shallowEqual);
const txList = useSelector(getCurrentNetworkTransactions, shallowEqual);
const tradeTxId = useSelector(getTradeTxId);
const approveTxId = useSelector(getApproveTxId);
const aggregatorMetadata = useSelector(getAggregatorMetadata, shallowEqual);

View File

@ -73,7 +73,7 @@ const state = {
isERC721: false,
},
],
unapprovedTxs: {},
transactions: [],
keyringTypes: [],
keyrings: [
{

View File

@ -30,9 +30,12 @@ import {
} from '../../shared/modules/conversion.utils';
import { getAveragePriceEstimateInHexWEI } from './custom-gas';
import { getCurrentChainId, deprecatedGetCurrentNetworkId } from './selectors';
import { checkNetworkAndAccountSupports1559 } from '.';
import {
checkNetworkAndAccountSupports1559,
getUnapprovedTransactions,
} from '.';
const unapprovedTxsSelector = (state) => state.metamask.unapprovedTxs;
const unapprovedTxsSelector = (state) => getUnapprovedTransactions(state);
const unapprovedMsgsSelector = (state) => state.metamask.unapprovedMsgs;
const unapprovedPersonalMsgsSelector = (state) =>
state.metamask.unapprovedPersonalMsgs;

View File

@ -81,9 +81,7 @@ const getStateTree = ({
unapprovedMsgs,
selectedAddress: SENDERS.ONE,
featureFlags: {},
transactions: [...incomingTxList],
incomingTransactions: [...incomingTxList],
currentNetworkTxList: [...txList],
transactions: [...incomingTxList, ...txList],
incomingTransactionsPreferences: {},
},
});

View File

@ -2,17 +2,12 @@
import { SubjectType } from '@metamask/subject-metadata-controller';
///: END:ONLY_INCLUDE_IN
import { ApprovalType } from '@metamask/controller-utils';
import {
createSelector,
createSelectorCreator,
defaultMemoize,
} from 'reselect';
import {
///: BEGIN:ONLY_INCLUDE_IN(snaps)
memoize,
///: END:ONLY_INCLUDE_IN
isEqual,
} from 'lodash';
import { createSelector } from 'reselect';
import { addHexPrefix } from '../../app/scripts/lib/util';
import {
TEST_CHAINS,
@ -97,10 +92,16 @@ import {
NOTIFICATION_DROP_LEDGER_FIREFOX,
NOTIFICATION_OPEN_BETA_SNAPS,
} from '../../shared/notifications';
import {
getCurrentNetworkTransactions,
getUnapprovedTransactions,
} from './transactions';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
// eslint-disable-next-line import/order
import { SNAPS_VIEW_ROUTE } from '../helpers/constants/routes';
import { getPermissionSubjects } from './permissions';
///: END:ONLY_INCLUDE_IN
import { createDeepEqualSelector } from './util';
/**
* Returns true if the currently selected network is inaccessible or whether no
@ -578,7 +579,7 @@ export function getTotalUnapprovedSignatureRequestCount(state) {
}
export function getUnapprovedTxCount(state) {
const { unapprovedTxs = {} } = state.metamask;
const unapprovedTxs = getUnapprovedTransactions(state);
return Object.keys(unapprovedTxs).length;
}
@ -852,8 +853,6 @@ export function getShowWhatsNewPopup(state) {
return state.appState.showWhatsNewPopup;
}
const createDeepEqualSelector = createSelectorCreator(defaultMemoize, isEqual);
export const getMemoizedMetaMaskIdentities = createDeepEqualSelector(
getMetaMaskIdentities,
(identities) => identities,
@ -875,16 +874,10 @@ export const getMemoizedMetadataContractName = createDeepEqualSelector(
},
);
export const getUnapprovedTransactions = (state) =>
state.metamask.unapprovedTxs;
export const getCurrentNetworkTransactionList = (state) =>
state.metamask.currentNetworkTxList;
export const getTxData = (state) => state.confirmTransaction.txData;
export const getUnapprovedTransaction = createDeepEqualSelector(
getUnapprovedTransactions,
(state) => getUnapprovedTransactions(state),
(_, transactionId) => transactionId,
(unapprovedTxs, transactionId) => {
return (
@ -894,7 +887,7 @@ export const getUnapprovedTransaction = createDeepEqualSelector(
);
export const getTransaction = createDeepEqualSelector(
getCurrentNetworkTransactionList,
(state) => getCurrentNetworkTransactions(state),
(_, transactionId) => transactionId,
(unapprovedTxs, transactionId) => {
return (

View File

@ -1,5 +1,5 @@
import { createSelector } from 'reselect';
import { ApprovalType } from '@metamask/controller-utils';
import { createSelector } from 'reselect';
import {
PRIORITY_STATUS_HASH,
PENDING_STATUS_HASH,
@ -19,32 +19,67 @@ import {
getSelectedAddress,
} from './selectors';
import { hasPendingApprovals, getApprovalRequestsByType } from './approvals';
import { createDeepEqualSelector } from './util';
const INVALID_INITIAL_TRANSACTION_TYPES = [
TransactionType.cancel,
TransactionType.retry,
];
export const incomingTxListSelector = (state) => {
const { incomingTransactionsPreferences } = state.metamask;
if (!incomingTransactionsPreferences) {
return [];
}
const { networkId } = state.metamask;
const { chainId } = getProviderConfig(state);
const selectedAddress = getSelectedAddress(state);
return Object.values(state.metamask.transactions || {}).filter(
(tx) =>
tx.type === TransactionType.incoming &&
tx.txParams.to === selectedAddress &&
transactionMatchesNetwork(tx, chainId, networkId),
);
};
export const unapprovedMsgsSelector = (state) => state.metamask.unapprovedMsgs;
export const currentNetworkTxListSelector = (state) =>
state.metamask.currentNetworkTxList;
export const getCurrentNetworkTransactions = createDeepEqualSelector(
(state) => {
const { transactions, networkId } = state.metamask ?? {};
if (!transactions?.length) {
return [];
}
const { chainId } = getProviderConfig(state);
return transactions.filter((transaction) =>
transactionMatchesNetwork(transaction, chainId, networkId),
);
},
(transactions) => transactions,
);
export const getUnapprovedTransactions = createDeepEqualSelector(
(state) => {
const currentNetworkTransactions = getCurrentNetworkTransactions(state);
return currentNetworkTransactions
.filter(
(transaction) => transaction.status === TransactionStatus.unapproved,
)
.reduce((result, transaction) => {
result[transaction.id] = transaction;
return result;
}, {});
},
(transactions) => transactions,
);
export const incomingTxListSelector = createDeepEqualSelector(
(state) => {
const { incomingTransactionsPreferences } = state.metamask;
if (!incomingTransactionsPreferences) {
return [];
}
const currentNetworkTransactions = getCurrentNetworkTransactions(state);
const selectedAddress = getSelectedAddress(state);
return currentNetworkTransactions.filter(
(tx) =>
tx.type === TransactionType.incoming &&
tx.txParams.to === selectedAddress,
);
},
(transactions) => transactions,
);
export const unapprovedPersonalMsgsSelector = (state) =>
state.metamask.unapprovedPersonalMsgs;
export const unapprovedDecryptMsgsSelector = (state) =>
@ -69,7 +104,7 @@ export const smartTransactionsListSelector = (state) =>
export const selectedAddressTxListSelector = createSelector(
getSelectedAddress,
currentNetworkTxListSelector,
getCurrentNetworkTransactions,
smartTransactionsListSelector,
(selectedAddress, transactions = [], smTransactions = []) => {
return transactions
@ -540,7 +575,7 @@ export const submittedPendingTransactionsSelector = createSelector(
);
const hasUnapprovedTransactionsInCurrentNetwork = (state) => {
const { unapprovedTxs } = state.metamask;
const unapprovedTxs = getUnapprovedTransactions(state);
const unapprovedTxRequests = getApprovalRequestsByType(
state,
ApprovalType.Transaction,

View File

@ -106,7 +106,7 @@ describe('Transaction Selectors', () => {
});
describe('transactionsSelector', () => {
it('selects the currentNetworkTxList', () => {
it('selects the current network transactions', () => {
const state = {
metamask: {
providerConfig: {
@ -115,9 +115,10 @@ describe('Transaction Selectors', () => {
},
featureFlags: {},
selectedAddress: '0xAddress',
currentNetworkTxList: [
transactions: [
{
id: 0,
chainId: CHAIN_IDS.MAINNET,
time: 0,
txParams: {
from: '0xAddress',
@ -126,6 +127,7 @@ describe('Transaction Selectors', () => {
},
{
id: 1,
chainId: CHAIN_IDS.MAINNET,
time: 1,
txParams: {
from: '0xAddress',
@ -136,7 +138,7 @@ describe('Transaction Selectors', () => {
},
};
const orderedTxList = state.metamask.currentNetworkTxList.sort(
const orderedTxList = state.metamask.transactions.sort(
(a, b) => b.time - a.time,
);
@ -177,7 +179,7 @@ describe('Transaction Selectors', () => {
},
selectedAddress: '0xAddress',
featureFlags: {},
currentNetworkTxList: [tx1, tx2],
transactions: [tx1, tx2],
},
};
@ -259,12 +261,7 @@ describe('Transaction Selectors', () => {
},
selectedAddress: '0xAddress',
featureFlags: {},
currentNetworkTxList: [
submittedTx,
unapprovedTx,
approvedTx,
confirmedTx,
],
transactions: [submittedTx, unapprovedTx, approvedTx, confirmedTx],
},
};
@ -352,12 +349,13 @@ describe('Transaction Selectors', () => {
requestState: null,
},
},
unapprovedTxs: {
2: {
transactions: [
{
id: '2',
chainId: mockNetworkId,
status: TransactionStatus.unapproved,
},
},
],
},
};
@ -365,11 +363,13 @@ describe('Transaction Selectors', () => {
const result = hasTransactionPendingApprovals(mockedState);
expect(result).toBe(true);
});
it('should return false if there is a pending transaction on different network', () => {
mockedState.metamask.unapprovedTxs['2'].chainId = 'differentNetworkId';
mockedState.metamask.transactions[0].chainId = 'differentNetworkId';
const result = hasTransactionPendingApprovals(mockedState);
expect(result).toBe(false);
});
it.each([
[ApprovalType.EthDecrypt],
[ApprovalType.EthGetEncryptionPublicKey],

7
ui/selectors/util.js Normal file
View File

@ -0,0 +1,7 @@
import { isEqual } from 'lodash';
import { createSelectorCreator, defaultMemoize } from 'reselect';
export const createDeepEqualSelector = createSelectorCreator(
defaultMemoize,
isEqual,
);

View File

@ -41,6 +41,7 @@ import {
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(keyring-snaps)
getPermissionSubjects,
getCurrentNetworkTransactions,
///: END:ONLY_INCLUDE_IN
} from '../selectors';
import {
@ -2066,7 +2067,8 @@ export function createCancelTransaction(
return;
}
if (newState) {
const { currentNetworkTxList } = newState;
const currentNetworkTxList =
getCurrentNetworkTransactions(newState);
const { id } =
currentNetworkTxList[currentNetworkTxList.length - 1];
newTxId = id;
@ -2103,7 +2105,8 @@ export function createSpeedUpTransaction(
}
if (newState) {
const { currentNetworkTxList } = newState;
const currentNetworkTxList =
getCurrentNetworkTransactions(newState);
newTx = currentNetworkTxList[currentNetworkTxList.length - 1];
resolve(newState);
}
@ -2135,7 +2138,8 @@ export function createRetryTransaction(
return;
}
if (newState) {
const { currentNetworkTxList } = newState;
const currentNetworkTxList =
getCurrentNetworkTransactions(newState);
newTx = currentNetworkTxList[currentNetworkTxList.length - 1];
resolve(newState);
}

View File

@ -39,7 +39,7 @@ const defaultState = {
},
},
},
currentNetworkTxList: [
transactions: [
{
id: 0,
time: 0,
@ -232,7 +232,7 @@ describe('#updateCustodyState', () => {
},
featureFlags: {},
selectedAddress: '0xAddress',
currentNetworkTxList: [
transactions: [
{
id: 0,
time: 0,
@ -285,7 +285,7 @@ describe('#updateCustodyState', () => {
},
featureFlags: {},
selectedAddress: '0xAddress',
currentNetworkTxList: [
transactions: [
{
id: 0,
time: 0,

View File

@ -12,6 +12,8 @@ import {
MessagesIndexedById,
} from '../store';
import { toChecksumHexAddress } from '../../../shared/modules/hexstring-utils';
import { getCurrentNetworkTransactions } from '../../selectors';
import { TransactionMeta } from '../../../shared/constants/transaction';
export function showInteractiveReplacementTokenModal(): ThunkAction<
void,
@ -57,13 +59,19 @@ export function updateCustodyState(
newState: MetaMaskReduxState['metamask'],
state: CombinedBackgroundAndReduxState & any,
) {
if (!newState.currentNetworkTxList || !state.metamask.currentNetworkTxList) {
if (!newState.transactions || !state.metamask.transactions) {
return;
}
const differentTxs = newState.currentNetworkTxList.filter(
(item) =>
state.metamask.currentNetworkTxList.filter(
const newCurrentNetworkTxList = getCurrentNetworkTransactions({
metamask: newState,
});
const oldCurrentNetworkTxList = getCurrentNetworkTransactions(state);
const differentTxs = newCurrentNetworkTxList.filter(
(item: TransactionMeta) =>
oldCurrentNetworkTxList.filter(
(tx: { [key: string]: any }) =>
tx.custodyId === item.custodyId &&
tx.custodyStatus !== item.custodyStatus,
@ -71,7 +79,7 @@ export function updateCustodyState(
);
const txStateSaysDeepLinkShouldClose = Boolean(
differentTxs.find((tx) => {
differentTxs.find((tx: TransactionMeta) => {
const custodyAccountDetails =
state.metamask.custodyAccountDetails[
toChecksumHexAddress(tx.txParams.from)

View File

@ -53,7 +53,7 @@ interface TemporaryBackgroundState {
providerConfig: {
chainId: string;
};
currentNetworkTxList: TransactionMeta[];
transactions: TransactionMeta[];
selectedAddress: string;
identities: {
[address: string]: {
@ -62,9 +62,6 @@ interface TemporaryBackgroundState {
};
ledgerTransportType: LedgerTransportTypes;
unapprovedDecryptMsgs: MessagesIndexedById;
unapprovedTxs: {
[transactionId: string]: TransactionMeta;
};
unapprovedMsgs: MessagesIndexedById;
unapprovedPersonalMsgs: MessagesIndexedById;
unapprovedTypedMessages: MessagesIndexedById;