Skip to content

Commit ba18bb7

Browse files
authored
Merge pull request #107 from EdgeApp/paul/coinrankAsset
Paul/coinrank asset
2 parents e0e41d7 + 990a7a2 commit ba18bb7

File tree

8 files changed

+274
-188
lines changed

8 files changed

+274
-188
lines changed

src/coinrankEngine.ts

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,60 +14,60 @@ const NUM_PAGES = 8
1414
const { defaultFiatCode } = config
1515

1616
export const coinrankEngine = async (): Promise<void> => {
17-
const { coinrankOffsetSeconds, coinrankIntervalSeconds } = config
18-
const delay = getDelay({
19-
now: new Date(),
20-
intervalSeconds: coinrankIntervalSeconds,
21-
offsetSeconds: coinrankOffsetSeconds
22-
})
17+
while (true) {
18+
try {
19+
const { coinrankOffsetSeconds, coinrankIntervalSeconds } = config
20+
const delay = getDelay({
21+
now: new Date(),
22+
intervalSeconds: coinrankIntervalSeconds,
23+
offsetSeconds: coinrankOffsetSeconds
24+
})
2325

24-
logger(`**** COINRANK ENGINE SNOOZING ${delay / 1000}s`)
25-
await snooze(delay)
26+
logger(`**** COINRANK ENGINE SNOOZING ${delay / 1000}s`)
27+
await snooze(delay)
2628

27-
logger('Updating Coinrank Cache')
28-
try {
29-
let wait = DEFAULT_WAIT_MS
29+
logger('Updating Coinrank Cache')
30+
let wait = DEFAULT_WAIT_MS
3031

31-
const lastUpdate = new Date().toISOString()
32-
const { apiKey, uri } = config.providers.coingeckopro
33-
let markets: CoinrankMarkets = []
34-
let page = 1
35-
while (true) {
36-
const url = `${uri}/api/v3/coins/markets?x_cg_pro_api_key=${apiKey}&vs_currency=USD&page=${page}&per_page=${PAGE_SIZE}&price_change_percentage=1h,24h,7d,14d,30d,1y`
37-
const response = await fetch(url)
38-
if (!response.ok) {
39-
const text = await response.text()
40-
logger(text)
41-
if (response.status === 429) {
42-
// retry. 10 req/min so need to delay
43-
logger(`Coinrank Rate Limited Snoozing ${wait.toString()}ms`)
44-
wait = Math.min(wait * 2, MAX_WAIT_MS)
45-
await snooze(wait)
46-
continue
32+
const lastUpdate = new Date().toISOString()
33+
const { apiKey, uri } = config.providers.coingeckopro
34+
let markets: CoinrankMarkets = []
35+
let page = 1
36+
while (true) {
37+
const url = `${uri}/api/v3/coins/markets?x_cg_pro_api_key=${apiKey}&vs_currency=USD&page=${page}&per_page=${PAGE_SIZE}&price_change_percentage=1h,24h,7d,14d,30d,1y`
38+
const response = await fetch(url)
39+
if (!response.ok) {
40+
const text = await response.text()
41+
logger(text)
42+
if (response.status === 429) {
43+
// retry. 10 req/min so need to delay
44+
logger(`Coinrank Rate Limited Snoozing ${wait.toString()}ms`)
45+
wait = Math.min(wait * 2, MAX_WAIT_MS)
46+
await snooze(wait)
47+
continue
48+
}
49+
logger(`Coinrank returned code ${response.status}`)
50+
throw new Error(text)
4751
}
48-
logger(`Coinrank returned code ${response.status}`)
49-
throw new Error(text)
50-
}
51-
logger(`coinrank queried page ${page}`)
52-
wait = DEFAULT_WAIT_MS
52+
logger(`coinrank queried page ${page}`)
53+
wait = DEFAULT_WAIT_MS
5354

54-
const reply = await response.json()
55-
const marketsPage = asCoingeckoMarkets(reply)
56-
markets = [...markets, ...marketsPage]
57-
page++
58-
if (page > NUM_PAGES) break
55+
const reply = await response.json()
56+
const marketsPage = asCoingeckoMarkets(reply)
57+
markets = [...markets, ...marketsPage]
58+
page++
59+
if (page > NUM_PAGES) break
60+
}
61+
const data: CoinrankRedis = { lastUpdate, markets }
62+
await setAsync(
63+
`${REDIS_COINRANK_KEY_PREFIX}_${defaultFiatCode}`,
64+
JSON.stringify(data)
65+
)
66+
} catch (e) {
67+
const err: any = e // Weird TS issue causing :any to get removed from above line
68+
const message = `coinrankEngine failure: ${err.message}`
69+
slackMessage(message).catch(e => logger(e))
70+
logger(message)
5971
}
60-
const data: CoinrankRedis = { lastUpdate, markets }
61-
await setAsync(
62-
`${REDIS_COINRANK_KEY_PREFIX}_${defaultFiatCode}`,
63-
JSON.stringify(data)
64-
)
65-
} catch (e) {
66-
const err: any = e // Weird TS issue causing :any to get removed from above line
67-
const message = `coinrankEngine failure: ${err.message}`
68-
slackMessage(message).catch(e => logger(e))
69-
logger(message)
70-
} finally {
71-
coinrankEngine().catch(e => logger(e))
7272
}
7373
}

src/exchangeRateRouter.ts

Lines changed: 128 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,21 @@ import { config } from './config'
99
import { REDIS_COINRANK_KEY_PREFIX } from './constants'
1010
import { asReturnGetRate, getExchangeRates } from './rates'
1111
import {
12+
asCoinrankAssetReq,
1213
asCoinrankReq,
1314
asExchangeRateResponse,
15+
CoinrankAssetReq,
1416
CoinrankRedis,
1517
CoinrankReq
1618
} from './types'
1719
import { asExtendedReq } from './utils/asExtendedReq'
18-
import { DbDoc, getAsync, hmgetAsync, setAsync } from './utils/dbUtils'
20+
import {
21+
DbDoc,
22+
getAsync,
23+
hgetallAsync,
24+
hmgetAsync,
25+
setAsync
26+
} from './utils/dbUtils'
1927
import {
2028
addIso,
2129
fromCode,
@@ -314,15 +322,127 @@ const sendExchangeRates: express.RequestHandler = (req, res, next): void => {
314322
res.json({ data: exReq.requestedRatesResult.data })
315323
}
316324

317-
const sendCoinranks: express.RequestHandler = async (
325+
const getRedisMarkets = async (
326+
fiatCode: string
327+
): Promise<CoinrankRedis | undefined> => {
328+
const { ratesServerAddress } = config
329+
const now = new Date()
330+
const nowTimestamp = now.getTime()
331+
332+
const jsonString = await getAsync(`${REDIS_COINRANK_KEY_PREFIX}_${fiatCode}`)
333+
let redisResult: CoinrankRedis = JSON.parse(jsonString)
334+
335+
if (fiatCode !== defaultFiatCode) {
336+
const lastUpdated =
337+
redisResult != null ? new Date(redisResult.lastUpdate).getTime() : 0
338+
339+
// If no result in redis or it's out of date
340+
if (redisResult == null || nowTimestamp - lastUpdated > EXPIRE_TIME) {
341+
// Attempt to scale prices by foreign exchange rate
342+
343+
// Get exchange rate
344+
const result = await fetch(
345+
`${ratesServerAddress}/v2/exchangeRate?currency_pair=${defaultFiatCode}_${fiatCode}`
346+
)
347+
const resultJson = await result.json()
348+
const { exchangeRate } = asExchangeRateResponse(resultJson)
349+
const rate = Number(exchangeRate)
350+
351+
// Get USD rankings
352+
const jsonString = await getAsync(
353+
`${REDIS_COINRANK_KEY_PREFIX}_${defaultFiatCode}`
354+
)
355+
redisResult = JSON.parse(jsonString)
356+
let { markets } = redisResult
357+
358+
// Modify fiat-related fields with the forex rate
359+
markets = markets.map(m => ({
360+
...m,
361+
marketCap: m.marketCap * rate,
362+
price: m.price * rate,
363+
volume24h: m.volume24h * rate,
364+
high24h: m.high24h * rate,
365+
low24h: m.low24h * rate,
366+
priceChange24h: m.priceChange24h * rate,
367+
marketCapChange24h: m.marketCapChange24h * rate,
368+
circulatingSupply: m.circulatingSupply * rate,
369+
totalSupply: m.totalSupply * rate,
370+
maxSupply: m.maxSupply * rate,
371+
allTimeHigh: m.allTimeHigh * rate,
372+
allTimeLow: m.allTimeLow * rate
373+
}))
374+
375+
// Update redis cache
376+
const redisData: CoinrankRedis = {
377+
markets,
378+
lastUpdate: now.toISOString()
379+
}
380+
await setAsync(
381+
`${REDIS_COINRANK_KEY_PREFIX}_${fiatCode}`,
382+
JSON.stringify(redisData)
383+
)
384+
}
385+
}
386+
387+
return redisResult
388+
}
389+
390+
const sendCoinrankList: express.RequestHandler = async (
318391
req,
319392
res,
320393
next
321394
): Promise<void> => {
322-
const { ratesServerAddress } = config
323-
const now = new Date()
324-
const nowTimestamp = now.getTime()
395+
const data = await hgetallAsync('coingecko')
396+
res.json({ data })
397+
}
325398

399+
const sendCoinrankAsset: express.RequestHandler = async (
400+
req,
401+
res,
402+
next
403+
): Promise<void> => {
404+
const exReq = req as ExpressRequest
405+
if (exReq == null) return next(500)
406+
407+
let query: CoinrankAssetReq
408+
try {
409+
query = asCoinrankAssetReq(req.query)
410+
} catch (e) {
411+
res
412+
.status(400)
413+
.send(`Error: ${e instanceof Error ? e.message : 'Unknown error'}`)
414+
return
415+
}
416+
const { fiatCode } = query
417+
const { assetId } = req.params
418+
419+
try {
420+
const redisResult = await getRedisMarkets(fiatCode)
421+
422+
if (redisResult == null) {
423+
res.status(400).send(`Unable to get results for fiatCode ${fiatCode}`)
424+
return
425+
}
426+
427+
const { markets } = redisResult
428+
const market = markets.find(m => m.assetId === assetId)
429+
if (market == null) {
430+
res.status(404).send(`assetId ${assetId} not found`)
431+
return
432+
}
433+
434+
res.json({ data: market })
435+
} catch (e) {
436+
const err: any = e
437+
res.status(500).send(err.message)
438+
}
439+
}
440+
441+
const sendCoinranks: express.RequestHandler = async (
442+
req,
443+
res,
444+
next
445+
): Promise<void> => {
326446
const exReq = req as ExpressRequest
327447
if (exReq == null) return next(500)
328448

@@ -350,67 +470,8 @@ const sendCoinranks: express.RequestHandler = async (
350470
.send(`Invalid length param: ${length}. Must be between 1-100`)
351471
return
352472
}
353-
const jsonString = await getAsync(
354-
`${REDIS_COINRANK_KEY_PREFIX}_${fiatCode}`
355-
)
473+
const redisResult = await getRedisMarkets(fiatCode)
356474

357-
let redisResult: CoinrankRedis = JSON.parse(jsonString)
358-
359-
if (fiatCode !== defaultFiatCode) {
360-
const lastUpdated =
361-
redisResult != null ? new Date(redisResult.lastUpdate).getTime() : 0
362-
363-
// If no result in redis or it's out of date
364-
if (redisResult == null || nowTimestamp - lastUpdated > EXPIRE_TIME) {
365-
// Attempt to scale prices by foreign exchange rate
366-
367-
// Get exchange rate
368-
const result = await fetch(
369-
`${ratesServerAddress}/v2/exchangeRate?currency_pair=${defaultFiatCode}_${fiatCode}`
370-
)
371-
const resultJson = await result.json()
372-
const { exchangeRate } = asExchangeRateResponse(resultJson)
373-
const rate = Number(exchangeRate)
374-
375-
// Get USD rankings
376-
const jsonString = await getAsync(
377-
`${REDIS_COINRANK_KEY_PREFIX}_${defaultFiatCode}`
378-
)
379-
redisResult = JSON.parse(jsonString)
380-
let { markets } = redisResult
381-
382-
// Modify fiat-related fields with the forex rate
383-
markets = markets.map(m => ({
384-
...m,
385-
marketCap: m.marketCap * rate,
386-
price: m.price * rate,
387-
volume24h: m.volume24h * rate,
388-
high24h: m.high24h * rate,
389-
low24h: m.low24h * rate,
390-
priceChange24h: m.priceChange24h * rate,
391-
marketCapChange24h: m.marketCapChange24h * rate,
392-
circulatingSupply: m.circulatingSupply * rate,
393-
totalSupply: m.totalSupply * rate,
394-
maxSupply: m.maxSupply * rate,
395-
allTimeHigh: m.allTimeHigh * rate,
396-
allTimeLow: m.allTimeLow * rate
397-
}))
398-
const data = markets.slice(start - 1, start + length)
399-
400-
res.json({ data })
401-
402-
// Update redis cache
403-
const redisData: CoinrankRedis = {
404-
markets,
405-
lastUpdate: now.toISOString()
406-
}
407-
await setAsync(
408-
`${REDIS_COINRANK_KEY_PREFIX}_${fiatCode}`,
409-
JSON.stringify(redisData)
410-
)
411-
return
412-
}
413-
}
414475
if (redisResult == null) {
415476
res.status(400).send(`Unable to get results for fiatCode ${fiatCode}`)
416477
return
@@ -472,6 +533,8 @@ export const exchangeRateRouterV2 = (): express.Router => {
472533
])
473534

474535
router.get('/coinrank', [sendCoinranks])
536+
router.get('/coinrankAsset/:assetId', [sendCoinrankAsset])
537+
router.get('/coinrankList', [sendCoinrankList])
475538

476539
return router
477540
}

src/indexEngines.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import { logger } from './utils/utils'
1010
// Initialize DB
1111
async function initDb(): Promise<void> {
1212
await setupDatabase(config.couchUri, ratesDbSetup)
13-
ratesEngine().catch(e => logger('ratesEnginee failure', e))
14-
uidEngine().catch(e => logger('uidEnginee failure', e))
15-
coinrankEngine().catch(e => logger('uidEnginee failure', e))
13+
ratesEngine().catch(e => logger('ratesEngine failure', e))
14+
uidEngine().catch(e => logger('uidEngine failure', e))
15+
coinrankEngine().catch(e => logger('coinrankEngine failure', e))
1616
}
1717

1818
initDb().catch(e => logger('initDbe failure', e))

src/providers/coingecko.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ const asCoingeckoAssetResponse = asArray(
174174
})
175175
)
176176

177-
const DEFAULT_WAIT_MS = 30 * 1000
177+
const DEFAULT_WAIT_MS = 2 * 1000
178178
const MAX_WAIT_MS = 5 * 60 * 1000
179179

180180
export const coingeckoAssets = async (): Promise<AssetMap> => {
@@ -184,7 +184,7 @@ export const coingeckoAssets = async (): Promise<AssetMap> => {
184184
let wait = DEFAULT_WAIT_MS
185185
while (true) {
186186
const response = await fetch(
187-
`${uri}/api/v3/coins/markets?vs_currency=usd&per_page=${perPage}&page=${page}&order=market_cap_asc`
187+
`${uri}/api/v3/coins/markets?x_cg_pro_api_key=${apiKey}&vs_currency=usd&per_page=${perPage}&page=${page}&order=market_cap_asc`
188188
)
189189
if (!response.ok) {
190190
const text = await response.text()

0 commit comments

Comments
 (0)