1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-21 17:37:01 +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: { image: {
src: 'images/global-menu-block-explorer.svg', src: 'images/global-menu-block-explorer.svg',
}, },
} },
}, },
tokenList: { tokenList: {
'0x514910771af9ca656af840dff83e8264ecf986ca': { '0x514910771af9ca656af840dff83e8264ecf986ca': {
@ -314,8 +314,8 @@ const state = {
address: '0x9d0ba4ddac06032527b140912ec808ab9451b788', address: '0x9d0ba4ddac06032527b140912ec808ab9451b788',
}, },
}, },
unapprovedTxs: { transactions: [
3111025347726181: { {
id: 3111025347726181, id: 3111025347726181,
time: 1620710815484, time: 1620710815484,
status: 'unapproved', status: 'unapproved',
@ -365,7 +365,7 @@ const state = {
], ],
], ],
}, },
}, ],
addressBook: { addressBook: {
undefined: { undefined: {
0: { 0: {
@ -574,7 +574,7 @@ const state = {
}, },
}, },
currentBlockGasLimit: '0x793af4', currentBlockGasLimit: '0x793af4',
currentNetworkTxList: [ transactions: [
{ {
chainId: '0x38', chainId: '0x38',
dappSuggestedGasFees: null, 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} 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 {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} 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 {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 {Array} addressBook - A list of previously sent to addresses.
* @property {object} contractExchangeRates - Info about current token prices. * @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 {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 {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 {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 {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 {number} unapprovedMsgCount - The number of messages in unapprovedMsgs.
* @property {object} unapprovedPersonalMsgs - An object of messages pending approval, mapping a unique ID to the options. * @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: () => isEnabled: () =>
Boolean( Boolean(
this.preferencesStore.getState().incomingTransactionsPreferences?.[ this.preferencesStore.getState().incomingTransactionsPreferences?.[
this._getChainId() this._getCurrentChainId()
] && this._hasCompletedOnboarding(), ] && this._hasCompletedOnboarding(),
), ),
lastFetchedBlockNumbers: opts.initState?.lastFetchedBlockNumbers || {}, lastFetchedBlockNumbers: opts.initState?.lastFetchedBlockNumbers || {},
@ -2135,16 +2135,12 @@ export default class TransactionController extends EventEmitter {
* Updates the memStore in transaction controller * Updates the memStore in transaction controller
*/ */
_updateMemstore() { _updateMemstore() {
const { transactions } = this.store.getState(); const transactions = this.getTransactions({
const unapprovedTxs = this.txStateManager.getUnapprovedTxList(); filterToCurrentNetwork: false,
const currentNetworkTxList = this.txStateManager.getTransactions({
limit: MAX_MEMSTORE_TX_LIST_SIZE, limit: MAX_MEMSTORE_TX_LIST_SIZE,
}); });
this.memStore.updateState({ this.memStore.updateState({
unapprovedTxs,
currentNetworkTxList,
transactions, transactions,
}); });
} }

View File

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

View File

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

View File

@ -180,57 +180,6 @@
} }
}, },
"cachedBalances": {}, "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", "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"accounts": { "accounts": {
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
@ -347,7 +296,55 @@
"occurrences": 12 "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, "id": 3387511061307736,
"time": 1528133130531, "time": 1528133130531,

View File

@ -227,59 +227,6 @@
"0xaa36a7": true, "0xaa36a7": true,
"0xe704": 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", "selectedAddress": "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc",
"accounts": { "accounts": {
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": { "0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {
@ -643,7 +590,57 @@
"occurrences": 12 "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, "id": 3387511061307736,
"time": 1528133130531, "time": 1528133130531,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,6 +44,7 @@ import {
getMetaMetricsId, getMetaMetricsId,
///: END:ONLY_INCLUDE_IN(build-mmi) ///: END:ONLY_INCLUDE_IN(build-mmi)
getSelectedAddress, getSelectedAddress,
getUnapprovedTransactions,
///: BEGIN:ONLY_INCLUDE_IN(snaps) ///: BEGIN:ONLY_INCLUDE_IN(snaps)
getUnreadNotificationsCount, getUnreadNotificationsCount,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
@ -69,10 +70,11 @@ export const GlobalMenu = ({ closeMenu, anchorElement }) => {
const trackEvent = useContext(MetaMetricsContext); const trackEvent = useContext(MetaMetricsContext);
const history = useHistory(); const history = useHistory();
const address = useSelector(getSelectedAddress); 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) ///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
const metaMetricsId = useSelector(getMetaMetricsId); const metaMetricsId = useSelector(getMetaMetricsId);
const mmiPortfolioUrl = useSelector(getMmiPortfolioUrl); const mmiPortfolioUrl = useSelector(getMmiPortfolioUrl);

View File

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

View File

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

View File

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

View File

@ -27,7 +27,7 @@ const initialState = {
isAccountMenuOpen: false, isAccountMenuOpen: false,
isNetworkMenuOpen: false, isNetworkMenuOpen: false,
identities: {}, identities: {},
unapprovedTxs: {}, transactions: [],
networkConfigurations: {}, networkConfigurations: {},
addressBook: [], addressBook: [],
contractExchangeRates: {}, contractExchangeRates: {},
@ -111,8 +111,8 @@ export default function reduceMetamask(state = initialState, action) {
case actionConstants.UPDATE_TRANSACTION_PARAMS: { case actionConstants.UPDATE_TRANSACTION_PARAMS: {
const { id: txId, value } = action; const { id: txId, value } = action;
let { currentNetworkTxList } = metamaskState; let { transactions } = metamaskState;
currentNetworkTxList = currentNetworkTxList.map((tx) => { transactions = transactions.map((tx) => {
if (tx.id === txId) { if (tx.id === txId) {
const newTx = { ...tx }; const newTx = { ...tx };
newTx.txParams = value; newTx.txParams = value;
@ -123,7 +123,7 @@ export default function reduceMetamask(state = initialState, action) {
return { return {
...metamaskState, ...metamaskState,
currentNetworkTxList, transactions,
}; };
} }
@ -300,10 +300,6 @@ export function getSendToAccounts(state) {
return [...fromAccounts, ...addressBookAccounts]; 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 * 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, getNativeCurrency,
getSendHexDataFeatureFlagState, getSendHexDataFeatureFlagState,
getSendToAccounts, getSendToAccounts,
getUnapprovedTxs,
isNotEIP1559Network, isNotEIP1559Network,
} from './metamask'; } from './metamask';
@ -91,8 +90,8 @@ describe('MetaMask Reducers', () => {
}, },
}, },
}, },
unapprovedTxs: { transactions: [
4768706228115573: { {
id: 4768706228115573, id: 4768706228115573,
time: 1487363153561, time: 1487363153561,
status: TransactionStatus.unapproved, status: TransactionStatus.unapproved,
@ -111,7 +110,7 @@ describe('MetaMask Reducers', () => {
maxCost: 'de234b52e4a0800', maxCost: 'de234b52e4a0800',
gasPrice: '4a817c800', gasPrice: '4a817c800',
}, },
}, ],
}, },
{}, {},
), ),
@ -175,7 +174,7 @@ describe('MetaMask Reducers', () => {
it('updates value of tx by id', () => { it('updates value of tx by id', () => {
const oldState = { const oldState = {
currentNetworkTxList: [ transactions: [
{ {
id: 1, id: 1,
txParams: 'foo', txParams: 'foo',
@ -189,7 +188,7 @@ describe('MetaMask Reducers', () => {
value: 'bar', value: 'bar',
}); });
expect(state.currentNetworkTxList[0].txParams).toStrictEqual('bar'); expect(state.transactions[0].txParams).toStrictEqual('bar');
}); });
it('close welcome screen', () => { 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()', () => { describe('isNotEIP1559Network()', () => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,9 +30,12 @@ import {
} from '../../shared/modules/conversion.utils'; } from '../../shared/modules/conversion.utils';
import { getAveragePriceEstimateInHexWEI } from './custom-gas'; import { getAveragePriceEstimateInHexWEI } from './custom-gas';
import { getCurrentChainId, deprecatedGetCurrentNetworkId } from './selectors'; 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 unapprovedMsgsSelector = (state) => state.metamask.unapprovedMsgs;
const unapprovedPersonalMsgsSelector = (state) => const unapprovedPersonalMsgsSelector = (state) =>
state.metamask.unapprovedPersonalMsgs; state.metamask.unapprovedPersonalMsgs;

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { createSelector } from 'reselect';
import { ApprovalType } from '@metamask/controller-utils'; import { ApprovalType } from '@metamask/controller-utils';
import { createSelector } from 'reselect';
import { import {
PRIORITY_STATUS_HASH, PRIORITY_STATUS_HASH,
PENDING_STATUS_HASH, PENDING_STATUS_HASH,
@ -19,32 +19,67 @@ import {
getSelectedAddress, getSelectedAddress,
} from './selectors'; } from './selectors';
import { hasPendingApprovals, getApprovalRequestsByType } from './approvals'; import { hasPendingApprovals, getApprovalRequestsByType } from './approvals';
import { createDeepEqualSelector } from './util';
const INVALID_INITIAL_TRANSACTION_TYPES = [ const INVALID_INITIAL_TRANSACTION_TYPES = [
TransactionType.cancel, TransactionType.cancel,
TransactionType.retry, 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 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) => export const unapprovedPersonalMsgsSelector = (state) =>
state.metamask.unapprovedPersonalMsgs; state.metamask.unapprovedPersonalMsgs;
export const unapprovedDecryptMsgsSelector = (state) => export const unapprovedDecryptMsgsSelector = (state) =>
@ -69,7 +104,7 @@ export const smartTransactionsListSelector = (state) =>
export const selectedAddressTxListSelector = createSelector( export const selectedAddressTxListSelector = createSelector(
getSelectedAddress, getSelectedAddress,
currentNetworkTxListSelector, getCurrentNetworkTransactions,
smartTransactionsListSelector, smartTransactionsListSelector,
(selectedAddress, transactions = [], smTransactions = []) => { (selectedAddress, transactions = [], smTransactions = []) => {
return transactions return transactions
@ -540,7 +575,7 @@ export const submittedPendingTransactionsSelector = createSelector(
); );
const hasUnapprovedTransactionsInCurrentNetwork = (state) => { const hasUnapprovedTransactionsInCurrentNetwork = (state) => {
const { unapprovedTxs } = state.metamask; const unapprovedTxs = getUnapprovedTransactions(state);
const unapprovedTxRequests = getApprovalRequestsByType( const unapprovedTxRequests = getApprovalRequestsByType(
state, state,
ApprovalType.Transaction, ApprovalType.Transaction,

View File

@ -106,7 +106,7 @@ describe('Transaction Selectors', () => {
}); });
describe('transactionsSelector', () => { describe('transactionsSelector', () => {
it('selects the currentNetworkTxList', () => { it('selects the current network transactions', () => {
const state = { const state = {
metamask: { metamask: {
providerConfig: { providerConfig: {
@ -115,9 +115,10 @@ describe('Transaction Selectors', () => {
}, },
featureFlags: {}, featureFlags: {},
selectedAddress: '0xAddress', selectedAddress: '0xAddress',
currentNetworkTxList: [ transactions: [
{ {
id: 0, id: 0,
chainId: CHAIN_IDS.MAINNET,
time: 0, time: 0,
txParams: { txParams: {
from: '0xAddress', from: '0xAddress',
@ -126,6 +127,7 @@ describe('Transaction Selectors', () => {
}, },
{ {
id: 1, id: 1,
chainId: CHAIN_IDS.MAINNET,
time: 1, time: 1,
txParams: { txParams: {
from: '0xAddress', 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, (a, b) => b.time - a.time,
); );
@ -177,7 +179,7 @@ describe('Transaction Selectors', () => {
}, },
selectedAddress: '0xAddress', selectedAddress: '0xAddress',
featureFlags: {}, featureFlags: {},
currentNetworkTxList: [tx1, tx2], transactions: [tx1, tx2],
}, },
}; };
@ -259,12 +261,7 @@ describe('Transaction Selectors', () => {
}, },
selectedAddress: '0xAddress', selectedAddress: '0xAddress',
featureFlags: {}, featureFlags: {},
currentNetworkTxList: [ transactions: [submittedTx, unapprovedTx, approvedTx, confirmedTx],
submittedTx,
unapprovedTx,
approvedTx,
confirmedTx,
],
}, },
}; };
@ -352,12 +349,13 @@ describe('Transaction Selectors', () => {
requestState: null, requestState: null,
}, },
}, },
unapprovedTxs: { transactions: [
2: { {
id: '2', id: '2',
chainId: mockNetworkId, chainId: mockNetworkId,
status: TransactionStatus.unapproved,
}, },
}, ],
}, },
}; };
@ -365,11 +363,13 @@ describe('Transaction Selectors', () => {
const result = hasTransactionPendingApprovals(mockedState); const result = hasTransactionPendingApprovals(mockedState);
expect(result).toBe(true); expect(result).toBe(true);
}); });
it('should return false if there is a pending transaction on different network', () => { 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); const result = hasTransactionPendingApprovals(mockedState);
expect(result).toBe(false); expect(result).toBe(false);
}); });
it.each([ it.each([
[ApprovalType.EthDecrypt], [ApprovalType.EthDecrypt],
[ApprovalType.EthGetEncryptionPublicKey], [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 ///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(keyring-snaps) ///: BEGIN:ONLY_INCLUDE_IN(keyring-snaps)
getPermissionSubjects, getPermissionSubjects,
getCurrentNetworkTransactions,
///: END:ONLY_INCLUDE_IN ///: END:ONLY_INCLUDE_IN
} from '../selectors'; } from '../selectors';
import { import {
@ -2066,7 +2067,8 @@ export function createCancelTransaction(
return; return;
} }
if (newState) { if (newState) {
const { currentNetworkTxList } = newState; const currentNetworkTxList =
getCurrentNetworkTransactions(newState);
const { id } = const { id } =
currentNetworkTxList[currentNetworkTxList.length - 1]; currentNetworkTxList[currentNetworkTxList.length - 1];
newTxId = id; newTxId = id;
@ -2103,7 +2105,8 @@ export function createSpeedUpTransaction(
} }
if (newState) { if (newState) {
const { currentNetworkTxList } = newState; const currentNetworkTxList =
getCurrentNetworkTransactions(newState);
newTx = currentNetworkTxList[currentNetworkTxList.length - 1]; newTx = currentNetworkTxList[currentNetworkTxList.length - 1];
resolve(newState); resolve(newState);
} }
@ -2135,7 +2138,8 @@ export function createRetryTransaction(
return; return;
} }
if (newState) { if (newState) {
const { currentNetworkTxList } = newState; const currentNetworkTxList =
getCurrentNetworkTransactions(newState);
newTx = currentNetworkTxList[currentNetworkTxList.length - 1]; newTx = currentNetworkTxList[currentNetworkTxList.length - 1];
resolve(newState); resolve(newState);
} }

View File

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

View File

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

View File

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