diff options
author | Dan Miller <danjm.com@gmail.com> | 2018-09-13 16:47:05 +0800 |
---|---|---|
committer | Dan Miller <danjm.com@gmail.com> | 2018-12-04 11:36:04 +0800 |
commit | 7de3f22d63748ed5a81e947755db056d4cdef3db (patch) | |
tree | 748482505ce65c3de4e71787d27b69d71156b522 | |
parent | 6ada9b4a3c02014f8d00b1f45a149afbf47f700d (diff) | |
download | tangerine-wallet-browser-7de3f22d63748ed5a81e947755db056d4cdef3db.tar tangerine-wallet-browser-7de3f22d63748ed5a81e947755db056d4cdef3db.tar.gz tangerine-wallet-browser-7de3f22d63748ed5a81e947755db056d4cdef3db.tar.bz2 tangerine-wallet-browser-7de3f22d63748ed5a81e947755db056d4cdef3db.tar.lz tangerine-wallet-browser-7de3f22d63748ed5a81e947755db056d4cdef3db.tar.xz tangerine-wallet-browser-7de3f22d63748ed5a81e947755db056d4cdef3db.tar.zst tangerine-wallet-browser-7de3f22d63748ed5a81e947755db056d4cdef3db.zip |
Connects remained of the gas customization component to redux.
22 files changed, 497 insertions, 194 deletions
diff --git a/ui/app/actions.js b/ui/app/actions.js index e3cf57c9e..b747a97fb 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -3,7 +3,6 @@ const pify = require('pify') const getBuyEthUrl = require('../../app/scripts/lib/buy-eth-url') const { getTokenAddressFromTokenObject } = require('./util') const { - calcGasTotal, calcTokenBalance, estimateGas, } = require('./components/send/send.utils') @@ -12,6 +11,7 @@ const { fetchLocale } = require('../i18n-helper') const log = require('loglevel') const { ENVIRONMENT_TYPE_NOTIFICATION } = require('../../app/scripts/lib/enums') const { hasUnconfirmedTransactions } = require('./helpers/confirm-transaction/util') +const gasDuck = require('./ducks/gas.duck') const WebcamUtils = require('../lib/webcam-utils') var actions = { @@ -921,6 +921,7 @@ function setGasTotal (gasTotal) { } function updateGasData ({ + gasPrice, blockGasLimit, recentBlocks, selectedAddress, @@ -931,34 +932,19 @@ function updateGasData ({ }) { return (dispatch) => { dispatch(actions.gasLoadingStarted()) - return new Promise((resolve, reject) => { - background.getGasPrice((err, data) => { - if (err) return reject(err) - return resolve(data) - }) - }) - .then(estimateGasPrice => { - return Promise.all([ - Promise.resolve(estimateGasPrice), - estimateGas({ - estimateGasMethod: background.estimateGas, - blockGasLimit, - selectedAddress, - selectedToken, - to, - value, - estimateGasPrice, - data, - }), - ]) - }) - .then(([gasPrice, gas]) => { - dispatch(actions.setGasPrice(gasPrice)) + return estimateGas({ + estimateGasMethod: background.estimateGas, + blockGasLimit, + selectedAddress, + selectedToken, + to, + value, + estimateGasPrice: gasPrice, + data, + }) + .then(gas => { dispatch(actions.setGasLimit(gas)) - return calcGasTotal(gas, gasPrice) - }) - .then((gasEstimate) => { - dispatch(actions.setGasTotal(gasEstimate)) + dispatch(gasDuck.setCustomGasLimit(gas)) dispatch(updateSendErrors({ gasLoadingError: null })) dispatch(actions.gasLoadingFinished()) }) diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/advanced-tab-content.component.js b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/advanced-tab-content.component.js index 38fa117f9..56d10cc2b 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/advanced-tab-content.component.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/advanced-tab-content.component.js @@ -1,9 +1,5 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' -import { - MIN_GAS_PRICE_DEC, - MIN_GAS_LIMIT_DEC, -} from '../../../send/send.constants' import TimeRemaining from './time-remaining' export default class AdvancedTabContent extends Component { @@ -76,8 +72,8 @@ export default class AdvancedTabContent extends Component { renderGasEditRows (customGasPrice, updateCustomGasPrice, customGasLimit, updateCustomGasLimit) { return ( <div className="advanced-tab__gas-edit-rows"> - { this.renderGasEditRow('gasPriceNoDenom', customGasPrice, updateCustomGasPrice, MIN_GAS_PRICE_DEC, 9, true) } - { this.renderGasEditRow('gasLimit', customGasLimit, updateCustomGasLimit, MIN_GAS_LIMIT_DEC, 0) } + { this.renderGasEditRow('gasPriceNoDenom', customGasPrice, updateCustomGasPrice, customGasPrice, 9, true) } + { this.renderGasEditRow('gasLimit', customGasLimit, updateCustomGasLimit, customGasLimit, 0) } </div> ) } diff --git a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/tests/advanced-tab-content-component.test.js b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/tests/advanced-tab-content-component.test.js index 5cdcf687e..0ef286b8a 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/tests/advanced-tab-content-component.test.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/advanced-tab-content/tests/advanced-tab-content-component.test.js @@ -138,10 +138,10 @@ describe('AdvancedTabContent Component', function () { const renderGasEditRowSpyArgs = AdvancedTabContent.prototype.renderGasEditRow.args assert.equal(renderGasEditRowSpyArgs.length, 2) assert.deepEqual(renderGasEditRowSpyArgs[0].map(String), [ - 'gasPriceNoDenom', 'mockGasPrice', () => 'mockUpdateCustomGasPriceReturn', '0', 9, true, + 'gasPriceNoDenom', 'mockGasPrice', () => 'mockUpdateCustomGasPriceReturn', 'mockGasPrice', 9, true, ].map(String)) assert.deepEqual(renderGasEditRowSpyArgs[1].map(String), [ - 'gasLimit', 'mockGasLimit', () => 'mockUpdateCustomGasLimitReturn', 21000, '0', + 'gasLimit', 'mockGasLimit', () => 'mockUpdateCustomGasLimitReturn', 'mockGasLimit', 0, ].map(String)) }) }) diff --git a/ui/app/components/gas-customization/gas-modal-page-container/basic-tab-content/basic-tab-content.component.js b/ui/app/components/gas-customization/gas-modal-page-container/basic-tab-content/basic-tab-content.component.js index 164da4a32..0e5f15519 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/basic-tab-content/basic-tab-content.component.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/basic-tab-content/basic-tab-content.component.js @@ -17,9 +17,7 @@ export default class BasicTabContent extends Component { <div className="basic-tab-content__title">Suggest gas fee increases</div> <GasPriceButtonGroup className="gas-price-button-group" - noButtonActiveByDefault={true} showCheck={true} - handleGasPriceSelection={(newPrice) => console.log('New Price:', newPrice)} {...this.props.gasPriceButtonGroupProps} /> </div> diff --git a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js index 399520baf..89065472d 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js @@ -17,34 +17,42 @@ export default class GasModalPageContainer extends Component { customGasPrice: PropTypes.number, customGasLimit: PropTypes.number, gasPriceButtonGroupProps: PropTypes.object, + infoRowProps: PropTypes.shape({ + originalTotalFiat: PropTypes.string, + originalTotalEth: PropTypes.string, + newTotalFiat: PropTypes.string, + newTotalEth: PropTypes.string, + }), + onSubmit: PropTypes.func, + customGasPriceInHex: PropTypes.string, + customGasLimitInHex: PropTypes.string, } state = {} - renderBasicTabContent () { + renderBasicTabContent (gasPriceButtonGroupProps) { return ( <BasicTabContent - gasPriceButtonGroupProps={this.props.gasPriceButtonGroupProps} + gasPriceButtonGroupProps={gasPriceButtonGroupProps} /> ) } - renderAdvancedTabContent = () => { - const { - updateCustomGasPrice, - updateCustomGasLimit, - customGasPrice, - customGasLimit, - } = this.props - + renderAdvancedTabContent ({ + convertThenUpdateCustomGasPrice, + convertThenUpdateCustomGasLimit, + customGasPrice, + customGasLimit, + newTotalFiat, + }) { return ( <AdvancedTabContent - updateCustomGasPrice={updateCustomGasPrice} - updateCustomGasLimit={updateCustomGasLimit} + updateCustomGasPrice={convertThenUpdateCustomGasPrice} + updateCustomGasLimit={convertThenUpdateCustomGasLimit} customGasPrice={customGasPrice} customGasLimit={customGasLimit} millisecondsRemaining={91000} - totalFee={'$0.30'} + totalFee={newTotalFiat} /> ) } @@ -64,41 +72,67 @@ export default class GasModalPageContainer extends Component { ) } - renderTabContent (mainTabContent) { + renderInfoRows (originalTotalFiat, originalTotalEth, newTotalFiat, newTotalEth) { return ( - <div className="gas-modal-content"> - { mainTabContent } - { this.renderInfoRow('gas-modal-content__info-row--fade', 'originalTotal', '$20.02 USD', '0.06685 ETH') } - { this.renderInfoRow('gas-modal-content__info-row', 'newTotal', '$20.02 USD', '0.06685 ETH') } + <div> + { this.renderInfoRow('gas-modal-content__info-row--fade', 'originalTotal', originalTotalFiat, originalTotalEth) } + { this.renderInfoRow('gas-modal-content__info-row', 'newTotal', newTotalFiat, newTotalEth) } </div> ) } - renderTabs () { + renderTabs ({ + originalTotalFiat, + originalTotalEth, + newTotalFiat, + newTotalEth, + }, + { + gasPriceButtonGroupProps, + ...advancedTabProps + }) { return ( <Tabs> <Tab name={this.context.t('basic')}> - { this.renderTabContent(this.renderBasicTabContent()) } + <div className="gas-modal-content"> + { this.renderBasicTabContent(gasPriceButtonGroupProps) } + { this.renderInfoRows(originalTotalFiat, originalTotalEth, newTotalFiat, newTotalEth) } + </div> </Tab> <Tab name={this.context.t('advanced')}> - { this.renderTabContent(this.renderAdvancedTabContent()) } + <div className="gas-modal-content"> + { this.renderAdvancedTabContent(advancedTabProps) } + { this.renderInfoRows(originalTotalFiat, originalTotalEth, newTotalFiat, newTotalEth) } + </div> </Tab> </Tabs> ) } render () { - const { hideModal } = this.props + const { + hideModal, + infoRowProps, + onSubmit, + customGasPriceInHex, + customGasLimitInHex, + ...tabProps + } = this.props return ( <div className="gas-modal-page-container"> <PageContainer title={this.context.t('customGas')} subtitle={this.context.t('customGasSubTitle')} - tabsComponent={this.renderTabs()} + tabsComponent={this.renderTabs(infoRowProps, tabProps)} disabled={false} onCancel={() => hideModal()} onClose={() => hideModal()} + onSubmit={() => { + onSubmit(customGasLimitInHex, customGasPriceInHex) + hideModal() + }} + submitText={this.context.t('save')} /> </div> ) diff --git a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js index ebdd035ea..7e2a7e144 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js @@ -1,36 +1,161 @@ import { connect } from 'react-redux' +import { pipe, partialRight } from 'ramda' import GasModalPageContainer from './gas-modal-page-container.component' -import { hideModal } from '../../../actions' +import { + hideModal, + setGasLimit, + setGasPrice, +} from '../../../actions' import { setCustomGasPrice, setCustomGasLimit, } from '../../../ducks/gas.duck' import { + updateGasAndCalculate, +} from '../../../ducks/confirm-transaction.duck' +import { + getCurrentCurrency, + conversionRateSelector as getConversionRate, + getSelectedToken, +} from '../../../selectors.js' +import { getCustomGasPrice, getCustomGasLimit, getRenderableBasicEstimateData, getBasicGasEstimateLoadingStatus, + getAveragePriceEstimateInHexWEI, + getDefaultActiveButtonIndex, } from '../../../selectors/custom-gas' +import { + formatCurrency, +} from '../../../helpers/confirm-transaction/util' +import { + addHexWEIsToDec, + decEthToConvertedCurrency as ethTotalToConvertedCurrency, + decGWEIToHexWEI, + hexWEIToDecGWEI, +} from '../../../helpers/conversions.util' +import { + formatETHFee, +} from '../../../helpers/formatters' +import { + calcGasTotal, +} from '../../send/send.utils' +import { addHexPrefix } from 'ethereumjs-util' const mapStateToProps = state => { const buttonDataLoading = getBasicGasEstimateLoadingStatus(state) + const { gasPrice, gas: gasLimit, value } = getTxParams(state) + const gasTotal = calcGasTotal(gasLimit, gasPrice) + + const customGasPriceInHex = getCustomGasPrice(state) + const customGasLimitInHex = getCustomGasLimit(state) + const customGasTotal = calcGasTotal(customGasLimitInHex || gasLimit, customGasPriceInHex || gasPrice) + + const gasButtonInfo = getRenderableBasicEstimateData(state) + + const currentCurrency = getCurrentCurrency(state) + const conversionRate = getConversionRate(state) + + const newTotalFiat = addHexWEIsToRenderableFiat(value, customGasTotal, currentCurrency, conversionRate) + return { - customGasPrice: getCustomGasPrice(state), - customGasLimit: getCustomGasLimit(state), + isConfirm: isConfirm(state), + customGasPriceInHex, + customGasLimitInHex, + customGasPrice: calcCustomGasPrice(customGasPriceInHex, gasPrice), + customGasLimit: calcCustomGasLimit(customGasLimitInHex, gasLimit), + newTotalFiat, gasPriceButtonGroupProps: { buttonDataLoading, - gasButtonInfo: getRenderableBasicEstimateData(state), + defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customGasPriceInHex, gasPrice), + gasButtonInfo, + }, + infoRowProps: { + originalTotalFiat: addHexWEIsToRenderableFiat(value, gasTotal, currentCurrency, conversionRate), + originalTotalEth: addHexWEIsToRenderableEth(value, gasTotal), + newTotalFiat, + newTotalEth: addHexWEIsToRenderableEth(value, customGasTotal), }, } } const mapDispatchToProps = dispatch => { + const updateCustomGasPrice = newPrice => dispatch(setCustomGasPrice(addHexPrefix(newPrice))) + return { hideModal: () => dispatch(hideModal()), - updateCustomGasPrice: (newPrice) => dispatch(setCustomGasPrice(newPrice)), - updateCustomGasLimit: (newLimit) => dispatch(setCustomGasLimit(newLimit)), - handleGasPriceSelection: newPrice => console.log('NewPrice: ', newPrice), + updateCustomGasPrice, + convertThenUpdateCustomGasPrice: newPrice => updateCustomGasPrice(decGWEIToHexWEI(newPrice)), + convertThenUpdateCustomGasLimit: newLimit => dispatch(setCustomGasLimit(addHexPrefix(newLimit.toString(16)))), + setGasData: (newLimit, newPrice) => { + dispatch(setGasLimit(newLimit)) + dispatch(setGasPrice(newPrice)) + }, + updateConfirmTxGasAndCalculate: (gasLimit, gasPrice) => { + return dispatch(updateGasAndCalculate({ gasLimit, gasPrice })) + }, } } -export default connect(mapStateToProps, mapDispatchToProps)(GasModalPageContainer) +const mergeProps = (stateProps, dispatchProps, ownProps) => { + const { gasPriceButtonGroupProps, isConfirm } = stateProps + const { + updateCustomGasPrice: dispatchUpdateCustomGasPrice, + setGasData: dispatchSetGasData, + updateConfirmTxGasAndCalculate: dispatchUpdateConfirmTxGasAndCalculate, + ...otherDispatchProps + } = dispatchProps + + return { + ...stateProps, + ...otherDispatchProps, + ...ownProps, + onSubmit: isConfirm ? dispatchUpdateConfirmTxGasAndCalculate : dispatchSetGasData, + gasPriceButtonGroupProps: { + ...gasPriceButtonGroupProps, + handleGasPriceSelection: dispatchUpdateCustomGasPrice, + }, + } +} + +export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(GasModalPageContainer) + +function isConfirm (state) { + return Boolean(Object.keys(state.confirmTransaction.txData).length) +} + +function calcCustomGasPrice (customGasPriceInHex, gasPrice) { + return Number(hexWEIToDecGWEI(customGasPriceInHex || gasPrice)) +} + +function calcCustomGasLimit (customGasLimitInHex, gasLimit) { + return parseInt(customGasLimitInHex || gasLimit, 16) +} + +function getTxParams (state) { + const { confirmTransaction: { txData }, metamask: { send } } = state + + return txData.txParams || { + from: send.from, + gas: send.gasLimit, + gasPrice: send.gasPrice || getAveragePriceEstimateInHexWEI(state), + to: send.to, + value: getSelectedToken(state) ? '0x0' : send.amount, + } +} + +function addHexWEIsToRenderableEth (aHexWEI, bHexWEI) { + return pipe( + addHexWEIsToDec, + formatETHFee + )(aHexWEI, bHexWEI) +} + +function addHexWEIsToRenderableFiat (aHexWEI, bHexWEI, convertedCurrency, conversionRate) { + return pipe( + addHexWEIsToDec, + partialRight(ethTotalToConvertedCurrency, [convertedCurrency, conversionRate]), + partialRight(formatCurrency, [convertedCurrency]), + )(aHexWEI, bHexWEI) +} diff --git a/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-component.test.js b/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-component.test.js index 86286b615..c41adca83 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-component.test.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-component.test.js @@ -5,13 +5,12 @@ import sinon from 'sinon' import GasModalPageContainer from '../gas-modal-page-container.component.js' import PageContainer from '../../../page-container' -import BasicTabContent from '../basic-tab-content' -import AdvancedTabContent from '../advanced-tab-content' import { Tab } from '../../../tabs' const propsMethodSpies = { hideModal: sinon.spy(), + onSubmit: sinon.spy(), } const mockGasPriceButtonGroupProps = { @@ -41,18 +40,29 @@ const mockGasPriceButtonGroupProps = { noButtonActiveByDefault: true, showCheck: true, } +const mockInfoRowProps = { + originalTotalFiat: 'mockOriginalTotalFiat', + originalTotalEth: 'mockOriginalTotalEth', + newTotalFiat: 'mockNewTotalFiat', + newTotalEth: 'mockNewTotalEth', +} +const GP = GasModalPageContainer.prototype describe('GasModalPageContainer Component', function () { let wrapper beforeEach(() => { wrapper = shallow(<GasModalPageContainer hideModal={propsMethodSpies.hideModal} + onSubmit={propsMethodSpies.onSubmit} updateCustomGasPrice={() => 'mockupdateCustomGasPrice'} updateCustomGasLimit={() => 'mockupdateCustomGasLimit'} customGasPrice={21} customGasLimit={54321} gasPriceButtonGroupProps={mockGasPriceButtonGroupProps} + infoRowProps={mockInfoRowProps} + customGasPriceInHex={'mockCustomGasPriceInHex'} + customGasLimitInHex={'mockCustomGasLimitInHex'} />, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } }) }) @@ -89,7 +99,7 @@ describe('GasModalPageContainer Component', function () { }) it('should pass the correct renderTabs property to PageContainer', () => { - sinon.stub(GasModalPageContainer.prototype, 'renderTabs').returns('mockTabs') + sinon.stub(GP, 'renderTabs').returns('mockTabs') const renderTabsWrapperTester = shallow(<GasModalPageContainer />, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } }) const { tabsComponent } = renderTabsWrapperTester.find(PageContainer).props() assert.equal(tabsComponent, 'mockTabs') @@ -98,8 +108,23 @@ describe('GasModalPageContainer Component', function () { }) describe('renderTabs', () => { + beforeEach(() => { + sinon.spy(GP, 'renderBasicTabContent') + sinon.spy(GP, 'renderAdvancedTabContent') + sinon.spy(GP, 'renderInfoRows') + }) + + afterEach(() => { + GP.renderBasicTabContent.restore() + GP.renderAdvancedTabContent.restore() + GP.renderInfoRows.restore() + }) + it('should render a Tabs component with "Basic" and "Advanced" tabs', () => { - const renderTabsResult = wrapper.instance().renderTabs() + const renderTabsResult = wrapper.instance().renderTabs(mockInfoRowProps, { + gasPriceButtonGroupProps: mockGasPriceButtonGroupProps, + otherProps: 'mockAdvancedTabProps', + }) const renderedTabs = shallow(renderTabsResult) assert.equal(renderedTabs.props().className, 'tabs') @@ -113,45 +138,28 @@ describe('GasModalPageContainer Component', function () { assert.equal(tabs.at(1).childAt(0).props().className, 'gas-modal-content') }) - it('should render the expected children of each tab', () => { - const GP = GasModalPageContainer.prototype - sinon.spy(GP, 'renderTabContent') - assert.equal(GP.renderTabContent.callCount, 0) + it('should call renderBasicTabContent and renderAdvancedTabContent with the expected props', () => { + assert.equal(GP.renderBasicTabContent.callCount, 0) + assert.equal(GP.renderAdvancedTabContent.callCount, 0) - wrapper.instance().renderTabs() + wrapper.instance().renderTabs(mockInfoRowProps, { gasPriceButtonGroupProps: mockGasPriceButtonGroupProps, otherProps: 'mockAdvancedTabProps' }) - assert.equal(GP.renderTabContent.callCount, 2) + assert.equal(GP.renderBasicTabContent.callCount, 1) + assert.equal(GP.renderAdvancedTabContent.callCount, 1) - assert.equal(GP.renderTabContent.firstCall.args.type, BasicTabContent.type) - assert.equal(GP.renderTabContent.secondCall.args.type, AdvancedTabContent.type) - - GP.renderTabContent.restore() + assert.deepEqual(GP.renderBasicTabContent.getCall(0).args[0], mockGasPriceButtonGroupProps) + assert.deepEqual(GP.renderAdvancedTabContent.getCall(0).args[0], { otherProps: 'mockAdvancedTabProps' }) }) - }) - describe('renderTabContent', () => { - it('should render a root gas-modal-content div', () => { - const renderTabContentResult = wrapper.instance().renderTabContent(() => {}) - const renderedTabContent = shallow(renderTabContentResult) - assert.equal(renderedTabContent.props().className, 'gas-modal-content') - }) - - it('should render the passed element as its first child', () => { - const renderTabContentResult = wrapper.instance().renderTabContent(<span>Mock content</span>) - const renderedTabContent = shallow(renderTabContentResult) - assert(renderedTabContent.childAt(0).equals(<span>Mock content</span>)) - }) + it('should call renderInfoRows with the expected props', () => { + assert.equal(GP.renderInfoRows.callCount, 0) - it('should render the element results of renderInfoRow calls as second and third childs', () => { - const GP = GasModalPageContainer.prototype - sinon.stub(GP, 'renderInfoRow').callsFake((...args) => args.join(',')) + wrapper.instance().renderTabs(mockInfoRowProps, { gasPriceButtonGroupProps: mockGasPriceButtonGroupProps, otherProps: 'mockAdvancedTabProps' }) - const renderTabContentResult = wrapper.instance().renderTabContent(() => <span>Mock content</span>) - const renderedTabContent = shallow(renderTabContentResult) - assert.equal(renderedTabContent.childAt(1).text(), 'gas-modal-content__info-row--fade,originalTotal,$20.02 USD,0.06685 ETH') - assert.equal(renderedTabContent.childAt(2).text(), 'gas-modal-content__info-row,newTotal,$20.02 USD,0.06685 ETH') + assert.equal(GP.renderInfoRows.callCount, 2) - GP.renderInfoRow.restore() + assert.deepEqual(GP.renderInfoRows.getCall(0).args, ['mockOriginalTotalFiat', 'mockOriginalTotalEth', 'mockNewTotalFiat', 'mockNewTotalEth']) + assert.deepEqual(GP.renderInfoRows.getCall(1).args, ['mockOriginalTotalFiat', 'mockOriginalTotalEth', 'mockNewTotalFiat', 'mockNewTotalEth']) }) }) @@ -176,7 +184,7 @@ describe('GasModalPageContainer Component', function () { describe('renderBasicTabContent', () => { it('should render', () => { - const renderBasicTabContentResult = wrapper.instance().renderBasicTabContent() + const renderBasicTabContentResult = wrapper.instance().renderBasicTabContent(mockGasPriceButtonGroupProps) assert.deepEqual( renderBasicTabContentResult.props.gasPriceButtonGroupProps, @@ -187,15 +195,20 @@ describe('GasModalPageContainer Component', function () { describe('renderAdvancedTabContent', () => { it('should render with the correct props', () => { - const renderAdvancedTabContentResult = wrapper.instance().renderAdvancedTabContent() + const renderAdvancedTabContentResult = wrapper.instance().renderAdvancedTabContent({ + convertThenUpdateCustomGasPrice: () => 'mockConvertThenUpdateCustomGasPrice', + convertThenUpdateCustomGasLimit: () => 'mockConvertThenUpdateCustomGasLimit', + customGasPrice: 123, + customGasLimit: 456, + newTotalFiat: '$0.30', + }) const advancedTabContentProps = renderAdvancedTabContentResult.props - assert.equal(advancedTabContentProps.updateCustomGasPrice(), 'mockupdateCustomGasPrice') - assert.equal(advancedTabContentProps.updateCustomGasLimit(), 'mockupdateCustomGasLimit') - assert.equal(advancedTabContentProps.customGasPrice, 21) - assert.equal(advancedTabContentProps.customGasLimit, 54321) + assert.equal(advancedTabContentProps.updateCustomGasPrice(), 'mockConvertThenUpdateCustomGasPrice') + assert.equal(advancedTabContentProps.updateCustomGasLimit(), 'mockConvertThenUpdateCustomGasLimit') + assert.equal(advancedTabContentProps.customGasPrice, 123) + assert.equal(advancedTabContentProps.customGasLimit, 456) assert.equal(advancedTabContentProps.millisecondsRemaining, 91000) assert.equal(advancedTabContentProps.totalFee, '$0.30') - assert(shallow(renderAdvancedTabContentResult).hasClass('advanced-tab')) }) }) }) diff --git a/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js b/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js index 4067265b5..e01fd3898 100644 --- a/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js +++ b/ui/app/components/gas-customization/gas-modal-page-container/tests/gas-modal-page-container-container.test.js @@ -7,13 +7,19 @@ let mapDispatchToProps const actionSpies = { hideModal: sinon.spy(), + setGasLimit: sinon.spy(), + setGasPrice: sinon.spy(), } -const customGasActionSpies = { +const gasActionSpies = { setCustomGasPrice: sinon.spy(), setCustomGasLimit: sinon.spy(), } +const confirmTransactionActionSpies = { + updateGasAndCalculate: sinon.spy(), +} + proxyquire('../gas-modal-page-container.container.js', { 'react-redux': { connect: (ms, md) => { @@ -23,13 +29,13 @@ proxyquire('../gas-modal-page-container.container.js', { }, }, '../../../selectors/custom-gas': { - getCustomGasPrice: (s) => `mockGasPrice:${s}`, - getCustomGasLimit: (s) => `mockGasLimit:${s}`, - getBasicGasEstimateLoadingStatus: (s) => `mockBasicGasEstimateLoadingStatus:${s}`, - getRenderableBasicEstimateData: (s) => `mockRenderableBasicEstimateData:${s}`, + getBasicGasEstimateLoadingStatus: (s) => `mockBasicGasEstimateLoadingStatus:${Object.keys(s).length}`, + getRenderableBasicEstimateData: (s) => `mockRenderableBasicEstimateData:${Object.keys(s).length}`, + getDefaultActiveButtonIndex: (a, b, c) => a + b + c, }, '../../../actions': actionSpies, - '../../../ducks/gas.duck': customGasActionSpies, + '../../../ducks/gas.duck': gasActionSpies, + '../../../ducks/confirm-transaction.duck': confirmTransactionActionSpies, }) describe('gas-modal-page-container container', () => { @@ -37,10 +43,54 @@ describe('gas-modal-page-container container', () => { describe('mapStateToProps()', () => { it('should map the correct properties to props', () => { - assert.equal(mapStateToProps('mockState').customGasPrice, 'mockGasPrice:mockState') - assert.equal(mapStateToProps('mockState').customGasLimit, 'mockGasLimit:mockState') - assert.equal(mapStateToProps('mockState').gasPriceButtonGroupProps.buttonDataLoading, 'mockBasicGasEstimateLoadingStatus:mockState') - assert.equal(mapStateToProps('mockState').gasPriceButtonGroupProps.gasButtonInfo, 'mockRenderableBasicEstimateData:mockState') + const mockState2 = { + metamask: { + send: { + gasLimit: '16', + gasPrice: '32', + amount: '64', + }, + currentCurrency: 'abc', + conversionRate: 50, + }, + gas: { + customData: { + limit: 'aaaaaaaa', + price: 'ffffffff', + }, + }, + confirmTransaction: { + txData: { + txParams: { + gas: '0x1600000', + gasPrice: '0x3200000', + value: '0x640000000000000', + }, + }, + }, + } + const result2 = mapStateToProps(mockState2) + + assert.deepEqual(result2, { + isConfirm: true, + customGasPriceInHex: 'ffffffff', + customGasLimitInHex: 'aaaaaaaa', + customGasPrice: 4.294967295, + customGasLimit: 2863311530, + newTotalFiat: '637.41', + gasPriceButtonGroupProps: + { + buttonDataLoading: 'mockBasicGasEstimateLoadingStatus:3', + defaultActiveButtonIndex: 'mockRenderableBasicEstimateData:3ffffffff0x3200000', + gasButtonInfo: 'mockRenderableBasicEstimateData:3', + }, + infoRowProps: { + originalTotalFiat: '22.58', + originalTotalEth: '0.451569 ETH', + newTotalFiat: '637.41', + newTotalEth: '12.748189 ETH', + }, + }) }) }) @@ -54,6 +104,12 @@ describe('gas-modal-page-container container', () => { mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy) }) + afterEach(() => { + actionSpies.hideModal.resetHistory() + gasActionSpies.setCustomGasPrice.resetHistory() + gasActionSpies.setCustomGasLimit.resetHistory() + }) + describe('hideModal()', () => { it('should dispatch a hideModal action', () => { mapDispatchToPropsObject.hideModal() @@ -63,18 +119,50 @@ describe('gas-modal-page-container container', () => { }) describe('updateCustomGasPrice()', () => { - it('should dispatch a setCustomGasPrice action', () => { - mapDispatchToPropsObject.updateCustomGasPrice() + it('should dispatch a setCustomGasPrice action with the arg passed to updateCustomGasPrice hex prefixed', () => { + mapDispatchToPropsObject.updateCustomGasPrice('ffff') + assert(dispatchSpy.calledOnce) + assert(gasActionSpies.setCustomGasPrice.calledOnce) + assert.equal(gasActionSpies.setCustomGasPrice.getCall(0).args[0], '0xffff') + }) + }) + + describe('convertThenUpdateCustomGasPrice()', () => { + it('should dispatch a setCustomGasPrice action with the arg passed to convertThenUpdateCustomGasPrice converted to WEI', () => { + mapDispatchToPropsObject.convertThenUpdateCustomGasPrice('0xffff') assert(dispatchSpy.calledOnce) - assert(customGasActionSpies.setCustomGasPrice.calledOnce) + assert(gasActionSpies.setCustomGasPrice.calledOnce) + assert.equal(gasActionSpies.setCustomGasPrice.getCall(0).args[0], '0x3b9a8e653600') + }) + }) + + + describe('convertThenUpdateCustomGasLimit()', () => { + it('should dispatch a setCustomGasLimit action with the arg passed to convertThenUpdateCustomGasLimit converted to hex', () => { + mapDispatchToPropsObject.convertThenUpdateCustomGasLimit(16) + assert(dispatchSpy.calledOnce) + assert(gasActionSpies.setCustomGasLimit.calledOnce) + assert.equal(gasActionSpies.setCustomGasLimit.getCall(0).args[0], '0x10') + }) + }) + + describe('setGasData()', () => { + it('should dispatch a setGasPrice and setGasLimit action with the correct props', () => { + mapDispatchToPropsObject.setGasData('ffff', 'aaaa') + assert(dispatchSpy.calledTwice) + assert(actionSpies.setGasPrice.calledOnce) + assert(actionSpies.setGasLimit.calledOnce) + assert.equal(actionSpies.setGasLimit.getCall(0).args[0], 'ffff') + assert.equal(actionSpies.setGasPrice.getCall(0).args[0], 'aaaa') }) }) - describe('updateCustomGasLimit()', () => { - it('should dispatch a setCustomGasLimit action', () => { - mapDispatchToPropsObject.updateCustomGasLimit() + describe('updateConfirmTxGasAndCalculate()', () => { + it('should dispatch a updateGasAndCalculate action with the correct props', () => { + mapDispatchToPropsObject.updateConfirmTxGasAndCalculate('ffff', 'aaaa') assert(dispatchSpy.calledOnce) - assert(customGasActionSpies.setCustomGasLimit.calledOnce) + assert(confirmTransactionActionSpies.updateGasAndCalculate.calledOnce) + assert.deepEqual(confirmTransactionActionSpies.updateGasAndCalculate.getCall(0).args[0], { gasLimit: 'ffff', gasPrice: 'aaaa' }) }) }) diff --git a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js index a3ec82863..1e2270432 100644 --- a/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js +++ b/ui/app/components/pages/confirm-transaction-base/confirm-transaction-base.container.js @@ -29,7 +29,7 @@ const casedContractMap = Object.keys(contractMap).reduce((acc, base) => { const mapStateToProps = (state, props) => { const { toAddress: propsToAddress } = props - const { confirmTransaction, metamask } = state + const { confirmTransaction, metamask, gas } = state const { ethTransactionAmount, ethTransactionFee, @@ -60,6 +60,12 @@ const mapStateToProps = (state, props) => { unapprovedTxs, } = metamask const assetImage = assetImages[txParamsToAddress] + + const { + customGasLimit, + customGasPrice, + } = gas + const { balance } = accounts[selectedAddress] const { name: fromName } = identities[selectedAddress] const toAddress = propsToAddress || txParamsToAddress @@ -106,6 +112,10 @@ const mapStateToProps = (state, props) => { unapprovedTxs, unapprovedTxCount, currentNetworkUnapprovedTxs, + customGas: { + gasLimit: customGasLimit || txData.gasPrice, + gasPrice: customGasPrice || txData.gasLimit, + }, } } @@ -192,7 +202,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { ...ownProps, showCustomizeGasModal: () => dispatchShowCustomizeGasModal({ txData, - onSubmit: txData => dispatchUpdateGasAndCalculate(txData), + onSubmit: customGas => dispatchUpdateGasAndCalculate(customGas), validate: validateEditGas, }), cancelAllTransactions: () => dispatchCancelAllTransactions(valuesFor(unapprovedTxs)), diff --git a/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js b/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js index 3ac656d73..614b3e584 100644 --- a/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js +++ b/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js @@ -32,6 +32,7 @@ export default class ConfirmTransaction extends Component { setTransactionToConfirm: PropTypes.func, confirmTransaction: PropTypes.object, clearConfirmTransaction: PropTypes.func, + fetchGasEstimates: PropTypes.func, } getParamsTransactionId () { @@ -45,6 +46,7 @@ export default class ConfirmTransaction extends Component { send = {}, history, confirmTransaction: { txData: { id: transactionId } = {} }, + fetchGasEstimates, } = this.props if (!totalUnapprovedCount && !send.to) { @@ -53,6 +55,7 @@ export default class ConfirmTransaction extends Component { } if (!transactionId) { + fetchGasEstimates() this.setTransactionToConfirm() } } diff --git a/ui/app/components/pages/confirm-transaction/confirm-transaction.container.js b/ui/app/components/pages/confirm-transaction/confirm-transaction.container.js index 1bc2f1efb..cb1a737f9 100644 --- a/ui/app/components/pages/confirm-transaction/confirm-transaction.container.js +++ b/ui/app/components/pages/confirm-transaction/confirm-transaction.container.js @@ -5,6 +5,9 @@ import { setTransactionToConfirm, clearConfirmTransaction, } from '../../../ducks/confirm-transaction.duck' +import { + fetchGasEstimates, +} from '../../../ducks/gas.duck' import ConfirmTransaction from './confirm-transaction.component' import { getTotalUnapprovedCount } from '../../../selectors' import { unconfirmedTransactionsListSelector } from '../../../selectors/confirm-transaction' @@ -24,6 +27,7 @@ const mapDispatchToProps = dispatch => { return { setTransactionToConfirm: transactionId => dispatch(setTransactionToConfirm(transactionId)), clearConfirmTransaction: () => dispatch(clearConfirmTransaction()), + fetchGasEstimates: () => dispatch(fetchGasEstimates()), } } diff --git a/ui/app/components/send/send.component.js b/ui/app/components/send/send.component.js index a639c24b0..5c12826ea 100644 --- a/ui/app/components/send/send.component.js +++ b/ui/app/components/send/send.component.js @@ -73,10 +73,10 @@ export default class SendTransactionScreen extends PersistentForm { selectedAddress, selectedToken = {}, to: currentToAddress, - updateAndSetGasTotal, + updateAndSetGasLimit, } = this.props - updateAndSetGasTotal({ + updateAndSetGasLimit({ blockGasLimit, editingTransactionId, gasLimit, @@ -164,6 +164,9 @@ export default class SendTransactionScreen extends PersistentForm { componentDidMount () { this.props.fetchGasEstimates() + .then(() => { + this.updateGas() + }) } componentWillMount () { @@ -173,12 +176,12 @@ export default class SendTransactionScreen extends PersistentForm { tokenContract, updateSendTokenBalance, } = this.props + updateSendTokenBalance({ selectedToken, tokenContract, address, }) - this.updateGas() // Show QR Scanner modal if ?scan=true if (window.location.search === '?scan=true') { diff --git a/ui/app/components/send/send.container.js b/ui/app/components/send/send.container.js index a00fb3545..f240774d4 100644 --- a/ui/app/components/send/send.container.js +++ b/ui/app/components/send/send.container.js @@ -79,7 +79,7 @@ function mapStateToProps (state) { function mapDispatchToProps (dispatch) { return { - updateAndSetGasTotal: ({ + updateAndSetGasLimit: ({ blockGasLimit, editingTransactionId, gasLimit, @@ -92,7 +92,7 @@ function mapDispatchToProps (dispatch) { data, }) => { !editingTransactionId - ? dispatch(updateGasData({ recentBlocks, selectedAddress, selectedToken, blockGasLimit, to, value, data })) + ? dispatch(updateGasData({ gasPrice, recentBlocks, selectedAddress, selectedToken, blockGasLimit, to, value, data })) : dispatch(setGasTotal(calcGasTotal(gasLimit, gasPrice))) }, updateSendTokenBalance: ({ selectedToken, tokenContract, address }) => { diff --git a/ui/app/components/send/send.selectors.js b/ui/app/components/send/send.selectors.js index c217d848e..71eb93d61 100644 --- a/ui/app/components/send/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -8,7 +8,11 @@ const { } = require('../../selectors') const { estimateGasPriceFromRecentBlocks, + calcGasTotal, } = require('./send.utils') +import { + getAveragePriceEstimateInHexWEI, +} from '../../selectors/custom-gas' const selectors = { accountsWithSendEtherInfoSelector, @@ -131,11 +135,11 @@ function getForceGasMin (state) { } function getGasLimit (state) { - return state.metamask.send.gasLimit + return state.metamask.send.gasLimit || '0' } function getGasPrice (state) { - return state.metamask.send.gasPrice + return state.metamask.send.gasPrice || getAveragePriceEstimateInHexWEI(state) } function getGasPriceFromRecentBlocks (state) { @@ -143,7 +147,7 @@ function getGasPriceFromRecentBlocks (state) { } function getGasTotal (state) { - return state.metamask.send.gasTotal + return calcGasTotal(getGasLimit(state), getGasPrice(state)) } function getPrimaryCurrency (state) { diff --git a/ui/app/components/send/tests/send-component.test.js b/ui/app/components/send/tests/send-component.test.js index 0f3902c07..82e84de30 100644 --- a/ui/app/components/send/tests/send-component.test.js +++ b/ui/app/components/send/tests/send-component.test.js @@ -9,11 +9,11 @@ import SendContent from '../send-content/send-content.component' import SendFooter from '../send-footer/send-footer.container' const propsMethodSpies = { - updateAndSetGasTotal: sinon.spy(), + updateAndSetGasLimit: sinon.spy(), updateSendErrors: sinon.spy(), updateSendTokenBalance: sinon.spy(), resetSendState: sinon.spy(), - fetchGasEstimates: sinon.spy(), + fetchGasEstimates: sinon.stub().returns(Promise.resolve()), } const utilsMethodStubs = { getAmountErrorObject: sinon.stub().returns({ amount: 'mockAmountError' }), @@ -52,7 +52,7 @@ describe('Send Component', function () { showHexData={true} tokenBalance={'mockTokenBalance'} tokenContract={'mockTokenContract'} - updateAndSetGasTotal={propsMethodSpies.updateAndSetGasTotal} + updateAndSetGasLimit={propsMethodSpies.updateAndSetGasLimit} updateSendErrors={propsMethodSpies.updateSendErrors} updateSendTokenBalance={propsMethodSpies.updateSendTokenBalance} resetSendState={propsMethodSpies.resetSendState} @@ -66,7 +66,7 @@ describe('Send Component', function () { utilsMethodStubs.getAmountErrorObject.resetHistory() utilsMethodStubs.getGasFeeErrorObject.resetHistory() propsMethodSpies.fetchGasEstimates.resetHistory() - propsMethodSpies.updateAndSetGasTotal.resetHistory() + propsMethodSpies.updateAndSetGasLimit.resetHistory() propsMethodSpies.updateSendErrors.resetHistory() propsMethodSpies.updateSendTokenBalance.resetHistory() }) @@ -75,16 +75,6 @@ describe('Send Component', function () { assert(SendTransactionScreen.prototype.componentDidMount.calledOnce) }) - describe('componentWillMount', () => { - it('should call this.updateGas', () => { - SendTransactionScreen.prototype.updateGas.resetHistory() - propsMethodSpies.updateSendErrors.resetHistory() - assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0) - wrapper.instance().componentWillMount() - assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 1) - }) - }) - describe('componentDidMount', () => { it('should call props.fetchGasEstimates', () => { propsMethodSpies.fetchGasEstimates.resetHistory() @@ -92,6 +82,14 @@ describe('Send Component', function () { wrapper.instance().componentDidMount() assert.equal(propsMethodSpies.fetchGasEstimates.callCount, 1) }) + + it('should call this.updateGas', () => { + SendTransactionScreen.prototype.updateGas.resetHistory() + propsMethodSpies.updateSendErrors.resetHistory() + assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 0) + wrapper.instance().componentDidMount() + setTimeout(() => assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 1), 250) + }) }) describe('componentWillUnmount', () => { @@ -283,12 +281,12 @@ describe('Send Component', function () { }) describe('updateGas', () => { - it('should call updateAndSetGasTotal with the correct params if no to prop is passed', () => { - propsMethodSpies.updateAndSetGasTotal.resetHistory() + it('should call updateAndSetGasLimit with the correct params if no to prop is passed', () => { + propsMethodSpies.updateAndSetGasLimit.resetHistory() wrapper.instance().updateGas() - assert.equal(propsMethodSpies.updateAndSetGasTotal.callCount, 1) + assert.equal(propsMethodSpies.updateAndSetGasLimit.callCount, 1) assert.deepEqual( - propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0], + propsMethodSpies.updateAndSetGasLimit.getCall(0).args[0], { blockGasLimit: 'mockBlockGasLimit', editingTransactionId: 'mockEditingTransactionId', @@ -304,20 +302,20 @@ describe('Send Component', function () { ) }) - it('should call updateAndSetGasTotal with the correct params if a to prop is passed', () => { - propsMethodSpies.updateAndSetGasTotal.resetHistory() + it('should call updateAndSetGasLimit with the correct params if a to prop is passed', () => { + propsMethodSpies.updateAndSetGasLimit.resetHistory() wrapper.setProps({ to: 'someAddress' }) wrapper.instance().updateGas() assert.equal( - propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0].to, + propsMethodSpies.updateAndSetGasLimit.getCall(0).args[0].to, 'someaddress', ) }) - it('should call updateAndSetGasTotal with to set to lowercase if passed', () => { - propsMethodSpies.updateAndSetGasTotal.resetHistory() + it('should call updateAndSetGasLimit with to set to lowercase if passed', () => { + propsMethodSpies.updateAndSetGasLimit.resetHistory() wrapper.instance().updateGas({ to: '0xABC' }) - assert.equal(propsMethodSpies.updateAndSetGasTotal.getCall(0).args[0].to, '0xabc') + assert.equal(propsMethodSpies.updateAndSetGasLimit.getCall(0).args[0].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 6aa4bf826..d8fe85d22 100644 --- a/ui/app/components/send/tests/send-container.test.js +++ b/ui/app/components/send/tests/send-container.test.js @@ -94,7 +94,7 @@ describe('send container', () => { mapDispatchToPropsObject = mapDispatchToProps(dispatchSpy) }) - describe('updateAndSetGasTotal()', () => { + describe('updateAndSetGasLimit()', () => { const mockProps = { blockGasLimit: 'mockBlockGasLimit', editingTransactionId: '0x2', @@ -109,7 +109,7 @@ describe('send container', () => { } it('should dispatch a setGasTotal action when editingTransactionId is truthy', () => { - mapDispatchToPropsObject.updateAndSetGasTotal(mockProps) + mapDispatchToPropsObject.updateAndSetGasLimit(mockProps) assert(dispatchSpy.calledOnce) assert.equal( actionSpies.setGasTotal.getCall(0).args[0], @@ -118,14 +118,14 @@ describe('send container', () => { }) it('should dispatch an updateGasData action when editingTransactionId is falsy', () => { - const { selectedAddress, selectedToken, recentBlocks, blockGasLimit, to, value, data } = mockProps - mapDispatchToPropsObject.updateAndSetGasTotal( + const { gasPrice, selectedAddress, selectedToken, recentBlocks, blockGasLimit, to, value, data } = mockProps + mapDispatchToPropsObject.updateAndSetGasLimit( Object.assign({}, mockProps, {editingTransactionId: false}) ) assert(dispatchSpy.calledOnce) assert.deepEqual( actionSpies.updateGasData.getCall(0).args[0], - { selectedAddress, selectedToken, recentBlocks, blockGasLimit, to, value, data } + { gasPrice, selectedAddress, selectedToken, recentBlocks, blockGasLimit, to, value, } ) }) }) diff --git a/ui/app/components/send/tests/send-selectors.test.js b/ui/app/components/send/tests/send-selectors.test.js index e7e901f0d..cdc86fe59 100644 --- a/ui/app/components/send/tests/send-selectors.test.js +++ b/ui/app/components/send/tests/send-selectors.test.js @@ -237,7 +237,7 @@ describe('send selectors', () => { it('should return the send.gasTotal', () => { assert.equal( getGasTotal(mockState), - '0xb451dc41b578' + 'a9ff56' ) }) }) diff --git a/ui/app/ducks/gas.duck.js b/ui/app/ducks/gas.duck.js index 8b2fbcfdb..7f18b2272 100644 --- a/ui/app/ducks/gas.duck.js +++ b/ui/app/ducks/gas.duck.js @@ -13,8 +13,8 @@ const SET_CUSTOM_GAS_TOTAL = 'metamask/gas/SET_CUSTOM_GAS_TOTAL' // TODO: determine if this approach to initState is consistent with conventional ducks pattern const initState = { customData: { - price: 0, - limit: 21000, + price: null, + limit: '0x5208', }, basicEstimates: { average: null, diff --git a/ui/app/ducks/tests/gas-duck.test.js b/ui/app/ducks/tests/gas-duck.test.js index 7fd74f815..66cf376a7 100644 --- a/ui/app/ducks/tests/gas-duck.test.js +++ b/ui/app/ducks/tests/gas-duck.test.js @@ -47,8 +47,8 @@ describe('Gas Duck', () => { } const initState = { customData: { - price: 0, - limit: 21000, + price: null, + limit: '0x5208', }, basicEstimates: { average: null, diff --git a/ui/app/helpers/conversions.util.js b/ui/app/helpers/conversions.util.js index cb5e1b90b..d8e0d87fe 100644 --- a/ui/app/helpers/conversions.util.js +++ b/ui/app/helpers/conversions.util.js @@ -1,6 +1,7 @@ import ethUtil from 'ethereumjs-util' import { conversionUtil } from '../conversion-util' import { ETH, GWEI, WEI } from '../constants/common' +import { conversionUtil, addCurrencies } from '../conversion-util' export function bnToHex (inputBn) { return ethUtil.addHexPrefix(inputBn.toString(16)) @@ -82,3 +83,41 @@ export function getWeiHexFromDecimalValue ({ toDenomination: WEI, }) } + +export function addHexWEIsToDec (aHexWEI, bHexWEI) { + return addCurrencies(aHexWEI, bHexWEI, { + aBase: 16, + bBase: 16, + fromDenomination: 'WEI', + numberOfDecimals: 6, + }) +} + +export function decEthToConvertedCurrency (ethTotal, convertedCurrency, conversionRate) { + return conversionUtil(ethTotal, { + fromNumericBase: 'dec', + toNumericBase: 'dec', + fromCurrency: 'ETH', + toCurrency: convertedCurrency, + numberOfDecimals: 2, + conversionRate, + }) +} + +export function decGWEIToHexWEI (decGWEI) { + return conversionUtil(decGWEI, { + fromNumericBase: 'dec', + toNumericBase: 'hex', + fromDenomination: 'GWEI', + toDenomination: 'WEI', + }) +} + +export function hexWEIToDecGWEI (decGWEI) { + return conversionUtil(decGWEI, { + fromNumericBase: 'hex', + toNumericBase: 'dec', + fromDenomination: 'WEI', + toDenomination: 'GWEI', + }) +} diff --git a/ui/app/helpers/formatters.js b/ui/app/helpers/formatters.js new file mode 100644 index 000000000..106a2520d --- /dev/null +++ b/ui/app/helpers/formatters.js @@ -0,0 +1,3 @@ +export function formatETHFee (ethFee) { + return ethFee + ' ETH' +} diff --git a/ui/app/selectors/custom-gas.js b/ui/app/selectors/custom-gas.js index ff2fd0e05..61c0525df 100644 --- a/ui/app/selectors/custom-gas.js +++ b/ui/app/selectors/custom-gas.js @@ -1,9 +1,5 @@ import { pipe, partialRight } from 'ramda' import { - getConversionRate, - getGasLimit, -} from '../components/send/send.selectors' -import { conversionUtil, multiplyCurrencies, } from '../conversion-util' @@ -14,6 +10,12 @@ import { formatCurrency, } from '../helpers/confirm-transaction/util' import { + decEthToConvertedCurrency as ethTotalToConvertedCurrency, +} from '../helpers/conversions.util' +import { + formatETHFee, +} from '../helpers/formatters' +import { calcGasTotal, } from '../components/send/send.utils' import { addHexPrefix } from 'ethereumjs-util' @@ -25,6 +27,9 @@ const selectors = { getCustomGasTotal, getRenderableBasicEstimateData, getBasicGasEstimateLoadingStatus, + getAveragePriceEstimateInHexWEI, + getDefaultActiveButtonIndex, + priceEstimateToWei, } module.exports = selectors @@ -49,6 +54,16 @@ function getBasicGasEstimateLoadingStatus (state) { return state.gas.basicEstimateIsLoading } +function getAveragePriceEstimateInHexWEI (state) { + const averagePriceEstimate = state.gas.basicEstimates.average + return getGasPriceInHexWei(averagePriceEstimate || '0x0') +} + +function getDefaultActiveButtonIndex (gasButtonInfo, customGasPriceInHex, gasPrice) { + return gasButtonInfo.findIndex(({ priceInHexWei }) => { + return priceInHexWei === addHexPrefix(customGasPriceInHex || gasPrice) + }) +} function apiEstimateModifiedToGWEI (estimate) { return multiplyCurrencies(estimate, 0.10, { @@ -68,21 +83,6 @@ function basicPriceEstimateToETHTotal (estimate, gasLimit) { }) } -function ethTotalToConvertedCurrency (ethTotal, convertedCurrency, conversionRate) { - return conversionUtil(ethTotal, { - fromNumericBase: 'dec', - toNumericBase: 'dec', - fromCurrency: 'ETH', - toCurrency: convertedCurrency, - numberOfDecimals: 2, - conversionRate, - }) -} - -function formatETHFee (ethFee) { - return ethFee + ' ETH' -} - function getRenderableEthFee (estimate, gasLimit) { return pipe( apiEstimateModifiedToGWEI, @@ -150,9 +150,8 @@ function getRenderableBasicEstimateData (state) { if (getBasicGasEstimateLoadingStatus(state)) { return [] } - - const gasLimit = getGasLimit(state) - const conversionRate = getConversionRate(state) + const gasLimit = state.metamask.send.gasLimit || getCustomGasLimit(state) + const conversionRate = state.metamask.conversionRate const currentCurrency = getCurrentCurrency(state) const { gas: { |