Merge pull request #1443 from MetaMask/i1412-decimalizethegas

Add decimals to gas price input
feature/default_network_editable
kumavis 8 years ago committed by GitHub
commit 1ffc6ea0b3
  1. 4
      CHANGELOG.md
  2. 51
      test/unit/components/bn-as-decimal-input-test.js
  3. 1
      test/unit/components/pending-tx-test.js
  4. 174
      ui/app/components/bn-as-decimal-input.js
  5. 43
      ui/app/components/pending-tx.js

@ -3,6 +3,8 @@
## Current Master ## Current Master
- Now when switching networks the extension does not restart - Now when switching networks the extension does not restart
- Cleanup decimal bugs in our gas inputs.
- Fix bug where submit button was enabled for invalid gas inputs.
## 3.7.0 2017-5-23 ## 3.7.0 2017-5-23
@ -16,6 +18,8 @@
- Fix bug where edited gas parameters would not take effect. - Fix bug where edited gas parameters would not take effect.
- Trim currency list. - Trim currency list.
- Enable decimals in our gas prices.
- Fix reset button.
- Fix event filter bug introduced by newer versions of Geth. - Fix event filter bug introduced by newer versions of Geth.
- Fix bug where decimals in gas inputs could result in strange values. - Fix bug where decimals in gas inputs could result in strange values.

@ -0,0 +1,51 @@
var assert = require('assert')
const additions = require('react-testutils-additions')
const h = require('react-hyperscript')
const ReactTestUtils = require('react-addons-test-utils')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
var BnInput = require('../../../ui/app/components/bn-as-decimal-input')
describe('BnInput', function () {
it('can tolerate a gas decimal number at a high precision', function (done) {
const renderer = ReactTestUtils.createRenderer()
let valueStr = '20'
while (valueStr.length < 20) {
valueStr += '0'
}
const value = new BN(valueStr, 10)
let inputStr = '2.3'
let targetStr = '23'
while (targetStr.length < 19) {
targetStr += '0'
}
const target = new BN(targetStr, 10)
const precision = 18 // ether precision
const scale = 18
const props = {
value,
scale,
precision,
onChange: (newBn) => {
assert.equal(newBn.toString(), target.toString(), 'should tolerate increase')
done()
},
}
const inputComponent = h(BnInput, props)
const component = additions.renderIntoDocument(inputComponent)
renderer.render(inputComponent)
const input = additions.find(component, 'input.hex-input')[0]
ReactTestUtils.Simulate.change(input, { preventDefault() {}, target: {
value: inputStr,
checkValidity() { return true } },
})
})
})

