mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
parent
c2cd6f8097
commit
a0c4febfce
@ -20,6 +20,10 @@
|
|||||||
@media screen and (min-width: $break-large) {
|
@media screen and (min-width: $break-large) {
|
||||||
height: 620px;
|
height: 620px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__reject {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__typed-container {
|
&__typed-container {
|
||||||
|
@ -38,6 +38,9 @@ export default class SignatureRequestOriginal extends Component {
|
|||||||
hardwareWalletRequiresConnection: PropTypes.bool,
|
hardwareWalletRequiresConnection: PropTypes.bool,
|
||||||
isLedgerWallet: PropTypes.bool,
|
isLedgerWallet: PropTypes.bool,
|
||||||
nativeCurrency: PropTypes.string.isRequired,
|
nativeCurrency: PropTypes.string.isRequired,
|
||||||
|
messagesCount: PropTypes.number,
|
||||||
|
showRejectTransactionsConfirmationModal: PropTypes.func.isRequired,
|
||||||
|
cancelAll: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -315,7 +318,31 @@ export default class SignatureRequestOriginal extends Component {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleCancelAll = () => {
|
||||||
|
const {
|
||||||
|
cancelAll,
|
||||||
|
clearConfirmTransaction,
|
||||||
|
history,
|
||||||
|
mostRecentOverviewPage,
|
||||||
|
showRejectTransactionsConfirmationModal,
|
||||||
|
messagesCount,
|
||||||
|
} = this.props;
|
||||||
|
const unapprovedTxCount = messagesCount;
|
||||||
|
|
||||||
|
showRejectTransactionsConfirmationModal({
|
||||||
|
unapprovedTxCount,
|
||||||
|
onSubmit: async () => {
|
||||||
|
await cancelAll();
|
||||||
|
clearConfirmTransaction();
|
||||||
|
history.push(mostRecentOverviewPage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
render = () => {
|
render = () => {
|
||||||
|
const { messagesCount } = this.props;
|
||||||
|
const { t } = this.context;
|
||||||
|
const rejectNText = t('rejectTxsN', [messagesCount]);
|
||||||
return (
|
return (
|
||||||
<div className="request-signature__container">
|
<div className="request-signature__container">
|
||||||
{this.renderHeader()}
|
{this.renderHeader()}
|
||||||
@ -326,6 +353,15 @@ export default class SignatureRequestOriginal extends Component {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
{this.renderFooter()}
|
{this.renderFooter()}
|
||||||
|
{messagesCount > 1 ? (
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
className="request-signature__container__reject"
|
||||||
|
onClick={() => this.handleCancelAll()}
|
||||||
|
>
|
||||||
|
{rejectNText}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,14 +3,16 @@ import { compose } from 'redux';
|
|||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
|
import { MESSAGE_TYPE } from '../../../../shared/constants/app';
|
||||||
import { goHome } from '../../../store/actions';
|
import { goHome, cancelMsgs, showModal } from '../../../store/actions';
|
||||||
import {
|
import {
|
||||||
accountsWithSendEtherInfoSelector,
|
accountsWithSendEtherInfoSelector,
|
||||||
conversionRateSelector,
|
conversionRateSelector,
|
||||||
getSubjectMetadata,
|
getSubjectMetadata,
|
||||||
doesAddressRequireLedgerHidConnection,
|
doesAddressRequireLedgerHidConnection,
|
||||||
|
unconfirmedMessagesHashSelector,
|
||||||
|
getTotalUnapprovedMessagesCount,
|
||||||
} from '../../../selectors';
|
} from '../../../selectors';
|
||||||
import { getAccountByAddress } from '../../../helpers/utils/util';
|
import { getAccountByAddress, valuesFor } from '../../../helpers/utils/util';
|
||||||
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck';
|
import { clearConfirmTransaction } from '../../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||||
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
|
import { getMostRecentOverviewPage } from '../../../ducks/history/history';
|
||||||
import {
|
import {
|
||||||
@ -29,6 +31,8 @@ function mapStateToProps(state, ownProps) {
|
|||||||
from,
|
from,
|
||||||
);
|
);
|
||||||
const isLedgerWallet = isAddressLedger(state, from);
|
const isLedgerWallet = isAddressLedger(state, from);
|
||||||
|
const messagesList = unconfirmedMessagesHashSelector(state);
|
||||||
|
const messagesCount = getTotalUnapprovedMessagesCount(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
requester: null,
|
requester: null,
|
||||||
@ -41,6 +45,8 @@ function mapStateToProps(state, ownProps) {
|
|||||||
// not passed to component
|
// not passed to component
|
||||||
allAccounts: accountsWithSendEtherInfoSelector(state),
|
allAccounts: accountsWithSendEtherInfoSelector(state),
|
||||||
subjectMetadata: getSubjectMetadata(state),
|
subjectMetadata: getSubjectMetadata(state),
|
||||||
|
messagesList,
|
||||||
|
messagesCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +54,19 @@ function mapDispatchToProps(dispatch) {
|
|||||||
return {
|
return {
|
||||||
goHome: () => dispatch(goHome()),
|
goHome: () => dispatch(goHome()),
|
||||||
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
|
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
|
||||||
|
showRejectTransactionsConfirmationModal: ({
|
||||||
|
onSubmit,
|
||||||
|
unapprovedTxCount: messagesCount,
|
||||||
|
}) => {
|
||||||
|
return dispatch(
|
||||||
|
showModal({
|
||||||
|
name: 'REJECT_TRANSACTIONS',
|
||||||
|
onSubmit,
|
||||||
|
unapprovedTxCount: messagesCount,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cancelAll: (messagesList) => dispatch(cancelMsgs(messagesList)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +81,7 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
|
|||||||
txData,
|
txData,
|
||||||
} = ownProps;
|
} = ownProps;
|
||||||
|
|
||||||
const { allAccounts, ...otherStateProps } = stateProps;
|
const { allAccounts, messagesList, ...otherStateProps } = stateProps;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
type,
|
type,
|
||||||
@ -71,6 +90,8 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
|
|||||||
|
|
||||||
const fromAccount = getAccountByAddress(allAccounts, from);
|
const fromAccount = getAccountByAddress(allAccounts, from);
|
||||||
|
|
||||||
|
const { cancelAll: dispatchCancelAll } = dispatchProps;
|
||||||
|
|
||||||
let cancel;
|
let cancel;
|
||||||
let sign;
|
let sign;
|
||||||
if (type === MESSAGE_TYPE.PERSONAL_SIGN) {
|
if (type === MESSAGE_TYPE.PERSONAL_SIGN) {
|
||||||
@ -92,6 +113,7 @@ function mergeProps(stateProps, dispatchProps, ownProps) {
|
|||||||
txData,
|
txData,
|
||||||
cancel,
|
cancel,
|
||||||
sign,
|
sign,
|
||||||
|
cancelAll: () => dispatchCancelAll(valuesFor(messagesList)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +117,29 @@ export const unconfirmedTransactionsHashSelector = createSelector(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const unconfirmedMessagesHashSelector = createSelector(
|
||||||
|
unapprovedMsgsSelector,
|
||||||
|
unapprovedPersonalMsgsSelector,
|
||||||
|
unapprovedDecryptMsgsSelector,
|
||||||
|
unapprovedEncryptionPublicKeyMsgsSelector,
|
||||||
|
unapprovedTypedMessagesSelector,
|
||||||
|
(
|
||||||
|
unapprovedMsgs = {},
|
||||||
|
unapprovedPersonalMsgs = {},
|
||||||
|
unapprovedDecryptMsgs = {},
|
||||||
|
unapprovedEncryptionPublicKeyMsgs = {},
|
||||||
|
unapprovedTypedMessages = {},
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
...unapprovedMsgs,
|
||||||
|
...unapprovedPersonalMsgs,
|
||||||
|
...unapprovedDecryptMsgs,
|
||||||
|
...unapprovedEncryptionPublicKeyMsgs,
|
||||||
|
...unapprovedTypedMessages,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const unapprovedMsgCountSelector = (state) => state.metamask.unapprovedMsgCount;
|
const unapprovedMsgCountSelector = (state) => state.metamask.unapprovedMsgCount;
|
||||||
const unapprovedPersonalMsgCountSelector = (state) =>
|
const unapprovedPersonalMsgCountSelector = (state) =>
|
||||||
state.metamask.unapprovedPersonalMsgCount;
|
state.metamask.unapprovedPersonalMsgCount;
|
||||||
|
@ -470,6 +470,24 @@ export function getTotalUnapprovedCount(state) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTotalUnapprovedMessagesCount(state) {
|
||||||
|
const {
|
||||||
|
unapprovedMsgCount = 0,
|
||||||
|
unapprovedPersonalMsgCount = 0,
|
||||||
|
unapprovedDecryptMsgCount = 0,
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount = 0,
|
||||||
|
unapprovedTypedMessagesCount = 0,
|
||||||
|
} = state.metamask;
|
||||||
|
|
||||||
|
return (
|
||||||
|
unapprovedMsgCount +
|
||||||
|
unapprovedPersonalMsgCount +
|
||||||
|
unapprovedDecryptMsgCount +
|
||||||
|
unapprovedEncryptionPublicKeyMsgCount +
|
||||||
|
unapprovedTypedMessagesCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getUnapprovedTxCount(state) {
|
function getUnapprovedTxCount(state) {
|
||||||
const { unapprovedTxs = {} } = state.metamask;
|
const { unapprovedTxs = {} } = state.metamask;
|
||||||
return Object.keys(unapprovedTxs).length;
|
return Object.keys(unapprovedTxs).length;
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
ENVIRONMENT_TYPE_NOTIFICATION,
|
ENVIRONMENT_TYPE_NOTIFICATION,
|
||||||
ORIGIN_METAMASK,
|
ORIGIN_METAMASK,
|
||||||
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
POLLING_TOKEN_ENVIRONMENT_TYPES,
|
||||||
|
MESSAGE_TYPE,
|
||||||
} from '../../shared/constants/app';
|
} from '../../shared/constants/app';
|
||||||
import { hasUnconfirmedTransactions } from '../helpers/utils/confirm-tx.util';
|
import { hasUnconfirmedTransactions } from '../helpers/utils/confirm-tx.util';
|
||||||
import txHelper from '../helpers/utils/tx-helper';
|
import txHelper from '../helpers/utils/tx-helper';
|
||||||
@ -1021,6 +1022,96 @@ export function cancelMsg(msgData) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels all of the given messages
|
||||||
|
*
|
||||||
|
* @param {Array<object>} msgDataList - a list of msg data objects
|
||||||
|
* @returns {function(*): Promise<void>}
|
||||||
|
*/
|
||||||
|
export function cancelMsgs(msgDataList) {
|
||||||
|
return async (dispatch) => {
|
||||||
|
dispatch(showLoadingIndication());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const msgIds = msgDataList.map((id) => id);
|
||||||
|
const cancellations = msgDataList.map(
|
||||||
|
({ id, type }) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
switch (type) {
|
||||||
|
case MESSAGE_TYPE.ETH_SIGN_TYPED_DATA:
|
||||||
|
background.cancelTypedMessage(id, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case MESSAGE_TYPE.PERSONAL_SIGN:
|
||||||
|
background.cancelPersonalMessage(id, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case MESSAGE_TYPE.ETH_DECRYPT:
|
||||||
|
background.cancelDecryptMessage(id, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case MESSAGE_TYPE.ETH_GET_ENCRYPTION_PUBLIC_KEY:
|
||||||
|
background.cancelEncryptionPublicKeyMsg(id, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case MESSAGE_TYPE.ETH_SIGN:
|
||||||
|
background.cancelMessage(id, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`MetaMask Message Signature: Unknown message type: ${id}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(cancellations);
|
||||||
|
const newState = await updateMetamaskStateFromBackground();
|
||||||
|
dispatch(updateMetamaskState(newState));
|
||||||
|
|
||||||
|
msgIds.forEach((id) => {
|
||||||
|
dispatch(completedTx(id));
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
log.error(err);
|
||||||
|
} finally {
|
||||||
|
if (getEnvironmentType() === ENVIRONMENT_TYPE_NOTIFICATION) {
|
||||||
|
closeNotificationPopup();
|
||||||
|
} else {
|
||||||
|
dispatch(hideLoadingIndication());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function cancelPersonalMsg(msgData) {
|
export function cancelPersonalMsg(msgData) {
|
||||||
return async (dispatch) => {
|
return async (dispatch) => {
|
||||||
dispatch(showLoadingIndication());
|
dispatch(showLoadingIndication());
|
||||||
|
@ -1696,4 +1696,58 @@ describe('Actions', () => {
|
|||||||
expect(expectedAction.value.id).toStrictEqual(txId);
|
expect(expectedAction.value.id).toStrictEqual(txId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#cancelMsgs', () => {
|
||||||
|
it('creates COMPLETED_TX with the cancelled messages IDs', async () => {
|
||||||
|
const store = mockStore();
|
||||||
|
|
||||||
|
const cancelTypedMessageStub = sinon.stub().callsFake((_, cb) => cb());
|
||||||
|
|
||||||
|
const cancelPersonalMessageStub = sinon.stub().callsFake((_, cb) => cb());
|
||||||
|
|
||||||
|
background.getApi.returns({
|
||||||
|
cancelTypedMessage: cancelTypedMessageStub,
|
||||||
|
cancelPersonalMessage: cancelPersonalMessageStub,
|
||||||
|
getState: sinon.stub().callsFake((cb) =>
|
||||||
|
cb(null, {
|
||||||
|
currentLocale: 'test',
|
||||||
|
selectedAddress: '0xFirstAddress',
|
||||||
|
provider: {
|
||||||
|
chainId: '0x1',
|
||||||
|
},
|
||||||
|
accounts: {
|
||||||
|
'0xFirstAddress': {
|
||||||
|
balance: '0x0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cachedBalances: {
|
||||||
|
'0x1': {
|
||||||
|
'0xFirstAddress': '0x0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const msgsList = [
|
||||||
|
{ id: 7648683973086304, status: 'unapproved', type: 'personal_sign' },
|
||||||
|
{
|
||||||
|
id: 7648683973086303,
|
||||||
|
status: 'unapproved',
|
||||||
|
type: 'eth_signTypedData',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
actions._setBackgroundConnection(background.getApi());
|
||||||
|
|
||||||
|
await store.dispatch(actions.cancelMsgs(msgsList));
|
||||||
|
const resultantActions = store.getActions();
|
||||||
|
const expectedActions = resultantActions.filter(
|
||||||
|
(action) => action.type === 'COMPLETED_TX',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(expectedActions[0].value.id).toStrictEqual(msgsList[0]);
|
||||||
|
expect(expectedActions[1].value.id).toStrictEqual(msgsList[1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user