Validate custom spend limit (#7920)

The custom spend limit was previously not validated. It did have a
minimum of zero set, but this didn't have any affect (that minimum is
used for form constraint validation, and this field wasn't in a form).
The field was never checked to ensure the contents didn't exceed the
maximum.

The field is now checked for values that exceed the maximum, and
invalid values in general (including negative values).

The parameters to the `showEditApprovalPermissionModal` were also
alphabetized to make them easier to read. In the course of doing this,
I noticed that the origin was missing from one of the calls. This was
responsible for the modal saying "Spend limit requested by undefined"
when clicking "Edit" under the transaction details. This has been
fixed.
feature/default_network_editable
Mark Stacey 5 years ago committed by GitHub
parent e8a42ba6b0
commit 59a1746afc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      app/_locales/en/messages.json
  2. 49
      ui/app/components/app/modals/edit-approval-permission/edit-approval-permission.component.js
  3. 16
      ui/app/pages/confirm-approve/confirm-approve-content/confirm-approve-content.component.js
  4. 1
      ui/app/pages/confirm-approve/confirm-approve.component.js
  5. 18
      ui/app/pages/confirm-approve/confirm-approve.container.js

@ -1307,6 +1307,12 @@
"message": "Spend limit requested by $1", "message": "Spend limit requested by $1",
"description": "Origin of the site requesting the spend limit" "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": { "switchNetworks": {
"message": "Switch Networks" "message": "Switch Networks"
}, },

@ -1,13 +1,18 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import log from 'loglevel'
import Modal from '../../modal' import Modal from '../../modal'
import Identicon from '../../../ui/identicon' import Identicon from '../../../ui/identicon'
import TextField from '../../../ui/text-field' import TextField from '../../../ui/text-field'
import { calcTokenAmount } from '../../../../helpers/utils/token-util'
import classnames from 'classnames' import classnames from 'classnames'
import BigNumber from 'bignumber.js' 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 { export default class EditApprovalPermission extends PureComponent {
static propTypes = { static propTypes = {
decimals: PropTypes.number,
hideModal: PropTypes.func.isRequired, hideModal: PropTypes.func.isRequired,
selectedIdentity: PropTypes.object, selectedIdentity: PropTypes.object,
tokenAmount: PropTypes.string, tokenAmount: PropTypes.string,
@ -15,7 +20,7 @@ export default class EditApprovalPermission extends PureComponent {
tokenSymbol: PropTypes.string, tokenSymbol: PropTypes.string,
tokenBalance: PropTypes.string, tokenBalance: PropTypes.string,
setCustomAmount: PropTypes.func, setCustomAmount: PropTypes.func,
origin: PropTypes.string, origin: PropTypes.string.isRequired,
} }
static contextTypes = { static contextTypes = {
@ -27,7 +32,7 @@ export default class EditApprovalPermission extends PureComponent {
selectedOptionIsUnlimited: !this.props.customTokenAmount, selectedOptionIsUnlimited: !this.props.customTokenAmount,
} }
renderModalContent () { renderModalContent (error) {
const { t } = this.context const { t } = this.context
const { const {
hideModal, hideModal,
@ -136,7 +141,6 @@ export default class EditApprovalPermission extends PureComponent {
<div className="edit-approval-permission__edit-section__option-input" > <div className="edit-approval-permission__edit-section__option-input" >
<TextField <TextField
type="number" type="number"
min="0"
placeholder={ `${Number(customTokenAmount || tokenAmount)} ${tokenSymbol}` } placeholder={ `${Number(customTokenAmount || tokenAmount)} ${tokenSymbol}` }
onChange={(event) => { onChange={(event) => {
this.setState({ customSpendLimit: event.target.value }) this.setState({ customSpendLimit: event.target.value })
@ -147,6 +151,7 @@ export default class EditApprovalPermission extends PureComponent {
fullWidth fullWidth
margin="dense" margin="dense"
value={ this.state.customSpendLimit } value={ this.state.customSpendLimit }
error={error}
/> />
</div> </div>
</div> </div>
@ -156,10 +161,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 () { render () {
const { t } = this.context const { t } = this.context
const { setCustomAmount, hideModal, customTokenAmount } = this.props const { setCustomAmount, hideModal, customTokenAmount } = this.props
const { selectedOptionIsUnlimited, customSpendLimit } = this.state const { selectedOptionIsUnlimited, customSpendLimit } = this.state
const error = this.validateSpendLimit()
const disabled = Boolean(
(customSpendLimit === customTokenAmount && !selectedOptionIsUnlimited) ||
error
)
return ( return (
<Modal <Modal
onSubmit={() => { onSubmit={() => {
@ -170,9 +209,9 @@ export default class EditApprovalPermission extends PureComponent {
submitType="primary" submitType="primary"
contentClass="edit-approval-permission-modal-content" contentClass="edit-approval-permission-modal-content"
containerClass="edit-approval-permission-modal-container" containerClass="edit-approval-permission-modal-container"
submitDisabled={ (customSpendLimit === customTokenAmount) && !selectedOptionIsUnlimited } submitDisabled={disabled}
> >
{ this.renderModalContent() } { this.renderModalContent(error) }
</Modal> </Modal>
) )
} }

@ -13,6 +13,7 @@ export default class ConfirmApproveContent extends Component {
} }
static propTypes = { static propTypes = {
decimals: PropTypes.number,
tokenAmount: PropTypes.string, tokenAmount: PropTypes.string,
customTokenAmount: PropTypes.string, customTokenAmount: PropTypes.string,
tokenSymbol: PropTypes.string, tokenSymbol: PropTypes.string,
@ -127,6 +128,7 @@ export default class ConfirmApproveContent extends Component {
render () { render () {
const { t } = this.context const { t } = this.context
const { const {
decimals,
siteImage, siteImage,
tokenAmount, tokenAmount,
customTokenAmount, customTokenAmount,
@ -164,7 +166,15 @@ export default class ConfirmApproveContent extends Component {
> >
<div <div
className="confirm-approve-content__medium-link-text cursor-pointer" className="confirm-approve-content__medium-link-text cursor-pointer"
onClick={() => showEditApprovalPermissionModal({ customTokenAmount, tokenAmount, tokenSymbol, setCustomAmount, tokenBalance, origin })} onClick={() => showEditApprovalPermissionModal({
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenSymbol,
tokenBalance,
})}
> >
{ t('editPermission') } { t('editPermission') }
</div> </div>
@ -209,10 +219,12 @@ export default class ConfirmApproveContent extends Component {
showEdit: true, showEdit: true,
onEditClick: () => showEditApprovalPermissionModal({ onEditClick: () => showEditApprovalPermissionModal({
customTokenAmount, customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount, tokenAmount,
tokenSymbol, tokenSymbol,
tokenBalance, tokenBalance,
setCustomAmount,
}), }),
})} })}
</div> </div>

@ -87,6 +87,7 @@ export default class ConfirmApprove extends Component {
title={tokensText} title={tokensText}
contentComponent={( contentComponent={(
<ConfirmApproveContent <ConfirmApproveContent
decimals={decimals}
siteImage={siteImage} siteImage={siteImage}
setCustomAmount={(newAmount) => { setCustomAmount={(newAmount) => {
this.setState({ customPermissionAmount: newAmount }) this.setState({ customPermissionAmount: newAmount })

@ -76,20 +76,22 @@ const mapDispatchToProps = (dispatch) => {
return { return {
showCustomizeGasModal: (txData) => dispatch(showModal({ name: 'CUSTOMIZE_GAS', txData })), showCustomizeGasModal: (txData) => dispatch(showModal({ name: 'CUSTOMIZE_GAS', txData })),
showEditApprovalPermissionModal: ({ showEditApprovalPermissionModal: ({
tokenAmount,
customTokenAmount, customTokenAmount,
tokenSymbol, decimals,
tokenBalance,
setCustomAmount,
origin, origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
}) => dispatch(showModal({ }) => dispatch(showModal({
name: 'EDIT_APPROVAL_PERMISSION', name: 'EDIT_APPROVAL_PERMISSION',
tokenAmount,
customTokenAmount, customTokenAmount,
tokenSymbol, decimals,
tokenBalance,
setCustomAmount,
origin, origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
})), })),
} }
} }

Loading…
Cancel
Save