From 7f2c5c09de67a67972fcbaae254d39aac6c96f56 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Tue, 13 Nov 2018 13:06:52 -0330 Subject: Uses more reliable api on main send screen; caches basic api results in modal --- .../gas-modal-page-container.component.js | 19 +++++ .../gas-modal-page-container.container.js | 10 ++- .../gas-modal-page-container-component.test.js | 32 ++++++- .../gas-modal-page-container-container.test.js | 4 + .../confirm-transaction.component.js | 6 +- .../confirm-transaction.container.js | 4 +- .../send-gas-row/send-gas-row.container.js | 4 +- .../tests/send-gas-row-container.test.js | 2 +- ui/app/components/send/send.component.js | 4 +- ui/app/components/send/send.container.js | 2 - ui/app/components/send/send.selectors.js | 4 +- .../components/send/tests/send-component.test.js | 18 +--- .../transaction-list-item.component.js | 6 +- .../transaction-list-item.container.js | 4 +- ui/app/ducks/gas.duck.js | 98 +++++++++++++++++++--- ui/app/ducks/tests/gas-duck.test.js | 34 ++++++-- ui/app/selectors/custom-gas.js | 94 +++++++++++++-------- ui/app/selectors/tests/custom-gas.test.js | 94 ++++++++++----------- 18 files changed, 300 insertions(+), 139 deletions(-) (limited to 'ui/app') 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 a804f7b64..b5b13c849 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 @@ -12,10 +12,13 @@ export default class GasModalPageContainer extends Component { static propTypes = { hideModal: PropTypes.func, + hideBasic: PropTypes.bool, updateCustomGasPrice: PropTypes.func, updateCustomGasLimit: PropTypes.func, customGasPrice: PropTypes.number, customGasLimit: PropTypes.number, + fetchBasicGasAndTimeEstimates: PropTypes.func, + fetchGasEstimates: PropTypes.func, gasPriceButtonGroupProps: PropTypes.object, infoRowProps: PropTypes.shape({ originalTotalFiat: PropTypes.string, @@ -28,10 +31,26 @@ export default class GasModalPageContainer extends Component { customModalGasLimitInHex: PropTypes.string, cancelAndClose: PropTypes.func, transactionFee: PropTypes.string, + blockTime: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + ]), } state = {} + componentDidMount () { + const promise = this.props.hideBasic + ? Promise.resolve(this.props.blockTime) + : this.props.fetchBasicGasAndTimeEstimates() + .then(basicEstimates => basicEstimates.blockTime) + + promise + .then(blockTime => { + this.props.fetchGasEstimates(blockTime) + }) + } + renderBasicTabContent (gasPriceButtonGroupProps) { return ( { customGasLimit: calcCustomGasLimit(customModalGasLimitInHex), newTotalFiat, currentTimeEstimate: getRenderableTimeEstimate(customGasPrice, gasPrices, estimatedTimes), + blockTime: getBasicGasEstimateBlockTime(state), gasPriceButtonGroupProps: { buttonDataLoading, defaultActiveButtonIndex: getDefaultActiveButtonIndex(gasButtonInfo, customModalGasPriceInHex), @@ -150,6 +154,8 @@ const mapDispatchToProps = dispatch => { hideGasButtonGroup: () => dispatch(hideGasButtonGroup()), setCustomTimeEstimate: (timeEstimateInSeconds) => dispatch(setCustomTimeEstimate(timeEstimateInSeconds)), hideSidebar: () => dispatch(hideSidebar()), + fetchGasEstimates: (blockTime) => dispatch(fetchGasEstimates(blockTime)), + fetchBasicGasAndTimeEstimates: () => dispatch(fetchBasicGasAndTimeEstimates()), } } @@ -209,7 +215,7 @@ function getTxParams (state, transactionId) { return txData.txParams || pendingTxParams || { from: send.from, gas: send.gasLimit, - gasPrice: send.gasPrice || getAveragePriceEstimateInHexWEI(state), + gasPrice: send.gasPrice || getFastPriceEstimateInHexWEI(state, true), to: send.to, value: getSelectedToken(state) ? '0x0' : send.amount, } 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 16f4b8cd7..22f2f02dd 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 @@ -3,14 +3,21 @@ import assert from 'assert' import shallow from '../../../../../lib/shallow-with-context' import sinon from 'sinon' import GasModalPageContainer from '../gas-modal-page-container.component.js' +import timeout from '../../../../../lib/test-timeout' import PageContainer from '../../../page-container' import { Tab } from '../../../tabs' +const mockBasicGasEstimates = { + blockTime: 'mockBlockTime', +} + const propsMethodSpies = { cancelAndClose: sinon.spy(), onSubmit: sinon.spy(), + fetchBasicGasAndTimeEstimates: sinon.stub().returns(Promise.resolve(mockBasicGasEstimates)), + fetchGasEstimates: sinon.spy(), } const mockGasPriceButtonGroupProps = { @@ -59,6 +66,8 @@ describe('GasModalPageContainer Component', function () { wrapper = shallow( 'mockupdateCustomGasPrice'} updateCustomGasLimit={() => 'mockupdateCustomGasLimit'} customGasPrice={21} @@ -76,6 +85,24 @@ describe('GasModalPageContainer Component', function () { propsMethodSpies.cancelAndClose.resetHistory() }) + describe('componentDidMount', () => { + it('should call props.fetchBasicGasAndTimeEstimates', () => { + propsMethodSpies.fetchBasicGasAndTimeEstimates.resetHistory() + assert.equal(propsMethodSpies.fetchBasicGasAndTimeEstimates.callCount, 0) + wrapper.instance().componentDidMount() + assert.equal(propsMethodSpies.fetchBasicGasAndTimeEstimates.callCount, 1) + }) + + it('should call props.fetchGasEstimates with the block time returned by fetchBasicGasAndTimeEstimates', async () => { + propsMethodSpies.fetchGasEstimates.resetHistory() + assert.equal(propsMethodSpies.fetchGasEstimates.callCount, 0) + wrapper.instance().componentDidMount() + await timeout(250) + assert.equal(propsMethodSpies.fetchGasEstimates.callCount, 1) + assert.equal(propsMethodSpies.fetchGasEstimates.getCall(0).args[0], 'mockBlockTime') + }) + }) + describe('render', () => { it('should render a PageContainer compenent', () => { assert.equal(wrapper.find(PageContainer).length, 1) @@ -106,7 +133,10 @@ describe('GasModalPageContainer Component', function () { it('should pass the correct renderTabs property to PageContainer', () => { sinon.stub(GP, 'renderTabs').returns('mockTabs') - const renderTabsWrapperTester = shallow(, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } }) + const renderTabsWrapperTester = shallow(, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } }) const { tabsComponent } = renderTabsWrapperTester.find(PageContainer).props() assert.equal(tabsComponent, 'mockTabs') GasModalPageContainer.prototype.renderTabs.restore() 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 1ed28f33e..ba2cfe282 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 @@ -73,6 +73,9 @@ describe('gas-modal-page-container container', () => { conversionRate: 50, }, gas: { + basicEstimates: { + blockTime: 12, + }, customData: { limit: 'aaaaaaaa', price: 'ffffffff', @@ -100,6 +103,7 @@ describe('gas-modal-page-container container', () => { customGasLimit: 2863311530, currentTimeEstimate: '~1 min 11 sec', newTotalFiat: '637.41', + blockTime: 12, customModalGasLimitInHex: 'aaaaaaaa', customModalGasPriceInHex: 'ffffffff', gasChartProps: { 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 7a3b58ffa..2e460f377 100644 --- a/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js +++ b/ui/app/components/pages/confirm-transaction/confirm-transaction.component.js @@ -32,7 +32,7 @@ export default class ConfirmTransaction extends Component { setTransactionToConfirm: PropTypes.func, confirmTransaction: PropTypes.object, clearConfirmTransaction: PropTypes.func, - fetchBasicGasEstimates: PropTypes.func, + fetchBasicGasAndTimeEstimates: PropTypes.func, } getParamsTransactionId () { @@ -46,7 +46,7 @@ export default class ConfirmTransaction extends Component { send = {}, history, confirmTransaction: { txData: { id: transactionId } = {} }, - fetchBasicGasEstimates, + fetchBasicGasAndTimeEstimates, } = this.props if (!totalUnapprovedCount && !send.to) { @@ -55,7 +55,7 @@ export default class ConfirmTransaction extends Component { } if (!transactionId) { - fetchBasicGasEstimates() + fetchBasicGasAndTimeEstimates() 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 a7e6966af..46342dc76 100644 --- a/ui/app/components/pages/confirm-transaction/confirm-transaction.container.js +++ b/ui/app/components/pages/confirm-transaction/confirm-transaction.container.js @@ -6,7 +6,7 @@ import { clearConfirmTransaction, } from '../../../ducks/confirm-transaction.duck' import { - fetchBasicGasEstimates, + fetchBasicGasAndTimeEstimates, } from '../../../ducks/gas.duck' import ConfirmTransaction from './confirm-transaction.component' import { getTotalUnapprovedCount } from '../../../selectors' @@ -27,7 +27,7 @@ const mapDispatchToProps = dispatch => { return { setTransactionToConfirm: transactionId => dispatch(setTransactionToConfirm(transactionId)), clearConfirmTransaction: () => dispatch(clearConfirmTransaction()), - fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()), + fetchBasicGasAndTimeEstimates: () => dispatch(fetchBasicGasAndTimeEstimates()), } } 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 39266e590..977f8ab3c 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 @@ -7,7 +7,7 @@ import { } from '../../send.selectors.js' import { getBasicGasEstimateLoadingStatus, - getRenderableEstimateDataForSmallButtons, + getRenderableEstimateDataForSmallButtonsFromGWEI, getDefaultActiveButtonIndex, } from '../../../../selectors/custom-gas' import { @@ -23,7 +23,7 @@ import SendGasRow from './send-gas-row.component' export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(SendGasRow) function mapStateToProps (state) { - const gasButtonInfo = getRenderableEstimateDataForSmallButtons(state) + const gasButtonInfo = getRenderableEstimateDataForSmallButtonsFromGWEI(state) const activeButtonIndex = getDefaultActiveButtonIndex(gasButtonInfo, getGasPrice(state)) return { 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 766bf6cab..f0c82e4f7 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 @@ -42,7 +42,7 @@ proxyquire('../send-gas-row.container.js', { '../../../../actions': actionSpies, '../../../../selectors/custom-gas': { getBasicGasEstimateLoadingStatus: (s) => `mockBasicGasEstimateLoadingStatus:${s}`, - getRenderableEstimateDataForSmallButtons: (s) => `mockGasButtonInfo:${s}`, + getRenderableEstimateDataForSmallButtonsFromGWEI: (s) => `mockGasButtonInfo:${s}`, getDefaultActiveButtonIndex: (gasButtonInfo, gasPrice) => gasButtonInfo.length + gasPrice.length, }, '../../../../ducks/send.duck': sendDuckSpies, diff --git a/ui/app/components/send/send.component.js b/ui/app/components/send/send.component.js index 301acb1db..9b512aaf6 100644 --- a/ui/app/components/send/send.component.js +++ b/ui/app/components/send/send.component.js @@ -35,6 +35,7 @@ export default class SendTransactionScreen extends PersistentForm { selectedToken: PropTypes.object, tokenBalance: PropTypes.string, tokenContract: PropTypes.object, + fetchBasicGasEstimates: PropTypes.func, updateAndSetGasTotal: PropTypes.func, updateSendErrors: PropTypes.func, updateSendTokenBalance: PropTypes.func, @@ -164,9 +165,8 @@ export default class SendTransactionScreen extends PersistentForm { componentDidMount () { this.props.fetchBasicGasEstimates() - .then(basicEstimates => { + .then(() => { this.updateGas() - this.props.fetchGasEstimates(basicEstimates.blockTime) }) } diff --git a/ui/app/components/send/send.container.js b/ui/app/components/send/send.container.js index ac804cf2a..402e4bbe5 100644 --- a/ui/app/components/send/send.container.js +++ b/ui/app/components/send/send.container.js @@ -38,7 +38,6 @@ import { } from '../../ducks/send.duck' import { fetchBasicGasEstimates, - fetchGasEstimates, } from '../../ducks/gas.duck' import { calcGasTotal, @@ -109,6 +108,5 @@ function mapDispatchToProps (dispatch) { qrCodeDetected: (data) => dispatch(qrCodeDetected(data)), updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)), fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()), - fetchGasEstimates: (blockTime) => dispatch(fetchGasEstimates(blockTime)), } } diff --git a/ui/app/components/send/send.selectors.js b/ui/app/components/send/send.selectors.js index 71eb93d61..443c82af5 100644 --- a/ui/app/components/send/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -11,7 +11,7 @@ const { calcGasTotal, } = require('./send.utils') import { - getAveragePriceEstimateInHexWEI, + getFastPriceEstimateInHexWEI, } from '../../selectors/custom-gas' const selectors = { @@ -139,7 +139,7 @@ function getGasLimit (state) { } function getGasPrice (state) { - return state.metamask.send.gasPrice || getAveragePriceEstimateInHexWEI(state) + return state.metamask.send.gasPrice || getFastPriceEstimateInHexWEI(state) } function getGasPriceFromRecentBlocks (state) { diff --git a/ui/app/components/send/tests/send-component.test.js b/ui/app/components/send/tests/send-component.test.js index 68d2fc47e..81955cc1d 100644 --- a/ui/app/components/send/tests/send-component.test.js +++ b/ui/app/components/send/tests/send-component.test.js @@ -3,17 +3,12 @@ import assert from 'assert' import proxyquire from 'proxyquire' import { shallow } from 'enzyme' import sinon from 'sinon' +import timeout from '../../../../lib/test-timeout' import SendHeader from '../send-header/send-header.container' import SendContent from '../send-content/send-content.component' import SendFooter from '../send-footer/send-footer.container' -function timeout (time) { - return new Promise((resolve, reject) => { - setTimeout(resolve, time || 1500) - }) -} - const mockBasicGasEstimates = { blockTime: 'mockBlockTime', } @@ -88,7 +83,7 @@ describe('Send Component', function () { }) describe('componentDidMount', () => { - it('should call props.fetchBasicGasEstimates', () => { + it('should call props.fetchBasicGasAndTimeEstimates', () => { propsMethodSpies.fetchBasicGasEstimates.resetHistory() assert.equal(propsMethodSpies.fetchBasicGasEstimates.callCount, 0) wrapper.instance().componentDidMount() @@ -103,15 +98,6 @@ describe('Send Component', function () { await timeout(250) assert.equal(SendTransactionScreen.prototype.updateGas.callCount, 1) }) - - it('should call props.fetchGasEstimates with the block time returned by fetchBasicGasEstimates', async () => { - propsMethodSpies.fetchGasEstimates.resetHistory() - assert.equal(propsMethodSpies.fetchGasEstimates.callCount, 0) - wrapper.instance().componentDidMount() - await timeout(250) - assert.equal(propsMethodSpies.fetchGasEstimates.callCount, 1) - assert.equal(propsMethodSpies.fetchGasEstimates.getCall(0).args[0], 'mockBlockTime') - }) }) describe('componentWillUnmount', () => { diff --git a/ui/app/components/transaction-list-item/transaction-list-item.component.js b/ui/app/components/transaction-list-item/transaction-list-item.component.js index d34dba210..5334484db 100644 --- a/ui/app/components/transaction-list-item/transaction-list-item.component.js +++ b/ui/app/components/transaction-list-item/transaction-list-item.component.js @@ -27,7 +27,7 @@ export default class TransactionListItem extends PureComponent { tokenData: PropTypes.object, transaction: PropTypes.object, value: PropTypes.string, - fetchBasicGasEstimates: PropTypes.func, + fetchBasicGasAndTimeEstimates: PropTypes.func, fetchGasEstimates: PropTypes.func, } @@ -71,8 +71,8 @@ export default class TransactionListItem extends PureComponent { } resubmit () { - const { transaction, retryTransaction, fetchBasicGasEstimates, fetchGasEstimates } = this.props - fetchBasicGasEstimates().then(basicEstimates => { + const { transaction, retryTransaction, fetchBasicGasAndTimeEstimates, fetchGasEstimates } = this.props + fetchBasicGasAndTimeEstimates().then(basicEstimates => { fetchGasEstimates(basicEstimates.blockTime) }).then(() => { retryTransaction(transaction) diff --git a/ui/app/components/transaction-list-item/transaction-list-item.container.js b/ui/app/components/transaction-list-item/transaction-list-item.container.js index df408b36b..61ecb04d0 100644 --- a/ui/app/components/transaction-list-item/transaction-list-item.container.js +++ b/ui/app/components/transaction-list-item/transaction-list-item.container.js @@ -8,7 +8,7 @@ import { hexToDecimal } from '../../helpers/conversions.util' import { getTokenData } from '../../helpers/transactions.util' import { formatDate } from '../../util' import { - fetchBasicGasEstimates, + fetchBasicGasAndTimeEstimates, fetchGasEstimates, setCustomGasPrice, setCustomGasLimit, @@ -29,7 +29,7 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = dispatch => { return { - fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()), + fetchBasicGasAndTimeEstimates: () => dispatch(fetchBasicGasAndTimeEstimates()), fetchGasEstimates: (blockTime) => dispatch(fetchGasEstimates(blockTime)), setSelectedToken: tokenAddress => dispatch(setSelectedToken(tokenAddress)), retryTransaction: (transaction) => { diff --git a/ui/app/ducks/gas.duck.js b/ui/app/ducks/gas.duck.js index 6ae927b12..ffd1e773f 100644 --- a/ui/app/ducks/gas.duck.js +++ b/ui/app/ducks/gas.duck.js @@ -19,6 +19,7 @@ const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE' const SET_CUSTOM_GAS_TOTAL = 'metamask/gas/SET_CUSTOM_GAS_TOTAL' const SET_PRICE_AND_TIME_ESTIMATES = 'metamask/gas/SET_PRICE_AND_TIME_ESTIMATES' const SET_API_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_API_ESTIMATES_LAST_RETRIEVED' +const SET_BASIC_API_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_BASIC_API_ESTIMATES_LAST_RETRIEVED' // TODO: determine if this approach to initState is consistent with conventional ducks pattern const initState = { @@ -42,7 +43,9 @@ const initState = { basicEstimateIsLoading: true, gasEstimatesLoading: true, priceAndTimeEstimates: [], + basicPriceAndTimeEstimates: [], priceAndTimeEstimatesLastRetrieved: 0, + basicPriceAndTimeEstimatesLastRetrieved: 0, errors: {}, } @@ -118,6 +121,11 @@ export default function reducer ({ gas: gasState = initState }, action = {}) { ...newState, priceAndTimeEstimatesLastRetrieved: action.value, } + case SET_BASIC_API_ESTIMATES_LAST_RETRIEVED: + return { + ...newState, + basicPriceAndTimeEstimatesLastRetrieved: action.value, + } case RESET_CUSTOM_DATA: return { ...newState, @@ -159,9 +167,9 @@ export function fetchBasicGasEstimates () { return (dispatch) => { dispatch(basicGasEstimatesLoadingStarted()) - return fetch('https://ethgasstation.info/json/ethgasAPI.json', { + return fetch('https://dev.blockscale.net/api/gasexpress.json', { 'headers': {}, - 'referrer': 'http://ethgasstation.info/json/', + 'referrer': 'https://dev.blockscale.net/api/', 'referrerPolicy': 'no-referrer-when-downgrade', 'body': null, 'method': 'GET', @@ -169,23 +177,53 @@ export function fetchBasicGasEstimates () { ) .then(r => r.json()) .then(({ - average, - avgWait, - block_time: blockTime, - blockNum, + safeLow, + standard: average, fast, fastest, - fastestWait, - fastWait, - safeLow, - safeLowWait, - speed, + block_time: blockTime, + blockNum, }) => { const basicEstimates = { + safeLow, average, - avgWait, + fast, + fastest, blockTime, blockNum, + } + dispatch(setBasicGasEstimateData(basicEstimates)) + dispatch(basicGasEstimatesLoadingFinished()) + return basicEstimates + }) + } +} + +export function fetchBasicGasAndTimeEstimates () { + return (dispatch, getState) => { + const { + basicPriceAndTimeEstimatesLastRetrieved, + basicPriceAndTimeEstimates, + } = getState().gas + const timeLastRetrieved = basicPriceAndTimeEstimatesLastRetrieved || loadLocalStorageData('BASIC_GAS_AND_TIME_API_ESTIMATES_LAST_RETRIEVED') || 0 + + dispatch(basicGasEstimatesLoadingStarted()) + + const promiseToFetch = Date.now() - timeLastRetrieved > 75000 + ? fetch('https://ethgasstation.info/json/ethgasAPI.json', { + 'headers': {}, + 'referrer': 'http://ethgasstation.info/json/', + 'referrerPolicy': 'no-referrer-when-downgrade', + 'body': null, + 'method': 'GET', + 'mode': 'cors'} + ) + .then(r => r.json()) + .then(({ + average, + avgWait, + block_time: blockTime, + blockNum, fast, fastest, fastestWait, @@ -193,7 +231,34 @@ export function fetchBasicGasEstimates () { safeLow, safeLowWait, speed, - } + }) => { + const basicEstimates = { + average, + avgWait, + blockTime, + blockNum, + fast, + fastest, + fastestWait, + fastWait, + safeLow, + safeLowWait, + speed, + } + + const timeRetrieved = Date.now() + dispatch(setBasicApiEstimatesLastRetrieved(timeRetrieved)) + saveLocalStorageData(timeRetrieved, 'BASIC_GAS_AND_TIME_API_ESTIMATES_LAST_RETRIEVED') + saveLocalStorageData(basicEstimates, 'BASIC_GAS_AND_TIME_API_ESTIMATES') + + return basicEstimates + }) + : Promise.resolve(basicPriceAndTimeEstimates.length + ? basicPriceAndTimeEstimates + : loadLocalStorageData('BASIC_GAS_AND_TIME_API_ESTIMATES') + ) + + return promiseToFetch.then(basicEstimates => { dispatch(setBasicGasEstimateData(basicEstimates)) dispatch(basicGasEstimatesLoadingFinished()) return basicEstimates @@ -301,6 +366,13 @@ export function setApiEstimatesLastRetrieved (retrievalTime) { } } +export function setBasicApiEstimatesLastRetrieved (retrievalTime) { + return { + type: SET_BASIC_API_ESTIMATES_LAST_RETRIEVED, + value: retrievalTime, + } +} + export function resetCustomGasState () { return { type: RESET_CUSTOM_GAS_STATE } } diff --git a/ui/app/ducks/tests/gas-duck.test.js b/ui/app/ducks/tests/gas-duck.test.js index 009758cde..dff154dea 100644 --- a/ui/app/ducks/tests/gas-duck.test.js +++ b/ui/app/ducks/tests/gas-duck.test.js @@ -19,7 +19,7 @@ const { setCustomGasTotal, setCustomGasErrors, resetCustomGasState, - fetchBasicGasEstimates, + fetchBasicGasAndTimeEstimates, gasEstimatesLoadingStarted, gasEstimatesLoadingFinished, setPricesAndTimeEstimates, @@ -100,6 +100,9 @@ describe('Gas Duck', () => { gasEstimatesLoading: true, priceAndTimeEstimates: [], priceAndTimeEstimatesLastRetrieved: 0, + basicPriceAndTimeEstimates: [], + basicPriceAndTimeEstimatesLastRetrieved: 0, + } const BASIC_GAS_ESTIMATE_LOADING_FINISHED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_FINISHED' @@ -114,6 +117,7 @@ describe('Gas Duck', () => { const SET_CUSTOM_GAS_TOTAL = 'metamask/gas/SET_CUSTOM_GAS_TOTAL' const SET_PRICE_AND_TIME_ESTIMATES = 'metamask/gas/SET_PRICE_AND_TIME_ESTIMATES' const SET_API_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_API_ESTIMATES_LAST_RETRIEVED' + const SET_BASIC_API_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_BASIC_API_ESTIMATES_LAST_RETRIEVED' describe('GasReducer()', () => { it('should initialize state', () => { @@ -224,7 +228,7 @@ describe('Gas Duck', () => { ) }) - it('should set priceAndTimeEstimatesLastRetrieved when receivinga SET_API_ESTIMATES_LAST_RETRIEVED action', () => { + it('should set priceAndTimeEstimatesLastRetrieved when receiving a SET_API_ESTIMATES_LAST_RETRIEVED action', () => { assert.deepEqual( GasReducer(mockState, { type: SET_API_ESTIMATES_LAST_RETRIEVED, @@ -234,6 +238,16 @@ describe('Gas Duck', () => { ) }) + it('should set priceAndTimeEstimatesLastRetrieved when receiving a SET_BASIC_API_ESTIMATES_LAST_RETRIEVED action', () => { + assert.deepEqual( + GasReducer(mockState, { + type: SET_BASIC_API_ESTIMATES_LAST_RETRIEVED, + value: 1700000000000, + }), + Object.assign({ basicPriceAndTimeEstimatesLastRetrieved: 1700000000000 }, mockState.gas) + ) + }) + it('should set errors when receiving a SET_CUSTOM_GAS_ERRORS action', () => { assert.deepEqual( GasReducer(mockState, { @@ -272,10 +286,14 @@ describe('Gas Duck', () => { }) }) - describe('fetchBasicGasEstimates', () => { + describe('fetchBasicGasAndTimeEstimates', () => { const mockDistpatch = sinon.spy() it('should call fetch with the expected params', async () => { - await fetchBasicGasEstimates()(mockDistpatch) + await fetchBasicGasAndTimeEstimates()(mockDistpatch, () => ({ gas: Object.assign( + {}, + initState, + { basicPriceAndTimeEstimatesLastRetrieved: 1000000 } + ) })) assert.deepEqual( mockDistpatch.getCall(0).args, [{ type: BASIC_GAS_ESTIMATE_LOADING_STARTED} ] @@ -294,8 +312,14 @@ describe('Gas Duck', () => { }, ] ) + assert.deepEqual( mockDistpatch.getCall(1).args, + [{ type: SET_BASIC_API_ESTIMATES_LAST_RETRIEVED, value: 2000000 } ] + ) + + assert.deepEqual( + mockDistpatch.getCall(2).args, [{ type: SET_BASIC_GAS_ESTIMATE_DATA, value: { @@ -314,7 +338,7 @@ describe('Gas Duck', () => { }] ) assert.deepEqual( - mockDistpatch.getCall(2).args, + mockDistpatch.getCall(3).args, [{ type: BASIC_GAS_ESTIMATE_LOADING_FINISHED }] ) }) diff --git a/ui/app/selectors/custom-gas.js b/ui/app/selectors/custom-gas.js index 1c577c1b4..f06fb710d 100644 --- a/ui/app/selectors/custom-gas.js +++ b/ui/app/selectors/custom-gas.js @@ -23,7 +23,9 @@ import { addHexPrefix } from 'ethereumjs-util' const selectors = { formatTimeEstimate, getAveragePriceEstimateInHexWEI, + getFastPriceEstimateInHexWEI, getBasicGasEstimateLoadingStatus, + getBasicGasEstimateBlockTime, getCustomGasErrors, getCustomGasLimit, getCustomGasPrice, @@ -33,7 +35,7 @@ const selectors = { getEstimatedGasTimes, getPriceAndTimeEstimates, getRenderableBasicEstimateData, - getRenderableEstimateDataForSmallButtons, + getRenderableEstimateDataForSmallButtonsFromGWEI, priceEstimateToWei, } @@ -78,12 +80,21 @@ function getAveragePriceEstimateInHexWEI (state) { return getGasPriceInHexWei(averagePriceEstimate || '0x0') } +function getFastPriceEstimateInHexWEI (state, convertFromDecGWEI) { + const fastPriceEstimate = state.gas.basicEstimates.fast + return getGasPriceInHexWei(fastPriceEstimate || '0x0', convertFromDecGWEI) +} + function getDefaultActiveButtonIndex (gasButtonInfo, customGasPriceInHex, gasPrice) { return gasButtonInfo.findIndex(({ priceInHexWei }) => { return priceInHexWei === addHexPrefix(customGasPriceInHex || gasPrice) }) } +function getBasicGasEstimateBlockTime (state) { + return state.gas.basicEstimates.blockTime +} + function apiEstimateModifiedToGWEI (estimate) { return multiplyCurrencies(estimate, 0.10, { toNumericBase: 'hex', @@ -102,26 +113,34 @@ function basicPriceEstimateToETHTotal (estimate, gasLimit, numberOfDecimals = 9) }) } -function getRenderableEthFee (estimate, gasLimit, numberOfDecimals = 9) { +function getRenderableEthFee (estimate, gasLimit, numberOfDecimals = 9, convertFromDecGWEI) { + const initialConversion = convertFromDecGWEI + ? x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }) + : apiEstimateModifiedToGWEI + return pipe( - apiEstimateModifiedToGWEI, + initialConversion, partialRight(basicPriceEstimateToETHTotal, [gasLimit, numberOfDecimals]), formatETHFee )(estimate, gasLimit) } -function getRenderableConvertedCurrencyFee (estimate, gasLimit, convertedCurrency, conversionRate) { +function getRenderableConvertedCurrencyFee (estimate, gasLimit, convertedCurrency, conversionRate, convertFromDecGWEI) { + const initialConversion = convertFromDecGWEI + ? x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }) + : apiEstimateModifiedToGWEI + return pipe( - apiEstimateModifiedToGWEI, + initialConversion, partialRight(basicPriceEstimateToETHTotal, [gasLimit]), partialRight(ethTotalToConvertedCurrency, [convertedCurrency, conversionRate]), partialRight(formatCurrency, [convertedCurrency]) )(estimate, gasLimit, convertedCurrency, conversionRate) } -function getTimeEstimateInSeconds (blockWaitEstimate, currentBlockTime) { - return multiplyCurrencies(blockWaitEstimate, currentBlockTime, { +function getTimeEstimateInSeconds (blockWaitEstimate) { + return multiplyCurrencies(blockWaitEstimate, 60, { toNumericBase: 'dec', multiplicandBase: 10, multiplierBase: 10, @@ -141,11 +160,11 @@ function formatTimeEstimate (totalSeconds) { return formattedCombined } -function getRenderableTimeEstimate (blockWaitEstimate, currentBlockTime) { +function getRenderableTimeEstimate (blockWaitEstimate) { return pipe( getTimeEstimateInSeconds, formatTimeEstimate - )(blockWaitEstimate, currentBlockTime) + )(blockWaitEstimate) } function priceEstimateToWei (priceEstimate) { @@ -158,9 +177,13 @@ function priceEstimateToWei (priceEstimate) { }) } -function getGasPriceInHexWei (price) { +function getGasPriceInHexWei (price, convertFromDecGWEI) { + const initialConversion = convertFromDecGWEI + ? x => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }) + : apiEstimateModifiedToGWEI + return pipe( - apiEstimateModifiedToGWEI, + initialConversion, priceEstimateToWei, addHexPrefix )(price) @@ -177,11 +200,10 @@ function getRenderableBasicEstimateData (state) { gas: { basicEstimates: { safeLow, - average, fast, - blockTime, + fastest, safeLowWait, - avgWait, + fastestWait, fastWait, }, }, @@ -190,29 +212,29 @@ function getRenderableBasicEstimateData (state) { return [ { labelKey: 'fastest', - feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate), - feeInSecondaryCurrency: getRenderableEthFee(fast, gasLimit), - timeEstimate: getRenderableTimeEstimate(fastWait, blockTime), - priceInHexWei: getGasPriceInHexWei(fast), + feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(fastest, gasLimit, currentCurrency, conversionRate), + feeInSecondaryCurrency: getRenderableEthFee(fastest, gasLimit), + timeEstimate: fastestWait && getRenderableTimeEstimate(fastestWait), + priceInHexWei: getGasPriceInHexWei(fastest), }, { labelKey: 'fast', - feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(average, gasLimit, currentCurrency, conversionRate), - feeInSecondaryCurrency: getRenderableEthFee(average, gasLimit), - timeEstimate: getRenderableTimeEstimate(avgWait, blockTime), - priceInHexWei: getGasPriceInHexWei(average), + feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate), + feeInSecondaryCurrency: getRenderableEthFee(fast, gasLimit), + timeEstimate: fastWait && getRenderableTimeEstimate(fastWait), + priceInHexWei: getGasPriceInHexWei(fast), }, { labelKey: 'slow', feeInPrimaryCurrency: getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate), feeInSecondaryCurrency: getRenderableEthFee(safeLow, gasLimit), - timeEstimate: getRenderableTimeEstimate(safeLowWait, blockTime), + timeEstimate: safeLowWait && getRenderableTimeEstimate(safeLowWait), priceInHexWei: getGasPriceInHexWei(safeLow), }, ] } -function getRenderableEstimateDataForSmallButtons (state) { +function getRenderableEstimateDataForSmallButtonsFromGWEI (state) { if (getBasicGasEstimateLoadingStatus(state)) { return [] } @@ -223,30 +245,30 @@ function getRenderableEstimateDataForSmallButtons (state) { gas: { basicEstimates: { safeLow, - average, fast, + fastest, }, }, } = state return [ { - labelKey: 'fast', - feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate), - feeInPrimaryCurrency: getRenderableEthFee(fast, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS), - priceInHexWei: getGasPriceInHexWei(fast), + labelKey: 'fastest', + feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fastest, gasLimit, currentCurrency, conversionRate, true), + feeInPrimaryCurrency: getRenderableEthFee(fastest, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), + priceInHexWei: getGasPriceInHexWei(fastest, true), }, { - labelKey: 'average', - feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(average, gasLimit, currentCurrency, conversionRate), - feeInPrimaryCurrency: getRenderableEthFee(average, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS), - priceInHexWei: getGasPriceInHexWei(average), + labelKey: 'fast', + feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(fast, gasLimit, currentCurrency, conversionRate, true), + feeInPrimaryCurrency: getRenderableEthFee(fast, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), + priceInHexWei: getGasPriceInHexWei(fast, true), }, { labelKey: 'slow', - feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate), - feeInPrimaryCurrency: getRenderableEthFee(safeLow, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS), - priceInHexWei: getGasPriceInHexWei(safeLow), + feeInSecondaryCurrency: getRenderableConvertedCurrencyFee(safeLow, gasLimit, currentCurrency, conversionRate, true), + feeInPrimaryCurrency: getRenderableEthFee(safeLow, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, true), + priceInHexWei: getGasPriceInHexWei(safeLow, true), }, ] } diff --git a/ui/app/selectors/tests/custom-gas.test.js b/ui/app/selectors/tests/custom-gas.test.js index 8a6e7e351..037b1e86e 100644 --- a/ui/app/selectors/tests/custom-gas.test.js +++ b/ui/app/selectors/tests/custom-gas.test.js @@ -10,7 +10,7 @@ const { getEstimatedGasTimes, getPriceAndTimeEstimates, getRenderableBasicEstimateData, - getRenderableEstimateDataForSmallButtons, + getRenderableEstimateDataForSmallButtonsFromGWEI, } = proxyquire('../custom-gas', {}) describe('custom-gas selectors', () => { @@ -80,21 +80,21 @@ describe('custom-gas selectors', () => { labelKey: 'fastest', feeInPrimaryCurrency: '$0.05', feeInSecondaryCurrency: '0.00021 ETH', - timeEstimate: '~7 sec', + timeEstimate: '~30 sec', priceInHexWei: '0x2540be400', }, { labelKey: 'fast', feeInPrimaryCurrency: '$0.03', feeInSecondaryCurrency: '0.000105 ETH', - timeEstimate: '~46 sec', + timeEstimate: '~3 min 18 sec', priceInHexWei: '0x12a05f200', }, { labelKey: 'slow', feeInPrimaryCurrency: '$0.01', feeInSecondaryCurrency: '0.0000525 ETH', - timeEstimate: '~1 min 33 sec', + timeEstimate: '~6 min 36 sec', priceInHexWei: '0x9502f900', }, ], @@ -111,10 +111,10 @@ describe('custom-gas selectors', () => { blockTime: 14.16326530612245, safeLow: 25, safeLowWait: 6.6, - average: 50, - avgWait: 3.3, - fast: 100, - fastWait: 0.5, + fast: 50, + fastWait: 3.3, + fastest: 100, + fastestWait: 0.5, }, }, }, @@ -125,21 +125,21 @@ describe('custom-gas selectors', () => { labelKey: 'fastest', feeInPrimaryCurrency: '$1.07', feeInSecondaryCurrency: '0.00042 ETH', - timeEstimate: '~14 sec', + timeEstimate: '~1 min', priceInHexWei: '0x4a817c800', }, { labelKey: 'fast', feeInPrimaryCurrency: '$0.54', feeInSecondaryCurrency: '0.00021 ETH', - timeEstimate: '~1 min 33 sec', + timeEstimate: '~6 min 36 sec', priceInHexWei: '0x2540be400', }, { labelKey: 'slow', feeInPrimaryCurrency: '$0.27', feeInSecondaryCurrency: '0.000105 ETH', - timeEstimate: '~3 min 7 sec', + timeEstimate: '~13 min 12 sec', priceInHexWei: '0x12a05f200', }, ], @@ -156,10 +156,10 @@ describe('custom-gas selectors', () => { blockTime: 14.16326530612245, safeLow: 50, safeLowWait: 13.2, - average: 100, - avgWait: 6.6, - fast: 200, - fastWait: 1.0, + fast: 100, + fastWait: 6.6, + fastest: 200, + fastestWait: 1.0, }, }, }, @@ -176,27 +176,27 @@ describe('custom-gas selectors', () => { }) - describe('getRenderableEstimateDataForSmallButtons()', () => { + describe('getRenderableEstimateDataForSmallButtonsFromGWEI()', () => { const tests = [ { expectedResult: [ { - feeInSecondaryCurrency: '$0.05', - feeInPrimaryCurrency: '0.00021 ETH', - labelKey: 'fast', - priceInHexWei: '0x2540be400', + feeInSecondaryCurrency: '$0.54', + feeInPrimaryCurrency: '0.0021 ETH', + labelKey: 'fastest', + priceInHexWei: '0x174876e800', }, { - feeInSecondaryCurrency: '$0.03', - feeInPrimaryCurrency: '0.0001 ETH', - labelKey: 'average', - priceInHexWei: '0x12a05f200', + feeInSecondaryCurrency: '$0.27', + feeInPrimaryCurrency: '0.00105 ETH', + labelKey: 'fast', + priceInHexWei: '0xba43b7400', }, { - feeInSecondaryCurrency: '$0.01', - feeInPrimaryCurrency: '0.00005 ETH', + feeInSecondaryCurrency: '$0.13', + feeInPrimaryCurrency: '0.00052 ETH', labelKey: 'slow', - priceInHexWei: '0x9502f900', + priceInHexWei: '0x5d21dba00', }, ], mockState: { @@ -212,10 +212,10 @@ describe('custom-gas selectors', () => { blockTime: 14.16326530612245, safeLow: 25, safeLowWait: 6.6, - average: 50, - avgWait: 3.3, - fast: 100, - fastWait: 0.5, + fast: 50, + fastWait: 3.3, + fastest: 100, + fastestWait: 0.5, }, }, }, @@ -223,22 +223,22 @@ describe('custom-gas selectors', () => { { expectedResult: [ { - feeInSecondaryCurrency: '$1.07', - feeInPrimaryCurrency: '0.00042 ETH', - labelKey: 'fast', - priceInHexWei: '0x4a817c800', + feeInSecondaryCurrency: '$10.74', + feeInPrimaryCurrency: '0.0042 ETH', + labelKey: 'fastest', + priceInHexWei: '0x2e90edd000', }, { - feeInSecondaryCurrency: '$0.54', - feeInPrimaryCurrency: '0.00021 ETH', - labelKey: 'average', - priceInHexWei: '0x2540be400', + feeInSecondaryCurrency: '$5.37', + feeInPrimaryCurrency: '0.0021 ETH', + labelKey: 'fast', + priceInHexWei: '0x174876e800', }, { - feeInSecondaryCurrency: '$0.27', - feeInPrimaryCurrency: '0.0001 ETH', + feeInSecondaryCurrency: '$2.68', + feeInPrimaryCurrency: '0.00105 ETH', labelKey: 'slow', - priceInHexWei: '0x12a05f200', + priceInHexWei: '0xba43b7400', }, ], mockState: { @@ -254,10 +254,10 @@ describe('custom-gas selectors', () => { blockTime: 14.16326530612245, safeLow: 50, safeLowWait: 13.2, - average: 100, - avgWait: 6.6, - fast: 200, - fastWait: 1.0, + fast: 100, + fastWait: 6.6, + fastest: 200, + fastestWait: 1.0, }, }, }, @@ -266,7 +266,7 @@ describe('custom-gas selectors', () => { it('should return renderable data about basic estimates appropriate for buttons with less info', () => { tests.forEach(test => { assert.deepEqual( - getRenderableEstimateDataForSmallButtons(test.mockState), + getRenderableEstimateDataForSmallButtonsFromGWEI(test.mockState), test.expectedResult ) }) -- cgit