mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Improve customize gas modal error handling
This commit is contained in:
parent
a7069acf2e
commit
e737a9565a
@ -6,23 +6,46 @@ const actions = require('../../actions')
|
||||
const GasModalCard = require('./gas-modal-card')
|
||||
|
||||
const {
|
||||
MIN_GAS_PRICE,
|
||||
MIN_GAS_LIMIT,
|
||||
MIN_GAS_PRICE_DEC,
|
||||
MIN_GAS_LIMIT_DEC,
|
||||
MIN_GAS_PRICE_GWEI,
|
||||
} = require('../send/send-constants')
|
||||
|
||||
const { conversionUtil, multiplyCurrencies } = require('../../conversion-util')
|
||||
const {
|
||||
isBalanceSufficient,
|
||||
} = require('../send/send-utils')
|
||||
|
||||
const {
|
||||
conversionUtil,
|
||||
multiplyCurrencies,
|
||||
conversionGreaterThan,
|
||||
} = require('../../conversion-util')
|
||||
|
||||
const {
|
||||
getGasPrice,
|
||||
getGasLimit,
|
||||
conversionRateSelector,
|
||||
getSendAmount,
|
||||
getSelectedToken,
|
||||
getSendFrom,
|
||||
getCurrentAccountWithSendEtherInfo,
|
||||
getSelectedTokenToFiatRate,
|
||||
} = require('../../selectors')
|
||||
|
||||
function mapStateToProps (state) {
|
||||
const selectedToken = getSelectedToken(state)
|
||||
const currentAccount = getSendFrom(state) || getCurrentAccountWithSendEtherInfo(state)
|
||||
const conversionRate = conversionRateSelector(state)
|
||||
|
||||
return {
|
||||
gasPrice: getGasPrice(state),
|
||||
gasLimit: getGasLimit(state),
|
||||
conversionRate: conversionRateSelector(state),
|
||||
conversionRate,
|
||||
amount: getSendAmount(state),
|
||||
balance: currentAccount.balance,
|
||||
primaryCurrency: selectedToken && selectedToken.symbol,
|
||||
selectedToken,
|
||||
amountConversionRate: selectedToken ? getSelectedTokenToFiatRate(state) : conversionRate,
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,15 +62,26 @@ inherits(CustomizeGasModal, Component)
|
||||
function CustomizeGasModal (props) {
|
||||
Component.call(this)
|
||||
|
||||
const gasPrice = props.gasPrice || MIN_GAS_PRICE_DEC
|
||||
const gasLimit = props.gasLimit || MIN_GAS_LIMIT_DEC
|
||||
|
||||
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
})
|
||||
|
||||
this.state = {
|
||||
gasPrice: props.gasPrice || MIN_GAS_PRICE,
|
||||
gasLimit: props.gasLimit || MIN_GAS_LIMIT,
|
||||
gasPrice,
|
||||
gasLimit,
|
||||
gasTotal,
|
||||
error: null,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(CustomizeGasModal)
|
||||
|
||||
CustomizeGasModal.prototype.save = function (gasPrice, gasLimit) {
|
||||
CustomizeGasModal.prototype.save = function (gasPrice, gasLimit, gasTotal) {
|
||||
const {
|
||||
updateGasPrice,
|
||||
updateGasLimit,
|
||||
@ -55,41 +89,101 @@ CustomizeGasModal.prototype.save = function (gasPrice, gasLimit) {
|
||||
updateGasTotal
|
||||
} = this.props
|
||||
|
||||
const newGasTotal = multiplyCurrencies(gasLimit, gasPrice, {
|
||||
updateGasPrice(gasPrice)
|
||||
updateGasLimit(gasLimit)
|
||||
updateGasTotal(gasTotal)
|
||||
hideModal()
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.validate = function ({ gasTotal, gasLimit }) {
|
||||
const {
|
||||
amount,
|
||||
balance,
|
||||
primaryCurrency,
|
||||
selectedToken,
|
||||
amountConversionRate,
|
||||
conversionRate,
|
||||
} = this.props
|
||||
|
||||
let error = null
|
||||
|
||||
const balanceIsSufficient = isBalanceSufficient({
|
||||
amount,
|
||||
gasTotal,
|
||||
balance,
|
||||
primaryCurrency,
|
||||
selectedToken,
|
||||
amountConversionRate,
|
||||
conversionRate,
|
||||
})
|
||||
|
||||
if (!balanceIsSufficient) {
|
||||
error = 'Insufficient balance for current gas total'
|
||||
}
|
||||
|
||||
const gasLimitTooLow = gasLimit && conversionGreaterThan(
|
||||
{
|
||||
value: MIN_GAS_LIMIT_DEC,
|
||||
fromNumericBase: 'dec',
|
||||
conversionRate,
|
||||
},
|
||||
{
|
||||
value: gasLimit,
|
||||
fromNumericBase: 'hex',
|
||||
},
|
||||
)
|
||||
|
||||
if (gasLimitTooLow) {
|
||||
error = 'Gas limit must be at least 21000'
|
||||
}
|
||||
|
||||
this.setState({ error })
|
||||
return error
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) {
|
||||
const { gasPrice } = this.state
|
||||
|
||||
const gasLimit = conversionUtil(newGasLimit, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'hex',
|
||||
})
|
||||
|
||||
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
})
|
||||
|
||||
updateGasPrice(gasPrice)
|
||||
updateGasLimit(gasLimit)
|
||||
updateGasTotal(newGasTotal)
|
||||
hideModal()
|
||||
}
|
||||
this.validate({ gasTotal, gasLimit })
|
||||
|
||||
CustomizeGasModal.prototype.convertAndSetGasLimit = function (newGasLimit) {
|
||||
const convertedGasLimit = conversionUtil(newGasLimit, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'hex',
|
||||
})
|
||||
|
||||
this.setState({ gasLimit: convertedGasLimit })
|
||||
this.setState({ gasTotal, gasLimit })
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.convertAndSetGasPrice = function (newGasPrice) {
|
||||
const convertedGasPrice = conversionUtil(newGasPrice, {
|
||||
const { gasLimit } = this.state
|
||||
|
||||
const gasPrice = conversionUtil(newGasPrice, {
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'hex',
|
||||
fromDenomination: 'GWEI',
|
||||
toDenomination: 'WEI',
|
||||
})
|
||||
|
||||
this.setState({ gasPrice: convertedGasPrice })
|
||||
const gasTotal = multiplyCurrencies(gasLimit, gasPrice, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
})
|
||||
|
||||
this.validate({ gasTotal })
|
||||
|
||||
this.setState({ gasTotal, gasPrice })
|
||||
}
|
||||
|
||||
CustomizeGasModal.prototype.render = function () {
|
||||
const { hideModal, conversionRate } = this.props
|
||||
const { gasPrice, gasLimit } = this.state
|
||||
const { gasPrice, gasLimit, gasTotal, error } = this.state
|
||||
|
||||
const convertedGasPrice = conversionUtil(gasPrice, {
|
||||
fromNumericBase: 'hex',
|
||||
@ -120,7 +214,7 @@ CustomizeGasModal.prototype.render = function () {
|
||||
|
||||
h(GasModalCard, {
|
||||
value: convertedGasPrice,
|
||||
min: MIN_GAS_PRICE,
|
||||
min: MIN_GAS_PRICE_GWEI,
|
||||
// max: 1000,
|
||||
step: 1,
|
||||
onChange: value => this.convertAndSetGasPrice(value),
|
||||
@ -130,7 +224,7 @@ CustomizeGasModal.prototype.render = function () {
|
||||
|
||||
h(GasModalCard, {
|
||||
value: convertedGasLimit,
|
||||
min: MIN_GAS_LIMIT,
|
||||
min: 1,
|
||||
// max: 100000,
|
||||
step: 1,
|
||||
onChange: value => this.convertAndSetGasLimit(value),
|
||||
@ -141,6 +235,10 @@ CustomizeGasModal.prototype.render = function () {
|
||||
]),
|
||||
|
||||
h('div.send-v2__customize-gas__footer', {}, [
|
||||
|
||||
error && h('div.send-v2__customize-gas__error-message', [
|
||||
error,
|
||||
]),
|
||||
|
||||
h('div.send-v2__customize-gas__revert', {
|
||||
onClick: () => console.log('Revert'),
|
||||
@ -151,8 +249,8 @@ CustomizeGasModal.prototype.render = function () {
|
||||
onClick: this.props.hideModal,
|
||||
}, ['CANCEL']),
|
||||
|
||||
h('div.send-v2__customize-gas__save', {
|
||||
onClick: () => this.save(gasPrice, gasLimit),
|
||||
h(`div.send-v2__customize-gas__save${error ? '__error' : ''}`, {
|
||||
onClick: () => !error && this.save(gasPrice, gasLimit, gasTotal),
|
||||
}, ['SAVE']),
|
||||
])
|
||||
|
||||
|
@ -50,7 +50,7 @@ ConfirmSendEther.prototype.getAmount = function () {
|
||||
const { conversionRate, currentCurrency } = this.props
|
||||
const txMeta = this.gatherTxMeta()
|
||||
const txParams = txMeta.txParams || {}
|
||||
console.log(`conversionRate, currentCurrency`, conversionRate, currentCurrency);
|
||||
|
||||
const FIAT = conversionUtil(txParams.value, {
|
||||
fromNumericBase: 'hex',
|
||||
toNumericBase: 'dec',
|
||||
|
@ -3,12 +3,19 @@ const { multiplyCurrencies } = require('../../conversion-util')
|
||||
|
||||
const MIN_GAS_PRICE_GWEI = '1'
|
||||
const GWEI_FACTOR = '1e9'
|
||||
const MIN_GAS_PRICE = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
|
||||
const MIN_GAS_PRICE_HEX = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
toNumericBase: 'hex',
|
||||
})
|
||||
const MIN_GAS_LIMIT = (21000).toString(16)
|
||||
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT, MIN_GAS_PRICE, {
|
||||
const MIN_GAS_PRICE_DEC = multiplyCurrencies(GWEI_FACTOR, MIN_GAS_PRICE_GWEI, {
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
toNumericBase: 'dec',
|
||||
})
|
||||
const MIN_GAS_LIMIT_HEX = (21000).toString(16)
|
||||
const MIN_GAS_LIMIT_DEC = 21000
|
||||
const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT_HEX, MIN_GAS_PRICE_HEX, {
|
||||
toNumericBase: 'hex',
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 16,
|
||||
@ -16,8 +23,9 @@ const MIN_GAS_TOTAL = multiplyCurrencies(MIN_GAS_LIMIT, MIN_GAS_PRICE, {
|
||||
|
||||
module.exports = {
|
||||
MIN_GAS_PRICE_GWEI,
|
||||
GWEI_FACTOR,
|
||||
MIN_GAS_PRICE,
|
||||
MIN_GAS_LIMIT,
|
||||
MIN_GAS_PRICE_HEX,
|
||||
MIN_GAS_PRICE_DEC,
|
||||
MIN_GAS_LIMIT_HEX,
|
||||
MIN_GAS_LIMIT_DEC,
|
||||
MIN_GAS_TOTAL,
|
||||
}
|
||||
|
39
ui/app/components/send/send-utils.js
Normal file
39
ui/app/components/send/send-utils.js
Normal file
@ -0,0 +1,39 @@
|
||||
const { addCurrencies, conversionGreaterThan } = require('../../conversion-util')
|
||||
|
||||
function isBalanceSufficient({
|
||||
amount,
|
||||
gasTotal,
|
||||
balance,
|
||||
primaryCurrency,
|
||||
selectedToken,
|
||||
amountConversionRate,
|
||||
conversionRate,
|
||||
}) {
|
||||
const totalAmount = addCurrencies(amount, gasTotal, {
|
||||
aBase: 16,
|
||||
bBase: 16,
|
||||
toNumericBase: 'hex',
|
||||
})
|
||||
|
||||
const balanceIsSufficient = conversionGreaterThan(
|
||||
{
|
||||
value: balance,
|
||||
fromNumericBase: 'hex',
|
||||
fromCurrency: primaryCurrency,
|
||||
conversionRate,
|
||||
},
|
||||
{
|
||||
value: totalAmount,
|
||||
fromNumericBase: 'hex',
|
||||
conversionRate: amountConversionRate,
|
||||
fromCurrency: selectedToken || primaryCurrency,
|
||||
conversionRate: amountConversionRate,
|
||||
},
|
||||
)
|
||||
|
||||
return balanceIsSufficient
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isBalanceSufficient,
|
||||
}
|
@ -17,6 +17,7 @@ const {
|
||||
getAddressBook,
|
||||
getSendFrom,
|
||||
getCurrentCurrency,
|
||||
getSelectedTokenToFiatRate,
|
||||
} = require('../../selectors')
|
||||
|
||||
module.exports = connect(mapStateToProps, mapDispatchToProps)(SendEther)
|
||||
@ -26,7 +27,6 @@ function mapStateToProps (state) {
|
||||
const selectedAddress = getSelectedAddress(state)
|
||||
const selectedToken = getSelectedToken(state)
|
||||
const tokenExchangeRates = state.metamask.tokenExchangeRates
|
||||
const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state)
|
||||
const conversionRate = conversionRateSelector(state)
|
||||
|
||||
let data;
|
||||
@ -40,11 +40,7 @@ function mapStateToProps (state) {
|
||||
|
||||
primaryCurrency = selectedToken.symbol
|
||||
|
||||
tokenToFiatRate = multiplyCurrencies(
|
||||
conversionRate,
|
||||
selectedTokenExchangeRate,
|
||||
{ toNumericBase: 'dec' }
|
||||
)
|
||||
tokenToFiatRate = getSelectedTokenToFiatRate(state)
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -738,6 +738,7 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 22px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
@ -747,7 +748,7 @@
|
||||
margin-right: 21.25px;
|
||||
}
|
||||
|
||||
&__revert, &__cancel, &__save {
|
||||
&__revert, &__cancel, &__save, &__save__error {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@ -760,7 +761,7 @@
|
||||
margin-left: 21.25px;
|
||||
}
|
||||
|
||||
&__cancel, &__save {
|
||||
&__cancel, &__save, &__save__error {
|
||||
height: 34.64px;
|
||||
width: 85.74px;
|
||||
border: 1px solid $dusty-gray;
|
||||
@ -769,6 +770,21 @@
|
||||
font-size: 12px;
|
||||
color: $dusty-gray;
|
||||
}
|
||||
|
||||
&__save__error {
|
||||
opacity: 0.5;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
&__error-message {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
&__gas-modal-card {
|
||||
|
@ -1,5 +1,9 @@
|
||||
const valuesFor = require('./util').valuesFor
|
||||
|
||||
const {
|
||||
multiplyCurrencies,
|
||||
} = require('./conversion-util')
|
||||
|
||||
const selectors = {
|
||||
getSelectedAddress,
|
||||
getSelectedIdentity,
|
||||
@ -16,6 +20,8 @@ const selectors = {
|
||||
getAddressBook,
|
||||
getSendFrom,
|
||||
getCurrentCurrency,
|
||||
getSendAmount,
|
||||
getSelectedTokenToFiatRate,
|
||||
}
|
||||
|
||||
module.exports = selectors
|
||||
@ -123,6 +129,23 @@ function getSendFrom (state) {
|
||||
return state.metamask.send.from
|
||||
}
|
||||
|
||||
function getSendAmount (state) {
|
||||
return state.metamask.send.amount
|
||||
}
|
||||
|
||||
function getCurrentCurrency (state) {
|
||||
return state.metamask.currentCurrency
|
||||
}
|
||||
|
||||
function getSelectedTokenToFiatRate (state) {
|
||||
const selectedTokenExchangeRate = getSelectedTokenExchangeRate(state)
|
||||
const conversionRate = conversionRateSelector(state)
|
||||
|
||||
const tokenToFiatRate = multiplyCurrencies(
|
||||
conversionRate,
|
||||
selectedTokenExchangeRate,
|
||||
{ toNumericBase: 'dec' }
|
||||
)
|
||||
|
||||
return tokenToFiatRate
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ const {
|
||||
conversionGreaterThan,
|
||||
addCurrencies,
|
||||
} = require('./conversion-util')
|
||||
const {
|
||||
isBalanceSufficient,
|
||||
} = require('./components/send/send-utils.js')
|
||||
const { isValidAddress } = require('./util')
|
||||
|
||||
module.exports = SendTransactionScreen
|
||||
@ -237,28 +240,16 @@ SendTransactionScreen.prototype.validateAmount = function (value) {
|
||||
|
||||
let amountError = null
|
||||
|
||||
const totalAmount = addCurrencies(amount, gasTotal, {
|
||||
aBase: 16,
|
||||
bBase: 16,
|
||||
toNumericBase: 'hex',
|
||||
const sufficientBalance = isBalanceSufficient({
|
||||
amount,
|
||||
gasTotal,
|
||||
balance,
|
||||
primaryCurrency,
|
||||
selectedToken,
|
||||
amountConversionRate,
|
||||
conversionRate,
|
||||
})
|
||||
|
||||
const sufficientBalance = conversionGreaterThan(
|
||||
{
|
||||
value: balance,
|
||||
fromNumericBase: 'hex',
|
||||
fromCurrency: primaryCurrency,
|
||||
conversionRate,
|
||||
},
|
||||
{
|
||||
value: totalAmount,
|
||||
fromNumericBase: 'hex',
|
||||
conversionRate: amountConversionRate,
|
||||
fromCurrency: selectedToken || primaryCurrency,
|
||||
conversionRate: amountConversionRate,
|
||||
},
|
||||
)
|
||||
|
||||
const amountLessThanZero = conversionGreaterThan(
|
||||
{ value: 0, fromNumericBase: 'dec' },
|
||||
{ value: amount, fromNumericBase: 'hex' },
|
||||
@ -387,7 +378,7 @@ SendTransactionScreen.prototype.renderFooter = function () {
|
||||
clearSend,
|
||||
errors: { amount: amountError, to: toError }
|
||||
} = this.props
|
||||
|
||||
|
||||
const noErrors = amountError === null && toError === null
|
||||
const errorClass = noErrors ? '' : '__disabled'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user