From 662c19d4da7f04872f15a7605f4be55fdca8d560 Mon Sep 17 00:00:00 2001
From: dragana8 <92531782+dragana8@users.noreply.github.com>
Date: Wed, 23 Feb 2022 16:03:01 +0100
Subject: [PATCH] Implement Network specific insufficient currency warning
#12965 (#13182)
* added warning
fix
added warning for send screen
fix
changed condition
fix
fixed test
review updates
* review updates
* fix
* fixed failing test
* added check for transaction type
* fixed failing unit test
* added description for localization
* review updates
* review updates
---
app/_locales/en/messages.json | 14 ++++
...onfirm-page-container-content.component.js | 71 +++++++++++++++-
...m-page-container-content.component.test.js | 3 +
.../confirm-page-container-content/index.scss | 8 ++
.../confirm-page-container.component.js | 80 ++++++++++++++++---
.../confirm-page-container.container.js | 12 ++-
.../app/confirm-page-container/index.scss | 8 ++
.../confirm-transaction-base.component.js | 10 +++
.../confirm-transaction-base.container.js | 1 +
.../transaction-alerts/transaction-alerts.js | 55 ++++++++++++-
.../transaction-alerts.scss | 8 ++
.../transaction-alerts.test.js | 14 +++-
12 files changed, 267 insertions(+), 17 deletions(-)
diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index cb4496c6d..7494f39df 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -406,6 +406,13 @@
"message": "Transak supports debit card and bank transfers (depending on location) in 59+ countries. $1 deposits into your MetaMask account.",
"description": "$1 represents the cypto symbol to be purchased"
},
+ "buyEth": {
+ "message": "Buy ETH"
+ },
+ "buyOther": {
+ "message": "Buy $1 or deposit from another account.",
+ "description": "$1 is a token symbol"
+ },
"buyWithWyre": {
"message": "Buy ETH with Wyre"
},
@@ -1480,6 +1487,10 @@
"insufficientBalance": {
"message": "Insufficient balance."
},
+ "insufficientCurrency": {
+ "message": "You do not have enough $1 in your account to pay for transaction fees on $2 network.",
+ "description": "$1 is currency, $2 is network"
+ },
"insufficientFunds": {
"message": "Insufficient funds."
},
@@ -2223,6 +2234,9 @@
"or": {
"message": "or"
},
+ "orDeposit": {
+ "message": "or deposit from another account."
+ },
"origin": {
"message": "Origin"
},
diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
index 665d1924f..54dcb6d62 100644
--- a/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
+++ b/ui/components/app/confirm-page-container/confirm-page-container-content/confirm-page-container-content.component.js
@@ -2,9 +2,16 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { Tabs, Tab } from '../../../ui/tabs';
-import ErrorMessage from '../../../ui/error-message';
+import Button from '../../../ui/button';
import ActionableMessage from '../../../ui/actionable-message/actionable-message';
import { PageContainerFooter } from '../../../ui/page-container';
+import ErrorMessage from '../../../ui/error-message';
+import { INSUFFICIENT_FUNDS_ERROR_KEY } from '../../../../helpers/constants/error-keys';
+import Typography from '../../../ui/typography';
+import { TYPOGRAPHY } from '../../../../helpers/constants/design-system';
+import { TRANSACTION_TYPES } from '../../../../../shared/constants/transaction';
+import { MAINNET_CHAIN_ID } from '../../../../../shared/constants/network';
+
import { ConfirmPageContainerSummary, ConfirmPageContainerWarning } from '.';
export default class ConfirmPageContainerContent extends Component {
@@ -44,6 +51,10 @@ export default class ConfirmPageContainerContent extends Component {
hideTitle: PropTypes.bool,
supportsEIP1559V2: PropTypes.bool,
hasTopBorder: PropTypes.bool,
+ currentTransaction: PropTypes.string,
+ nativeCurrency: PropTypes.string,
+ networkName: PropTypes.string,
+ showBuyModal: PropTypes.func,
};
renderContent() {
@@ -113,6 +124,10 @@ export default class ConfirmPageContainerContent extends Component {
hideUserAcknowledgedGasMissing,
supportsEIP1559V2,
hasTopBorder,
+ currentTransaction,
+ nativeCurrency,
+ networkName,
+ showBuyModal,
} = this.props;
const primaryAction = hideUserAcknowledgedGasMissing
@@ -121,6 +136,14 @@ export default class ConfirmPageContainerContent extends Component {
label: this.context.t('tryAnywayOption'),
onClick: setUserAcknowledgedGasMissing,
};
+ const { t } = this.context;
+
+ const showInsuffienctFundsError =
+ supportsEIP1559V2 &&
+ !hasSimulationError &&
+ (errorKey || errorMessage) &&
+ errorKey === INSUFFICIENT_FUNDS_ERROR_KEY &&
+ currentTransaction.type === TRANSACTION_TYPES.SIMPLE_SEND;
return (
)}
@@ -160,11 +183,53 @@ export default class ConfirmPageContainerContent extends Component {
{this.renderContent()}
{!supportsEIP1559V2 &&
!hasSimulationError &&
- (errorKey || errorMessage) && (
+ (errorKey || errorMessage) &&
+ currentTransaction.type !== TRANSACTION_TYPES.SIMPLE_SEND && (
)}
+ {showInsuffienctFundsError && (
+
+ {currentTransaction.chainId === MAINNET_CHAIN_ID ? (
+
+ {t('insufficientCurrency', [nativeCurrency, networkName])}
+
+
+ {t('orDeposit')}
+
+ }
+ useIcon
+ iconFillColor="#d73a49"
+ type="danger"
+ />
+ ) : (
+
+ {t('insufficientCurrency', [nativeCurrency, networkName])}
+ {t('buyOther', [nativeCurrency])}
+
+ }
+ useIcon
+ iconFillColor="#d73a49"
+ type="danger"
+ />
+ )}
+
+ )}
+
{
props.hasSimulationError = false;
props.disabled = true;
props.errorKey = TRANSACTION_ERROR_KEY;
+ props.currentTransaction = {
+ type: 'transfer',
+ };
const { queryByText, getByText } = renderWithProvider(
,
store,
diff --git a/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss b/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss
index b932af183..f52888db2 100644
--- a/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss
+++ b/ui/components/app/confirm-page-container/confirm-page-container-content/index.scss
@@ -100,4 +100,12 @@
&__total-value {
position: relative;
}
+
+ &__link {
+ background: transparent;
+ border: 0 transparent;
+ display: inline;
+ padding: 0;
+ font-size: $font-size-h7;
+ }
}
diff --git a/ui/components/app/confirm-page-container/confirm-page-container.component.js b/ui/components/app/confirm-page-container/confirm-page-container.component.js
index 10f326594..25c9ff937 100644
--- a/ui/components/app/confirm-page-container/confirm-page-container.component.js
+++ b/ui/components/app/confirm-page-container/confirm-page-container.component.js
@@ -4,10 +4,15 @@ import PropTypes from 'prop-types';
import { EDIT_GAS_MODES } from '../../../../shared/constants/gas';
import { GasFeeContextProvider } from '../../../contexts/gasFee';
import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
+import {
+ NETWORK_TO_NAME_MAP,
+ MAINNET_CHAIN_ID,
+} from '../../../../shared/constants/network';
import { PageContainerFooter } from '../../ui/page-container';
import Dialog from '../../ui/dialog';
-import ErrorMessage from '../../ui/error-message';
+import Button from '../../ui/button';
+import ActionableMessage from '../../ui/actionable-message/actionable-message';
import SenderToRecipient from '../../ui/sender-to-recipient';
import NicknamePopovers from '../modals/nickname-popovers';
@@ -15,6 +20,10 @@ import NicknamePopovers from '../modals/nickname-popovers';
import AdvancedGasFeePopover from '../advanced-gas-fee-popover';
import EditGasFeePopover from '../edit-gas-fee-popover/edit-gas-fee-popover';
import EditGasPopover from '../edit-gas-popover';
+import ErrorMessage from '../../ui/error-message';
+import { INSUFFICIENT_FUNDS_ERROR_KEY } from '../../../helpers/constants/error-keys';
+import Typography from '../../ui/typography';
+import { TYPOGRAPHY } from '../../../helpers/constants/design-system';
import EnableEIP1559V2Notice from './enableEIP1559V2-notice';
import {
@@ -87,6 +96,8 @@ export default class ConfirmPageContainer extends Component {
contact: PropTypes.object,
isOwnedAccount: PropTypes.bool,
supportsEIP1559V2: PropTypes.bool,
+ nativeCurrency: PropTypes.string,
+ showBuyModal: PropTypes.func,
};
render() {
@@ -139,6 +150,8 @@ export default class ConfirmPageContainer extends Component {
contact = {},
isOwnedAccount,
supportsEIP1559V2,
+ nativeCurrency,
+ showBuyModal,
} = this.props;
const showAddToAddressDialog =
@@ -152,6 +165,10 @@ export default class ConfirmPageContainer extends Component {
currentTransaction.type === TRANSACTION_TYPES.DEPLOY_CONTRACT) &&
currentTransaction.txParams?.value === '0x0';
+ const networkName = NETWORK_TO_NAME_MAP[currentTransaction.chainId];
+
+ const { t } = this.context;
+
return (
@@ -192,7 +209,7 @@ export default class ConfirmPageContainer extends Component {
className="send__dialog"
onClick={() => this.setState({ showNicknamePopovers: true })}
>
- {this.context.t('newAccountDetectedDialogMessage')}
+ {t('newAccountDetectedDialogMessage')}
{this.state.showNicknamePopovers ? (
)}
- {shouldDisplayWarning && (
+ {shouldDisplayWarning && errorKey === INSUFFICIENT_FUNDS_ERROR_KEY && (
+
+ {currentTransaction.chainId === MAINNET_CHAIN_ID ? (
+
+ {t('insufficientCurrency', [nativeCurrency, networkName])}
+
+
+ {t('orDeposit')}
+
+ }
+ useIcon
+ iconFillColor="#d73a49"
+ type="danger"
+ />
+ ) : (
+
+ {t('insufficientCurrency', [nativeCurrency, networkName])}
+ {t('buyOther', [nativeCurrency])}
+
+ }
+ useIcon
+ iconFillColor="#d73a49"
+ type="danger"
+ />
+ )}
+
+ )}
+ {shouldDisplayWarning && errorKey !== INSUFFICIENT_FUNDS_ERROR_KEY && (
@@ -245,14 +307,14 @@ export default class ConfirmPageContainer extends Component {
{contentComponent && (
{unapprovedTxCount > 1 && (
- {this.context.t('rejectTxsN', [unapprovedTxCount])}
+ {t('rejectTxsN', [unapprovedTxCount])}
)}
diff --git a/ui/components/app/confirm-page-container/confirm-page-container.container.js b/ui/components/app/confirm-page-container/confirm-page-container.container.js
index ec5ba84c0..d0d287aed 100644
--- a/ui/components/app/confirm-page-container/confirm-page-container.container.js
+++ b/ui/components/app/confirm-page-container/confirm-page-container.container.js
@@ -1,5 +1,6 @@
import { connect } from 'react-redux';
import { getAccountsWithLabels, getAddressBookEntry } from '../../../selectors';
+import { showModal } from '../../../store/actions';
import ConfirmPageContainer from './confirm-page-container.component';
function mapStateToProps(state, ownProps) {
@@ -16,4 +17,13 @@ function mapStateToProps(state, ownProps) {
};
}
-export default connect(mapStateToProps)(ConfirmPageContainer);
+const mapDispatchToProps = (dispatch) => {
+ return {
+ showBuyModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER' })),
+ };
+};
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps,
+)(ConfirmPageContainer);
diff --git a/ui/components/app/confirm-page-container/index.scss b/ui/components/app/confirm-page-container/index.scss
index 9723c2d23..20aa1d94f 100644
--- a/ui/components/app/confirm-page-container/index.scss
+++ b/ui/components/app/confirm-page-container/index.scss
@@ -7,4 +7,12 @@
&__content-component-wrapper {
height: 100%;
}
+
+ &__link {
+ background: transparent;
+ border: 0 transparent;
+ display: inline;
+ padding: 0;
+ font-size: $font-size-h7;
+ }
}
diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
index f7f9774f3..a7202b6ca 100644
--- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
+++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
@@ -60,6 +60,7 @@ import {
import Typography from '../../components/ui/typography/typography';
import { MIN_GAS_LIMIT_DEC } from '../send/send.constants';
+import { NETWORK_TO_NAME_MAP } from '../../../shared/constants/network';
import TransactionAlerts from './transaction-alerts';
@@ -144,6 +145,7 @@ export default class ConfirmTransactionBase extends Component {
hardwareWalletRequiresConnection: PropTypes.bool,
isMultiLayerFeeNetwork: PropTypes.bool,
eip1559V2Enabled: PropTypes.bool,
+ showBuyModal: PropTypes.func,
};
state = {
@@ -323,6 +325,7 @@ export default class ConfirmTransactionBase extends Component {
supportsEIP1559,
isMultiLayerFeeNetwork,
nativeCurrency,
+ showBuyModal,
} = this.props;
const { t } = this.context;
const { userAcknowledgedGasMissing } = this.state;
@@ -335,6 +338,7 @@ export default class ConfirmTransactionBase extends Component {
const hasSimulationError = Boolean(txData.simulationFails);
const renderSimulationFailureWarning =
hasSimulationError && !userAcknowledgedGasMissing;
+ const networkName = NETWORK_TO_NAME_MAP[txData.chainId];
const renderTotalMaxAmount = () => {
if (
@@ -582,6 +586,11 @@ export default class ConfirmTransactionBase extends Component {
this.setUserAcknowledgedGasMissing()
}
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
+ chainId={txData.chainId}
+ nativeCurrency={nativeCurrency}
+ networkName={networkName}
+ showBuyModal={showBuyModal}
+ type={txData.type}
/>
this.handleCloseEditGas()}
currentTransaction={txData}
supportsEIP1559V2={this.supportsEIP1559V2}
+ nativeCurrency={nativeCurrency}
/>
);
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 8b13a1034..c6a6598ad 100644
--- a/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js
+++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.container.js
@@ -285,6 +285,7 @@ export const mapDispatchToProps = (dispatch) => {
updateTransactionGasFees: (gasFees) => {
dispatch(updateTransactionGasFees({ ...gasFees, expectHexWei: true }));
},
+ showBuyModal: () => dispatch(showModal({ name: 'DEPOSIT_ETHER' })),
};
};
diff --git a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js
index ae6b195ad..bbb1883ab 100644
--- a/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js
+++ b/ui/pages/confirm-transaction-base/transaction-alerts/transaction-alerts.js
@@ -3,19 +3,25 @@ import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { PRIORITY_LEVELS } from '../../../../shared/constants/gas';
-import { INSUFFICIENT_FUNDS_ERROR_KEY } from '../../../helpers/constants/error-keys';
import { submittedPendingTransactionsSelector } from '../../../selectors/transactions';
import { useGasFeeContext } from '../../../contexts/gasFee';
import { useI18nContext } from '../../../hooks/useI18nContext';
import ActionableMessage from '../../../components/ui/actionable-message/actionable-message';
-import ErrorMessage from '../../../components/ui/error-message';
import I18nValue from '../../../components/ui/i18n-value';
+import Button from '../../../components/ui/button';
import Typography from '../../../components/ui/typography';
import { TYPOGRAPHY } from '../../../helpers/constants/design-system';
+import { TRANSACTION_TYPES } from '../../../../shared/constants/transaction';
+import { MAINNET_CHAIN_ID } from '../../../../shared/constants/network';
const TransactionAlerts = ({
userAcknowledgedGasMissing,
setUserAcknowledgedGasMissing,
+ chainId,
+ nativeCurrency,
+ networkName,
+ showBuyModal,
+ type,
}) => {
const {
balanceError,
@@ -90,7 +96,45 @@ const TransactionAlerts = ({
type="warning"
/>
)}
- {balanceError && }
+ {balanceError &&
+ chainId === MAINNET_CHAIN_ID &&
+ type === TRANSACTION_TYPES.DEPLOY_CONTRACT ? (
+
+ {t('insufficientCurrency', [nativeCurrency, networkName])}{' '}
+ {' '}
+ {t('orDeposit')}
+
+ }
+ useIcon
+ iconFillColor="#d73a49"
+ type="danger"
+ />
+ ) : null}
+ {balanceError &&
+ chainId !== MAINNET_CHAIN_ID &&
+ type === TRANSACTION_TYPES.DEPLOY_CONTRACT ? (
+
+ {t('insufficientCurrency', [nativeCurrency, networkName])}
+ {t('buyOther', [nativeCurrency])}
+
+ }
+ useIcon
+ iconFillColor="#d73a49"
+ type="danger"
+ />
+ ) : null}
{estimateUsed === PRIORITY_LEVELS.LOW && (
{
supportsEIP1559V2: true,
balanceError: true,
},
+ componentProps: {
+ nativeCurrency: 'ETH',
+ networkName: 'Ropsten',
+ showBuyModal: jest.fn(),
+ chainId: '0x1',
+ type: TRANSACTION_TYPES.DEPLOY_CONTRACT,
+ },
});
- expect(getByText('Insufficient funds.')).toBeInTheDocument();
+ expect(
+ getByText(
+ /You do not have enough ETH in your account to pay for transaction fees on Ropsten network./u,
+ ),
+ ).toBeInTheDocument();
});
});