aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ui/app/actions.js33
-rw-r--r--ui/app/components/send-token/index.js118
-rw-r--r--ui/app/send.js61
3 files changed, 183 insertions, 29 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js
index 0a2b4a636..a0dbbbf11 100644
--- a/ui/app/actions.js
+++ b/ui/app/actions.js
@@ -129,6 +129,9 @@ var actions = {
cancelAllTx: cancelAllTx,
viewPendingTx: viewPendingTx,
VIEW_PENDING_TX: 'VIEW_PENDING_TX',
+ // send screen
+ estimateGas,
+ getGasPrice,
// app messages
confirmSeedWords: confirmSeedWords,
showAccountDetail: showAccountDetail,
@@ -449,6 +452,36 @@ function signTx (txData) {
}
}
+function estimateGas ({ to, amount }) {
+ return (dispatch) => {
+ return new Promise((resolve, reject) => {
+ global.ethQuery.estimateGas({ to, amount }, (err, data) => {
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
+ dispatch(actions.hideWarning())
+ return resolve(data)
+ })
+ })
+ }
+}
+
+function getGasPrice () {
+ return (dispatch) => {
+ return new Promise((resolve, reject) => {
+ global.ethQuery.gasPrice((err, data) => {
+ if (err) {
+ dispatch(actions.displayWarning(err.message))
+ return reject(err)
+ }
+ dispatch(actions.hideWarning())
+ return resolve(data)
+ })
+ })
+ }
+}
+
function sendTx (txData) {
log.info(`actions - sendTx: ${JSON.stringify(txData.txParams)}`)
return (dispatch) => {
diff --git a/ui/app/components/send-token/index.js b/ui/app/components/send-token/index.js
index 60fe2ac8b..379f63883 100644
--- a/ui/app/components/send-token/index.js
+++ b/ui/app/components/send-token/index.js
@@ -5,7 +5,7 @@ const classnames = require('classnames')
const inherits = require('util').inherits
const actions = require('../../actions')
const selectors = require('../../selectors')
-const { isValidAddress } = require('../../util')
+const { isValidAddress, allNull } = require('../../util')
// const BalanceComponent = require('./balance-component')
const Identicon = require('../identicon')
@@ -57,6 +57,9 @@ function mapDispatchToProps (dispatch) {
dispatch(actions.signTokenTx(tokenAddress, toAddress, amount, txData))
),
updateTokenExchangeRate: token => dispatch(actions.updateTokenExchangeRate(token)),
+ estimateGas: ({ to, amount }) => dispatch(actions.estimateGas({ to, amount })),
+ getGasPrice: () => dispatch(actions.getGasPrice()),
+
}
}
@@ -65,11 +68,12 @@ function SendTokenScreen () {
Component.call(this)
this.state = {
to: '',
- amount: '',
+ amount: '0x0',
+ amountToSend: '0x0',
selectedCurrency: 'USD',
isGasTooltipOpen: false,
- gasPrice: '0x5d21dba00',
- gasLimit: '0x7b0d',
+ gasPrice: null,
+ gasLimit: null,
errors: {},
}
}
@@ -83,6 +87,24 @@ SendTokenScreen.prototype.componentWillMount = function () {
updateTokenExchangeRate(symbol)
}
+SendTokenScreen.prototype.estimateGasAndPrice = function () {
+ const { selectedToken } = this.props
+ const { errors, amount, to } = this.state
+
+ if (!errors.to && !errors.amount && amount > 0) {
+ Promise.all([
+ this.props.getGasPrice(),
+ this.props.estimateGas({ to, amount: this.getAmountToSend(amount, selectedToken) }),
+ ])
+ .then(([blockGasPrice, estimatedGas]) => {
+ this.setState({
+ blockGasPrice,
+ estimatedGas,
+ })
+ })
+ }
+}
+
SendTokenScreen.prototype.validate = function () {
const {
to,
@@ -113,6 +135,46 @@ SendTokenScreen.prototype.validate = function () {
}
}
+SendTokenScreen.prototype.setErrorsFor = function (field) {
+ const { balance, selectedToken } = this.props
+ const { errors: previousErrors } = this.state
+
+ const {
+ isValid,
+ errors: newErrors
+ } = this.validate()
+
+ const nextErrors = Object.assign({}, previousErrors, {
+ [field]: newErrors[field] || null
+ })
+
+ if (!isValid) {
+ this.setState({
+ errors: nextErrors,
+ isValid,
+ })
+ }
+}
+
+SendTokenScreen.prototype.clearErrorsFor = function (field) {
+ const { errors: previousErrors } = this.state
+ const nextErrors = Object.assign({}, previousErrors, {
+ [field]: null
+ })
+
+ this.setState({
+ errors: nextErrors,
+ isValid: allNull(nextErrors),
+ })
+}
+
+SendTokenScreen.prototype.getAmountToSend = function (amount, selectedToken) {
+ const { decimals } = selectedToken || {}
+ const multiplier = Math.pow(10, Number(decimals || 0))
+ const sendAmount = Number(amount * multiplier).toString(16)
+ return sendAmount
+}
+
SendTokenScreen.prototype.submit = function () {
const {
to,
@@ -132,11 +194,6 @@ SendTokenScreen.prototype.submit = function () {
} = this.props
const { nickname = ' ' } = identities[to] || {}
- const { isValid, errors } = this.validate()
-
- if (!isValid) {
- return this.setState({ errors })
- }
hideWarning()
addToAddressBook(to, nickname)
@@ -148,9 +205,7 @@ SendTokenScreen.prototype.submit = function () {
gasPrice: gasPrice,
}
- const { decimals } = selectedToken || {}
- const multiplier = Math.pow(10, Number(decimals || 0))
- const sendAmount = Number(amount * multiplier).toString(16)
+ const sendAmount = this.getAmountToSend(amount, selectedToken)
signTokenTx(selectedTokenAddress, to, sendAmount, txParams)
}
@@ -181,7 +236,14 @@ SendTokenScreen.prototype.renderToAddressInput = function () {
to: e.target.value,
errors: {},
}),
- onFocus: event => to && event.target.select(),
+ onBlur: () => {
+ this.setErrorsFor('to')
+ this.estimateGasAndPrice()
+ },
+ onFocus: event => {
+ if (to) event.target.select()
+ this.clearErrorsFor('to')
+ },
}),
h('datalist#addresses', [
// Corresponds to the addresses owned.
@@ -235,8 +297,12 @@ SendTokenScreen.prototype.renderAmountInput = function () {
value: amount,
onChange: e => this.setState({
amount: e.target.value,
- errors: {},
}),
+ onBlur: () => {
+ this.setErrorsFor('amount')
+ this.estimateGasAndPrice()
+ },
+ onFocus: () => this.clearErrorsFor('amount'),
}),
h('div.send-screen-input-wrapper__error-message', [ errorMessage ]),
])
@@ -247,6 +313,8 @@ SendTokenScreen.prototype.renderGasInput = function () {
isGasTooltipOpen,
gasPrice,
gasLimit,
+ blockGasPrice,
+ estimatedGas,
selectedCurrency,
errors: {
gasPrice: gasPriceErrorMessage,
@@ -267,12 +335,20 @@ SendTokenScreen.prototype.renderGasInput = function () {
}, [
isGasTooltipOpen && h(GasTooltip, {
className: 'send-tooltip',
- gasPrice,
- gasLimit,
+ gasPrice: gasPrice || blockGasPrice || '0x0',
+ gasLimit: gasLimit || estimatedGas || '0x0',
onClose: () => this.setState({ isGasTooltipOpen: false }),
onFeeChange: ({ gasLimit, gasPrice }) => {
this.setState({ gasLimit, gasPrice, errors: {} })
},
+ onBlur: () => {
+ this.setErrorsFor('gasLimit')
+ this.setErrorsFor('gasPrice')
+ },
+ onFocus: () => {
+ this.clearErrorsFor('gasLimit')
+ this.clearErrorsFor('gasPrice')
+ },
}),
h('div.send-screen-gas-labels', {}, [
@@ -283,9 +359,9 @@ SendTokenScreen.prototype.renderGasInput = function () {
h(GasFeeDisplay, {
conversionRate,
tokenExchangeRate,
- gasPrice,
+ gasPrice: gasPrice || blockGasPrice || '0x0',
activeCurrency: selectedCurrency,
- gas: gasLimit,
+ gas: gasLimit || estimatedGas || '0x0',
blockGasLimit: currentBlockGasLimit,
}),
h(
@@ -312,10 +388,12 @@ SendTokenScreen.prototype.renderMemoInput = function () {
SendTokenScreen.prototype.renderButtons = function () {
const { selectedAddress, backToAccountDetail } = this.props
+ const { isValid } = this.validate()
return h('div.send-token__button-group', [
h('button.send-token__button-next.btn-secondary', {
- onClick: () => this.submit(),
+ className: !isValid && 'send-screen__send-button__disabled',
+ onClick: () => isValid && this.submit(),
}, ['Next']),
h('button.send-token__button-cancel.btn-tertiary', {
onClick: () => backToAccountDetail(selectedAddress),
@@ -347,7 +425,7 @@ SendTokenScreen.prototype.render = function () {
this.renderAmountInput(),
this.renderGasInput(),
this.renderMemoInput(),
- warning && h('div.send-screen-input-wrapper--error',
+ warning && h('div.send-screen-input-wrapper--error', {},
h('div.send-screen-input-wrapper__error-message', [
warning,
])
diff --git a/ui/app/send.js b/ui/app/send.js
index 6c701f982..8791e9124 100644
--- a/ui/app/send.js
+++ b/ui/app/send.js
@@ -16,6 +16,8 @@ const {
hideWarning,
addToAddressBook,
signTx,
+ estimateGas,
+ getGasPrice,
} = require('./actions')
const { stripHexPrefix, addHexPrefix } = require('ethereumjs-util')
const { isHex, numericBalance, isValidAddress, allNull } = require('./util')
@@ -33,6 +35,8 @@ function mapStateToProps (state) {
addressBook,
conversionRate,
currentBlockGasLimit: blockGasLimit,
+ estimatedGas,
+ blockGasPrice,
} = state.metamask
const { warning } = state.appState
const selectedIdentity = getSelectedIdentity(state)
@@ -65,13 +69,15 @@ function SendTransactionScreen () {
newTx: {
from: '',
to: '',
- amount: 0,
amountToSend: '0x0',
- gasPrice: '0x5d21dba00',
- gas: '0x7b0d',
+ gasPrice: null,
+ gas: null,
+ amount: '0x0',
txData: null,
memo: '',
},
+ blockGasPrice: null,
+ estimatedGas: null,
activeCurrency: 'USD',
tooltipIsOpen: false,
errors: {},
@@ -87,6 +93,7 @@ function SendTransactionScreen () {
this.getAmountToSend = this.getAmountToSend.bind(this)
this.setErrorsFor = this.setErrorsFor.bind(this)
this.clearErrorsFor = this.clearErrorsFor.bind(this)
+ this.estimateGasAndPrice = this.estimateGasAndPrice.bind(this)
this.renderFromInput = this.renderFromInput.bind(this)
this.renderToInput = this.renderToInput.bind(this)
@@ -162,7 +169,10 @@ SendTransactionScreen.prototype.renderToInput = function (to, identities, addres
},
})
},
- onBlur: () => this.setErrorsFor('to'),
+ onBlur: () => {
+ this.setErrorsFor('to')
+ this.estimateGasAndPrice()
+ },
onFocus: event => {
this.clearErrorsFor('to')
this.state.newTx.to && event.target.select()
@@ -218,7 +228,10 @@ SendTransactionScreen.prototype.renderAmountInput = function (activeCurrency) {
),
})
},
- onBlur: () => this.setErrorsFor('amount'),
+ onBlur: () => {
+ this.setErrorsFor('amount')
+ this.estimateGasAndPrice()
+ },
onFocus: () => this.clearErrorsFor('amount'),
}),
@@ -301,7 +314,14 @@ SendTransactionScreen.prototype.render = function () {
conversionRate,
} = props
- const { blockGasLimit, newTx, activeCurrency, isValid } = this.state
+ const {
+ blockGasLimit,
+ newTx,
+ activeCurrency,
+ isValid,
+ blockGasPrice,
+ estimatedGas,
+ } = this.state
const { gas, gasPrice } = newTx
return (
@@ -322,7 +342,13 @@ SendTransactionScreen.prototype.render = function () {
this.renderAmountInput(activeCurrency),
- this.renderGasInput(gasPrice, gas, activeCurrency, conversionRate, blockGasLimit),
+ this.renderGasInput(
+ gasPrice || blockGasPrice || '0x0',
+ gas || estimatedGas || '0x0',
+ activeCurrency,
+ conversionRate,
+ blockGasLimit
+ ),
this.renderMemoInput(),
@@ -357,6 +383,23 @@ SendTransactionScreen.prototype.setActiveCurrency = function (newCurrency) {
this.setState({ activeCurrency: newCurrency })
}
+SendTransactionScreen.prototype.estimateGasAndPrice = function () {
+ const { errors, sendAmount, newTx } = this.state
+
+ if (!errors.to && !errors.amount && newTx.amount > 0) {
+ Promise.all([
+ this.props.dispatch(getGasPrice()),
+ this.props.dispatch(estimateGas({ to: newTx.to, amount: sendAmount })),
+ ])
+ .then(([blockGasPrice, estimatedGas]) => {
+ this.setState({
+ blockGasPrice,
+ estimatedGas,
+ })
+ })
+ }
+}
+
SendTransactionScreen.prototype.back = function () {
var address = this.props.address
this.props.dispatch(backToAccountDetail(address))
@@ -477,7 +520,7 @@ SendTransactionScreen.prototype.clearErrorsFor = function (field) {
SendTransactionScreen.prototype.onSubmit = function (event) {
event.preventDefault()
- const { warning, balance, amountToSend } = this.props
+ const { warning, balance } = this.props
const state = this.state || {}
const recipient = state.newTx.to
@@ -495,7 +538,7 @@ SendTransactionScreen.prototype.onSubmit = function (event) {
from: this.state.newTx.from,
to: this.state.newTx.to,
- value: amountToSend,
+ value: this.state.newTx.amountToSend,
gas: this.state.newTx.gas,
gasPrice: this.state.newTx.gasPrice,