Skip to content
This repository was archived by the owner on Aug 12, 2023. It is now read-only.

Commit 5ef8e2d

Browse files
authored
Token endpoints improvements (#357)
* Add granularity param to tokens endpoint * Add price to token endpoint * Add price change to tokens endpoint * Fix tests * Fix image urls on tokens endpoint
1 parent 553ba6c commit 5ef8e2d

7 files changed

Lines changed: 208 additions & 18 deletions

File tree

src/app/routes/v1/metrics.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const Router = require('koa-router');
33

44
const { TIME_PERIOD } = require('../../../constants');
55
const checkTraderExists = require('../../../traders/check-trader-exists');
6-
const determineGranularityForTimePeriod = require('../../../metrics/determine-granularity-for-time-period');
76
const getActiveTraderMetrics = require('../../../metrics/get-active-trader-metrics');
87
const getNetworkMetrics = require('../../../metrics/get-network-metrics');
98
const getProtocolMetrics = require('../../../metrics/get-protocol-metrics');
@@ -39,6 +38,10 @@ const createRouter = () => {
3938
router.get(
4039
'/token',
4140
middleware.timePeriod('period', TIME_PERIOD.MONTH),
41+
middleware.metricGranularity({
42+
period: 'period',
43+
granularity: 'granularity',
44+
}),
4245
async ({ params, request, response }, next) => {
4346
const tokenAddress = request.query.token;
4447

@@ -55,8 +58,7 @@ const createRouter = () => {
5558
);
5659
}
5760

58-
const { period } = params;
59-
const granularity = determineGranularityForTimePeriod(period);
61+
const { granularity, period } = params;
6062
const metrics = await getTokenMetrics(token.address, period, granularity);
6163

6264
response.body = metrics;

src/app/routes/v1/token.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
const _ = require('lodash');
22
const Router = require('koa-router');
33

4+
const getDatesForTimePeriod = require('../../../util/get-dates-for-time-period');
5+
const getTokenPrice = require('../../../tokens/get-token-price');
46
const Token = require('../../../model/token');
57
const transformToken = require('./util/transform-token');
68

7-
const createRouter = ({ transformer } = {}) => {
9+
const createRouter = () => {
810
const router = new Router();
911

1012
router.get('/tokens/:tokenAddress', async ({ params, response }, next) => {
@@ -16,7 +18,13 @@ const createRouter = ({ transformer } = {}) => {
1618
return;
1719
}
1820

19-
response.body = transformer ? transformer(token) : transformToken(token);
21+
const { dateFrom, dateTo } = getDatesForTimePeriod('day');
22+
const price = await getTokenPrice(token.address, {
23+
from: dateFrom,
24+
to: dateTo,
25+
});
26+
27+
response.body = transformToken(token, price);
2028

2129
await next();
2230
});

src/app/routes/v1/util/transform-token.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
const _ = require('lodash');
22

3+
const { TOKEN_TYPE } = require('../../../../constants');
34
const formatTokenType = require('../../../../tokens/format-token-type');
45
const getCdnTokenImageUrl = require('../../../../tokens/get-cdn-token-image-url');
56

6-
const transformToken = token => {
7+
const transformToken = (token, price) => {
78
return {
89
address: token.address,
910
imageUrl: _.isString(token.imageUrl)
1011
? getCdnTokenImageUrl(token.imageUrl)
1112
: undefined,
12-
lastTrade: _.get(token, 'price.lastTrade'),
13-
name: token.name,
14-
price: _.isNumber(token, 'price.lastPrice')
13+
lastTrade: _.isObject(price)
1514
? {
16-
...token.price,
17-
last: token.price.lastPrice,
15+
date: price.date,
16+
id: price.fillId,
1817
}
19-
: undefined,
18+
: null,
19+
name: token.name,
20+
price: {
21+
change:
22+
token.type === TOKEN_TYPE.ERC20
23+
? _.get(price, 'priceChange', null)
24+
: null,
25+
last:
26+
token.type === TOKEN_TYPE.ERC20 ? _.get(price, 'priceUSD', null) : null,
27+
},
2028
symbol: token.symbol,
2129
type: formatTokenType(token.type),
2230
};

src/app/routes/v1/util/transform-token.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ it('should only return white-listed fields', () => {
3434
expect(transformed).toEqual({
3535
address: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359',
3636
imageUrl: 'http://tmpuri.org/test.jpg',
37+
lastTrade: null,
3738
name: 'DAI Stablecoin',
39+
price: {
40+
change: null,
41+
last: null,
42+
},
3843
symbol: 'DAI',
3944
});
4045
});
@@ -49,7 +54,12 @@ it('should transform tokens which dont have an image url', () => {
4954

5055
expect(transformed).toEqual({
5156
address: '0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359',
57+
lastTrade: null,
5258
name: 'DAI Stablecoin',
59+
price: {
60+
change: null,
61+
last: null,
62+
},
5363
symbol: 'DAI',
5464
});
5565
});

src/tokens/get-token-price.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const _ = require('lodash');
2+
3+
const getTokenPrices = require('./get-token-prices');
4+
5+
const getTokenPrice = async (address, period) => {
6+
const tokenPrices = await getTokenPrices([address], period);
7+
8+
return _.get(tokenPrices, 0, null);
9+
};
10+
11+
module.exports = getTokenPrice;

src/tokens/get-token-prices.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
const _ = require('lodash');
2+
const elasticsearch = require('../util/elasticsearch');
3+
4+
async function getTokenPrices(tokenAddresses, period) {
5+
const res = await elasticsearch.getClient().search({
6+
index: 'traded_tokens',
7+
body: {
8+
size: 0,
9+
query: {
10+
bool: {
11+
filter: [
12+
{
13+
exists: {
14+
field: 'relayerId',
15+
},
16+
},
17+
{
18+
exists: {
19+
field: 'priceUSD',
20+
},
21+
},
22+
{
23+
terms: {
24+
tokenAddress: tokenAddresses,
25+
},
26+
},
27+
{
28+
range: {
29+
priceUSD: { gt: 0 },
30+
},
31+
},
32+
{ range: { date: { lt: period.to } } },
33+
],
34+
},
35+
},
36+
aggs: {
37+
currentPrices: {
38+
terms: {
39+
field: 'tokenAddress',
40+
size: tokenAddresses.length,
41+
},
42+
aggs: {
43+
mostRecentTrade: {
44+
top_hits: {
45+
size: 1,
46+
sort: {
47+
date: {
48+
order: 'desc',
49+
},
50+
},
51+
_source: {
52+
includes: ['date', 'fillId', 'priceUSD'],
53+
},
54+
},
55+
},
56+
},
57+
},
58+
previousPrices: {
59+
terms: {
60+
field: 'tokenAddress',
61+
size: tokenAddresses.length,
62+
},
63+
aggs: {
64+
filter_by_date: {
65+
filter: {
66+
range: {
67+
date: {
68+
lt: period.from,
69+
},
70+
},
71+
},
72+
aggs: {
73+
lastTrade: {
74+
top_hits: {
75+
size: 1,
76+
sort: {
77+
date: {
78+
order: 'desc',
79+
},
80+
},
81+
_source: {
82+
includes: ['priceUSD'],
83+
},
84+
},
85+
},
86+
},
87+
},
88+
},
89+
},
90+
},
91+
},
92+
});
93+
94+
const prices = res.body.aggregations.currentPrices.buckets.map(bucket => {
95+
const tokenAddress = bucket.key;
96+
const {
97+
date,
98+
fillId,
99+
priceUSD,
100+
} = bucket.mostRecentTrade.hits.hits[0]._source;
101+
102+
const previousPriceBucket = res.body.aggregations.previousPrices.buckets.find(
103+
pb => pb.key === tokenAddress,
104+
);
105+
106+
const previousPriceUSD = _.get(
107+
previousPriceBucket,
108+
'filter_by_date.lastTrade.hits.hits.0._source.priceUSD',
109+
null,
110+
);
111+
112+
const priceChange =
113+
previousPriceUSD === null
114+
? null
115+
: ((priceUSD - previousPriceUSD) / previousPriceUSD) * 100;
116+
117+
return {
118+
date,
119+
fillId,
120+
priceChange,
121+
priceUSD,
122+
tokenAddress,
123+
};
124+
});
125+
126+
return prices;
127+
}
128+
129+
module.exports = getTokenPrices;

src/tokens/get-tokens-with-stats-for-dates.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
const _ = require('lodash');
22

3+
const { TOKEN_TYPE } = require('../constants');
34
const elasticsearch = require('../util/elasticsearch');
5+
const getCdnTokenImageUrl = require('./get-cdn-token-image-url');
6+
const getTokenPrices = require('./get-token-prices');
47
const Token = require('../model/token');
58

69
const nullifyValueIfZero = value => (value === 0 ? null : value);
@@ -79,19 +82,38 @@ const getTokensWithStatsForDates = async (dateFrom, dateTo, options) => {
7982
const tokenAddresses = tokenStats.map(x => x.key);
8083
const tokenCount = res.body.aggregations.tokenCount.value;
8184

82-
const tokens = await Token.find({
83-
address: { $in: tokenAddresses },
84-
}).lean();
85+
const [tokens, prices] = await Promise.all([
86+
Token.find({
87+
address: { $in: tokenAddresses },
88+
}).lean(),
89+
getTokenPrices(tokenAddresses, { from: dateFrom, to: dateTo }),
90+
]);
8591

8692
return {
8793
tokens: tokenStats.map(stats => {
8894
const token = tokens.find(t => t.address === stats.key);
95+
const price = prices.find(t => t.tokenAddress === stats.key);
8996

9097
return {
91-
..._.pick(token, ['address', 'imageUrl', 'name', 'symbol', 'type']),
92-
lastTrade: _.get(token, 'price.lastTrade', null),
98+
..._.pick(token, ['address', 'name', 'symbol', 'type']),
99+
imageUrl: _.isString(token.imageUrl)
100+
? getCdnTokenImageUrl(token.imageUrl)
101+
: undefined,
102+
lastTrade: _.has(price, 'fillId')
103+
? {
104+
date: price.date,
105+
id: price.fillId,
106+
}
107+
: null,
93108
price: {
94-
last: _.get(token, 'price.lastPrice', null),
109+
change:
110+
token.type === TOKEN_TYPE.ERC20
111+
? _.get(price, 'priceChange', null)
112+
: null,
113+
last:
114+
token.type === TOKEN_TYPE.ERC20
115+
? _.get(price, 'priceUSD', null)
116+
: null,
95117
},
96118
stats: {
97119
fillCount: stats.fillCount.value,

0 commit comments

Comments
 (0)