aboutsummaryrefslogtreecommitdiffstats
path: root/ui/app/components/send_
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/components/send_')
-rw-r--r--ui/app/components/send_/account-list-item/account-list-item.container.js4
-rw-r--r--ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js2
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js17
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js4
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js9
-rw-r--r--ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js2
-rw-r--r--ui/app/components/send_/send-content/send-content.component.js2
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js61
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js1
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js55
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js2
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js4
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js2
-rw-r--r--ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js2
-rw-r--r--ui/app/components/send_/send-content/send-to-row/send-to-row.component.js6
-rw-r--r--ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js6
-rw-r--r--ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js12
-rw-r--r--ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js6
-rw-r--r--ui/app/components/send_/send.component.js8
-rw-r--r--ui/app/components/send_/send.constants.js2
-rw-r--r--ui/app/components/send_/send.container.js2
-rw-r--r--ui/app/components/send_/send.selectors.js5
-rw-r--r--ui/app/components/send_/send.utils.js49
-rw-r--r--ui/app/components/send_/tests/send-component.test.js14
-rw-r--r--ui/app/components/send_/tests/send-container.test.js2
-rw-r--r--ui/app/components/send_/tests/send-selectors.test.js10
-rw-r--r--ui/app/components/send_/tests/send-utils.test.js48
27 files changed, 280 insertions, 57 deletions
diff --git a/ui/app/components/send_/account-list-item/account-list-item.container.js b/ui/app/components/send_/account-list-item/account-list-item.container.js
index 3151b1f1d..4b4519288 100644
--- a/ui/app/components/send_/account-list-item/account-list-item.container.js
+++ b/ui/app/components/send_/account-list-item/account-list-item.container.js
@@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import {
getConversionRate,
- getConvertedCurrency,
+ getCurrentCurrency,
} from '../send.selectors.js'
import AccountListItem from './account-list-item.component'
@@ -10,6 +10,6 @@ export default connect(mapStateToProps)(AccountListItem)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
- currentCurrency: getConvertedCurrency(state),
+ currentCurrency: getCurrentCurrency(state),
}
}
diff --git a/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js b/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js
index 49da920e6..af0859117 100644
--- a/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js
+++ b/ui/app/components/send_/account-list-item/tests/account-list-item-container.test.js
@@ -12,7 +12,7 @@ proxyquire('../account-list-item.container.js', {
},
'../send.selectors.js': {
getConversionRate: (s) => `mockConversionRate:${s}`,
- getConvertedCurrency: (s) => `mockCurrentCurrency:${s}`,
+ getCurrentCurrency: (s) => `mockCurrentCurrency:${s}`,
},
})
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
index 8aefeed4a..196538c11 100644
--- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.component.js
@@ -23,6 +23,7 @@ export default class SendAmountRow extends Component {
tokenBalance: PropTypes.string,
updateSendAmount: PropTypes.func,
updateSendAmountError: PropTypes.func,
+ updateGas: PropTypes.func,
}
validateAmount (amount) {
@@ -56,6 +57,14 @@ export default class SendAmountRow extends Component {
updateSendAmount(amount)
}
+ updateGas (amount) {
+ const { selectedToken, updateGas } = this.props
+
+ if (selectedToken) {
+ updateGas({ amount })
+ }
+ }
+
render () {
const {
amount,
@@ -77,12 +86,16 @@ export default class SendAmountRow extends Component {
<CurrencyDisplay
conversionRate={amountConversionRate}
convertedCurrency={convertedCurrency}
- onBlur={newAmount => this.updateAmount(newAmount)}
+ onBlur={newAmount => {
+ this.updateGas(newAmount)
+ this.updateAmount(newAmount)
+ }}
onChange={newAmount => this.validateAmount(newAmount)}
inError={inError}
primaryCurrency={primaryCurrency || 'ETH'}
selectedToken={selectedToken}
- value={amount || '0x0'}
+ value={amount}
+ step="any"
/>
</SendRowWrapper>
)
diff --git a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
index bbbf56971..b816d948f 100644
--- a/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
+++ b/ui/app/components/send_/send-content/send-amount-row/send-amount-row.container.js
@@ -2,7 +2,7 @@ import { connect } from 'react-redux'
import {
getAmountConversionRate,
getConversionRate,
- getConvertedCurrency,
+ getCurrentCurrency,
getGasTotal,
getPrimaryCurrency,
getSelectedToken,
@@ -31,7 +31,7 @@ function mapStateToProps (state) {
amountConversionRate: getAmountConversionRate(state),
balance: getSendFromBalance(state),
conversionRate: getConversionRate(state),
- convertedCurrency: getConvertedCurrency(state),
+ convertedCurrency: getCurrentCurrency(state),
gasTotal: getGasTotal(state),
inError: sendAmountIsInError(state),
primaryCurrency: getPrimaryCurrency(state),
diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
index 2205579ca..579e18585 100644
--- a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
+++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-component.test.js
@@ -12,10 +12,12 @@ const propsMethodSpies = {
setMaxModeTo: sinon.spy(),
updateSendAmount: sinon.spy(),
updateSendAmountError: sinon.spy(),
+ updateGas: sinon.spy(),
}
sinon.spy(SendAmountRow.prototype, 'updateAmount')
sinon.spy(SendAmountRow.prototype, 'validateAmount')
+sinon.spy(SendAmountRow.prototype, 'updateGas')
describe('SendAmountRow Component', function () {
let wrapper
@@ -36,6 +38,7 @@ describe('SendAmountRow Component', function () {
tokenBalance={'mockTokenBalance'}
updateSendAmount={propsMethodSpies.updateSendAmount}
updateSendAmountError={propsMethodSpies.updateSendAmountError}
+ updateGas={propsMethodSpies.updateGas}
/>, { context: { t: str => str + '_t' } })
instance = wrapper.instance()
})
@@ -139,8 +142,14 @@ describe('SendAmountRow Component', function () {
assert.equal(primaryCurrency, 'mockPrimaryCurrency')
assert.deepEqual(selectedToken, { address: 'mockTokenAddress' })
assert.equal(value, 'mockAmount')
+ assert.equal(SendAmountRow.prototype.updateGas.callCount, 0)
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 0)
onBlur('mockNewAmount')
+ assert.equal(SendAmountRow.prototype.updateGas.callCount, 1)
+ assert.deepEqual(
+ SendAmountRow.prototype.updateGas.getCall(0).args,
+ ['mockNewAmount']
+ )
assert.equal(SendAmountRow.prototype.updateAmount.callCount, 1)
assert.deepEqual(
SendAmountRow.prototype.updateAmount.getCall(0).args,
diff --git a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
index e4c913c69..94d9918a7 100644
--- a/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
+++ b/ui/app/components/send_/send-content/send-amount-row/tests/send-amount-row-container.test.js
@@ -24,7 +24,7 @@ proxyquire('../send-amount-row.container.js', {
'../../send.selectors': {
getAmountConversionRate: (s) => `mockAmountConversionRate:${s}`,
getConversionRate: (s) => `mockConversionRate:${s}`,
- getConvertedCurrency: (s) => `mockConvertedCurrency:${s}`,
+ getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`,
getGasTotal: (s) => `mockGasTotal:${s}`,
getPrimaryCurrency: (s) => `mockPrimaryCurrency:${s}`,
getSelectedToken: (s) => `mockSelectedToken:${s}`,
diff --git a/ui/app/components/send_/send-content/send-content.component.js b/ui/app/components/send_/send-content/send-content.component.js
index 3a14054eb..adc114c0e 100644
--- a/ui/app/components/send_/send-content/send-content.component.js
+++ b/ui/app/components/send_/send-content/send-content.component.js
@@ -18,7 +18,7 @@ export default class SendContent extends Component {
<div className="send-v2__form">
<SendFromRow />
<SendToRow updateGas={(updateData) => this.props.updateGas(updateData)} />
- <SendAmountRow />
+ <SendAmountRow updateGas={(updateData) => this.props.updateGas(updateData)} />
<SendGasRow />
</div>
</PageContainerContent>
diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js
new file mode 100644
index 000000000..b1fd67412
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/gas-fee-display.component.js
@@ -0,0 +1,61 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import CurrencyDisplay from '../../../../send/currency-display'
+
+
+export default class GasFeeDisplay extends Component {
+
+ static propTypes = {
+ conversionRate: PropTypes.number,
+ primaryCurrency: PropTypes.string,
+ convertedCurrency: PropTypes.string,
+ gasLoadingError: PropTypes.bool,
+ gasTotal: PropTypes.string,
+ onClick: PropTypes.func,
+ };
+
+ render() {
+ const {
+ conversionRate,
+ gasTotal,
+ onClick,
+ primaryCurrency = 'ETH',
+ convertedCurrency,
+ gasLoadingError,
+ } = this.props
+
+ return (
+ <div className="send-v2__gas-fee-display">
+ {gasTotal
+ ? <CurrencyDisplay
+ primaryCurrency={primaryCurrency}
+ convertedCurrency={convertedCurrency}
+ value={gasTotal}
+ conversionRate={conversionRate}
+ gasLoadingError={gasLoadingError}
+ convertedPrefix={'$'}
+ readOnly
+ />
+ : gasLoadingError
+ ? <div className="currency-display.currency-display--message">
+ {this.context.t('setGasPrice')}
+ </div>
+ : <div className="currency-display">
+ {this.context.t('loading')}
+ </div>
+ }
+ <button
+ className="sliders-icon-container"
+ onClick={onClick}
+ disabled={!gasTotal && !gasLoadingError}
+ >
+ <i className="fa fa-sliders sliders-icon" />
+ </button>
+ </div>
+ )
+ }
+}
+
+GasFeeDisplay.contextTypes = {
+ t: PropTypes.func,
+}
diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js
new file mode 100644
index 000000000..dba0edb7b
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/index.js
@@ -0,0 +1 @@
+export { default } from './gas-fee-display.component'
diff --git a/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js
new file mode 100644
index 000000000..66f3a94df
--- /dev/null
+++ b/ui/app/components/send_/send-content/send-gas-row/gas-fee-display/test/gas-fee-display.component.test.js
@@ -0,0 +1,55 @@
+import React from 'react'
+import assert from 'assert'
+import {shallow} from 'enzyme'
+import GasFeeDisplay from '../gas-fee-display.component'
+import CurrencyDisplay from '../../../../../send/currency-display'
+import sinon from 'sinon'
+
+
+const propsMethodSpies = {
+ showCustomizeGasModal: sinon.spy(),
+}
+
+describe('SendGasRow Component', function() {
+ let wrapper
+
+ beforeEach(() => {
+ wrapper = shallow(<GasFeeDisplay
+ conversionRate={20}
+ gasTotal={'mockGasTotal'}
+ onClick={propsMethodSpies.showCustomizeGasModal}
+ primaryCurrency={'mockPrimaryCurrency'}
+ convertedCurrency={'mockConvertedCurrency'}
+ />, {context: {t: str => str + '_t'}})
+ })
+
+ afterEach(() => {
+ propsMethodSpies.showCustomizeGasModal.resetHistory()
+ })
+
+ describe('render', () => {
+ it('should render a CurrencyDisplay component', () => {
+ assert.equal(wrapper.find(CurrencyDisplay).length, 1)
+ })
+
+ it('should render the CurrencyDisplay with the correct props', () => {
+ const {
+ conversionRate,
+ convertedCurrency,
+ value,
+ } = wrapper.find(CurrencyDisplay).props()
+ assert.equal(conversionRate, 20)
+ assert.equal(convertedCurrency, 'mockConvertedCurrency')
+ assert.equal(value, 'mockGasTotal')
+ })
+
+ it('should render the Button with the correct props', () => {
+ const {
+ onClick,
+ } = wrapper.find('button').props()
+ assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 0)
+ onClick()
+ assert.equal(propsMethodSpies.showCustomizeGasModal.callCount, 1)
+ })
+ })
+})
diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js
index c80d8c0bb..17cea3d4e 100644
--- a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js
+++ b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.component.js
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import SendRowWrapper from '../send-row-wrapper/'
-import GasFeeDisplay from '../../../send/gas-fee-display-v2'
+import GasFeeDisplay from './gas-fee-display/gas-fee-display.component'
export default class SendGasRow extends Component {
diff --git a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js
index 20d3daa59..6e6fbc8a8 100644
--- a/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js
+++ b/ui/app/components/send_/send-content/send-gas-row/send-gas-row.container.js
@@ -1,7 +1,7 @@
import { connect } from 'react-redux'
import {
getConversionRate,
- getConvertedCurrency,
+ getCurrentCurrency,
getGasTotal,
} from '../../send.selectors.js'
import { sendGasIsInError } from './send-gas-row.selectors.js'
@@ -13,7 +13,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(SendGasRow)
function mapStateToProps (state) {
return {
conversionRate: getConversionRate(state),
- convertedCurrency: getConvertedCurrency(state),
+ convertedCurrency: getCurrentCurrency(state),
gasTotal: getGasTotal(state),
gasLoadingError: sendGasIsInError(state),
}
diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js
index e4f05d708..db37f18be 100644
--- a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js
+++ b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-component.test.js
@@ -5,7 +5,7 @@ import sinon from 'sinon'
import SendGasRow from '../send-gas-row.component.js'
import SendRowWrapper from '../../send-row-wrapper/send-row-wrapper.component'
-import GasFeeDisplay from '../../../../send/gas-fee-display-v2'
+import GasFeeDisplay from '../gas-fee-display/gas-fee-display.component'
const propsMethodSpies = {
showCustomizeGasModal: sinon.spy(),
diff --git a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js
index 9135524d1..e928c8aba 100644
--- a/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js
+++ b/ui/app/components/send_/send-content/send-gas-row/tests/send-gas-row-container.test.js
@@ -19,7 +19,7 @@ proxyquire('../send-gas-row.container.js', {
},
'../../send.selectors.js': {
getConversionRate: (s) => `mockConversionRate:${s}`,
- getConvertedCurrency: (s) => `mockConvertedCurrency:${s}`,
+ getCurrentCurrency: (s) => `mockConvertedCurrency:${s}`,
getGasTotal: (s) => `mockGasTotal:${s}`,
},
'./send-gas-row.selectors.js': { sendGasIsInError: (s) => `mockGasLoadingError:${s}` },
diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js b/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js
index 0a83186a5..1c2ecdf9c 100644
--- a/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js
+++ b/ui/app/components/send_/send-content/send-to-row/send-to-row.component.js
@@ -19,9 +19,9 @@ export default class SendToRow extends Component {
updateSendToError: PropTypes.func,
};
- handleToChange (to, nickname = '') {
+ handleToChange (to, nickname = '', toError) {
const { updateSendTo, updateSendToError, updateGas } = this.props
- const toErrorObject = getToErrorObject(to)
+ const toErrorObject = getToErrorObject(to, toError)
updateSendTo(to, nickname)
updateSendToError(toErrorObject)
if (toErrorObject.to === null) {
@@ -53,7 +53,7 @@ export default class SendToRow extends Component {
inError={inError}
name={'address'}
network={network}
- onChange={(newTo, newNickname) => this.handleToChange(newTo, newNickname)}
+ onChange={({ toAddress, nickname, toError }) => this.handleToChange(toAddress, nickname, toError)}
openDropdown={() => openToDropdown()}
placeholder={this.context.t('recipientAddress')}
to={to}
diff --git a/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js b/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js
index cea51ee20..6b90a9f09 100644
--- a/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js
+++ b/ui/app/components/send_/send-content/send-to-row/send-to-row.utils.js
@@ -4,12 +4,10 @@ const {
} = require('../../send.constants')
const { isValidAddress } = require('../../../../util')
-function getToErrorObject (to) {
- let toError = null
-
+function getToErrorObject (to, toError = null) {
if (!to) {
toError = REQUIRED_ERROR
- } else if (!isValidAddress(to)) {
+ } else if (!isValidAddress(to) && !toError) {
toError = INVALID_RECIPIENT_ADDRESS_ERROR
}
diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js
index 58fe51dcf..781371004 100644
--- a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js
+++ b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-component.test.js
@@ -6,8 +6,8 @@ import proxyquire from 'proxyquire'
const SendToRow = proxyquire('../send-to-row.component.js', {
'./send-to-row.utils.js': {
- getToErrorObject: (to) => ({
- to: to === false ? null : `mockToErrorObject:${to}`,
+ getToErrorObject: (to, toError) => ({
+ to: to === false ? null : `mockToErrorObject:${to}${toError}`,
}),
},
}).default
@@ -67,11 +67,11 @@ describe('SendToRow Component', function () {
it('should call updateSendToError', () => {
assert.equal(propsMethodSpies.updateSendToError.callCount, 0)
- instance.handleToChange('mockTo2')
+ instance.handleToChange('mockTo2', '', 'mockToError')
assert.equal(propsMethodSpies.updateSendToError.callCount, 1)
assert.deepEqual(
propsMethodSpies.updateSendToError.getCall(0).args,
- [{ to: 'mockToErrorObject:mockTo2' }]
+ [{ to: 'mockToErrorObject:mockTo2mockToError' }]
)
})
@@ -138,11 +138,11 @@ describe('SendToRow Component', function () {
openDropdown()
assert.equal(propsMethodSpies.openToDropdown.callCount, 1)
assert.equal(SendToRow.prototype.handleToChange.callCount, 0)
- onChange('mockNewTo', 'mockNewNickname')
+ onChange({ toAddress: 'mockNewTo', nickname: 'mockNewNickname', toError: 'mockToError' })
assert.equal(SendToRow.prototype.handleToChange.callCount, 1)
assert.deepEqual(
SendToRow.prototype.handleToChange.getCall(0).args,
- ['mockNewTo', 'mockNewNickname']
+ ['mockNewTo', 'mockNewNickname', 'mockToError']
)
})
})
diff --git a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js
index 615c9581b..4d2447c32 100644
--- a/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js
+++ b/ui/app/components/send_/send-content/send-to-row/tests/send-to-row-utils.test.js
@@ -40,6 +40,12 @@ describe('send-to-row utils', () => {
to: null,
})
})
+
+ it('should return the passed error if to is truthy but invalid if to is truthy and valid', () => {
+ assert.deepEqual(getToErrorObject('invalid #$ 345878', 'someExplicitError'), {
+ to: 'someExplicitError',
+ })
+ })
})
})
diff --git a/ui/app/components/send_/send.component.js b/ui/app/components/send_/send.component.js
index 516251e22..219b362f2 100644
--- a/ui/app/components/send_/send.component.js
+++ b/ui/app/components/send_/send.component.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import PersistentForm from '../../../lib/persistent-form'
import {
getAmountErrorObject,
+ getToAddressForGasUpdate,
doesAmountErrorRequireUpdate,
} from './send.utils'
@@ -38,7 +39,7 @@ export default class SendTransactionScreen extends PersistentForm {
updateSendTokenBalance: PropTypes.func,
};
- updateGas ({ to } = {}) {
+ updateGas ({ to: updatedToAddress, amount: value } = {}) {
const {
amount,
blockGasLimit,
@@ -48,6 +49,7 @@ export default class SendTransactionScreen extends PersistentForm {
recentBlocks,
selectedAddress,
selectedToken = {},
+ to: currentToAddress,
updateAndSetGasTotal,
} = this.props
@@ -59,8 +61,8 @@ export default class SendTransactionScreen extends PersistentForm {
recentBlocks,
selectedAddress,
selectedToken,
- to: to && to.toLowerCase(),
- value: amount,
+ to: getToAddressForGasUpdate(updatedToAddress, currentToAddress),
+ value: value || amount,
})
}
diff --git a/ui/app/components/send_/send.constants.js b/ui/app/components/send_/send.constants.js
index df5dee371..8acdf0641 100644
--- a/ui/app/components/send_/send.constants.js
+++ b/ui/app/components/send_/send.constants.js
@@ -36,6 +36,7 @@ const ONE_GWEI_IN_WEI_HEX = ethUtil.addHexPrefix(conversionUtil('0x1', {
}))
const SIMPLE_GAS_COST = '0x5208' // Hex for 21000, cost of a simple send.
+const BASE_TOKEN_GAS_COST = '0x186a0' // Hex for 100000, a base estimate for token transfers.
module.exports = {
INSUFFICIENT_FUNDS_ERROR,
@@ -52,4 +53,5 @@ module.exports = {
REQUIRED_ERROR,
SIMPLE_GAS_COST,
TOKEN_TRANSFER_FUNCTION_SIGNATURE,
+ BASE_TOKEN_GAS_COST,
}
diff --git a/ui/app/components/send_/send.container.js b/ui/app/components/send_/send.container.js
index 1fd96d61f..185653c5f 100644
--- a/ui/app/components/send_/send.container.js
+++ b/ui/app/components/send_/send.container.js
@@ -19,6 +19,7 @@ import {
getSendAmount,
getSendEditingTransactionId,
getSendFromObject,
+ getSendTo,
getTokenBalance,
} from './send.selectors'
import {
@@ -54,6 +55,7 @@ function mapStateToProps (state) {
recentBlocks: getRecentBlocks(state),
selectedAddress: getSelectedAddress(state),
selectedToken: getSelectedToken(state),
+ to: getSendTo(state),
tokenBalance: getTokenBalance(state),
tokenContract: getSelectedTokenContract(state),
tokenToFiatRate: getSelectedTokenToFiatRate(state),
diff --git a/ui/app/components/send_/send.selectors.js b/ui/app/components/send_/send.selectors.js
index 7e7cfe2e9..f910f7caf 100644
--- a/ui/app/components/send_/send.selectors.js
+++ b/ui/app/components/send_/send.selectors.js
@@ -14,7 +14,6 @@ const selectors = {
getAmountConversionRate,
getBlockGasLimit,
getConversionRate,
- getConvertedCurrency,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency,
getCurrentNetwork,
@@ -98,10 +97,6 @@ function getConversionRate (state) {
return state.metamask.conversionRate
}
-function getConvertedCurrency (state) {
- return state.metamask.currentCurrency
-}
-
function getCurrentAccountWithSendEtherInfo (state) {
const currentAddress = getSelectedAddress(state)
const accounts = accountsWithSendEtherInfoSelector(state)
diff --git a/ui/app/components/send_/send.utils.js b/ui/app/components/send_/send.utils.js
index 67699be77..872df1d2f 100644
--- a/ui/app/components/send_/send.utils.js
+++ b/ui/app/components/send_/send.utils.js
@@ -4,11 +4,13 @@ const {
conversionGTE,
multiplyCurrencies,
conversionGreaterThan,
+ conversionLessThan,
} = require('../../conversion-util')
const {
calcTokenAmount,
} = require('../../token-util')
const {
+ BASE_TOKEN_GAS_COST,
INSUFFICIENT_FUNDS_ERROR,
INSUFFICIENT_TOKENS_ERROR,
NEGATIVE_ETH_ERROR,
@@ -20,6 +22,7 @@ const abi = require('ethereumjs-abi')
const ethUtil = require('ethereumjs-util')
module.exports = {
+ addGasBuffer,
calcGasTotal,
calcTokenBalance,
doesAmountErrorRequireUpdate,
@@ -27,6 +30,7 @@ module.exports = {
estimateGasPriceFromRecentBlocks,
generateTokenTransferData,
getAmountErrorObject,
+ getToAddressForGasUpdate,
isBalanceSufficient,
isTokenBalanceSufficient,
}
@@ -175,12 +179,13 @@ async function estimateGas ({ selectedAddress, selectedToken, blockGasLimit, to,
}
// if recipient has no code, gas is 21k max:
- const hasRecipient = Boolean(to)
- if (hasRecipient && !selectedToken) {
- const code = await global.eth.getCode(to)
+ if (!selectedToken) {
+ const code = Boolean(to) && await global.eth.getCode(to)
if (!code || code === '0x') {
return SIMPLE_GAS_COST
}
+ } else if (selectedToken && !to) {
+ return BASE_TOKEN_GAS_COST
}
paramsForGasEstimate.to = selectedToken ? selectedToken.address : to
@@ -201,16 +206,46 @@ async function estimateGas ({ selectedAddress, selectedToken, blockGasLimit, to,
err.message.includes('gas required exceeds allowance or always failing transaction')
)
if (simulationFailed) {
- return resolve(paramsForGasEstimate.gas)
+ const estimateWithBuffer = addGasBuffer(paramsForGasEstimate.gas, blockGasLimit, 1.5)
+ return resolve(ethUtil.addHexPrefix(estimateWithBuffer))
} else {
return reject(err)
}
}
- return resolve(estimatedGas.toString(16))
+ const estimateWithBuffer = addGasBuffer(estimatedGas.toString(16), blockGasLimit, 1.5)
+ return resolve(ethUtil.addHexPrefix(estimateWithBuffer))
})
})
}
+function addGasBuffer (initialGasLimitHex, blockGasLimitHex, bufferMultiplier = 1.5) {
+ const upperGasLimit = multiplyCurrencies(blockGasLimitHex, 0.9, {
+ toNumericBase: 'hex',
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ numberOfDecimals: '0',
+ })
+ const bufferedGasLimit = multiplyCurrencies(initialGasLimitHex, bufferMultiplier, {
+ toNumericBase: 'hex',
+ multiplicandBase: 16,
+ multiplierBase: 10,
+ numberOfDecimals: '0',
+ })
+
+ // if initialGasLimit is above blockGasLimit, dont modify it
+ if (conversionGreaterThan(
+ { value: initialGasLimitHex, fromNumericBase: 'hex' },
+ { value: upperGasLimit, fromNumericBase: 'hex' },
+ )) return initialGasLimitHex
+ // if bufferedGasLimit is below blockGasLimit, use bufferedGasLimit
+ if (conversionLessThan(
+ { value: bufferedGasLimit, fromNumericBase: 'hex' },
+ { value: upperGasLimit, fromNumericBase: 'hex' },
+ )) return bufferedGasLimit
+ // otherwise use blockGasLimit
+ return upperGasLimit
+}
+
function generateTokenTransferData ({ toAddress = '0x0', amount = '0x0', selectedToken }) {
if (!selectedToken) return
return TOKEN_TRANSFER_FUNCTION_SIGNATURE + Array.prototype.map.call(
@@ -237,3 +272,7 @@ function estimateGasPriceFromRecentBlocks (recentBlocks) {
return lowestPrices[Math.floor(lowestPrices.length / 2)]
}
+
+function getToAddressForGasUpdate (...addresses) {
+ return [...addresses, ''].find(str => str !== undefined && str !== null).toLowerCase()
+}
diff --git a/ui/app/components/send_/tests/send-component.test.js b/ui/app/components/send_/tests/send-component.test.js
index 4e33d8f63..4ba9b226d 100644
--- a/ui/app/components/send_/tests/send-component.test.js
+++ b/ui/app/components/send_/tests/send-component.test.js
@@ -201,7 +201,7 @@ describe('Send Component', function () {
})
describe('updateGas', () => {
- it('should call updateAndSetGasTotal with the correct params', () => {
+ it('should call updateAndSetGasTotal with the correct params if no to prop is passed', () => {
propsMethodSpies.updateAndSetGasTotal.resetHistory()
wrapper.instance().updateGas()
assert.equal(propsMethodSpies.updateAndSetGasTotal.callCount, 1)
@@ -215,12 +215,22 @@ describe('Send Component', function () {
recentBlocks: ['mockBlock'],
selectedAddress: 'mockSelectedAddress',
selectedToken: 'mockSelectedToken',
- to: undefined,
+ to: '',
value: 'mockAmount',
}
)
})
+ it('should call updateAndSetGasTotal with the correct params if a to prop is passed', () => {
+ propsMethodSpies.updateAndSetGasTotal.resetHistory()
+ wrapper.setProps({ to: 'someAddress' })
+ wrapper.instance().updateGas()
+ assert.equal(
+ propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0].to,
+ 'someaddress',
+ )
+ })
+
it('should call updateAndSetGasTotal with to set to lowercase if passed', () => {
propsMethodSpies.updateAndSetGasTotal.resetHistory()
wrapper.instance().updateGas({ to: '0xABC' })
diff --git a/ui/app/components/send_/tests/send-container.test.js b/ui/app/components/send_/tests/send-container.test.js
index 056aad148..91484f4d8 100644
--- a/ui/app/components/send_/tests/send-container.test.js
+++ b/ui/app/components/send_/tests/send-container.test.js
@@ -39,6 +39,7 @@ proxyquire('../send.container.js', {
getSelectedTokenContract: (s) => `mockTokenContract:${s}`,
getSelectedTokenToFiatRate: (s) => `mockTokenToFiatRate:${s}`,
getSendAmount: (s) => `mockAmount:${s}`,
+ getSendTo: (s) => `mockTo:${s}`,
getSendEditingTransactionId: (s) => `mockEditingTransactionId:${s}`,
getSendFromObject: (s) => `mockFrom:${s}`,
getTokenBalance: (s) => `mockTokenBalance:${s}`,
@@ -70,6 +71,7 @@ describe('send container', () => {
recentBlocks: 'mockRecentBlocks:mockState',
selectedAddress: 'mockSelectedAddress:mockState',
selectedToken: 'mockSelectedToken:mockState',
+ to: 'mockTo:mockState',
tokenBalance: 'mockTokenBalance:mockState',
tokenContract: 'mockTokenContract:mockState',
tokenToFiatRate: 'mockTokenToFiatRate:mockState',
diff --git a/ui/app/components/send_/tests/send-selectors.test.js b/ui/app/components/send_/tests/send-selectors.test.js
index 152af8059..218da656b 100644
--- a/ui/app/components/send_/tests/send-selectors.test.js
+++ b/ui/app/components/send_/tests/send-selectors.test.js
@@ -8,7 +8,6 @@ const {
getBlockGasLimit,
getAmountConversionRate,
getConversionRate,
- getConvertedCurrency,
getCurrentAccountWithSendEtherInfo,
getCurrentCurrency,
getCurrentNetwork,
@@ -154,15 +153,6 @@ describe('send selectors', () => {
})
})
- describe('getConvertedCurrency()', () => {
- it('should return the currently selected currency', () => {
- assert.equal(
- getConvertedCurrency(mockState),
- 'USD'
- )
- })
- })
-
describe('getCurrentAccountWithSendEtherInfo()', () => {
it('should return the currently selected account with identity info', () => {
assert.deepEqual(
diff --git a/ui/app/components/send_/tests/send-utils.test.js b/ui/app/components/send_/tests/send-utils.test.js
index b3f6372ef..a518a64e9 100644
--- a/ui/app/components/send_/tests/send-utils.test.js
+++ b/ui/app/components/send_/tests/send-utils.test.js
@@ -2,6 +2,7 @@ import assert from 'assert'
import sinon from 'sinon'
import proxyquire from 'proxyquire'
import {
+ BASE_TOKEN_GAS_COST,
ONE_GWEI_IN_WEI_HEX,
SIMPLE_GAS_COST,
} from '../send.constants'
@@ -18,10 +19,12 @@ const {
const stubs = {
addCurrencies: sinon.stub().callsFake((a, b, obj) => a + b),
conversionUtil: sinon.stub().callsFake((val, obj) => parseInt(val, 16)),
- conversionGTE: sinon.stub().callsFake((obj1, obj2) => obj1.value > obj2.value),
+ conversionGTE: sinon.stub().callsFake((obj1, obj2) => obj1.value >= obj2.value),
multiplyCurrencies: sinon.stub().callsFake((a, b) => `${a}x${b}`),
calcTokenAmount: sinon.stub().callsFake((a, d) => 'calc:' + a + d),
rawEncode: sinon.stub().returns([16, 1100]),
+ conversionGreaterThan: sinon.stub().callsFake((obj1, obj2) => obj1.value > obj2.value),
+ conversionLessThan: sinon.stub().callsFake((obj1, obj2) => obj1.value < obj2.value),
}
const sendUtils = proxyquire('../send.utils.js', {
@@ -30,6 +33,8 @@ const sendUtils = proxyquire('../send.utils.js', {
conversionUtil: stubs.conversionUtil,
conversionGTE: stubs.conversionGTE,
multiplyCurrencies: stubs.multiplyCurrencies,
+ conversionGreaterThan: stubs.conversionGreaterThan,
+ conversionLessThan: stubs.conversionLessThan,
},
'../../token-util': { calcTokenAmount: stubs.calcTokenAmount },
'ethereumjs-abi': {
@@ -44,6 +49,7 @@ const {
estimateGasPriceFromRecentBlocks,
generateTokenTransferData,
getAmountErrorObject,
+ getToAddressForGasUpdate,
calcTokenBalance,
isBalanceSufficient,
isTokenBalanceSufficient,
@@ -255,7 +261,7 @@ describe('send utils', () => {
estimateGasMethod: sinon.stub().callsFake(
(data, cb) => cb(
data.to.match(/willFailBecauseOf:/) ? { message: data.to.match(/:(.+)$/)[1] } : null,
- { toString: (n) => `mockToString:${n}` }
+ { toString: (n) => `0xabc${n}` }
)
),
}
@@ -279,13 +285,23 @@ describe('send utils', () => {
})
it('should call ethQuery.estimateGas with the expected params', async () => {
- const result = await estimateGas(baseMockParams)
+ const result = await sendUtils.estimateGas(baseMockParams)
assert.equal(baseMockParams.estimateGasMethod.callCount, 1)
assert.deepEqual(
baseMockParams.estimateGasMethod.getCall(0).args[0],
Object.assign({ gasPrice: undefined, value: undefined }, baseExpectedCall)
)
- assert.equal(result, 'mockToString:16')
+ assert.equal(result, '0xabc16')
+ })
+
+ it('should call ethQuery.estimateGas with the expected params when initialGasLimitHex is lower than the upperGasLimit', async () => {
+ const result = await estimateGas(Object.assign({}, baseMockParams, { blockGasLimit: '0xbcd' }))
+ assert.equal(baseMockParams.estimateGasMethod.callCount, 1)
+ assert.deepEqual(
+ baseMockParams.estimateGasMethod.getCall(0).args[0],
+ Object.assign({ gasPrice: undefined, value: undefined }, baseExpectedCall, { gas: '0xbcdx0.95' })
+ )
+ assert.equal(result, '0xabc16x1.5')
})
it('should call ethQuery.estimateGas with a value of 0x0 and the expected data and to if passed a selectedToken', async () => {
@@ -300,7 +316,7 @@ describe('send utils', () => {
to: 'mockAddress',
})
)
- assert.equal(result, 'mockToString:16')
+ assert.equal(result, '0xabc16')
})
it(`should return ${SIMPLE_GAS_COST} if ethQuery.getCode does not return '0x'`, async () => {
@@ -309,12 +325,23 @@ describe('send utils', () => {
assert.equal(result, SIMPLE_GAS_COST)
})
+ it(`should return ${SIMPLE_GAS_COST} if not passed a selectedToken or truthy to address`, async () => {
+ assert.equal(baseMockParams.estimateGasMethod.callCount, 0)
+ const result = await estimateGas(Object.assign({}, baseMockParams, { to: null }))
+ assert.equal(result, SIMPLE_GAS_COST)
+ })
+
it(`should not return ${SIMPLE_GAS_COST} if passed a selectedToken`, async () => {
assert.equal(baseMockParams.estimateGasMethod.callCount, 0)
const result = await estimateGas(Object.assign({}, baseMockParams, { to: '0x123', selectedToken: { address: '' } }))
assert.notEqual(result, SIMPLE_GAS_COST)
})
+ it(`should return ${BASE_TOKEN_GAS_COST} if passed a selectedToken but no to address`, async () => {
+ const result = await estimateGas(Object.assign({}, baseMockParams, { to: null, selectedToken: { address: '' } }))
+ assert.equal(result, BASE_TOKEN_GAS_COST)
+ })
+
it(`should return the adjusted blockGasLimit if it fails with a 'Transaction execution error.'`, async () => {
const result = await estimateGas(Object.assign({}, baseMockParams, {
to: 'isContract willFailBecauseOf:Transaction execution error.',
@@ -401,4 +428,15 @@ describe('send utils', () => {
assert.equal(estimateGasPriceFromRecentBlocks(mockRecentBlocks), '0x5')
})
})
+
+ describe('getToAddressForGasUpdate()', () => {
+ it('should return empty string if all params are undefined or null', () => {
+ assert.equal(getToAddressForGasUpdate(undefined, null), '')
+ })
+
+ it('should return the first string that is not defined or null in lower case', () => {
+ assert.equal(getToAddressForGasUpdate('A', null), 'a')
+ assert.equal(getToAddressForGasUpdate(undefined, 'B'), 'b')
+ })
+ })
})