@talismn/token-rates 1.0.4 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,12 @@
|
|
1
1
|
import { Token, TokenId } from "@talismn/chaindata-provider";
|
2
|
-
import { TokenRatesList } from "./types";
|
2
|
+
import { TokenRateCurrency, TokenRatesList } from "./types";
|
3
3
|
export declare class TokenRatesError extends Error {
|
4
4
|
response?: Response;
|
5
5
|
constructor(message: string, response?: Response);
|
6
6
|
}
|
7
|
-
export
|
7
|
+
export declare const ALL_CURRENCY_IDS: TokenRateCurrency[];
|
8
|
+
export type CoinsApiConfig = {
|
8
9
|
apiUrl: string;
|
9
|
-
apiKeyName?: string;
|
10
|
-
apiKeyValue?: string;
|
11
10
|
};
|
12
|
-
export declare const
|
13
|
-
export declare function fetchTokenRates(tokens: Record<TokenId, Token>, config?:
|
11
|
+
export declare const DEFAULT_COINSAPI_CONFIG: CoinsApiConfig;
|
12
|
+
export declare function fetchTokenRates(tokens: Record<TokenId, Token>, currencyIds?: TokenRateCurrency[], config?: CoinsApiConfig): Promise<TokenRatesList>;
|
@@ -135,19 +135,11 @@ class TokenRatesError extends Error {
|
|
135
135
|
this.response = response;
|
136
136
|
}
|
137
137
|
}
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
const coingeckoCurrencies = Object.keys(SUPPORTED_CURRENCIES);
|
142
|
-
// api returns a 414 error if the url is too long, max length is about 8100 characters
|
143
|
-
// so use 7900 to be safe
|
144
|
-
const MAX_COINGECKO_URL_LENGTH = 7900;
|
145
|
-
const DEFAULT_COINGECKO_CONFIG = {
|
146
|
-
apiUrl: "https://api.coingecko.com"
|
138
|
+
const ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES);
|
139
|
+
const DEFAULT_COINSAPI_CONFIG = {
|
140
|
+
apiUrl: "https://coins.talisman.xyz"
|
147
141
|
};
|
148
|
-
|
149
|
-
// export function tokenRates(tokens: WithCoingeckoId[]): TokenRatesList {}
|
150
|
-
async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
142
|
+
async function fetchTokenRates(tokens, currencyIds = ALL_CURRENCY_IDS, config = DEFAULT_COINSAPI_CONFIG) {
|
151
143
|
// create a map from `coingeckoId` -> `tokenId` for each token
|
152
144
|
const coingeckoIdToTokenIds = Object.values(tokens)
|
153
145
|
// ignore testnet tokens
|
@@ -192,56 +184,18 @@ async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
|
192
184
|
|
193
185
|
// skip network request if there is nothing for us to fetch
|
194
186
|
if (coingeckoIds.length < 1) return {};
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
return [...safelyGetCoingeckoUrls(coingeckoIds.slice(0, half)), ...safelyGetCoingeckoUrls(coingeckoIds.slice(half))];
|
205
|
-
}
|
206
|
-
return [queryUrl];
|
207
|
-
};
|
208
|
-
|
209
|
-
// fetch the token prices from coingecko
|
210
|
-
// the response should be in the format:
|
211
|
-
// {
|
212
|
-
// [coingeckoId]: {
|
213
|
-
// [currency]: rate
|
214
|
-
// [currency_24h_change]: percent
|
215
|
-
// [currency_market_cap]: value
|
216
|
-
// }
|
217
|
-
// }
|
218
|
-
|
219
|
-
const coingeckoHeaders = new Headers();
|
220
|
-
if (config.apiKeyName && config.apiKeyValue) {
|
221
|
-
coingeckoHeaders.set(config.apiKeyName, config.apiKeyValue);
|
222
|
-
}
|
223
|
-
const coingeckoPrices = await Promise.all(safelyGetCoingeckoUrls(coingeckoIds).map(async queryUrl => await fetch(queryUrl, {
|
224
|
-
headers: coingeckoHeaders
|
225
|
-
}).then(response => {
|
226
|
-
if (response.status !== 200) throw new TokenRatesError(`Failed to fetch token rates`, response);
|
227
|
-
return response.json();
|
228
|
-
}))).then(responses => Object.assign({}, ...responses));
|
187
|
+
const response = await fetch(`${config.apiUrl}/token-rates`, {
|
188
|
+
method: "POST",
|
189
|
+
body: JSON.stringify({
|
190
|
+
coingeckoIds,
|
191
|
+
currencyIds
|
192
|
+
})
|
193
|
+
});
|
194
|
+
const rawTokenRates = await response.json();
|
195
|
+
const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds);
|
229
196
|
|
230
197
|
// build a TokenRatesList from the token prices result
|
231
|
-
const ratesList = Object.fromEntries(
|
232
|
-
const tokenIds = coingeckoIdToTokenIds[coingeckoId];
|
233
|
-
const rates = newTokenRates();
|
234
|
-
for (const currency of coingeckoCurrencies) {
|
235
|
-
if (coingeckoPrices[coingeckoId]?.[currency]) rates[currency] = {
|
236
|
-
price: coingeckoPrices[coingeckoId][currency],
|
237
|
-
marketCap: coingeckoPrices[coingeckoId][`${currency}_market_cap`],
|
238
|
-
change24h: coingeckoPrices[coingeckoId][`${currency}_24h_change`]
|
239
|
-
};
|
240
|
-
}
|
241
|
-
return tokenIds.map(tokenId => [tokenId, rates]);
|
242
|
-
}));
|
243
|
-
|
244
|
-
// return the TokenRatesList
|
198
|
+
const ratesList = Object.fromEntries(Object.entries(tokens).map(([tokenId, token]) => [tokenId, token.coingeckoId ? tokenRates[token.coingeckoId] ?? null : null]));
|
245
199
|
return ratesList;
|
246
200
|
}
|
247
201
|
|
@@ -249,7 +203,28 @@ async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
|
249
203
|
// We can't import this directly from EvmErc20Module because this package doesn't depend on `@talismn/balances`
|
250
204
|
const evmErc20TokenId = (chainId, tokenContractAddress) => `${chainId}-evm-erc20-${tokenContractAddress}`.toLowerCase();
|
251
205
|
|
252
|
-
|
206
|
+
// To save on bandwidth and work around response size limits, values are returned without json property names
|
207
|
+
// (e.g. [[[12, 12332, 0.5]]] instead of { dot : {usd: { value: 12, marketCap: 12332, change24h: 0.5 }} })
|
208
|
+
|
209
|
+
const parseTokenRatesFromApi = (rawTokenRates, coingeckoIds, currencyIds) => {
|
210
|
+
return Object.fromEntries(coingeckoIds.map((coingeckoId, idx) => {
|
211
|
+
const rates = rawTokenRates[idx];
|
212
|
+
if (!rates) return [coingeckoId, null];
|
213
|
+
return [coingeckoId, Object.fromEntries(currencyIds.map((currencyId, idx) => {
|
214
|
+
const curRate = rates[idx];
|
215
|
+
if (!curRate) return [currencyId, null];
|
216
|
+
const [price, marketCap, change24h] = rates[idx];
|
217
|
+
return [currencyId, {
|
218
|
+
price,
|
219
|
+
marketCap,
|
220
|
+
change24h
|
221
|
+
}];
|
222
|
+
}))];
|
223
|
+
}));
|
224
|
+
};
|
225
|
+
|
226
|
+
exports.ALL_CURRENCY_IDS = ALL_CURRENCY_IDS;
|
227
|
+
exports.DEFAULT_COINSAPI_CONFIG = DEFAULT_COINSAPI_CONFIG;
|
253
228
|
exports.SUPPORTED_CURRENCIES = SUPPORTED_CURRENCIES;
|
254
229
|
exports.TalismanTokenRatesDatabase = TalismanTokenRatesDatabase;
|
255
230
|
exports.TokenRatesError = TokenRatesError;
|
@@ -135,19 +135,11 @@ class TokenRatesError extends Error {
|
|
135
135
|
this.response = response;
|
136
136
|
}
|
137
137
|
}
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
const coingeckoCurrencies = Object.keys(SUPPORTED_CURRENCIES);
|
142
|
-
// api returns a 414 error if the url is too long, max length is about 8100 characters
|
143
|
-
// so use 7900 to be safe
|
144
|
-
const MAX_COINGECKO_URL_LENGTH = 7900;
|
145
|
-
const DEFAULT_COINGECKO_CONFIG = {
|
146
|
-
apiUrl: "https://api.coingecko.com"
|
138
|
+
const ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES);
|
139
|
+
const DEFAULT_COINSAPI_CONFIG = {
|
140
|
+
apiUrl: "https://coins.talisman.xyz"
|
147
141
|
};
|
148
|
-
|
149
|
-
// export function tokenRates(tokens: WithCoingeckoId[]): TokenRatesList {}
|
150
|
-
async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
142
|
+
async function fetchTokenRates(tokens, currencyIds = ALL_CURRENCY_IDS, config = DEFAULT_COINSAPI_CONFIG) {
|
151
143
|
// create a map from `coingeckoId` -> `tokenId` for each token
|
152
144
|
const coingeckoIdToTokenIds = Object.values(tokens)
|
153
145
|
// ignore testnet tokens
|
@@ -192,56 +184,18 @@ async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
|
192
184
|
|
193
185
|
// skip network request if there is nothing for us to fetch
|
194
186
|
if (coingeckoIds.length < 1) return {};
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
return [...safelyGetCoingeckoUrls(coingeckoIds.slice(0, half)), ...safelyGetCoingeckoUrls(coingeckoIds.slice(half))];
|
205
|
-
}
|
206
|
-
return [queryUrl];
|
207
|
-
};
|
208
|
-
|
209
|
-
// fetch the token prices from coingecko
|
210
|
-
// the response should be in the format:
|
211
|
-
// {
|
212
|
-
// [coingeckoId]: {
|
213
|
-
// [currency]: rate
|
214
|
-
// [currency_24h_change]: percent
|
215
|
-
// [currency_market_cap]: value
|
216
|
-
// }
|
217
|
-
// }
|
218
|
-
|
219
|
-
const coingeckoHeaders = new Headers();
|
220
|
-
if (config.apiKeyName && config.apiKeyValue) {
|
221
|
-
coingeckoHeaders.set(config.apiKeyName, config.apiKeyValue);
|
222
|
-
}
|
223
|
-
const coingeckoPrices = await Promise.all(safelyGetCoingeckoUrls(coingeckoIds).map(async queryUrl => await fetch(queryUrl, {
|
224
|
-
headers: coingeckoHeaders
|
225
|
-
}).then(response => {
|
226
|
-
if (response.status !== 200) throw new TokenRatesError(`Failed to fetch token rates`, response);
|
227
|
-
return response.json();
|
228
|
-
}))).then(responses => Object.assign({}, ...responses));
|
187
|
+
const response = await fetch(`${config.apiUrl}/token-rates`, {
|
188
|
+
method: "POST",
|
189
|
+
body: JSON.stringify({
|
190
|
+
coingeckoIds,
|
191
|
+
currencyIds
|
192
|
+
})
|
193
|
+
});
|
194
|
+
const rawTokenRates = await response.json();
|
195
|
+
const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds);
|
229
196
|
|
230
197
|
// build a TokenRatesList from the token prices result
|
231
|
-
const ratesList = Object.fromEntries(
|
232
|
-
const tokenIds = coingeckoIdToTokenIds[coingeckoId];
|
233
|
-
const rates = newTokenRates();
|
234
|
-
for (const currency of coingeckoCurrencies) {
|
235
|
-
if (coingeckoPrices[coingeckoId]?.[currency]) rates[currency] = {
|
236
|
-
price: coingeckoPrices[coingeckoId][currency],
|
237
|
-
marketCap: coingeckoPrices[coingeckoId][`${currency}_market_cap`],
|
238
|
-
change24h: coingeckoPrices[coingeckoId][`${currency}_24h_change`]
|
239
|
-
};
|
240
|
-
}
|
241
|
-
return tokenIds.map(tokenId => [tokenId, rates]);
|
242
|
-
}));
|
243
|
-
|
244
|
-
// return the TokenRatesList
|
198
|
+
const ratesList = Object.fromEntries(Object.entries(tokens).map(([tokenId, token]) => [tokenId, token.coingeckoId ? tokenRates[token.coingeckoId] ?? null : null]));
|
245
199
|
return ratesList;
|
246
200
|
}
|
247
201
|
|
@@ -249,7 +203,28 @@ async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
|
249
203
|
// We can't import this directly from EvmErc20Module because this package doesn't depend on `@talismn/balances`
|
250
204
|
const evmErc20TokenId = (chainId, tokenContractAddress) => `${chainId}-evm-erc20-${tokenContractAddress}`.toLowerCase();
|
251
205
|
|
252
|
-
|
206
|
+
// To save on bandwidth and work around response size limits, values are returned without json property names
|
207
|
+
// (e.g. [[[12, 12332, 0.5]]] instead of { dot : {usd: { value: 12, marketCap: 12332, change24h: 0.5 }} })
|
208
|
+
|
209
|
+
const parseTokenRatesFromApi = (rawTokenRates, coingeckoIds, currencyIds) => {
|
210
|
+
return Object.fromEntries(coingeckoIds.map((coingeckoId, idx) => {
|
211
|
+
const rates = rawTokenRates[idx];
|
212
|
+
if (!rates) return [coingeckoId, null];
|
213
|
+
return [coingeckoId, Object.fromEntries(currencyIds.map((currencyId, idx) => {
|
214
|
+
const curRate = rates[idx];
|
215
|
+
if (!curRate) return [currencyId, null];
|
216
|
+
const [price, marketCap, change24h] = rates[idx];
|
217
|
+
return [currencyId, {
|
218
|
+
price,
|
219
|
+
marketCap,
|
220
|
+
change24h
|
221
|
+
}];
|
222
|
+
}))];
|
223
|
+
}));
|
224
|
+
};
|
225
|
+
|
226
|
+
exports.ALL_CURRENCY_IDS = ALL_CURRENCY_IDS;
|
227
|
+
exports.DEFAULT_COINSAPI_CONFIG = DEFAULT_COINSAPI_CONFIG;
|
253
228
|
exports.SUPPORTED_CURRENCIES = SUPPORTED_CURRENCIES;
|
254
229
|
exports.TalismanTokenRatesDatabase = TalismanTokenRatesDatabase;
|
255
230
|
exports.TokenRatesError = TokenRatesError;
|
@@ -133,19 +133,11 @@ class TokenRatesError extends Error {
|
|
133
133
|
this.response = response;
|
134
134
|
}
|
135
135
|
}
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
const coingeckoCurrencies = Object.keys(SUPPORTED_CURRENCIES);
|
140
|
-
// api returns a 414 error if the url is too long, max length is about 8100 characters
|
141
|
-
// so use 7900 to be safe
|
142
|
-
const MAX_COINGECKO_URL_LENGTH = 7900;
|
143
|
-
const DEFAULT_COINGECKO_CONFIG = {
|
144
|
-
apiUrl: "https://api.coingecko.com"
|
136
|
+
const ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES);
|
137
|
+
const DEFAULT_COINSAPI_CONFIG = {
|
138
|
+
apiUrl: "https://coins.talisman.xyz"
|
145
139
|
};
|
146
|
-
|
147
|
-
// export function tokenRates(tokens: WithCoingeckoId[]): TokenRatesList {}
|
148
|
-
async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
140
|
+
async function fetchTokenRates(tokens, currencyIds = ALL_CURRENCY_IDS, config = DEFAULT_COINSAPI_CONFIG) {
|
149
141
|
// create a map from `coingeckoId` -> `tokenId` for each token
|
150
142
|
const coingeckoIdToTokenIds = Object.values(tokens)
|
151
143
|
// ignore testnet tokens
|
@@ -190,56 +182,18 @@ async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
|
190
182
|
|
191
183
|
// skip network request if there is nothing for us to fetch
|
192
184
|
if (coingeckoIds.length < 1) return {};
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
return [...safelyGetCoingeckoUrls(coingeckoIds.slice(0, half)), ...safelyGetCoingeckoUrls(coingeckoIds.slice(half))];
|
203
|
-
}
|
204
|
-
return [queryUrl];
|
205
|
-
};
|
206
|
-
|
207
|
-
// fetch the token prices from coingecko
|
208
|
-
// the response should be in the format:
|
209
|
-
// {
|
210
|
-
// [coingeckoId]: {
|
211
|
-
// [currency]: rate
|
212
|
-
// [currency_24h_change]: percent
|
213
|
-
// [currency_market_cap]: value
|
214
|
-
// }
|
215
|
-
// }
|
216
|
-
|
217
|
-
const coingeckoHeaders = new Headers();
|
218
|
-
if (config.apiKeyName && config.apiKeyValue) {
|
219
|
-
coingeckoHeaders.set(config.apiKeyName, config.apiKeyValue);
|
220
|
-
}
|
221
|
-
const coingeckoPrices = await Promise.all(safelyGetCoingeckoUrls(coingeckoIds).map(async queryUrl => await fetch(queryUrl, {
|
222
|
-
headers: coingeckoHeaders
|
223
|
-
}).then(response => {
|
224
|
-
if (response.status !== 200) throw new TokenRatesError(`Failed to fetch token rates`, response);
|
225
|
-
return response.json();
|
226
|
-
}))).then(responses => Object.assign({}, ...responses));
|
185
|
+
const response = await fetch(`${config.apiUrl}/token-rates`, {
|
186
|
+
method: "POST",
|
187
|
+
body: JSON.stringify({
|
188
|
+
coingeckoIds,
|
189
|
+
currencyIds
|
190
|
+
})
|
191
|
+
});
|
192
|
+
const rawTokenRates = await response.json();
|
193
|
+
const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds);
|
227
194
|
|
228
195
|
// build a TokenRatesList from the token prices result
|
229
|
-
const ratesList = Object.fromEntries(
|
230
|
-
const tokenIds = coingeckoIdToTokenIds[coingeckoId];
|
231
|
-
const rates = newTokenRates();
|
232
|
-
for (const currency of coingeckoCurrencies) {
|
233
|
-
if (coingeckoPrices[coingeckoId]?.[currency]) rates[currency] = {
|
234
|
-
price: coingeckoPrices[coingeckoId][currency],
|
235
|
-
marketCap: coingeckoPrices[coingeckoId][`${currency}_market_cap`],
|
236
|
-
change24h: coingeckoPrices[coingeckoId][`${currency}_24h_change`]
|
237
|
-
};
|
238
|
-
}
|
239
|
-
return tokenIds.map(tokenId => [tokenId, rates]);
|
240
|
-
}));
|
241
|
-
|
242
|
-
// return the TokenRatesList
|
196
|
+
const ratesList = Object.fromEntries(Object.entries(tokens).map(([tokenId, token]) => [tokenId, token.coingeckoId ? tokenRates[token.coingeckoId] ?? null : null]));
|
243
197
|
return ratesList;
|
244
198
|
}
|
245
199
|
|
@@ -247,4 +201,24 @@ async function fetchTokenRates(tokens, config = DEFAULT_COINGECKO_CONFIG) {
|
|
247
201
|
// We can't import this directly from EvmErc20Module because this package doesn't depend on `@talismn/balances`
|
248
202
|
const evmErc20TokenId = (chainId, tokenContractAddress) => `${chainId}-evm-erc20-${tokenContractAddress}`.toLowerCase();
|
249
203
|
|
250
|
-
|
204
|
+
// To save on bandwidth and work around response size limits, values are returned without json property names
|
205
|
+
// (e.g. [[[12, 12332, 0.5]]] instead of { dot : {usd: { value: 12, marketCap: 12332, change24h: 0.5 }} })
|
206
|
+
|
207
|
+
const parseTokenRatesFromApi = (rawTokenRates, coingeckoIds, currencyIds) => {
|
208
|
+
return Object.fromEntries(coingeckoIds.map((coingeckoId, idx) => {
|
209
|
+
const rates = rawTokenRates[idx];
|
210
|
+
if (!rates) return [coingeckoId, null];
|
211
|
+
return [coingeckoId, Object.fromEntries(currencyIds.map((currencyId, idx) => {
|
212
|
+
const curRate = rates[idx];
|
213
|
+
if (!curRate) return [currencyId, null];
|
214
|
+
const [price, marketCap, change24h] = rates[idx];
|
215
|
+
return [currencyId, {
|
216
|
+
price,
|
217
|
+
marketCap,
|
218
|
+
change24h
|
219
|
+
}];
|
220
|
+
}))];
|
221
|
+
}));
|
222
|
+
};
|
223
|
+
|
224
|
+
export { ALL_CURRENCY_IDS, DEFAULT_COINSAPI_CONFIG, SUPPORTED_CURRENCIES, TalismanTokenRatesDatabase, TokenRatesError, db, fetchTokenRates, newTokenRates };
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@talismn/token-rates",
|
3
|
-
"version": "
|
3
|
+
"version": "2.0.1",
|
4
4
|
"author": "Talisman",
|
5
5
|
"homepage": "https://talisman.xyz",
|
6
6
|
"license": "GPL-3.0-or-later",
|
@@ -22,7 +22,7 @@
|
|
22
22
|
},
|
23
23
|
"dependencies": {
|
24
24
|
"dexie": "^4.0.9",
|
25
|
-
"@talismn/chaindata-provider": "0.
|
25
|
+
"@talismn/chaindata-provider": "0.10.0"
|
26
26
|
},
|
27
27
|
"devDependencies": {
|
28
28
|
"@types/jest": "^29.5.14",
|
@@ -30,8 +30,8 @@
|
|
30
30
|
"jest": "^29.7.0",
|
31
31
|
"ts-jest": "^29.2.5",
|
32
32
|
"typescript": "^5.6.3",
|
33
|
-
"@talismn/
|
34
|
-
"@talismn/
|
33
|
+
"@talismn/tsconfig": "0.0.2",
|
34
|
+
"@talismn/eslint-config": "0.0.3"
|
35
35
|
},
|
36
36
|
"eslintConfig": {
|
37
37
|
"root": true,
|