1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Uses more reliable api on main send screen; caches basic api results in modal

This commit is contained in:
Dan Miller 2018-11-13 13:06:52 -03:30
parent fe535159bb
commit 7f2c5c09de
21 changed files with 311 additions and 142 deletions

File diff suppressed because one or more lines are too long

View File

@ -74,7 +74,9 @@ describe('MetaMask', function () {
'if (args[0] === "https://ethgasstation.info/json/ethgasAPI.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasBasic + '\')) }); } else if ' +
'(args[0] === "https://ethgasstation.info/json/predictTable.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.ethGasPredictTable + '\')) }); } else if ' +
'(args[0] === "https://dev.blockscale.net/api/gasexpress.json") { return ' +
'Promise.resolve({ json: () => Promise.resolve(JSON.parse(\'' + fetchMockResponses.gasExpress + '\')) }); } ' +
'return window.fetch(...args); }'
)
})

View File

@ -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 (
<BasicTabContent

View File

@ -13,6 +13,8 @@ import {
setCustomGasLimit,
resetCustomData,
setCustomTimeEstimate,
fetchGasEstimates,
fetchBasicGasAndTimeEstimates,
} from '../../../ducks/gas.duck'
import {
hideGasButtonGroup,
@ -28,7 +30,7 @@ import {
} from '../../../selectors.js'
import {
formatTimeEstimate,
getAveragePriceEstimateInHexWEI,
getFastPriceEstimateInHexWEI,
getBasicGasEstimateLoadingStatus,
getCustomGasLimit,
getCustomGasPrice,
@ -36,6 +38,7 @@ import {
getEstimatedGasPrices,
getEstimatedGasTimes,
getRenderableBasicEstimateData,
getBasicGasEstimateBlockTime,
} from '../../../selectors/custom-gas'
import {
submittedPendingTransactionsSelector,
@ -100,6 +103,7 @@ const mapStateToProps = (state, ownProps) => {
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,
}

View File

@ -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(<GasModalPageContainer
cancelAndClose={propsMethodSpies.cancelAndClose}
onSubmit={propsMethodSpies.onSubmit}
fetchBasicGasAndTimeEstimates={propsMethodSpies.fetchBasicGasAndTimeEstimates}
fetchGasEstimates={propsMethodSpies.fetchGasEstimates}
updateCustomGasPrice={() => '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(<GasModalPageContainer />, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } })
const renderTabsWrapperTester = shallow(<GasModalPageContainer
fetchBasicGasAndTimeEstimates={propsMethodSpies.fetchBasicGasAndTimeEstimates}
fetchGasEstimates={propsMethodSpies.fetchGasEstimates}
/>, { context: { t: (str1, str2) => str2 ? str1 + str2 : str1 } })
const { tabsComponent } = renderTabsWrapperTester.find(PageContainer).props()
assert.equal(tabsComponent, 'mockTabs')
GasModalPageContainer.prototype.renderTabs.restore()

View File

@ -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: {

View File

@ -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()
}
}

View File

@ -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()),
}
}

View File

@ -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 {

View File

@ -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,

View File

@ -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)
})
}

View File

@ -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)),
}
}

View File

@ -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) {

View File

@ -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', () => {

View File

@ -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)

View File

@ -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) => {

View File

@ -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,7 +167,50 @@ 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': 'https://dev.blockscale.net/api/',
'referrerPolicy': 'no-referrer-when-downgrade',
'body': null,
'method': 'GET',
'mode': 'cors'}
)
.then(r => r.json())
.then(({
safeLow,
standard: average,
fast,
fastest,
block_time: blockTime,
blockNum,
}) => {
const basicEstimates = {
safeLow,
average,
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',
@ -194,6 +245,20 @@ export function fetchBasicGasEstimates () {
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 }
}

View File

@ -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', () => {
@ -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 }]
)
})

View File

@ -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),
},
]
}

View File

@ -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',
feeInSecondaryCurrency: '$0.54',
feeInPrimaryCurrency: '0.0021 ETH',
labelKey: 'fastest',
priceInHexWei: '0x174876e800',
},
{
feeInSecondaryCurrency: '$0.27',
feeInPrimaryCurrency: '0.00105 ETH',
labelKey: 'fast',
priceInHexWei: '0x2540be400',
priceInHexWei: '0xba43b7400',
},
{
feeInSecondaryCurrency: '$0.03',
feeInPrimaryCurrency: '0.0001 ETH',
labelKey: 'average',
priceInHexWei: '0x12a05f200',
},
{
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',
feeInSecondaryCurrency: '$10.74',
feeInPrimaryCurrency: '0.0042 ETH',
labelKey: 'fastest',
priceInHexWei: '0x2e90edd000',
},
{
feeInSecondaryCurrency: '$5.37',
feeInPrimaryCurrency: '0.0021 ETH',
labelKey: 'fast',
priceInHexWei: '0x4a817c800',
priceInHexWei: '0x174876e800',
},
{
feeInSecondaryCurrency: '$0.54',
feeInPrimaryCurrency: '0.00021 ETH',
labelKey: 'average',
priceInHexWei: '0x2540be400',
},
{
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
)
})

5
ui/lib/test-timeout.js Normal file
View File

@ -0,0 +1,5 @@
export default function timeout (time) {
return new Promise((resolve, reject) => {
setTimeout(resolve, time || 1500)
})
}