aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfrankiebee <frankie.diamond@gmail.com>2017-05-25 11:10:58 +0800
committerfrankiebee <frankie.diamond@gmail.com>2017-05-25 11:10:58 +0800
commit3b06a29e1b1b1cf55748a9df8bdbec4bd0c8a9a7 (patch)
tree42a0991d1170dfc7369d37497bb693c17a0daec6
parent473b88f399478b47bfa53d44ef9981aeb6d9960b (diff)
parent764806d211e026226bf8003cbdd6ff3ec570d5db (diff)
downloadtangerine-wallet-browser-3b06a29e1b1b1cf55748a9df8bdbec4bd0c8a9a7.tar
tangerine-wallet-browser-3b06a29e1b1b1cf55748a9df8bdbec4bd0c8a9a7.tar.gz
tangerine-wallet-browser-3b06a29e1b1b1cf55748a9df8bdbec4bd0c8a9a7.tar.bz2
tangerine-wallet-browser-3b06a29e1b1b1cf55748a9df8bdbec4bd0c8a9a7.tar.lz
tangerine-wallet-browser-3b06a29e1b1b1cf55748a9df8bdbec4bd0c8a9a7.tar.xz
tangerine-wallet-browser-3b06a29e1b1b1cf55748a9df8bdbec4bd0c8a9a7.tar.zst
tangerine-wallet-browser-3b06a29e1b1b1cf55748a9df8bdbec4bd0c8a9a7.zip
Merge branch 'master' into reloadDapp
-rw-r--r--CHANGELOG.md5
-rw-r--r--app/scripts/lib/inpage-provider.js3
-rw-r--r--app/scripts/lib/tx-utils.js13
-rw-r--r--test/unit/components/bn-as-decimal-input-test.js51
-rw-r--r--test/unit/components/pending-tx-test.js1
-rw-r--r--ui/app/components/bn-as-decimal-input.js174
-rw-r--r--ui/app/components/pending-tx.js45
7 files changed, 271 insertions, 21 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60c6a6f44..caa87b697 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,9 @@
## Current Master
- 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.
+- Now enforce 95% of block's gasLimit to protect users.
## 3.7.0 2017-5-23
@@ -16,6 +19,8 @@
- Fix bug where edited gas parameters would not take effect.
- Trim currency list.
+- Enable decimals in our gas prices.
+- Fix reset button.
- Fix event filter bug introduced by newer versions of Geth.
- Fix bug where decimals in gas inputs could result in strange values.
diff --git a/app/scripts/lib/inpage-provider.js b/app/scripts/lib/inpage-provider.js
index e54f547bd..39196e240 100644
--- a/app/scripts/lib/inpage-provider.js
+++ b/app/scripts/lib/inpage-provider.js
@@ -44,8 +44,9 @@ function MetamaskInpageProvider (connectionStream) {
(err) => logStreamDisconnectWarning('MetaMask RpcProvider', err)
)
- // start polling
+ // start and stop polling to unblock first block lock
engine.start()
+ engine.once('latest', () => engine.stop())
self.idMap = {}
// handle sendAsync requests via asyncProvider
diff --git a/app/scripts/lib/tx-utils.js b/app/scripts/lib/tx-utils.js
index 76b311653..8cf304d0b 100644
--- a/app/scripts/lib/tx-utils.js
+++ b/app/scripts/lib/tx-utils.js
@@ -21,19 +21,30 @@ module.exports = class txProviderUtils {
this.query.getBlockByNumber('latest', true, (err, block) => {
if (err) return cb(err)
async.waterfall([
+ self.setBlockGasLimit.bind(self, txMeta, block.gasLimit),
self.estimateTxGas.bind(self, txMeta, block.gasLimit),
self.setTxGas.bind(self, txMeta, block.gasLimit),
], cb)
})
}
+ setBlockGasLimit (txMeta, blockGasLimitHex, cb) {
+ const blockGasLimitBN = hexToBn(blockGasLimitHex)
+ const saferGasLimitBN = blockGasLimitBN.muln(0.95)
+ txMeta.blockGasLimit = bnToHex(saferGasLimitBN)
+ cb()
+ return
+ }
+
estimateTxGas (txMeta, blockGasLimitHex, cb) {
const txParams = txMeta.txParams
// check if gasLimit is already specified
txMeta.gasLimitSpecified = Boolean(txParams.gas)
// if not, fallback to block gasLimit
if (!txMeta.gasLimitSpecified) {
- txParams.gas = blockGasLimitHex
+ const blockGasLimitBN = hexToBn(blockGasLimitHex)
+ const saferGasLimitBN = blockGasLimitBN.muln(0.95)
+ txParams.gas = bnToHex(saferGasLimitBN)
}
// run tx, see if it will OOG
this.query.estimateGas(txParams, cb)
diff --git a/test/unit/components/bn-as-decimal-input-test.js b/test/unit/components/bn-as-decimal-input-test.js
new file mode 100644
index 000000000..b3365b6f9
--- /dev/null
+++ b/test/unit/components/bn-as-decimal-input-test.js
@@ -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 } },
+ })
+ })
+})
diff --git a/test/unit/components/pending-tx-test.js b/test/unit/components/pending-tx-test.js
index 9ff948604..52e5e5910 100644
--- a/test/unit/components/pending-tx-test.js
+++ b/test/unit/components/pending-tx-test.js
@@ -6,7 +6,6 @@ const ReactTestUtils = require('react-addons-test-utils')
const ethUtil = require('ethereumjs-util')
describe('PendingTx', function () {
-
const identities = {
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b826': {
name: 'Main Account 1',
diff --git a/ui/app/components/bn-as-decimal-input.js b/ui/app/components/bn-as-decimal-input.js
new file mode 100644
index 000000000..f3ace4720
--- /dev/null
+++ b/ui/app/components/bn-as-decimal-input.js
@@ -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
+}
diff --git a/ui/app/components/pending-tx.js b/ui/app/components/pending-tx.js
index 0d1f06ba6..b46f715bc 100644
--- a/ui/app/components/pending-tx.js
+++ b/ui/app/components/pending-tx.js
@@ -2,6 +2,7 @@ const Component = require('react').Component
const h = require('react-hyperscript')
const inherits = require('util').inherits
const actions = require('../actions')
+const clone = require('clone')
const ethUtil = require('ethereumjs-util')
const BN = ethUtil.BN
@@ -12,7 +13,7 @@ const EthBalance = require('./eth-balance')
const util = require('../util')
const addressSummary = util.addressSummary
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 GWEI_FACTOR = new BN(1e9)
@@ -46,11 +47,11 @@ PendingTx.prototype.render = function () {
// Gas
const gas = txParams.gas
const gasBn = hexToBn(gas)
+ const safeGasLimit = parseInt(txMeta.blockGasLimit)
// Gas Price
const gasPrice = txParams.gasPrice || MIN_GAS_PRICE_BN.toString(16)
const gasPriceBn = hexToBn(gasPrice)
- const gasPriceGweiBn = gasPriceBn.div(GWEI_FACTOR)
const txFeeBn = gasBn.mul(gasPriceBn)
const valueBn = hexToBn(txParams.value)
@@ -152,11 +153,14 @@ PendingTx.prototype.render = function () {
h('.cell.label', 'Gas Limit'),
h('.cell.value', {
}, [
- h(HexInput, {
+ h(BNInput, {
name: 'Gas Limit',
- value: gas,
+ value: gasBn,
+ precision: 0,
+ scale: 0,
// The hard lower limit for gas.
min: MIN_GAS_LIMIT_BN.toString(10),
+ max: safeGasLimit,
suffix: 'UNITS',
style: {
position: 'relative',
@@ -174,9 +178,11 @@ PendingTx.prototype.render = function () {
h('.cell.label', 'Gas Price'),
h('.cell.value', {
}, [
- h(HexInput, {
+ h(BNInput, {
name: 'Gas Price',
- value: gasPriceGweiBn.toString(16),
+ value: gasPriceBn,
+ precision: 9,
+ scale: 9,
suffix: 'GWEI',
min: MIN_GAS_PRICE_GWEI_BN.toString(10),
style: {
@@ -342,19 +348,24 @@ PendingTx.prototype.miniAccountPanelForRecipient = function () {
}
}
-PendingTx.prototype.gasPriceChanged = function (newHex) {
- log.info(`Gas price changed to: ${newHex}`)
- const inWei = hexToBn(newHex).mul(GWEI_FACTOR)
+PendingTx.prototype.gasPriceChanged = function (newBN, valid) {
+ log.info(`Gas price changed to: ${newBN.toString(10)}`)
const txMeta = this.gatherTxMeta()
- txMeta.txParams.gasPrice = inWei.toString(16)
- this.setState({ txData: txMeta })
+ txMeta.txParams.gasPrice = '0x' + newBN.toString('hex')
+ this.setState({
+ txData: clone(txMeta),
+ valid,
+ })
}
-PendingTx.prototype.gasLimitChanged = function (newHex) {
- log.info(`Gas limit changed to ${newHex}`)
+PendingTx.prototype.gasLimitChanged = function (newBN, valid) {
+ log.info(`Gas limit changed to ${newBN.toString(10)}`)
const txMeta = this.gatherTxMeta()
- txMeta.txParams.gas = newHex
- this.setState({ txData: txMeta })
+ txMeta.txParams.gas = '0x' + newBN.toString('hex')
+ this.setState({
+ txData: clone(txMeta),
+ valid,
+ })
}
PendingTx.prototype.resetGasFields = function () {
@@ -404,7 +415,7 @@ PendingTx.prototype.gatherTxMeta = function () {
log.debug(`pending-tx gatherTxMeta`)
const props = this.props
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)}`)
return txData
@@ -425,7 +436,6 @@ PendingTx.prototype._notZeroOrEmptyString = function (obj) {
function forwardCarrat () {
return (
-
h('img', {
src: 'images/forward-carrat.svg',
style: {
@@ -433,6 +443,5 @@ function forwardCarrat () {
height: '37px',
},
})
-
)
}