Skip to content

Commit 870351a

Browse files
committed
Connect gas price chart to gas station api.
1 parent a85966a commit 870351a

File tree

3 files changed

+204
-41
lines changed

3 files changed

+204
-41
lines changed

ui/app/ducks/gas.duck.js

+60-26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { mockGasEstimateData } from './mock-gas-estimate-data'
21
import { clone, uniqBy } from 'ramda'
32
import BigNumber from 'bignumber.js'
3+
import {
4+
loadLocalStorageData,
5+
saveLocalStorageData,
6+
} from '../../lib/local-storage-helpers'
47

58
// Actions
69
const BASIC_GAS_ESTIMATE_LOADING_FINISHED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_FINISHED'
@@ -15,6 +18,7 @@ const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT'
1518
const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE'
1619
const SET_CUSTOM_GAS_TOTAL = 'metamask/gas/SET_CUSTOM_GAS_TOTAL'
1720
const SET_PRICE_AND_TIME_ESTIMATES = 'metamask/gas/SET_PRICE_AND_TIME_ESTIMATES'
21+
const SET_API_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_API_ESTIMATES_LAST_RETRIEVED'
1822

1923
// TODO: determine if this approach to initState is consistent with conventional ducks pattern
2024
const initState = {
@@ -38,6 +42,7 @@ const initState = {
3842
basicEstimateIsLoading: true,
3943
gasEstimatesLoading: true,
4044
priceAndTimeEstimates: [],
45+
priceAndTimeEstimatesLastRetrieved: 0,
4146
errors: {},
4247
}
4348

@@ -108,6 +113,11 @@ export default function reducer ({ gas: gasState = initState }, action = {}) {
108113
...action.value,
109114
},
110115
}
116+
case SET_API_ESTIMATES_LAST_RETRIEVED:
117+
return {
118+
...newState,
119+
priceAndTimeEstimatesLastRetrieved: action.value,
120+
}
111121
case RESET_CUSTOM_DATA:
112122
return {
113123
...newState,
@@ -192,34 +202,51 @@ export function fetchBasicGasEstimates () {
192202
}
193203

194204
export function fetchGasEstimates (blockTime) {
195-
return (dispatch) => {
205+
return (dispatch, getState) => {
206+
const {
207+
priceAndTimeEstimatesLastRetrieved,
208+
priceAndTimeEstimates,
209+
} = getState().gas
210+
const timeLastRetrieved = priceAndTimeEstimatesLastRetrieved || loadLocalStorageData('GAS_API_ESTIMATES_LAST_RETRIEVED')
211+
196212
dispatch(gasEstimatesLoadingStarted())
197213

198-
// TODO: uncomment code when live api is ready
199-
// return fetch('https://ethgasstation.info/json/predictTable.json', {
200-
// 'headers': {},
201-
// 'referrer': 'http://ethgasstation.info/json/',
202-
// 'referrerPolicy': 'no-referrer-when-downgrade',
203-
// 'body': null,
204-
// 'method': 'GET',
205-
// 'mode': 'cors'}
206-
// )
207-
return new Promise(resolve => {
208-
resolve(mockGasEstimateData)
209-
})
210-
// .then(r => r.json())
211-
.then(r => {
212-
const estimatedPricesAndTimes = r.map(({ expectedTime, expectedWait, gasprice }) => ({ expectedTime, expectedWait, gasprice }))
213-
const estimatedTimeWithUniquePrices = uniqBy(({ expectedTime }) => expectedTime, estimatedPricesAndTimes)
214-
const timeMappedToSeconds = estimatedTimeWithUniquePrices.map(({ expectedWait, gasprice }) => {
215-
const expectedTime = (new BigNumber(expectedWait)).times(Number(blockTime), 10).toString(10)
216-
return {
217-
expectedTime,
218-
expectedWait,
219-
gasprice,
220-
}
214+
const promiseToFetch = Date.now() - timeLastRetrieved > 75000
215+
? fetch('https://ethgasstation.info/json/predictTable.json', {
216+
'headers': {},
217+
'referrer': 'http://ethgasstation.info/json/',
218+
'referrerPolicy': 'no-referrer-when-downgrade',
219+
'body': null,
220+
'method': 'GET',
221+
'mode': 'cors'}
222+
)
223+
.then(r => r.json())
224+
.then(r => {
225+
const estimatedPricesAndTimes = r.map(({ expectedTime, expectedWait, gasprice }) => ({ expectedTime, expectedWait, gasprice }))
226+
const estimatedTimeWithUniquePrices = uniqBy(({ expectedTime }) => expectedTime, estimatedPricesAndTimes)
227+
const timeMappedToSeconds = estimatedTimeWithUniquePrices.map(({ expectedWait, gasprice }) => {
228+
const expectedTime = (new BigNumber(expectedWait)).times(Number(blockTime), 10).toString(10)
229+
return {
230+
expectedTime,
231+
expectedWait,
232+
gasprice,
233+
}
234+
})
235+
236+
const timeRetrieved = Date.now()
237+
dispatch(setApiEstimatesLastRetrieved(timeRetrieved))
238+
saveLocalStorageData(timeRetrieved, 'GAS_API_ESTIMATES_LAST_RETRIEVED')
239+
saveLocalStorageData(timeMappedToSeconds.slice(1), 'GAS_API_ESTIMATES')
240+
241+
return timeMappedToSeconds.slice(1)
221242
})
222-
dispatch(setPricesAndTimeEstimates(timeMappedToSeconds.slice(1)))
243+
: Promise.resolve(priceAndTimeEstimates.length
244+
? priceAndTimeEstimates
245+
: loadLocalStorageData('GAS_API_ESTIMATES')
246+
)
247+
248+
return promiseToFetch.then(estimates => {
249+
dispatch(setPricesAndTimeEstimates(estimates))
223250
dispatch(gasEstimatesLoadingFinished())
224251
})
225252
}
@@ -267,6 +294,13 @@ export function setCustomGasErrors (newErrors) {
267294
}
268295
}
269296

297+
export function setApiEstimatesLastRetrieved (retrievalTime) {
298+
return {
299+
type: SET_API_ESTIMATES_LAST_RETRIEVED,
300+
value: retrievalTime,
301+
}
302+
}
303+
270304
export function resetCustomGasState () {
271305
return { type: RESET_CUSTOM_GAS_STATE }
272306
}

ui/app/ducks/tests/gas-duck.test.js

+124-15
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,52 @@ import GasReducer, {
1414
gasEstimatesLoadingStarted,
1515
gasEstimatesLoadingFinished,
1616
setPricesAndTimeEstimates,
17+
fetchGasEstimates,
18+
setApiEstimatesLastRetrieved,
1719
} from '../gas.duck.js'
1820

1921
describe('Gas Duck', () => {
2022
let tempFetch
21-
const fetchStub = sinon.stub().returns(new Promise(resolve => resolve({
22-
json: () => new Promise(resolve => resolve({
23-
average: 'mockAverage',
24-
avgWait: 'mockAvgWait',
25-
block_time: 'mockBlock_time',
26-
blockNum: 'mockBlockNum',
27-
fast: 'mockFast',
28-
fastest: 'mockFastest',
29-
fastestWait: 'mockFastestWait',
30-
fastWait: 'mockFastWait',
31-
safeLow: 'mockSafeLow',
32-
safeLowWait: 'mockSafeLowWait',
33-
speed: 'mockSpeed',
34-
})),
35-
})))
23+
let tempDateNow
24+
const mockEthGasApiResponse = {
25+
average: 'mockAverage',
26+
avgWait: 'mockAvgWait',
27+
block_time: 'mockBlock_time',
28+
blockNum: 'mockBlockNum',
29+
fast: 'mockFast',
30+
fastest: 'mockFastest',
31+
fastestWait: 'mockFastestWait',
32+
fastWait: 'mockFastWait',
33+
safeLow: 'mockSafeLow',
34+
safeLowWait: 'mockSafeLowWait',
35+
speed: 'mockSpeed',
36+
}
37+
const mockPredictTableResponse = [
38+
{ expectedTime: 100, expectedWait: 10, gasprice: 1, somethingElse: 'foobar' },
39+
{ expectedTime: 50, expectedWait: 5, gasprice: 2, somethingElse: 'foobar' },
40+
{ expectedTime: 20, expectedWait: 4, gasprice: 4, somethingElse: 'foobar' },
41+
{ expectedTime: 10, expectedWait: 2, gasprice: 10, somethingElse: 'foobar' },
42+
{ expectedTime: 1, expectedWait: 0.5, gasprice: 20, somethingElse: 'foobar' },
43+
]
44+
const fetchStub = sinon.stub().callsFake((url) => new Promise(resolve => {
45+
const dataToResolve = url.match(/ethgasAPI/)
46+
? mockEthGasApiResponse
47+
: mockPredictTableResponse
48+
resolve({
49+
json: () => new Promise(resolve => resolve(dataToResolve)),
50+
})
51+
}))
3652

3753
beforeEach(() => {
3854
tempFetch = global.fetch
55+
tempDateNow = global.Date.now
3956
global.fetch = fetchStub
57+
global.Date.now = () => 2000000
4058
})
4159

4260
afterEach(() => {
4361
global.fetch = tempFetch
62+
global.Date.now = tempDateNow
4463
})
4564

4665
const mockState = {
@@ -70,6 +89,7 @@ describe('Gas Duck', () => {
7089
errors: {},
7190
gasEstimatesLoading: true,
7291
priceAndTimeEstimates: [],
92+
priceAndTimeEstimatesLastRetrieved: 0,
7393

7494
}
7595
const BASIC_GAS_ESTIMATE_LOADING_FINISHED = 'metamask/gas/BASIC_GAS_ESTIMATE_LOADING_FINISHED'
@@ -83,6 +103,7 @@ describe('Gas Duck', () => {
83103
const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE'
84104
const SET_CUSTOM_GAS_TOTAL = 'metamask/gas/SET_CUSTOM_GAS_TOTAL'
85105
const SET_PRICE_AND_TIME_ESTIMATES = 'metamask/gas/SET_PRICE_AND_TIME_ESTIMATES'
106+
const SET_API_ESTIMATES_LAST_RETRIEVED = 'metamask/gas/SET_API_ESTIMATES_LAST_RETRIEVED'
86107

87108
describe('GasReducer()', () => {
88109
it('should initialize state', () => {
@@ -193,6 +214,16 @@ describe('Gas Duck', () => {
193214
)
194215
})
195216

217+
it('should set priceAndTimeEstimatesLastRetrieved when receivinga SET_API_ESTIMATES_LAST_RETRIEVED action', () => {
218+
assert.deepEqual(
219+
GasReducer(mockState, {
220+
type: SET_API_ESTIMATES_LAST_RETRIEVED,
221+
value: 1500000000000,
222+
}),
223+
Object.assign({ priceAndTimeEstimatesLastRetrieved: 1500000000000 }, mockState.gas)
224+
)
225+
})
226+
196227
it('should set errors when receiving a SET_CUSTOM_GAS_ERRORS action', () => {
197228
assert.deepEqual(
198229
GasReducer(mockState, {
@@ -279,6 +310,75 @@ describe('Gas Duck', () => {
279310
})
280311
})
281312

313+
describe('fetchGasEstimates', () => {
314+
const mockDistpatch = sinon.spy()
315+
it('should call fetch with the expected params', async () => {
316+
global.fetch.resetHistory()
317+
await fetchGasEstimates(5)(mockDistpatch, () => ({ gas: Object.assign(
318+
{},
319+
initState,
320+
{ priceAndTimeEstimatesLastRetrieved: 1000000 }
321+
) }))
322+
assert.deepEqual(
323+
mockDistpatch.getCall(0).args,
324+
[{ type: GAS_ESTIMATE_LOADING_STARTED} ]
325+
)
326+
assert.deepEqual(
327+
global.fetch.getCall(0).args,
328+
[
329+
'https://ethgasstation.info/json/predictTable.json',
330+
{
331+
'headers': {},
332+
'referrer': 'http://ethgasstation.info/json/',
333+
'referrerPolicy': 'no-referrer-when-downgrade',
334+
'body': null,
335+
'method': 'GET',
336+
'mode': 'cors',
337+
},
338+
]
339+
)
340+
341+
assert.deepEqual(
342+
mockDistpatch.getCall(1).args,
343+
[{ type: SET_API_ESTIMATES_LAST_RETRIEVED, value: 2000000 }]
344+
)
345+
346+
assert.deepEqual(
347+
mockDistpatch.getCall(2).args,
348+
[{
349+
type: SET_PRICE_AND_TIME_ESTIMATES,
350+
value: [
351+
{
352+
expectedTime: '25',
353+
expectedWait: 5,
354+
gasprice: 2,
355+
},
356+
{
357+
expectedTime: '20',
358+
expectedWait: 4,
359+
gasprice: 4,
360+
},
361+
{
362+
expectedTime: '10',
363+
expectedWait: 2,
364+
gasprice: 10,
365+
},
366+
{
367+
expectedTime: '2.5',
368+
expectedWait: 0.5,
369+
gasprice: 20,
370+
},
371+
],
372+
373+
}]
374+
)
375+
assert.deepEqual(
376+
mockDistpatch.getCall(3).args,
377+
[{ type: GAS_ESTIMATE_LOADING_FINISHED }]
378+
)
379+
})
380+
})
381+
282382
describe('gasEstimatesLoadingStarted', () => {
283383
it('should create the correct action', () => {
284384
assert.deepEqual(
@@ -351,6 +451,15 @@ describe('Gas Duck', () => {
351451
})
352452
})
353453

454+
describe('setApiEstimatesLastRetrieved', () => {
455+
it('should create the correct action', () => {
456+
assert.deepEqual(
457+
setApiEstimatesLastRetrieved(1234),
458+
{ type: SET_API_ESTIMATES_LAST_RETRIEVED, value: 1234 }
459+
)
460+
})
461+
})
462+
354463
describe('resetCustomGasState', () => {
355464
it('should create the correct action', () => {
356465
assert.deepEqual(

ui/lib/local-storage-helpers.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export function loadLocalStorageData (itemKey) {
2+
try {
3+
const serializedData = localStorage.getItem(itemKey)
4+
if (serializedData === null) {
5+
return undefined
6+
}
7+
return JSON.parse(serializedData)
8+
} catch (err) {
9+
return undefined
10+
}
11+
}
12+
13+
export function saveLocalStorageData (data, itemKey) {
14+
try {
15+
const serializedData = JSON.stringify(data)
16+
localStorage.setItem(itemKey, serializedData)
17+
} catch (err) {
18+
console.warn(err)
19+
}
20+
}

0 commit comments

Comments
 (0)