From 07adba59262cfb8591eb9e9a4a56e22cdc1264a0 Mon Sep 17 00:00:00 2001 From: Matthew Walsh Date: Mon, 4 Sep 2023 16:48:25 +0100 Subject: [PATCH] Use transactions array in frontend (#20523) Send the entire transactions array to the frontend rather than currentNetworkTxList and unapprovedTxs. --- .storybook/test-data.js | 10 +- app/scripts/background.js | 2 - app/scripts/controllers/transactions/index.js | 10 +- .../controllers/transactions/index.test.js | 21 +--- app/scripts/lib/setupSentry.js | 3 - test/data/mock-send-state.json | 101 ++++++++--------- test/data/mock-state.json | 105 +++++++++--------- ...rs-after-init-opt-in-background-state.json | 2 - .../errors-after-init-opt-in-ui-state.json | 2 - test/jest/mock-store.js | 4 +- .../confirm-legacy-gas-display.test.js | 22 ++-- .../signature-request-siwe.test.js | 9 +- .../transaction-list/transaction-list.test.js | 1 + .../multichain/app-header/app-header.js | 8 +- .../multichain/global-menu/global-menu.js | 8 +- .../global-menu/global-menu.test.js | 4 +- ui/ducks/app/app.test.js | 16 +-- .../confirm-transaction.duck.test.js | 6 +- ui/ducks/metamask/metamask.js | 12 +- ui/ducks/metamask/metamask.test.js | 35 +----- ui/ducks/send/send.js | 10 +- ui/ducks/send/send.test.js | 101 +++++++++++------ ui/index.js | 5 +- .../confirm-approve.stories-to-do.js | 8 +- .../confirm-send-ether.stories.js | 4 +- .../confirm-send-ether.test.js | 4 +- ui/pages/confirm-signature-request/index.js | 6 +- .../confirm-transaction-base.container.js | 12 +- .../confirm-transaction-base.test.js | 9 +- .../confirm-transaction-switch.container.js | 9 +- .../confirm-transaction.test.js | 14 +-- .../confirm-transaction.transaction.test.js | 4 +- .../confirmation/templates/success.test.js | 4 + .../templates/switch-ethereum-chain.test.js | 8 +- ui/pages/send/gas-display/gas-display.js | 3 +- .../add-recipient/add-recipient.container.js | 4 +- .../add-recipient.container.test.js | 2 +- ui/pages/send/send.test.js | 7 +- ui/pages/swaps/index.js | 4 +- .../token-allowance/token-allowance.test.js | 2 +- ui/selectors/confirm-transaction.js | 7 +- ...nonce-sorted-transactions-selector.test.js | 4 +- ui/selectors/selectors.js | 27 ++--- ui/selectors/transactions.js | 79 +++++++++---- ui/selectors/transactions.test.js | 28 ++--- ui/selectors/util.js | 7 ++ ui/store/actions.ts | 10 +- .../institutional/institution-actions.test.js | 6 +- ui/store/institutional/institution-actions.ts | 18 ++- ui/store/store.ts | 5 +- 50 files changed, 416 insertions(+), 376 deletions(-) create mode 100644 ui/selectors/util.js diff --git a/.storybook/test-data.js b/.storybook/test-data.js index 7d0cbaff4..d7c7db0d5 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -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, diff --git a/app/scripts/background.js b/app/scripts/background.js index 12cac703c..b68936848 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -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. diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index 7d4ebd7d2..d4bf0067b 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -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, }); } diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index 49ab66616..d882b1064 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -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'); }); }); diff --git a/app/scripts/lib/setupSentry.js b/app/scripts/lib/setupSentry.js index 6e2f21c2a..edc7925e2 100644 --- a/app/scripts/lib/setupSentry.js +++ b/app/scripts/lib/setupSentry.js @@ -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, }, }; diff --git a/test/data/mock-send-state.json b/test/data/mock-send-state.json index 92ea465a4..969f7fdcc 100644 --- a/test/data/mock-send-state.json +++ b/test/data/mock-send-state.json @@ -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, diff --git a/test/data/mock-state.json b/test/data/mock-state.json index dc244dffa..32730d99e 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -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, diff --git a/test/e2e/tests/state-snapshots/errors-after-init-opt-in-background-state.json b/test/e2e/tests/state-snapshots/errors-after-init-opt-in-background-state.json index a2031d804..a522a1b11 100644 --- a/test/e2e/tests/state-snapshots/errors-after-init-opt-in-background-state.json +++ b/test/e2e/tests/state-snapshots/errors-after-init-opt-in-background-state.json @@ -218,8 +218,6 @@ "allDetectedTokens": {} }, "TxController": { - "unapprovedTxs": "object", - "currentNetworkTxList": "object", "transactions": "object" } } diff --git a/test/e2e/tests/state-snapshots/errors-after-init-opt-in-ui-state.json b/test/e2e/tests/state-snapshots/errors-after-init-opt-in-ui-state.json index 4a6262ad2..aad4be321 100644 --- a/test/e2e/tests/state-snapshots/errors-after-init-opt-in-ui-state.json +++ b/test/e2e/tests/state-snapshots/errors-after-init-opt-in-ui-state.json @@ -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, diff --git a/test/jest/mock-store.js b/test/jest/mock-store.js index 3b1823edb..4ceaded9c 100644 --- a/test/jest/mock-store.js +++ b/test/jest/mock-store.js @@ -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, diff --git a/ui/components/app/confirm-gas-display/confirm-legacy-gas-display/confirm-legacy-gas-display.test.js b/ui/components/app/confirm-gas-display/confirm-legacy-gas-display/confirm-legacy-gas-display.test.js index 1d975e19e..6a96b3b43 100644 --- a/ui/components/app/confirm-gas-display/confirm-legacy-gas-display/confirm-legacy-gas-display.test.js +++ b/ui/components/app/confirm-gas-display/confirm-legacy-gas-display/confirm-legacy-gas-display.test.js @@ -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 }); diff --git a/ui/components/app/signature-request-siwe/signature-request-siwe.test.js b/ui/components/app/signature-request-siwe/signature-request-siwe.test.js index 69c708900..9df4b889a 100644 --- a/ui/components/app/signature-request-siwe/signature-request-siwe.test.js +++ b/ui/components/app/signature-request-siwe/signature-request-siwe.test.js @@ -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, }, }); diff --git a/ui/components/app/transaction-list/transaction-list.test.js b/ui/components/app/transaction-list/transaction-list.test.js index 0f79aede7..a18f68a3b 100644 --- a/ui/components/app/transaction-list/transaction-list.test.js +++ b/ui/components/app/transaction-list/transaction-list.test.js @@ -9,6 +9,7 @@ const render = () => { const store = configureStore({ metamask: { ...mockState.metamask, + transactions: [], }, }); return renderWithProvider(, store); diff --git a/ui/components/multichain/app-header/app-header.js b/ui/components/multichain/app-header/app-header.js index 70e643974..ff0d8a4df 100644 --- a/ui/components/multichain/app-header/app-header.js +++ b/ui/components/multichain/app-header/app-header.js @@ -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); diff --git a/ui/components/multichain/global-menu/global-menu.js b/ui/components/multichain/global-menu/global-menu.js index a0b8153cf..6dfa89431 100644 --- a/ui/components/multichain/global-menu/global-menu.js +++ b/ui/components/multichain/global-menu/global-menu.js @@ -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); diff --git a/ui/components/multichain/global-menu/global-menu.test.js b/ui/components/multichain/global-menu/global-menu.test.js index edf3482c1..c01bc64fa 100644 --- a/ui/components/multichain/global-menu/global-menu.test.js +++ b/ui/components/multichain/global-menu/global-menu.test.js @@ -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(); }); diff --git a/ui/ducks/app/app.test.js b/ui/ducks/app/app.test.js index 4f38785ab..714e2b514 100644 --- a/ui/ducks/app/app.test.js +++ b/ui/ducks/app/app.test.js @@ -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 }; diff --git a/ui/ducks/confirm-transaction/confirm-transaction.duck.test.js b/ui/ducks/confirm-transaction/confirm-transaction.duck.test.js index a3514f525..324591fec 100644 --- a/ui/ducks/confirm-transaction/confirm-transaction.duck.test.js +++ b/ui/ducks/confirm-transaction/confirm-transaction.duck.test.js @@ -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: {}, }; diff --git a/ui/ducks/metamask/metamask.js b/ui/ducks/metamask/metamask.js index 8f6ae2004..e55d6be2f 100644 --- a/ui/ducks/metamask/metamask.js +++ b/ui/ducks/metamask/metamask.js @@ -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 * diff --git a/ui/ducks/metamask/metamask.test.js b/ui/ducks/metamask/metamask.test.js index 38efb3ff1..1f43f9fea 100644 --- a/ui/ducks/metamask/metamask.test.js +++ b/ui/ducks/metamask/metamask.test.js @@ -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()', () => { diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index f63e4a13a..4af8d9bb8 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -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 = { diff --git a/ui/ducks/send/send.test.js b/ui/ducks/send/send.test.js index d85725de8..4835c4d43 100644 --- a/ui/ducks/send/send.test.js +++ b/ui/ducks/send/send.test.js @@ -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'); diff --git a/ui/index.js b/ui/index.js index 208006de1..e715e38fa 100644 --- a/ui/index.js +++ b/ui/index.js @@ -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, diff --git a/ui/pages/confirm-approve/confirm-approve.stories-to-do.js b/ui/pages/confirm-approve/confirm-approve.stories-to-do.js index 46dbc5260..3ab734e68 100644 --- a/ui/pages/confirm-approve/confirm-approve.stories-to-do.js +++ b/ui/pages/confirm-approve/confirm-approve.stories-to-do.js @@ -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], }), ), ); diff --git a/ui/pages/confirm-send-ether/confirm-send-ether.stories.js b/ui/pages/confirm-send-ether/confirm-send-ether.stories.js index 3f29014c2..0c6d26b85 100644 --- a/ui/pages/confirm-send-ether/confirm-send-ether.stories.js +++ b/ui/pages/confirm-send-ether/confirm-send-ether.stories.js @@ -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 { diff --git a/ui/pages/confirm-send-ether/confirm-send-ether.test.js b/ui/pages/confirm-send-ether/confirm-send-ether.test.js index 2ecab5826..446dbc7ed 100644 --- a/ui/pages/confirm-send-ether/confirm-send-ether.test.js +++ b/ui/pages/confirm-send-ether/confirm-send-ether.test.js @@ -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', () => { diff --git a/ui/pages/confirm-signature-request/index.js b/ui/pages/confirm-signature-request/index.js index a36efc84d..35c5b9a82 100644 --- a/ui/pages/confirm-signature-request/index.js +++ b/ui/pages/confirm-signature-request/index.js @@ -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); diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js index daf9e10ff..1390c604e 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -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; diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.test.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.test.js index ebcee960f..678bbf454 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.test.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.test.js @@ -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 }; }; diff --git a/ui/pages/confirm-transaction-switch/confirm-transaction-switch.container.js b/ui/pages/confirm-transaction-switch/confirm-transaction-switch.container.js index f0d0c5ec0..3207d5c56 100644 --- a/ui/pages/confirm-transaction-switch/confirm-transaction-switch.container.js +++ b/ui/pages/confirm-transaction-switch/confirm-transaction-switch.container.js @@ -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; diff --git a/ui/pages/confirm-transaction/confirm-transaction.test.js b/ui/pages/confirm-transaction/confirm-transaction.test.js index 996f9da75..3aae253ac 100644 --- a/ui/pages/confirm-transaction/confirm-transaction.test.js +++ b/ui/pages/confirm-transaction/confirm-transaction.test.js @@ -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(, 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(); diff --git a/ui/pages/confirm-transaction/confirm-transaction.transaction.test.js b/ui/pages/confirm-transaction/confirm-transaction.transaction.test.js index 9814476f2..500501a98 100644 --- a/ui/pages/confirm-transaction/confirm-transaction.transaction.test.js +++ b/ui/pages/confirm-transaction/confirm-transaction.transaction.test.js @@ -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( diff --git a/ui/pages/confirmation/templates/success.test.js b/ui/pages/confirmation/templates/success.test.js index b3c22d33e..6acaa2bdc 100644 --- a/ui/pages/confirmation/templates/success.test.js +++ b/ui/pages/confirmation/templates/success.test.js @@ -51,6 +51,10 @@ describe('success template', () => { type: ApprovalType.ResultSuccess, }, }, + providerConfig: { + chainId: '0x1', + }, + transactions: [], }, }; const store = configureMockStore(middleware)(testStore); diff --git a/ui/pages/confirmation/templates/switch-ethereum-chain.test.js b/ui/pages/confirmation/templates/switch-ethereum-chain.test.js index 5d3c8458d..b7762412c 100644 --- a/ui/pages/confirmation/templates/switch-ethereum-chain.test.js +++ b/ui/pages/confirmation/templates/switch-ethereum-chain.test.js @@ -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', }, - }, + ], }, }; diff --git a/ui/pages/send/gas-display/gas-display.js b/ui/pages/send/gas-display/gas-display.js index f0fcbbfc4..cbec15ae2 100644 --- a/ui/pages/send/gas-display/gas-display.js +++ b/ui/pages/send/gas-display/gas-display.js @@ -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]; diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.container.js b/ui/pages/send/send-content/add-recipient/add-recipient.container.js index 0b325a19f..0ec89653c 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.container.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.container.js @@ -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) diff --git a/ui/pages/send/send-content/add-recipient/add-recipient.container.test.js b/ui/pages/send/send-content/add-recipient/add-recipient.container.test.js index 512a9d811..0d5aefa0d 100644 --- a/ui/pages/send/send-content/add-recipient/add-recipient.container.test.js +++ b/ui/pages/send/send-content/add-recipient/add-recipient.container.test.js @@ -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', () => ({ diff --git a/ui/pages/send/send.test.js b/ui/pages/send/send.test.js index fd1c3537c..591e499fc 100644 --- a/ui/pages/send/send.test.js +++ b/ui/pages/send/send.test.js @@ -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]: {}, }, diff --git a/ui/pages/swaps/index.js b/ui/pages/swaps/index.js index 8a127063e..2ece7907a 100644 --- a/ui/pages/swaps/index.js +++ b/ui/pages/swaps/index.js @@ -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); diff --git a/ui/pages/token-allowance/token-allowance.test.js b/ui/pages/token-allowance/token-allowance.test.js index 094203598..8d39b9a46 100644 --- a/ui/pages/token-allowance/token-allowance.test.js +++ b/ui/pages/token-allowance/token-allowance.test.js @@ -73,7 +73,7 @@ const state = { isERC721: false, }, ], - unapprovedTxs: {}, + transactions: [], keyringTypes: [], keyrings: [ { diff --git a/ui/selectors/confirm-transaction.js b/ui/selectors/confirm-transaction.js index 70566ab9a..cdd7eed36 100644 --- a/ui/selectors/confirm-transaction.js +++ b/ui/selectors/confirm-transaction.js @@ -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; diff --git a/ui/selectors/nonce-sorted-transactions-selector.test.js b/ui/selectors/nonce-sorted-transactions-selector.test.js index b517de21a..81594efa0 100644 --- a/ui/selectors/nonce-sorted-transactions-selector.test.js +++ b/ui/selectors/nonce-sorted-transactions-selector.test.js @@ -81,9 +81,7 @@ const getStateTree = ({ unapprovedMsgs, selectedAddress: SENDERS.ONE, featureFlags: {}, - transactions: [...incomingTxList], - incomingTransactions: [...incomingTxList], - currentNetworkTxList: [...txList], + transactions: [...incomingTxList, ...txList], incomingTransactionsPreferences: {}, }, }); diff --git a/ui/selectors/selectors.js b/ui/selectors/selectors.js index 29d801fa3..4a2a50698 100644 --- a/ui/selectors/selectors.js +++ b/ui/selectors/selectors.js @@ -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 ( diff --git a/ui/selectors/transactions.js b/ui/selectors/transactions.js index 9e5aa8401..a1827f2c0 100644 --- a/ui/selectors/transactions.js +++ b/ui/selectors/transactions.js @@ -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, diff --git a/ui/selectors/transactions.test.js b/ui/selectors/transactions.test.js index e4fff0323..fb6bb6297 100644 --- a/ui/selectors/transactions.test.js +++ b/ui/selectors/transactions.test.js @@ -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], diff --git a/ui/selectors/util.js b/ui/selectors/util.js new file mode 100644 index 000000000..ca3bd0644 --- /dev/null +++ b/ui/selectors/util.js @@ -0,0 +1,7 @@ +import { isEqual } from 'lodash'; +import { createSelectorCreator, defaultMemoize } from 'reselect'; + +export const createDeepEqualSelector = createSelectorCreator( + defaultMemoize, + isEqual, +); diff --git a/ui/store/actions.ts b/ui/store/actions.ts index e69cd3c25..0ffabd573 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -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); } diff --git a/ui/store/institutional/institution-actions.test.js b/ui/store/institutional/institution-actions.test.js index d898f51d5..2295791bd 100644 --- a/ui/store/institutional/institution-actions.test.js +++ b/ui/store/institutional/institution-actions.test.js @@ -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, diff --git a/ui/store/institutional/institution-actions.ts b/ui/store/institutional/institution-actions.ts index 6a573e4f4..91ca1b32f 100644 --- a/ui/store/institutional/institution-actions.ts +++ b/ui/store/institutional/institution-actions.ts @@ -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) diff --git a/ui/store/store.ts b/ui/store/store.ts index 73b387574..fcc8c7f0c 100644 --- a/ui/store/store.ts +++ b/ui/store/store.ts @@ -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;