mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
feat: Refactor Transaction Confirmation selector (#18796)
This commit is contained in:
parent
033b529c17
commit
d37d5bf0ee
@ -713,7 +713,9 @@ export function setupController(
|
|||||||
// User Interface setup
|
// User Interface setup
|
||||||
//
|
//
|
||||||
|
|
||||||
|
controller.txController.initApprovals().then(() => {
|
||||||
updateBadge();
|
updateBadge();
|
||||||
|
});
|
||||||
controller.txController.on(
|
controller.txController.on(
|
||||||
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
|
METAMASK_CONTROLLER_EVENTS.UPDATE_BADGE,
|
||||||
updateBadge,
|
updateBadge,
|
||||||
|
@ -394,6 +394,22 @@ export default class TransactionController extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates approvals for all unapproved transactions in the txStateManager.
|
||||||
|
*
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async initApprovals() {
|
||||||
|
const unapprovedTxs = this.txStateManager.getUnapprovedTxList();
|
||||||
|
return Promise.all(
|
||||||
|
Object.values(unapprovedTxs).map((txMeta) =>
|
||||||
|
this._requestApproval(txMeta, {
|
||||||
|
shouldShowRequest: false,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ====================================================================================================================================================
|
// ====================================================================================================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2646,13 +2662,16 @@ export default class TransactionController extends EventEmitter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestApproval(txMeta) {
|
async _requestApproval(
|
||||||
|
txMeta,
|
||||||
|
{ shouldShowRequest } = { shouldShowRequest: true },
|
||||||
|
) {
|
||||||
const id = this._getApprovalId(txMeta);
|
const id = this._getApprovalId(txMeta);
|
||||||
const { origin } = txMeta;
|
const { origin } = txMeta;
|
||||||
const type = ApprovalType.Transaction;
|
const type = ApprovalType.Transaction;
|
||||||
const requestData = { txId: txMeta.id };
|
const requestData = { txId: txMeta.id };
|
||||||
|
|
||||||
this.messagingSystem
|
return this.messagingSystem
|
||||||
.call(
|
.call(
|
||||||
'ApprovalController:addRequest',
|
'ApprovalController:addRequest',
|
||||||
{
|
{
|
||||||
@ -2661,7 +2680,7 @@ export default class TransactionController extends EventEmitter {
|
|||||||
type,
|
type,
|
||||||
requestData,
|
requestData,
|
||||||
},
|
},
|
||||||
true,
|
shouldShowRequest,
|
||||||
)
|
)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// Intentionally ignored as promise not currently used
|
// Intentionally ignored as promise not currently used
|
||||||
|
@ -2992,4 +2992,58 @@ describe('Transaction Controller', function () {
|
|||||||
assert.equal(result.type, TransactionType.simpleSend);
|
assert.equal(result.type, TransactionType.simpleSend);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('initApprovals', function () {
|
||||||
|
it('adds unapprovedTxs as approvals', async function () {
|
||||||
|
const firstTxId = '1';
|
||||||
|
txController.addTransaction(
|
||||||
|
{
|
||||||
|
id: firstTxId,
|
||||||
|
origin: ORIGIN_METAMASK,
|
||||||
|
status: TransactionStatus.unapproved,
|
||||||
|
metamaskNetworkId: currentNetworkId,
|
||||||
|
txParams: {
|
||||||
|
to: VALID_ADDRESS,
|
||||||
|
from: VALID_ADDRESS_TWO,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
noop,
|
||||||
|
);
|
||||||
|
const secondTxId = '2';
|
||||||
|
txController.addTransaction(
|
||||||
|
{
|
||||||
|
id: secondTxId,
|
||||||
|
origin: ORIGIN_METAMASK,
|
||||||
|
status: TransactionStatus.unapproved,
|
||||||
|
metamaskNetworkId: currentNetworkId,
|
||||||
|
txParams: {
|
||||||
|
to: VALID_ADDRESS,
|
||||||
|
from: VALID_ADDRESS_TWO,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
noop,
|
||||||
|
);
|
||||||
|
await txController.initApprovals();
|
||||||
|
assert.deepEqual(messengerMock.call.getCall(0).args, [
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
{
|
||||||
|
id: firstTxId,
|
||||||
|
origin: ORIGIN_METAMASK,
|
||||||
|
requestData: { txId: firstTxId },
|
||||||
|
type: ApprovalType.Transaction,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
]);
|
||||||
|
assert.deepEqual(messengerMock.call.getCall(1).args, [
|
||||||
|
'ApprovalController:addRequest',
|
||||||
|
{
|
||||||
|
id: secondTxId,
|
||||||
|
origin: ORIGIN_METAMASK,
|
||||||
|
requestData: { txId: secondTxId },
|
||||||
|
type: ApprovalType.Transaction,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,6 @@ import currencyFormatter from 'currency-formatter';
|
|||||||
import currencies from 'currency-formatter/currencies';
|
import currencies from 'currency-formatter/currencies';
|
||||||
import { BigNumber } from 'bignumber.js';
|
import { BigNumber } from 'bignumber.js';
|
||||||
|
|
||||||
import { unconfirmedTransactionsCountSelector } from '../../selectors';
|
|
||||||
import { Numeric } from '../../../shared/modules/Numeric';
|
import { Numeric } from '../../../shared/modules/Numeric';
|
||||||
import { EtherDenomination } from '../../../shared/constants/common';
|
import { EtherDenomination } from '../../../shared/constants/common';
|
||||||
import { TransactionMeta } from '../../../shared/constants/transaction';
|
import { TransactionMeta } from '../../../shared/constants/transaction';
|
||||||
@ -86,22 +85,6 @@ export function convertTokenToFiat({
|
|||||||
return tokenInFiat.round(2).toString();
|
return tokenInFiat.round(2).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a selector and probably doesn't belong here but its staying for now
|
|
||||||
* Note: I did not go so far as to type the entirety of the MetaMask state tree
|
|
||||||
* which definitely needs to be done for the full conversion of TypeScript to
|
|
||||||
* be successful and as useful as possible.
|
|
||||||
* TODO: Type the MetaMask state tree and use that type here.
|
|
||||||
*
|
|
||||||
* @param state - MetaMask state
|
|
||||||
* @returns true if there are unconfirmed transactions in state
|
|
||||||
*/
|
|
||||||
export function hasUnconfirmedTransactions(
|
|
||||||
state: Record<string, any>,
|
|
||||||
): boolean {
|
|
||||||
return unconfirmedTransactionsCountSelector(state) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rounds the given decimal string to 4 significant digits.
|
* Rounds the given decimal string to 4 significant digits.
|
||||||
*
|
*
|
||||||
|
@ -107,7 +107,7 @@ export default class Home extends PureComponent {
|
|||||||
history: PropTypes.object,
|
history: PropTypes.object,
|
||||||
forgottenPassword: PropTypes.bool,
|
forgottenPassword: PropTypes.bool,
|
||||||
hasWatchAssetPendingApprovals: PropTypes.bool,
|
hasWatchAssetPendingApprovals: PropTypes.bool,
|
||||||
unconfirmedTransactionsCount: PropTypes.number,
|
hasTransactionPendingApprovals: PropTypes.bool.isRequired,
|
||||||
shouldShowSeedPhraseReminder: PropTypes.bool.isRequired,
|
shouldShowSeedPhraseReminder: PropTypes.bool.isRequired,
|
||||||
isPopup: PropTypes.bool,
|
isPopup: PropTypes.bool,
|
||||||
isNotification: PropTypes.bool.isRequired,
|
isNotification: PropTypes.bool.isRequired,
|
||||||
@ -197,7 +197,7 @@ export default class Home extends PureComponent {
|
|||||||
showAwaitingSwapScreen,
|
showAwaitingSwapScreen,
|
||||||
hasWatchAssetPendingApprovals,
|
hasWatchAssetPendingApprovals,
|
||||||
swapsFetchParams,
|
swapsFetchParams,
|
||||||
unconfirmedTransactionsCount,
|
hasTransactionPendingApprovals,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (shouldCloseNotificationPopup(props)) {
|
if (shouldCloseNotificationPopup(props)) {
|
||||||
@ -205,7 +205,7 @@ export default class Home extends PureComponent {
|
|||||||
closeNotificationPopup();
|
closeNotificationPopup();
|
||||||
} else if (
|
} else if (
|
||||||
firstPermissionsRequestId ||
|
firstPermissionsRequestId ||
|
||||||
unconfirmedTransactionsCount > 0 ||
|
hasTransactionPendingApprovals ||
|
||||||
hasWatchAssetPendingApprovals ||
|
hasWatchAssetPendingApprovals ||
|
||||||
(!isNotification &&
|
(!isNotification &&
|
||||||
(showAwaitingSwapScreen || haveSwapsQuotes || swapsFetchParams))
|
(showAwaitingSwapScreen || haveSwapsQuotes || swapsFetchParams))
|
||||||
@ -269,7 +269,7 @@ export default class Home extends PureComponent {
|
|||||||
history,
|
history,
|
||||||
isNotification,
|
isNotification,
|
||||||
hasWatchAssetPendingApprovals,
|
hasWatchAssetPendingApprovals,
|
||||||
unconfirmedTransactionsCount,
|
hasTransactionPendingApprovals,
|
||||||
haveSwapsQuotes,
|
haveSwapsQuotes,
|
||||||
showAwaitingSwapScreen,
|
showAwaitingSwapScreen,
|
||||||
swapsFetchParams,
|
swapsFetchParams,
|
||||||
@ -288,7 +288,7 @@ export default class Home extends PureComponent {
|
|||||||
history.push(BUILD_QUOTE_ROUTE);
|
history.push(BUILD_QUOTE_ROUTE);
|
||||||
} else if (firstPermissionsRequestId) {
|
} else if (firstPermissionsRequestId) {
|
||||||
history.push(`${CONNECT_ROUTE}/${firstPermissionsRequestId}`);
|
history.push(`${CONNECT_ROUTE}/${firstPermissionsRequestId}`);
|
||||||
} else if (unconfirmedTransactionsCount > 0) {
|
} else if (hasTransactionPendingApprovals) {
|
||||||
history.push(CONFIRM_TRANSACTION_ROUTE);
|
history.push(CONFIRM_TRANSACTION_ROUTE);
|
||||||
} else if (hasWatchAssetPendingApprovals) {
|
} else if (hasWatchAssetPendingApprovals) {
|
||||||
history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE);
|
history.push(CONFIRM_ADD_SUGGESTED_TOKEN_ROUTE);
|
||||||
|
@ -22,7 +22,6 @@ import {
|
|||||||
getTotalUnapprovedCount,
|
getTotalUnapprovedCount,
|
||||||
getUnapprovedTemplatedConfirmations,
|
getUnapprovedTemplatedConfirmations,
|
||||||
getWeb3ShimUsageStateForOrigin,
|
getWeb3ShimUsageStateForOrigin,
|
||||||
unconfirmedTransactionsCountSelector,
|
|
||||||
getInfuraBlocked,
|
getInfuraBlocked,
|
||||||
getShowWhatsNewPopup,
|
getShowWhatsNewPopup,
|
||||||
getSortedAnnouncementsToShow,
|
getSortedAnnouncementsToShow,
|
||||||
@ -36,7 +35,7 @@ import {
|
|||||||
getNewTokensImported,
|
getNewTokensImported,
|
||||||
getShouldShowSeedPhraseReminder,
|
getShouldShowSeedPhraseReminder,
|
||||||
getRemoveNftMessage,
|
getRemoveNftMessage,
|
||||||
hasPendingApprovalsSelector,
|
hasPendingApprovals,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -71,6 +70,7 @@ import {
|
|||||||
AlertTypes,
|
AlertTypes,
|
||||||
Web3ShimUsageAlertStates,
|
Web3ShimUsageAlertStates,
|
||||||
} from '../../../shared/constants/alerts';
|
} from '../../../shared/constants/alerts';
|
||||||
|
import { hasTransactionPendingApprovals } from '../../selectors/transactions';
|
||||||
import Home from './home.component';
|
import Home from './home.component';
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
const mapStateToProps = (state) => {
|
||||||
@ -121,7 +121,7 @@ const mapStateToProps = (state) => {
|
|||||||
hasUnsignedQRHardwareTransaction(state) ||
|
hasUnsignedQRHardwareTransaction(state) ||
|
||||||
hasUnsignedQRHardwareMessage(state);
|
hasUnsignedQRHardwareMessage(state);
|
||||||
|
|
||||||
const hasWatchAssetPendingApprovals = hasPendingApprovalsSelector(
|
const hasWatchAssetPendingApprovals = hasPendingApprovals(
|
||||||
state,
|
state,
|
||||||
ApprovalType.WatchAsset,
|
ApprovalType.WatchAsset,
|
||||||
);
|
);
|
||||||
@ -130,7 +130,7 @@ const mapStateToProps = (state) => {
|
|||||||
forgottenPassword,
|
forgottenPassword,
|
||||||
hasWatchAssetPendingApprovals,
|
hasWatchAssetPendingApprovals,
|
||||||
swapsEnabled,
|
swapsEnabled,
|
||||||
unconfirmedTransactionsCount: unconfirmedTransactionsCountSelector(state),
|
hasTransactionPendingApprovals: hasTransactionPendingApprovals(state),
|
||||||
shouldShowSeedPhraseReminder: getShouldShowSeedPhraseReminder(state),
|
shouldShowSeedPhraseReminder: getShouldShowSeedPhraseReminder(state),
|
||||||
isPopup,
|
isPopup,
|
||||||
isNotification,
|
isNotification,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { ApprovalType } from '@metamask/controller-utils';
|
import { ApprovalType } from '@metamask/controller-utils';
|
||||||
import { hasPendingApprovalsSelector } from './approvals';
|
import { hasPendingApprovals } from './approvals';
|
||||||
|
|
||||||
describe('approval selectors', () => {
|
describe('approval selectors', () => {
|
||||||
const mockedState = {
|
const mockedState = {
|
||||||
metamask: {
|
metamask: {
|
||||||
pendingApprovalCount: 2,
|
pendingApprovalCount: 3,
|
||||||
pendingApprovals: {
|
pendingApprovals: {
|
||||||
'1': {
|
'1': {
|
||||||
id: '1',
|
id: '1',
|
||||||
@ -18,28 +18,30 @@ describe('approval selectors', () => {
|
|||||||
id: '2',
|
id: '2',
|
||||||
origin: 'origin',
|
origin: 'origin',
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
type: ApprovalType.EthSignTypedData,
|
type: ApprovalType.Transaction,
|
||||||
requestData: {},
|
requestData: {},
|
||||||
requestState: null,
|
requestState: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
unapprovedTxs: {
|
||||||
|
'2': {
|
||||||
|
id: '2',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('hasPendingApprovalsSelector', () => {
|
describe('hasPendingApprovals', () => {
|
||||||
it('should return true if there is a pending approval request', () => {
|
it('should return true if there is a pending approval request', () => {
|
||||||
const result = hasPendingApprovalsSelector(
|
const result = hasPendingApprovals(mockedState, ApprovalType.WatchAsset);
|
||||||
mockedState,
|
|
||||||
ApprovalType.WatchAsset,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false if there is no pending approval request', () => {
|
it('should return false if there is no pending approval request', () => {
|
||||||
const result = hasPendingApprovalsSelector(
|
const result = hasPendingApprovals(
|
||||||
mockedState,
|
mockedState,
|
||||||
ApprovalType.Transaction,
|
ApprovalType.SnapDialogPrompt,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result).toBe(false);
|
expect(result).toBe(false);
|
||||||
|
@ -1,19 +1,35 @@
|
|||||||
import { ApprovalControllerState } from '@metamask/approval-controller';
|
import { ApprovalControllerState } from '@metamask/approval-controller';
|
||||||
import { ApprovalType } from '@metamask/controller-utils';
|
import { ApprovalType } from '@metamask/controller-utils';
|
||||||
|
import { TransactionMeta } from '../../shared/constants/transaction';
|
||||||
|
|
||||||
type ApprovalsMetaMaskState = {
|
type ApprovalsMetaMaskState = {
|
||||||
metamask: {
|
metamask: {
|
||||||
pendingApprovals: ApprovalControllerState['pendingApprovals'];
|
pendingApprovals: ApprovalControllerState['pendingApprovals'];
|
||||||
|
unapprovedTxs: {
|
||||||
|
[transactionId: string]: TransactionMeta;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function hasPendingApprovalsSelector(
|
export const getApprovalRequestsByType = (
|
||||||
state: ApprovalsMetaMaskState,
|
state: ApprovalsMetaMaskState,
|
||||||
approvalType: ApprovalType,
|
approvalType: ApprovalType,
|
||||||
) {
|
) => {
|
||||||
const pendingApprovalRequests = Object.values(
|
const pendingApprovalRequests = Object.values(
|
||||||
state.metamask.pendingApprovals,
|
state.metamask.pendingApprovals,
|
||||||
).filter(({ type }) => type === approvalType);
|
).filter(({ type }) => type === approvalType);
|
||||||
|
|
||||||
|
return pendingApprovalRequests;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function hasPendingApprovals(
|
||||||
|
state: ApprovalsMetaMaskState,
|
||||||
|
approvalType: ApprovalType,
|
||||||
|
) {
|
||||||
|
const pendingApprovalRequests = getApprovalRequestsByType(
|
||||||
|
state,
|
||||||
|
approvalType,
|
||||||
|
);
|
||||||
|
|
||||||
return pendingApprovalRequests.length > 0;
|
return pendingApprovalRequests.length > 0;
|
||||||
}
|
}
|
||||||
|
@ -142,50 +142,6 @@ export const unconfirmedMessagesHashSelector = createSelector(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const unapprovedMsgCountSelector = (state) => state.metamask.unapprovedMsgCount;
|
|
||||||
const unapprovedPersonalMsgCountSelector = (state) =>
|
|
||||||
state.metamask.unapprovedPersonalMsgCount;
|
|
||||||
const unapprovedDecryptMsgCountSelector = (state) =>
|
|
||||||
state.metamask.unapprovedDecryptMsgCount;
|
|
||||||
const unapprovedEncryptionPublicKeyMsgCountSelector = (state) =>
|
|
||||||
state.metamask.unapprovedEncryptionPublicKeyMsgCount;
|
|
||||||
const unapprovedTypedMessagesCountSelector = (state) =>
|
|
||||||
state.metamask.unapprovedTypedMessagesCount;
|
|
||||||
|
|
||||||
export const unconfirmedTransactionsCountSelector = createSelector(
|
|
||||||
unapprovedTxsSelector,
|
|
||||||
unapprovedMsgCountSelector,
|
|
||||||
unapprovedPersonalMsgCountSelector,
|
|
||||||
unapprovedDecryptMsgCountSelector,
|
|
||||||
unapprovedEncryptionPublicKeyMsgCountSelector,
|
|
||||||
unapprovedTypedMessagesCountSelector,
|
|
||||||
deprecatedGetCurrentNetworkId,
|
|
||||||
getCurrentChainId,
|
|
||||||
(
|
|
||||||
unapprovedTxs = {},
|
|
||||||
unapprovedMsgCount = 0,
|
|
||||||
unapprovedPersonalMsgCount = 0,
|
|
||||||
unapprovedDecryptMsgCount = 0,
|
|
||||||
unapprovedEncryptionPublicKeyMsgCount = 0,
|
|
||||||
unapprovedTypedMessagesCount = 0,
|
|
||||||
network,
|
|
||||||
chainId,
|
|
||||||
) => {
|
|
||||||
const filteredUnapprovedTxIds = Object.keys(unapprovedTxs).filter((txId) =>
|
|
||||||
transactionMatchesNetwork(unapprovedTxs[txId], chainId, network),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
filteredUnapprovedTxIds.length +
|
|
||||||
unapprovedTypedMessagesCount +
|
|
||||||
unapprovedMsgCount +
|
|
||||||
unapprovedPersonalMsgCount +
|
|
||||||
unapprovedDecryptMsgCount +
|
|
||||||
unapprovedEncryptionPublicKeyMsgCount
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export const currentCurrencySelector = (state) =>
|
export const currentCurrencySelector = (state) =>
|
||||||
state.metamask.currentCurrency;
|
state.metamask.currentCurrency;
|
||||||
export const conversionRateSelector = (state) => state.metamask.conversionRate;
|
export const conversionRateSelector = (state) => state.metamask.conversionRate;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { CHAIN_IDS } from '../../shared/constants/network';
|
|
||||||
import { TransactionType } from '../../shared/constants/transaction';
|
import { TransactionType } from '../../shared/constants/transaction';
|
||||||
import {
|
import {
|
||||||
unconfirmedTransactionsCountSelector,
|
|
||||||
sendTokenTokenAmountAndToAddressSelector,
|
sendTokenTokenAmountAndToAddressSelector,
|
||||||
contractExchangeRateSelector,
|
contractExchangeRateSelector,
|
||||||
conversionRateSelector,
|
conversionRateSelector,
|
||||||
@ -17,32 +15,6 @@ const getEthersArrayLikeFromObj = (obj) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('Confirm Transaction Selector', () => {
|
describe('Confirm Transaction Selector', () => {
|
||||||
describe('unconfirmedTransactionsCountSelector', () => {
|
|
||||||
const state = {
|
|
||||||
metamask: {
|
|
||||||
unapprovedTxs: {
|
|
||||||
1: {
|
|
||||||
metamaskNetworkId: '5',
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
chainId: CHAIN_IDS.MAINNET,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
unapprovedMsgCount: 1,
|
|
||||||
unapprovedPersonalMsgCount: 1,
|
|
||||||
unapprovedTypedMessagesCount: 1,
|
|
||||||
networkId: '5',
|
|
||||||
providerConfig: {
|
|
||||||
chainId: '0x5',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
it('returns number of txs in unapprovedTxs state with the same network plus unapproved signing method counts', () => {
|
|
||||||
expect(unconfirmedTransactionsCountSelector(state)).toStrictEqual(4);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sendTokenTokenAmountAndToAddressSelector', () => {
|
describe('sendTokenTokenAmountAndToAddressSelector', () => {
|
||||||
const state = {
|
const state = {
|
||||||
confirmTransaction: {
|
confirmTransaction: {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
import { ApprovalType } from '@metamask/controller-utils';
|
||||||
import {
|
import {
|
||||||
PRIORITY_STATUS_HASH,
|
PRIORITY_STATUS_HASH,
|
||||||
PENDING_STATUS_HASH,
|
PENDING_STATUS_HASH,
|
||||||
@ -17,6 +18,7 @@ import {
|
|||||||
deprecatedGetCurrentNetworkId,
|
deprecatedGetCurrentNetworkId,
|
||||||
getSelectedAddress,
|
getSelectedAddress,
|
||||||
} from './selectors';
|
} from './selectors';
|
||||||
|
import { hasPendingApprovals, getApprovalRequestsByType } from './approvals';
|
||||||
|
|
||||||
const INVALID_INITIAL_TRANSACTION_TYPES = [
|
const INVALID_INITIAL_TRANSACTION_TYPES = [
|
||||||
TransactionType.cancel,
|
TransactionType.cancel,
|
||||||
@ -534,3 +536,34 @@ export const submittedPendingTransactionsSelector = createSelector(
|
|||||||
(transaction) => transaction.status === TransactionStatus.submitted,
|
(transaction) => transaction.status === TransactionStatus.submitted,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hasUnapprovedTransactionsInCurrentNetwork = (state) => {
|
||||||
|
const { unapprovedTxs } = state.metamask;
|
||||||
|
const unapprovedTxRequests = getApprovalRequestsByType(
|
||||||
|
state,
|
||||||
|
ApprovalType.Transaction,
|
||||||
|
);
|
||||||
|
|
||||||
|
const chainId = getCurrentChainId(state);
|
||||||
|
|
||||||
|
const filteredUnapprovedTxInCurrentNetwork = unapprovedTxRequests.filter(
|
||||||
|
({ id }) => transactionMatchesNetwork(unapprovedTxs[id], chainId),
|
||||||
|
);
|
||||||
|
|
||||||
|
return filteredUnapprovedTxInCurrentNetwork.length > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TRANSACTION_APPROVAL_TYPES = [
|
||||||
|
ApprovalType.EthDecrypt,
|
||||||
|
ApprovalType.EthGetEncryptionPublicKey,
|
||||||
|
ApprovalType.EthSign,
|
||||||
|
ApprovalType.EthSignTypedData,
|
||||||
|
ApprovalType.PersonalSign,
|
||||||
|
];
|
||||||
|
|
||||||
|
export function hasTransactionPendingApprovals(state) {
|
||||||
|
return (
|
||||||
|
hasUnapprovedTransactionsInCurrentNetwork(state) ||
|
||||||
|
TRANSACTION_APPROVAL_TYPES.some((type) => hasPendingApprovals(state, type))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ApprovalType } from '@metamask/controller-utils';
|
||||||
import { CHAIN_IDS } from '../../shared/constants/network';
|
import { CHAIN_IDS } from '../../shared/constants/network';
|
||||||
import { TransactionStatus } from '../../shared/constants/transaction';
|
import { TransactionStatus } from '../../shared/constants/transaction';
|
||||||
import {
|
import {
|
||||||
@ -7,6 +8,7 @@ import {
|
|||||||
nonceSortedPendingTransactionsSelector,
|
nonceSortedPendingTransactionsSelector,
|
||||||
nonceSortedCompletedTransactionsSelector,
|
nonceSortedCompletedTransactionsSelector,
|
||||||
submittedPendingTransactionsSelector,
|
submittedPendingTransactionsSelector,
|
||||||
|
hasTransactionPendingApprovals,
|
||||||
} from './transactions';
|
} from './transactions';
|
||||||
|
|
||||||
describe('Transaction Selectors', () => {
|
describe('Transaction Selectors', () => {
|
||||||
@ -329,4 +331,78 @@ describe('Transaction Selectors', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('hasTransactionPendingApprovals', () => {
|
||||||
|
const mockNetworkId = 'mockNetworkId';
|
||||||
|
const mockedState = {
|
||||||
|
metamask: {
|
||||||
|
providerConfig: {
|
||||||
|
chainId: mockNetworkId,
|
||||||
|
},
|
||||||
|
pendingApprovalCount: 2,
|
||||||
|
pendingApprovals: {
|
||||||
|
1: {
|
||||||
|
id: '1',
|
||||||
|
origin: 'origin',
|
||||||
|
time: Date.now(),
|
||||||
|
type: ApprovalType.WatchAsset,
|
||||||
|
requestData: {},
|
||||||
|
requestState: null,
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
id: '2',
|
||||||
|
origin: 'origin',
|
||||||
|
time: Date.now(),
|
||||||
|
type: ApprovalType.Transaction,
|
||||||
|
requestData: {},
|
||||||
|
requestState: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unapprovedTxs: {
|
||||||
|
2: {
|
||||||
|
id: '2',
|
||||||
|
chainId: mockNetworkId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should return true if there is a pending transaction on same network', () => {
|
||||||
|
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';
|
||||||
|
const result = hasTransactionPendingApprovals(mockedState);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
it.each([
|
||||||
|
[ApprovalType.EthDecrypt],
|
||||||
|
[ApprovalType.EthGetEncryptionPublicKey],
|
||||||
|
[ApprovalType.EthSign],
|
||||||
|
[ApprovalType.EthSignTypedData],
|
||||||
|
[ApprovalType.PersonalSign],
|
||||||
|
])(
|
||||||
|
'should return true if there is a pending transaction of %s type',
|
||||||
|
(type) => {
|
||||||
|
const result = hasTransactionPendingApprovals({
|
||||||
|
...mockedState,
|
||||||
|
metamask: {
|
||||||
|
...mockedState.metamask,
|
||||||
|
pendingApprovals: {
|
||||||
|
2: {
|
||||||
|
id: '2',
|
||||||
|
origin: 'origin',
|
||||||
|
time: Date.now(),
|
||||||
|
type,
|
||||||
|
requestData: {},
|
||||||
|
requestState: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(result).toBe(true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -23,12 +23,12 @@ import {
|
|||||||
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
||||||
MESSAGE_TYPE,
|
MESSAGE_TYPE,
|
||||||
} from '../../shared/constants/app';
|
} from '../../shared/constants/app';
|
||||||
import { hasUnconfirmedTransactions } from '../helpers/utils/confirm-tx.util';
|
|
||||||
import { getEnvironmentType, addHexPrefix } from '../../app/scripts/lib/util';
|
import { getEnvironmentType, addHexPrefix } from '../../app/scripts/lib/util';
|
||||||
import {
|
import {
|
||||||
getMetaMaskAccounts,
|
getMetaMaskAccounts,
|
||||||
getPermittedAccountsForCurrentTab,
|
getPermittedAccountsForCurrentTab,
|
||||||
getSelectedAddress,
|
getSelectedAddress,
|
||||||
|
hasTransactionPendingApprovals,
|
||||||
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
///: BEGIN:ONLY_INCLUDE_IN(snaps)
|
||||||
getNotifications,
|
getNotifications,
|
||||||
///: END:ONLY_INCLUDE_IN
|
///: END:ONLY_INCLUDE_IN
|
||||||
@ -2698,7 +2698,7 @@ export function closeCurrentNotificationWindow(): ThunkAction<
|
|||||||
return (_, getState) => {
|
return (_, getState) => {
|
||||||
if (
|
if (
|
||||||
getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION &&
|
getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION &&
|
||||||
!hasUnconfirmedTransactions(getState())
|
!hasTransactionPendingApprovals(getState())
|
||||||
) {
|
) {
|
||||||
closeNotificationPopup();
|
closeNotificationPopup();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user