diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index bf84c91dc5d7..15604d2e41d0 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -1313,6 +1313,12 @@ "message": "Spend limit requested by $1", "description": "Origin of the site requesting the spend limit" }, + "spendLimitTooLarge": { + "message": "Spend limit too large" + }, + "spendLimitInvalid": { + "message": "Spend limit invalid; must be a positive number" + }, "switchNetworks": { "message": "Switch Networks" }, diff --git a/ui/app/components/app/modals/edit-approval-permission/edit-approval-permission.component.js b/ui/app/components/app/modals/edit-approval-permission/edit-approval-permission.component.js index b492010eabaa..e9720fec82da 100644 --- a/ui/app/components/app/modals/edit-approval-permission/edit-approval-permission.component.js +++ b/ui/app/components/app/modals/edit-approval-permission/edit-approval-permission.component.js @@ -1,13 +1,18 @@ import React, { PureComponent } from 'react' import PropTypes from 'prop-types' +import log from 'loglevel' import Modal from '../../modal' import Identicon from '../../../ui/identicon' import TextField from '../../../ui/text-field' +import { calcTokenAmount } from '../../../../helpers/utils/token-util' import classnames from 'classnames' import BigNumber from 'bignumber.js' +const MAX_UNSIGNED_256_INT = new BigNumber(2).pow(256).minus(1).toString(10) + export default class EditApprovalPermission extends PureComponent { static propTypes = { + decimals: PropTypes.number, hideModal: PropTypes.func.isRequired, selectedIdentity: PropTypes.object, tokenAmount: PropTypes.string, @@ -15,7 +20,7 @@ export default class EditApprovalPermission extends PureComponent { tokenSymbol: PropTypes.string, tokenBalance: PropTypes.string, setCustomAmount: PropTypes.func, - origin: PropTypes.string, + origin: PropTypes.string.isRequired, } static contextTypes = { @@ -27,7 +32,7 @@ export default class EditApprovalPermission extends PureComponent { selectedOptionIsUnlimited: !this.props.customTokenAmount, } - renderModalContent () { + renderModalContent (error) { const { t } = this.context const { hideModal, @@ -141,7 +146,6 @@ export default class EditApprovalPermission extends PureComponent {
{ this.setState({ customSpendLimit: event.target.value }) @@ -151,7 +155,8 @@ export default class EditApprovalPermission extends PureComponent { }} fullWidth margin="dense" - value={this.state.customSpendLimit} + value={ this.state.customSpendLimit } + error={error} />
@@ -161,10 +166,44 @@ export default class EditApprovalPermission extends PureComponent { ) } + validateSpendLimit () { + const { t } = this.context + const { decimals } = this.props + const { selectedOptionIsUnlimited, customSpendLimit } = this.state + + if (selectedOptionIsUnlimited || !customSpendLimit) { + return + } + + let customSpendLimitNumber + try { + customSpendLimitNumber = new BigNumber(customSpendLimit) + } catch (error) { + log.debug(`Error converting '${customSpendLimit}' to BigNumber:`, error) + return t('spendLimitInvalid') + } + + if (customSpendLimitNumber.isNegative()) { + return t('spendLimitInvalid') + } + + const maxTokenAmount = calcTokenAmount(MAX_UNSIGNED_256_INT, decimals) + if (customSpendLimitNumber.greaterThan(maxTokenAmount)) { + return t('spendLimitTooLarge') + } + } + render () { const { t } = this.context const { setCustomAmount, hideModal, customTokenAmount } = this.props const { selectedOptionIsUnlimited, customSpendLimit } = this.state + + const error = this.validateSpendLimit() + const disabled = Boolean( + (customSpendLimit === customTokenAmount && !selectedOptionIsUnlimited) || + error + ) + return ( { @@ -175,11 +214,9 @@ export default class EditApprovalPermission extends PureComponent { submitType="primary" contentClass="edit-approval-permission-modal-content" containerClass="edit-approval-permission-modal-container" - submitDisabled={ - customSpendLimit === customTokenAmount && !selectedOptionIsUnlimited - } + submitDisabled={disabled} > - {this.renderModalContent()} + { this.renderModalContent(error) } ) } diff --git a/ui/app/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js b/ui/app/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js index 291849c88551..c4436b66b60c 100644 --- a/ui/app/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js +++ b/ui/app/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js @@ -11,6 +11,7 @@ export default class ConfirmApproveContent extends Component { } static propTypes = { + decimals: PropTypes.number, tokenAmount: PropTypes.string, customTokenAmount: PropTypes.string, tokenSymbol: PropTypes.string, @@ -146,6 +147,7 @@ export default class ConfirmApproveContent extends Component { render () { const { t } = this.context const { + decimals, siteImage, tokenAmount, customTokenAmount, @@ -181,16 +183,15 @@ export default class ConfirmApproveContent extends Component {
- showEditApprovalPermissionModal({ - customTokenAmount, - tokenAmount, - tokenSymbol, - setCustomAmount, - tokenBalance, - origin, - }) - } + onClick={() => showEditApprovalPermissionModal({ + customTokenAmount, + decimals, + origin, + setCustomAmount, + tokenAmount, + tokenSymbol, + tokenBalance, + })} > {t('editPermission')}
@@ -228,34 +229,39 @@ export default class ConfirmApproveContent extends Component { })}
- {showFullTxDetails ? ( -
-
- {this.renderApproveContentCard({ - symbol: , - title: 'Permission', - content: this.renderPermissionContent(), - showEdit: true, - onEditClick: () => - showEditApprovalPermissionModal({ - customTokenAmount, - tokenAmount, - tokenSymbol, - tokenBalance, - setCustomAmount, - }), - })} -
-
- {this.renderApproveContentCard({ - symbol: , - title: 'Data', - content: this.renderDataContent(), - noBorder: true, - })} -
-
- ) : null} + { + showFullTxDetails + ? ( +
+
+ {this.renderApproveContentCard({ + symbol: , + title: 'Permission', + content: this.renderPermissionContent(), + showEdit: true, + onEditClick: () => showEditApprovalPermissionModal({ + customTokenAmount, + decimals, + origin, + setCustomAmount, + tokenAmount, + tokenSymbol, + tokenBalance, + }), + })} +
+
+ {this.renderApproveContentCard({ + symbol: , + title: 'Data', + content: this.renderDataContent(), + noBorder: true, + })} +
+
+ ) + : null + } ) } diff --git a/ui/app/pages/confirm-approve/confirm-approve.component.js b/ui/app/pages/confirm-approve/confirm-approve.component.js index bf3af94a8ef7..484423fc874b 100644 --- a/ui/app/pages/confirm-approve/confirm-approve.component.js +++ b/ui/app/pages/confirm-approve/confirm-approve.component.js @@ -97,6 +97,7 @@ export default class ConfirmApprove extends Component { title={tokensText} contentComponent={( { this.setState({ customPermissionAmount: newAmount }) diff --git a/ui/app/pages/confirm-approve/confirm-approve.container.js b/ui/app/pages/confirm-approve/confirm-approve.container.js index 92223142f29c..b14935d7987a 100644 --- a/ui/app/pages/confirm-approve/confirm-approve.container.js +++ b/ui/app/pages/confirm-approve/confirm-approve.container.js @@ -90,24 +90,23 @@ const mapDispatchToProps = dispatch => { showCustomizeGasModal: txData => dispatch(showModal({ name: 'CUSTOMIZE_GAS', txData })), showEditApprovalPermissionModal: ({ - tokenAmount, customTokenAmount, - tokenSymbol, - tokenBalance, + decimals, + origin, setCustomAmount, + tokenAmount, + tokenBalance, + tokenSymbol, + }) => dispatch(showModal({ + name: 'EDIT_APPROVAL_PERMISSION', + customTokenAmount, + decimals, origin, - }) => - dispatch( - showModal({ - name: 'EDIT_APPROVAL_PERMISSION', - tokenAmount, - customTokenAmount, - tokenSymbol, - tokenBalance, - setCustomAmount, - origin, - }) - ), + setCustomAmount, + tokenAmount, + tokenBalance, + tokenSymbol, + })), } }