@ -6,7 +6,6 @@ const ReactTestUtils = require('react-addons-test-utils')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
describe('PendingTx', function () { describe('PendingTx', function () {
const identities = { const identities = {
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b826': { '0xfdea65c8e26263f6d9a1b5de9555d2931a33b826': {
name: 'Main Account 1', name: 'Main Account 1',

@ -0,0 +1,174 @@
const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
const extend = require('xtend')
module.exports = BnAsDecimalInput
inherits(BnAsDecimalInput, Component)
function BnAsDecimalInput () {
this.state = { invalid: null }
Component.call(this)
}
/* Bn as Decimal Input
*
* A component for allowing easy, decimal editing
* of a passed in bn string value.
*
* On change, calls back its `onChange` function parameter
* and passes it an updated bn string.
*/
BnAsDecimalInput.prototype.render = function () {
const props = this.props
const state = this.state
const { value, scale, precision, onChange, min, max } = props
const suffix = props.suffix
const style = props.style
const valueString = value.toString(10)
const newValue = this.downsize(valueString, scale, precision)
return (
h('.flex-column', [
h('.flex-row', {
style: {
alignItems: 'flex-end',
lineHeight: '13px',
fontFamily: 'Montserrat Light',
textRendering: 'geometricPrecision',
},
}, [
h('input.hex-input', {
type: 'number',
step: 'any',
required: true,
min,
max,
style: extend({
display: 'block',
textAlign: 'right',
backgroundColor: 'transparent',
border: '1px solid #bdbdbd',
}, style),
value: newValue,
onBlur: (event) => {
this.updateValidity(event)
},
onChange: (event) => {
this.updateValidity(event)
const value = (event.target.value === '') ? '' : event.target.value
const scaledNumber = this.upsize(value, scale, precision)
const precisionBN = new BN(scaledNumber, 10)
onChange(precisionBN, event.target.checkValidity())
},
onInvalid: (event) => {
const msg = this.constructWarning()
if (msg === state.invalid) {
return
}
this.setState({ invalid: msg })
event.preventDefault()
return false
},
}),
h('div', {
style: {
color: ' #AEAEAE',
fontSize: '12px',
marginLeft: '5px',
marginRight: '6px',
width: '20px',
},
}, suffix),
]),
state.invalid ? h('span.error', {
style: {
position: 'absolute',
right: '0px',
textAlign: 'right',
transform: 'translateY(26px)',
padding: '3px',
background: 'rgba(255,255,255,0.85)',
zIndex: '1',
textTransform: 'capitalize',
border: '2px solid #E20202',
},
}, state.invalid) : null,
])
)
}
BnAsDecimalInput.prototype.setValid = function (message) {
this.setState({ invalid: null })
}
BnAsDecimalInput.prototype.updateValidity = function (event) {
const target = event.target
const value = this.props.value
const newValue = target.value
if (value === newValue) {
return
}
const valid = target.checkValidity()
if (valid) {
this.setState({ invalid: null })
}
}
BnAsDecimalInput.prototype.constructWarning = function () {
const { name, min, max } = this.props
let message = name ? name + ' ' : ''
if (min && max) {
message += `must be greater than or equal to ${min} and less than or equal to ${max}.`
} else if (min) {
message += `must be greater than or equal to ${min}.`
} else if (max) {
message += `must be less than or equal to ${max}.`
} else {
message += 'Invalid input.'
}
return message
}
BnAsDecimalInput.prototype.downsize = function (number, scale, precision) {
// if there is no scaling, simply return the number
if (scale === 0) {
return Number(number)
} else {
// if the scale is the same as the precision, account for this edge case.
var decimals = (scale === precision) ? -1 : scale - precision
return Number(number.slice(0, -scale) + '.' + number.slice(-scale, decimals))
}
}
BnAsDecimalInput.prototype.upsize = function (number, scale, precision) {
var stringArray = number.toString().split('.')
var decimalLength = stringArray[1] ? stringArray[1].length : 0
var newString = stringArray[0]
// If there is scaling and decimal parts exist, integrate them in.
if ((scale !== 0) && (decimalLength !== 0)) {
newString += stringArray[1].slice(0, precision)
}
// Add 0s to account for the upscaling.
for (var i = decimalLength; i < scale; i++) {
newString += '0'
}
return newString
}

@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript') const h = require('react-hyperscript')
const inherits = require('util').inherits const inherits = require('util').inherits
const actions = require('../actions') const actions = require('../actions')
const clone = require('clone')
const ethUtil = require('ethereumjs-util') const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN const BN = ethUtil.BN
@ -12,7 +13,7 @@ const EthBalance = require('./eth-balance')
const util = require('../util') const util = require('../util')
const addressSummary = util.addressSummary const addressSummary = util.addressSummary
const nameForAddress = require('../../lib/contract-namer') const nameForAddress = require('../../lib/contract-namer')
const HexInput = require('./hex-as-decimal-input') const BNInput = require('./bn-as-decimal-input')
const MIN_GAS_PRICE_GWEI_BN = new BN(2) const MIN_GAS_PRICE_GWEI_BN = new BN(2)
const GWEI_FACTOR = new BN(1e9) const GWEI_FACTOR = new BN(1e9)
@ -50,7 +51,6 @@ PendingTx.prototype.render = function () {
// Gas Price // Gas Price
const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16) const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16)
const gasPriceBn = hexToBn(gasPrice) const gasPriceBn = hexToBn(gasPrice)
const gasPriceGweiBn = gasPriceBn.div(GWEI_FACTOR)
const txFeeBn = gasBn.mul(gasPriceBn) const txFeeBn = gasBn.mul(gasPriceBn)
const valueBn = hexToBn(txParams.value) const valueBn = hexToBn(txParams.value)
@ -152,9 +152,11 @@ PendingTx.prototype.render = function () {
h('.cell.label', 'Gas Limit'), h('.cell.label', 'Gas Limit'),
h('.cell.value', { h('.cell.value', {
}, [ }, [
h(HexInput, { h(BNInput, {
name: 'Gas Limit', name: 'Gas Limit',
value: gas, value: gasBn,
precision: 0,
scale: 0,
// The hard lower limit for gas. // The hard lower limit for gas.
min: MIN_GAS_LIMIT_BN.toString(10), min: MIN_GAS_LIMIT_BN.toString(10),
suffix: 'UNITS', suffix: 'UNITS',
@ -174,9 +176,11 @@ PendingTx.prototype.render = function () {
h('.cell.label', 'Gas Price'), h('.cell.label', 'Gas Price'),
h('.cell.value', { h('.cell.value', {
}, [ }, [
h(HexInput, { h(BNInput, {
name: 'Gas Price', name: 'Gas Price',
value: gasPriceGweiBn.toString(16), value: gasPriceBn,
precision: 9,
scale: 9,
suffix: 'GWEI', suffix: 'GWEI',
min: MIN_GAS_PRICE_GWEI_BN.toString(10), min: MIN_GAS_PRICE_GWEI_BN.toString(10),
style: { style: {
@ -342,19 +346,24 @@ PendingTx.prototype.miniAccountPanelForRecipient = function () {
} }
} }
PendingTx.prototype.gasPriceChanged = function (newHex) { PendingTx.prototype.gasPriceChanged = function (newBN, valid) {
log.info(`Gas price changed to: ${newHex}`) log.info(`Gas price changed to: ${newBN.toString(10)}`)
const inWei = hexToBn(newHex).mul(GWEI_FACTOR)
const txMeta = this.gatherTxMeta() const txMeta = this.gatherTxMeta()
txMeta.txParams.gasPrice = inWei.toString(16) txMeta.txParams.gasPrice = '0x' + newBN.toString('hex')
this.setState({ txData: txMeta }) this.setState({
txData: clone(txMeta),
valid,
})
} }
PendingTx.prototype.gasLimitChanged = function (newHex) { PendingTx.prototype.gasLimitChanged = function (newBN, valid) {
log.info(`Gas limit changed to ${newHex}`) log.info(`Gas limit changed to ${newBN.toString(10)}`)
const txMeta = this.gatherTxMeta() const txMeta = this.gatherTxMeta()
txMeta.txParams.gas = newHex txMeta.txParams.gas = '0x' + newBN.toString('hex')
this.setState({ txData: txMeta }) this.setState({
txData: clone(txMeta),
valid,
})
} }
PendingTx.prototype.resetGasFields = function () { PendingTx.prototype.resetGasFields = function () {
@ -404,7 +413,7 @@ PendingTx.prototype.gatherTxMeta = function () {
log.debug(`pending-tx gatherTxMeta`) log.debug(`pending-tx gatherTxMeta`)
const props = this.props const props = this.props
const state = this.state const state = this.state
const txData = state.txData || props.txData const txData = clone(state.txData) || clone(props.txData)
log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`) log.debug(`UI has defaulted to tx meta ${JSON.stringify(txData)}`)
return txData return txData
@ -425,7 +434,6 @@ PendingTx.prototype._notZeroOrEmptyString = function (obj) {
function forwardCarrat () { function forwardCarrat () {
return ( return (
h('img', { h('img', {
src: 'images/forward-carrat.svg', src: 'images/forward-carrat.svg',
style: { style: {
@ -433,6 +441,5 @@ function forwardCarrat () {
height: '37px', height: '37px',
}, },
}) })
) )
} }

Loading…
Cancel
Save