From 9f1f0bff1ea3267702a2ab75af293e6265e255e4 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 22 Mar 2017 10:35:02 -0700 Subject: Some progress --- ui/app/components/pending-tx-details.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'ui/app/components/pending-tx-details.js') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index e92ce575f..822e5d84b 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -12,6 +12,10 @@ const addressSummary = util.addressSummary const nameForAddress = require('../../lib/contract-namer') const HexInput = require('./hex-as-decimal-input') +const DEFAULT_GAS_PRICE = '0x4a817c800' +const DEFAULT_GAS_PRICE_BN = new BN(DEFAULT_GAS_PRICE.substr(2), 16) +const FOUR_BN = new BN('4', 10) + module.exports = PendingTxDetails inherits(PendingTxDetails, Component) @@ -78,7 +82,6 @@ PTXP.render = function () { labelColor: '#F7861C', }), ]), - ]), forwardCarrat(), @@ -122,6 +125,7 @@ PTXP.render = function () { }, [ h(HexInput, { value: gas, + min: 21000, // The hard lower limit for gas. suffix: 'UNITS', style: { position: 'relative', @@ -143,6 +147,7 @@ PTXP.render = function () { h(HexInput, { value: gasPrice, suffix: 'WEI', + min: DEFAULT_GAS_PRICE_BN.div(FOUR_BN).toString(10), style: { position: 'relative', top: '5px', @@ -176,7 +181,8 @@ PTXP.render = function () { }, }, [ h(EthBalance, { - value: maxCost.toString(16), + value: '0x' + txFee.add(new BN(txParams.value, 16)).toString(16), + maxCost.toString(16), inline: true, labelColor: 'black', fontSize: '16px', @@ -267,7 +273,7 @@ PTXP.calculateGas = function () { var txParams = txMeta.txParams var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16) - var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || '0x4a817c800'), 16) + var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || DEFAULT_GAS_PRICE), 16) var txFee = gasCost.mul(gasPrice) var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16) var maxCost = txValue.add(txFee) @@ -280,10 +286,12 @@ PTXP.calculateGas = function () { txMeta.maxCost = maxCostHex txMeta.txParams.gasPrice = gasPriceHex - this.setState({ + const newState = { txFee: '0x' + txFee.toString('hex'), maxCost: '0x' + maxCost.toString('hex'), - }) + } + log.info(`tx form updating local state with ${JSON.stringify(newState)}`) + this.setState(newState) if (this.props.onTxChange) { this.props.onTxChange(txMeta) -- cgit From 77907038ffe0edbe5e3472f98fd10d73b76464b8 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Wed, 22 Mar 2017 15:14:33 -0700 Subject: Got basic validations working --- ui/app/components/pending-tx-details.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'ui/app/components/pending-tx-details.js') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js index 822e5d84b..375a50f22 100644 --- a/ui/app/components/pending-tx-details.js +++ b/ui/app/components/pending-tx-details.js @@ -12,15 +12,17 @@ const addressSummary = util.addressSummary const nameForAddress = require('../../lib/contract-namer') const HexInput = require('./hex-as-decimal-input') -const DEFAULT_GAS_PRICE = '0x4a817c800' -const DEFAULT_GAS_PRICE_BN = new BN(DEFAULT_GAS_PRICE.substr(2), 16) -const FOUR_BN = new BN('4', 10) +const DEFAULT_GAS_PRICE_BN = new BN(20000000000, '10') +const DEFAULT_GAS_PRICE = DEFAULT_GAS_PRICE_BN.toString(16) + +const MIN_GAS_PRICE_BN = new BN(20000000) module.exports = PendingTxDetails inherits(PendingTxDetails, Component) function PendingTxDetails () { Component.call(this) + this.state = { valid: true } } const PTXP = PendingTxDetails.prototype @@ -40,6 +42,7 @@ PTXP.render = function () { const gasPrice = (state.gasPrice === undefined) ? txData.gasPrice : state.gasPrice var txFee = state.txFee || txData.txFee || '' + var txFeeBn = new BN(txFee, 16) var maxCost = state.maxCost || txData.maxCost || '' var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons @@ -124,6 +127,7 @@ PTXP.render = function () { h('.cell.value', { }, [ h(HexInput, { + name: 'Gas Limit', value: gas, min: 21000, // The hard lower limit for gas. suffix: 'UNITS', @@ -145,9 +149,10 @@ PTXP.render = function () { h('.cell.value', { }, [ h(HexInput, { + name: 'Gas Price', value: gasPrice, suffix: 'WEI', - min: DEFAULT_GAS_PRICE_BN.div(FOUR_BN).toString(10), + min: MIN_GAS_PRICE_BN.toString(10), style: { position: 'relative', top: '5px', @@ -163,7 +168,7 @@ PTXP.render = function () { // Max Transaction Fee (calculated) h('.cell.row', [ h('.cell.label', 'Max Transaction Fee'), - h(EthBalance, { value: txFee.toString(16) }), + h(EthBalance, { value: txFeeBn.toString(16) }), ]), h('.cell.row', { @@ -181,8 +186,7 @@ PTXP.render = function () { }, }, [ h(EthBalance, { - value: '0x' + txFee.add(new BN(txParams.value, 16)).toString(16), - maxCost.toString(16), + value: maxCost.toString(16), inline: true, labelColor: 'black', fontSize: '16px', @@ -267,6 +271,10 @@ PTXP.componentDidUpdate = function (prevProps, previousState) { } } +PTXP.isValid = function () { + return this.state.valid +} + PTXP.calculateGas = function () { const txMeta = this.gatherParams() log.debug(`pending-tx-details calculating gas for ${JSON.stringify(txMeta)}`) @@ -274,6 +282,10 @@ PTXP.calculateGas = function () { var txParams = txMeta.txParams var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16) var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || DEFAULT_GAS_PRICE), 16) + + const valid = !gasPrice.lt(MIN_GAS_PRICE_BN) + this.props.validChanged(valid) + var txFee = gasCost.mul(gasPrice) var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16) var maxCost = txValue.add(txFee) -- cgit From e7a3330b980b150d4b7eae9c628e4ad2b0a1af34 Mon Sep 17 00:00:00 2001 From: Dan Finlay Date: Thu, 23 Mar 2017 13:44:09 -0700 Subject: Combine pending-tx-details component into pending-tx-details These were only separated originally so we could make the notification-based TX approval work, which provided its own buttons. This two templates are logically highly coupled, and keeping them working while separate has been difficult at times, and has even required resorting to dubious practices, like using React's `refs` pattern. This combines them into one fairly large component, but I think it's ok, we can still break this up into components, just not the separation that it had previously. --- ui/app/components/pending-tx-details.js | 364 -------------------------------- 1 file changed, 364 deletions(-) delete mode 100644 ui/app/components/pending-tx-details.js (limited to 'ui/app/components/pending-tx-details.js') diff --git a/ui/app/components/pending-tx-details.js b/ui/app/components/pending-tx-details.js deleted file mode 100644 index 375a50f22..000000000 --- a/ui/app/components/pending-tx-details.js +++ /dev/null @@ -1,364 +0,0 @@ -const Component = require('react').Component -const h = require('react-hyperscript') -const inherits = require('util').inherits -const extend = require('xtend') -const ethUtil = require('ethereumjs-util') -const BN = ethUtil.BN - -const MiniAccountPanel = require('./mini-account-panel') -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 DEFAULT_GAS_PRICE_BN = new BN(20000000000, '10') -const DEFAULT_GAS_PRICE = DEFAULT_GAS_PRICE_BN.toString(16) - -const MIN_GAS_PRICE_BN = new BN(20000000) - -module.exports = PendingTxDetails - -inherits(PendingTxDetails, Component) -function PendingTxDetails () { - Component.call(this) - this.state = { valid: true } -} - -const PTXP = PendingTxDetails.prototype - -PTXP.render = function () { - var props = this.props - var state = this.state || {} - var txData = state.txMeta || props.txData - - var txParams = txData.txParams || {} - var address = txParams.from || props.selectedAddress - var identity = props.identities[address] || { address: address } - var account = props.accounts[address] - var balance = account ? account.balance : '0x0' - - const gas = (state.gas === undefined) ? txParams.gas : state.gas - const gasPrice = (state.gasPrice === undefined) ? txData.gasPrice : state.gasPrice - - var txFee = state.txFee || txData.txFee || '' - var txFeeBn = new BN(txFee, 16) - var maxCost = state.maxCost || txData.maxCost || '' - var dataLength = txParams.data ? (txParams.data.length - 2) / 2 : 0 - var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons - - log.debug(`rendering gas: ${gas}, gasPrice: ${gasPrice}, txFee: ${txFee}, maxCost: ${maxCost}`) - - return ( - h('div', [ - - h('.flex-row.flex-center', { - style: { - maxWidth: '100%', - }, - }, [ - - h(MiniAccountPanel, { - imageSeed: address, - imageifyIdenticons: imageify, - picOrder: 'right', - }, [ - h('span.font-small', { - style: { - fontFamily: 'Montserrat Bold, Montserrat, sans-serif', - }, - }, identity.name), - h('span.font-small', { - style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', - }, - }, addressSummary(address, 6, 4, false)), - - h('span.font-small', { - style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', - }, - }, [ - h(EthBalance, { - value: balance, - inline: true, - labelColor: '#F7861C', - }), - ]), - ]), - - forwardCarrat(), - - this.miniAccountPanelForRecipient(), - ]), - - h('style', ` - .table-box { - margin: 7px 0px 0px 0px; - width: 100%; - } - .table-box .row { - margin: 0px; - background: rgb(236,236,236); - display: flex; - justify-content: space-between; - font-family: Montserrat Light, sans-serif; - font-size: 13px; - padding: 5px 25px; - } - .table-box .row .value { - font-family: Montserrat Regular; - } - `), - - h('.table-box', [ - - // Ether Value - // Currently not customizable, but easily modified - // in the way that gas and gasLimit currently are. - h('.row', [ - h('.cell.label', 'Amount'), - h(EthBalance, { value: txParams.value }), - ]), - - // Gas Limit (customizable) - h('.cell.row', [ - h('.cell.label', 'Gas Limit'), - h('.cell.value', { - }, [ - h(HexInput, { - name: 'Gas Limit', - value: gas, - min: 21000, // The hard lower limit for gas. - suffix: 'UNITS', - style: { - position: 'relative', - top: '5px', - }, - onChange: (newHex) => { - log.info(`Gas limit changed to ${newHex}`) - this.setState({ gas: newHex }) - }, - }), - ]), - ]), - - // Gas Price (customizable) - h('.cell.row', [ - h('.cell.label', 'Gas Price'), - h('.cell.value', { - }, [ - h(HexInput, { - name: 'Gas Price', - value: gasPrice, - suffix: 'WEI', - min: MIN_GAS_PRICE_BN.toString(10), - style: { - position: 'relative', - top: '5px', - }, - onChange: (newHex) => { - log.info(`Gas price changed to: ${newHex}`) - this.setState({ gasPrice: newHex }) - }, - }), - ]), - ]), - - // Max Transaction Fee (calculated) - h('.cell.row', [ - h('.cell.label', 'Max Transaction Fee'), - h(EthBalance, { value: txFeeBn.toString(16) }), - ]), - - h('.cell.row', { - style: { - fontFamily: 'Montserrat Regular', - background: 'white', - padding: '10px 25px', - }, - }, [ - h('.cell.label', 'Max Total'), - h('.cell.value', { - style: { - display: 'flex', - alignItems: 'center', - }, - }, [ - h(EthBalance, { - value: maxCost.toString(16), - inline: true, - labelColor: 'black', - fontSize: '16px', - }), - ]), - ]), - - // Data size row: - h('.cell.row', { - style: { - background: '#f7f7f7', - paddingBottom: '0px', - }, - }, [ - h('.cell.label'), - h('.cell.value', { - style: { - fontFamily: 'Montserrat Light', - fontSize: '11px', - }, - }, `Data included: ${dataLength} bytes`), - ]), - ]), // End of Table - - ]) - ) -} - -PTXP.miniAccountPanelForRecipient = function () { - var props = this.props - var txData = props.txData - var txParams = txData.txParams || {} - var isContractDeploy = !('to' in txParams) - var imageify = props.imageifyIdenticons === undefined ? true : props.imageifyIdenticons - - // If it's not a contract deploy, send to the account - if (!isContractDeploy) { - return h(MiniAccountPanel, { - imageSeed: txParams.to, - imageifyIdenticons: imageify, - picOrder: 'left', - }, [ - h('span.font-small', { - style: { - fontFamily: 'Montserrat Bold, Montserrat, sans-serif', - }, - }, nameForAddress(txParams.to, props.identities)), - h('span.font-small', { - style: { - fontFamily: 'Montserrat Light, Montserrat, sans-serif', - }, - }, addressSummary(txParams.to, 6, 4, false)), - ]) - } else { - return h(MiniAccountPanel, { - imageifyIdenticons: imageify, - picOrder: 'left', - }, [ - - h('span.font-small', { - style: { - fontFamily: 'Montserrat Bold, Montserrat, sans-serif', - }, - }, 'New Contract'), - - ]) - } -} - -PTXP.componentDidUpdate = function (prevProps, previousState) { - log.debug(`pending-tx-details componentDidUpdate`) - const state = this.state || {} - const prevState = previousState || {} - const { gas, gasPrice } = state - - // Only if gas or gasPrice changed: - if (!prevState || - (gas !== prevState.gas || - gasPrice !== prevState.gasPrice)) { - log.debug(`recalculating gas since prev state change: ${JSON.stringify({ prevState, state })}`) - this.calculateGas() - } -} - -PTXP.isValid = function () { - return this.state.valid -} - -PTXP.calculateGas = function () { - const txMeta = this.gatherParams() - log.debug(`pending-tx-details calculating gas for ${JSON.stringify(txMeta)}`) - - var txParams = txMeta.txParams - var gasCost = new BN(ethUtil.stripHexPrefix(txParams.gas || txMeta.estimatedGas), 16) - var gasPrice = new BN(ethUtil.stripHexPrefix(txParams.gasPrice || DEFAULT_GAS_PRICE), 16) - - const valid = !gasPrice.lt(MIN_GAS_PRICE_BN) - this.props.validChanged(valid) - - var txFee = gasCost.mul(gasPrice) - var txValue = new BN(ethUtil.stripHexPrefix(txParams.value || '0x0'), 16) - var maxCost = txValue.add(txFee) - - const txFeeHex = '0x' + txFee.toString('hex') - const maxCostHex = '0x' + maxCost.toString('hex') - const gasPriceHex = '0x' + gasPrice.toString('hex') - - txMeta.txFee = txFeeHex - txMeta.maxCost = maxCostHex - txMeta.txParams.gasPrice = gasPriceHex - - const newState = { - txFee: '0x' + txFee.toString('hex'), - maxCost: '0x' + maxCost.toString('hex'), - } - log.info(`tx form updating local state with ${JSON.stringify(newState)}`) - this.setState(newState) - - if (this.props.onTxChange) { - this.props.onTxChange(txMeta) - } -} - -PTXP.resetGasFields = function () { - log.debug(`pending-tx-details#resetGasFields`) - const txData = this.props.txData - this.setState({ - gas: txData.txParams.gas, - gasPrice: txData.gasPrice, - }) -} - -// After a customizable state value has been updated, -PTXP.gatherParams = function () { - log.debug(`pending-tx-details#gatherParams`) - const props = this.props - const state = this.state || {} - const txData = state.txData || props.txData - const txParams = txData.txParams - const gas = state.gas || txParams.gas - const gasPrice = state.gasPrice || txParams.gasPrice - const resultTx = extend(txParams, { - gas, - gasPrice, - }) - const resultTxMeta = extend(txData, { - txParams: resultTx, - }) - log.debug(`UI has computed tx params ${JSON.stringify(resultTx)}`) - return resultTxMeta -} - -PTXP.verifyGasParams = function () { - // We call this in case the gas has not been modified at all - if (!this.state) { return true } - return this._notZeroOrEmptyString(this.state.gas) && this._notZeroOrEmptyString(this.state.gasPrice) -} - -PTXP._notZeroOrEmptyString = function (obj) { - return obj !== '' && obj !== '0x0' -} - -function forwardCarrat () { - return ( - - h('img', { - src: 'images/forward-carrat.svg', - style: { - padding: '5px 6px 0px 10px', - height: '37px', - }, - }) - - ) -} -- cgit