@talismn/token-rates 3.0.15 → 3.0.17
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.
- package/dist/{declarations/src/types.d.ts → index.d.mts} +24 -8
- package/dist/index.d.ts +115 -0
- package/dist/index.js +206 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +173 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +24 -20
- package/dist/declarations/src/TalismanTokenRatesDatabase.d.ts +0 -1
- package/dist/declarations/src/TokenRates.d.ts +0 -12
- package/dist/declarations/src/index.d.ts +0 -3
- package/dist/talismn-token-rates.cjs.d.ts +0 -1
- package/dist/talismn-token-rates.cjs.dev.js +0 -259
- package/dist/talismn-token-rates.cjs.js +0 -7
- package/dist/talismn-token-rates.cjs.prod.js +0 -259
- package/dist/talismn-token-rates.esm.js +0 -251
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@talismn/token-rates",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.17",
|
|
4
4
|
"author": "Talisman",
|
|
5
5
|
"homepage": "https://talisman.xyz",
|
|
6
6
|
"license": "GPL-3.0-or-later",
|
|
@@ -10,37 +10,41 @@
|
|
|
10
10
|
"repository": {
|
|
11
11
|
"directory": "packages/token-rates",
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "https://github.com/
|
|
13
|
+
"url": "https://github.com/TalismanSociety/talisman.git"
|
|
14
14
|
},
|
|
15
|
-
"main": "dist/
|
|
16
|
-
"module": "dist/
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
17
17
|
"files": [
|
|
18
|
-
"
|
|
18
|
+
"dist"
|
|
19
19
|
],
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=20"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@talismn/chaindata-provider": "1.3.
|
|
24
|
+
"@talismn/chaindata-provider": "1.3.6"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/jest": "^29.5.14",
|
|
28
|
-
"eslint": "^8.57.1",
|
|
29
|
-
"jest": "^29.7.0",
|
|
30
|
-
"ts-jest": "^29.2.5",
|
|
31
27
|
"typescript": "^5.6.3",
|
|
32
|
-
"@talismn/
|
|
33
|
-
"@talismn/tsconfig": "0.0.3"
|
|
28
|
+
"@talismn/tsconfig": "0.0.4"
|
|
34
29
|
},
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
"@talismn/
|
|
39
|
-
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"@talismn/source": "./src/index.ts",
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./dist/index.d.mts",
|
|
36
|
+
"default": "./dist/index.mjs"
|
|
37
|
+
},
|
|
38
|
+
"require": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"default": "./dist/index.js"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
40
43
|
},
|
|
41
44
|
"scripts": {
|
|
42
|
-
"test": "
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"clean": "rm -rf dist .turbo node_modules",
|
|
47
|
+
"build": "tsup --silent",
|
|
48
|
+
"typecheck": "tsc --noEmit"
|
|
45
49
|
}
|
|
46
50
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const tryToDeleteOldTokenRatesDb: () => void;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { Token, TokenId } from "@talismn/chaindata-provider";
|
|
2
|
-
import { TokenRateCurrency, TokenRatesList } from "./types";
|
|
3
|
-
export declare class TokenRatesError extends Error {
|
|
4
|
-
response?: Response;
|
|
5
|
-
constructor(message: string, response?: Response);
|
|
6
|
-
}
|
|
7
|
-
export declare const ALL_CURRENCY_IDS: TokenRateCurrency[];
|
|
8
|
-
export type CoinsApiConfig = {
|
|
9
|
-
apiUrl: string;
|
|
10
|
-
};
|
|
11
|
-
export declare const DEFAULT_COINSAPI_CONFIG: CoinsApiConfig;
|
|
12
|
-
export declare function fetchTokenRates(tokens: Record<TokenId, Token>, currencyIds?: TokenRateCurrency[], config?: CoinsApiConfig): Promise<TokenRatesList>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./declarations/src/index";
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chaindataProvider = require('@talismn/chaindata-provider');
|
|
4
|
-
|
|
5
|
-
const tryToDeleteOldTokenRatesDb = () => {
|
|
6
|
-
try {
|
|
7
|
-
// try and delete it if it's still there
|
|
8
|
-
indexedDB.deleteDatabase("TalismanTokenRates");
|
|
9
|
-
} catch {
|
|
10
|
-
// dont care if it fails
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const SUPPORTED_CURRENCIES = {
|
|
15
|
-
btc: {
|
|
16
|
-
name: "Bitcoin",
|
|
17
|
-
symbol: "₿"
|
|
18
|
-
},
|
|
19
|
-
eth: {
|
|
20
|
-
name: "Ethereum",
|
|
21
|
-
symbol: "Ξ"
|
|
22
|
-
},
|
|
23
|
-
dot: {
|
|
24
|
-
name: "Polkadot",
|
|
25
|
-
symbol: "D"
|
|
26
|
-
},
|
|
27
|
-
tao: {
|
|
28
|
-
name: "Bittensor",
|
|
29
|
-
symbol: "τ"
|
|
30
|
-
},
|
|
31
|
-
usd: {
|
|
32
|
-
name: "US Dollar",
|
|
33
|
-
symbol: "$"
|
|
34
|
-
},
|
|
35
|
-
cny: {
|
|
36
|
-
name: "Chinese Yuan",
|
|
37
|
-
symbol: "¥"
|
|
38
|
-
},
|
|
39
|
-
eur: {
|
|
40
|
-
name: "Euro",
|
|
41
|
-
symbol: "€"
|
|
42
|
-
},
|
|
43
|
-
gbp: {
|
|
44
|
-
name: "British Pound",
|
|
45
|
-
symbol: "£"
|
|
46
|
-
},
|
|
47
|
-
cad: {
|
|
48
|
-
name: "Canadian Dollar",
|
|
49
|
-
symbol: "C$"
|
|
50
|
-
},
|
|
51
|
-
aud: {
|
|
52
|
-
name: "Australian Dollar",
|
|
53
|
-
symbol: "A$"
|
|
54
|
-
},
|
|
55
|
-
nzd: {
|
|
56
|
-
name: "New Zealand Dollar",
|
|
57
|
-
symbol: "NZ$"
|
|
58
|
-
},
|
|
59
|
-
jpy: {
|
|
60
|
-
name: "Japanese Yen",
|
|
61
|
-
symbol: "¥"
|
|
62
|
-
},
|
|
63
|
-
rub: {
|
|
64
|
-
name: "Russian Ruble",
|
|
65
|
-
symbol: "₽"
|
|
66
|
-
},
|
|
67
|
-
krw: {
|
|
68
|
-
name: "South Korean Won",
|
|
69
|
-
symbol: "₩"
|
|
70
|
-
},
|
|
71
|
-
idr: {
|
|
72
|
-
name: "Indonesian Rupiah",
|
|
73
|
-
symbol: "Rp"
|
|
74
|
-
},
|
|
75
|
-
php: {
|
|
76
|
-
name: "Philippine Peso",
|
|
77
|
-
symbol: "₱"
|
|
78
|
-
},
|
|
79
|
-
thb: {
|
|
80
|
-
name: "Thai Baht",
|
|
81
|
-
symbol: "฿"
|
|
82
|
-
},
|
|
83
|
-
vnd: {
|
|
84
|
-
name: "Vietnamese Dong",
|
|
85
|
-
symbol: "₫"
|
|
86
|
-
},
|
|
87
|
-
inr: {
|
|
88
|
-
name: "Indian Rupee",
|
|
89
|
-
symbol: "₹"
|
|
90
|
-
},
|
|
91
|
-
try: {
|
|
92
|
-
name: "Turkish Lira",
|
|
93
|
-
symbol: "₺"
|
|
94
|
-
},
|
|
95
|
-
// hkd: { name: "Hong Kong Dollar", symbol: "HK$" },
|
|
96
|
-
sgd: {
|
|
97
|
-
name: "Singapore Dollar",
|
|
98
|
-
symbol: "S$"
|
|
99
|
-
}
|
|
100
|
-
// twd: { name: "Taiwanese Dollar", symbol: "NT$" },
|
|
101
|
-
};
|
|
102
|
-
const newTokenRates = () => ({
|
|
103
|
-
btc: null,
|
|
104
|
-
eth: null,
|
|
105
|
-
dot: null,
|
|
106
|
-
tao: null,
|
|
107
|
-
usd: null,
|
|
108
|
-
cny: null,
|
|
109
|
-
eur: null,
|
|
110
|
-
gbp: null,
|
|
111
|
-
cad: null,
|
|
112
|
-
aud: null,
|
|
113
|
-
nzd: null,
|
|
114
|
-
jpy: null,
|
|
115
|
-
rub: null,
|
|
116
|
-
krw: null,
|
|
117
|
-
idr: null,
|
|
118
|
-
php: null,
|
|
119
|
-
thb: null,
|
|
120
|
-
vnd: null,
|
|
121
|
-
inr: null,
|
|
122
|
-
try: null,
|
|
123
|
-
// hkd: null,
|
|
124
|
-
sgd: null
|
|
125
|
-
// twd: null,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
class TokenRatesError extends Error {
|
|
129
|
-
constructor(message, response) {
|
|
130
|
-
super(message);
|
|
131
|
-
this.response = response;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
const ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES);
|
|
135
|
-
const DEFAULT_COINSAPI_CONFIG = {
|
|
136
|
-
apiUrl: "https://coins.talisman.xyz"
|
|
137
|
-
};
|
|
138
|
-
async function fetchTokenRates(tokens, currencyIds = ALL_CURRENCY_IDS, config = DEFAULT_COINSAPI_CONFIG) {
|
|
139
|
-
// create a map from `coingeckoId` -> `tokenId` for each token
|
|
140
|
-
const coingeckoIdToTokenIds = Object.values(tokens).flatMap(token => {
|
|
141
|
-
// BEGIN: LP tokens have a rate which is calculated later on, using the rates of two other tokens.
|
|
142
|
-
//
|
|
143
|
-
// This section contains the logic such that: if token is an LP token, then fetch the rates for the two underlying tokens.
|
|
144
|
-
if (token.type === "evm-uniswapv2") {
|
|
145
|
-
if (token.platform !== "ethereum") return [];
|
|
146
|
-
const getToken = (evmNetworkId, tokenAddress, coingeckoId) => ({
|
|
147
|
-
id: chaindataProvider.evmErc20TokenId(evmNetworkId, tokenAddress),
|
|
148
|
-
coingeckoId
|
|
149
|
-
});
|
|
150
|
-
const token0 = token.coingeckoId0 ? [getToken(token.networkId, token.tokenAddress0, token.coingeckoId0)] : [];
|
|
151
|
-
const token1 = token.coingeckoId1 ? [getToken(token.networkId, token.tokenAddress1, token.coingeckoId1)] : [];
|
|
152
|
-
return [...token0, ...token1];
|
|
153
|
-
}
|
|
154
|
-
// END: LP tokens have a rate which is calculated later on, using the rates of two other tokens.
|
|
155
|
-
|
|
156
|
-
// ignore tokens which don't have a coingeckoId
|
|
157
|
-
if (!token.coingeckoId) return [];
|
|
158
|
-
return [{
|
|
159
|
-
id: token.id,
|
|
160
|
-
coingeckoId: token.coingeckoId
|
|
161
|
-
}];
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
// get each token's coingeckoId
|
|
165
|
-
.reduce((coingeckoIdToTokenIds, {
|
|
166
|
-
id,
|
|
167
|
-
coingeckoId
|
|
168
|
-
}) => {
|
|
169
|
-
if (!coingeckoIdToTokenIds[coingeckoId]) coingeckoIdToTokenIds[coingeckoId] = [];
|
|
170
|
-
coingeckoIdToTokenIds[coingeckoId].push(id);
|
|
171
|
-
return coingeckoIdToTokenIds;
|
|
172
|
-
}, {});
|
|
173
|
-
|
|
174
|
-
// create a list of coingeckoIds we want to fetch
|
|
175
|
-
const coingeckoIds = Object.keys(coingeckoIdToTokenIds).sort();
|
|
176
|
-
|
|
177
|
-
// skip network request if there is nothing for us to fetch
|
|
178
|
-
if (coingeckoIds.length < 1) return {};
|
|
179
|
-
|
|
180
|
-
// If `currencyIds` includes `tao`, we need to always fetch the `bittensor` coingeckoId and the `usd` currency,
|
|
181
|
-
// we can use these to calculate the currency rate for TAO relative to all other tokens.
|
|
182
|
-
//
|
|
183
|
-
// We support showing balances in TAO just like we support BTC/ETH/DOT, but coingecko doesn't support TAO as a vs currency rate.
|
|
184
|
-
// We can macgyver our own TOKEN<>TAO rate by combining the TOKEN<>USD data with the TAO<>USD data.
|
|
185
|
-
const hasVsTao = currencyIds.includes("tao");
|
|
186
|
-
const [effectiveCoingeckoIds, effectiveCurrencyIds] = hasVsTao ? [[...new Set(coingeckoIds).add("bittensor")], [...new Set(
|
|
187
|
-
// don't request `tao` from coingecko (we calculate it from `usd`)
|
|
188
|
-
currencyIds.filter(c => c !== "tao"))
|
|
189
|
-
// always include `usd` (so we can calculate `tao`)
|
|
190
|
-
.add("usd")]] : [coingeckoIds, currencyIds];
|
|
191
|
-
const response = await fetch(`${config.apiUrl}/token-rates`, {
|
|
192
|
-
method: "POST",
|
|
193
|
-
body: JSON.stringify({
|
|
194
|
-
coingeckoIds: effectiveCoingeckoIds,
|
|
195
|
-
currencyIds: effectiveCurrencyIds
|
|
196
|
-
})
|
|
197
|
-
});
|
|
198
|
-
const rawTokenRates = await response.json();
|
|
199
|
-
if (hasVsTao) {
|
|
200
|
-
// calculate the TAO<>USD rate
|
|
201
|
-
const effectiveTaoIndex = effectiveCoingeckoIds.indexOf("bittensor");
|
|
202
|
-
const effectiveUsdIndex = effectiveCurrencyIds.indexOf("usd");
|
|
203
|
-
const taoUsdRate = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[0];
|
|
204
|
-
const taoUsdChange24h = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[2];
|
|
205
|
-
|
|
206
|
-
// insert TOKEN<>TAO rate (calculated based on TAO<>USD rate and TOKEN<>USD rate) into each TOKEN
|
|
207
|
-
const taoIndex = currencyIds.indexOf("tao");
|
|
208
|
-
rawTokenRates.forEach((rates, i) => {
|
|
209
|
-
// hardcoded rate for TAO<>TAO
|
|
210
|
-
if (i === effectiveTaoIndex) {
|
|
211
|
-
rates?.splice(taoIndex, 0, [1, null, null]);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// get TOKEN<>USD rate
|
|
216
|
-
const tokenUsdRate = rates?.[effectiveUsdIndex]?.[0];
|
|
217
|
-
// calculate TOKEN<>TAO rate
|
|
218
|
-
const tokenTaoRate = typeof tokenUsdRate === "number" && typeof taoUsdRate === "number" && taoUsdRate !== 0 ? tokenUsdRate / taoUsdRate : null;
|
|
219
|
-
const tokenUsdChange24h = rates?.[effectiveUsdIndex]?.[2];
|
|
220
|
-
const tokenTaoChange24h = typeof taoUsdChange24h === "number" && typeof tokenUsdChange24h === "number" ? (1 + tokenUsdChange24h) / (1 + taoUsdChange24h) - 1 : null;
|
|
221
|
-
|
|
222
|
-
// insert at the correct location (based on the index of `tao` in `currencyIds`)
|
|
223
|
-
rates?.splice(taoIndex, 0, [tokenTaoRate, null, tokenTaoChange24h]);
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds);
|
|
227
|
-
|
|
228
|
-
// build a TokenRatesList from the token prices result
|
|
229
|
-
const ratesList = Object.fromEntries(Object.entries(tokens).map(([tokenId, token]) => [tokenId, token.coingeckoId ? tokenRates[token.coingeckoId] ?? null : null]));
|
|
230
|
-
return ratesList;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// To save on bandwidth and work around response size limits, values are returned without json property names
|
|
234
|
-
// (e.g. [[[12, 12332, 0.5]]] instead of { dot : {usd: { value: 12, marketCap: 12332, change24h: 0.5 }} })
|
|
235
|
-
|
|
236
|
-
const parseTokenRatesFromApi = (rawTokenRates, coingeckoIds, currencyIds) => {
|
|
237
|
-
return Object.fromEntries(coingeckoIds.map((coingeckoId, idx) => {
|
|
238
|
-
const rates = rawTokenRates[idx];
|
|
239
|
-
if (!rates) return [coingeckoId, null];
|
|
240
|
-
return [coingeckoId, Object.fromEntries(currencyIds.map((currencyId, idx) => {
|
|
241
|
-
const curRate = rates[idx];
|
|
242
|
-
if (!curRate) return [currencyId, null];
|
|
243
|
-
const [price, marketCap, change24h] = rates[idx];
|
|
244
|
-
return [currencyId, {
|
|
245
|
-
price,
|
|
246
|
-
marketCap,
|
|
247
|
-
change24h
|
|
248
|
-
}];
|
|
249
|
-
}))];
|
|
250
|
-
}));
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
exports.ALL_CURRENCY_IDS = ALL_CURRENCY_IDS;
|
|
254
|
-
exports.DEFAULT_COINSAPI_CONFIG = DEFAULT_COINSAPI_CONFIG;
|
|
255
|
-
exports.SUPPORTED_CURRENCIES = SUPPORTED_CURRENCIES;
|
|
256
|
-
exports.TokenRatesError = TokenRatesError;
|
|
257
|
-
exports.fetchTokenRates = fetchTokenRates;
|
|
258
|
-
exports.newTokenRates = newTokenRates;
|
|
259
|
-
exports.tryToDeleteOldTokenRatesDb = tryToDeleteOldTokenRatesDb;
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chaindataProvider = require('@talismn/chaindata-provider');
|
|
4
|
-
|
|
5
|
-
const tryToDeleteOldTokenRatesDb = () => {
|
|
6
|
-
try {
|
|
7
|
-
// try and delete it if it's still there
|
|
8
|
-
indexedDB.deleteDatabase("TalismanTokenRates");
|
|
9
|
-
} catch {
|
|
10
|
-
// dont care if it fails
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const SUPPORTED_CURRENCIES = {
|
|
15
|
-
btc: {
|
|
16
|
-
name: "Bitcoin",
|
|
17
|
-
symbol: "₿"
|
|
18
|
-
},
|
|
19
|
-
eth: {
|
|
20
|
-
name: "Ethereum",
|
|
21
|
-
symbol: "Ξ"
|
|
22
|
-
},
|
|
23
|
-
dot: {
|
|
24
|
-
name: "Polkadot",
|
|
25
|
-
symbol: "D"
|
|
26
|
-
},
|
|
27
|
-
tao: {
|
|
28
|
-
name: "Bittensor",
|
|
29
|
-
symbol: "τ"
|
|
30
|
-
},
|
|
31
|
-
usd: {
|
|
32
|
-
name: "US Dollar",
|
|
33
|
-
symbol: "$"
|
|
34
|
-
},
|
|
35
|
-
cny: {
|
|
36
|
-
name: "Chinese Yuan",
|
|
37
|
-
symbol: "¥"
|
|
38
|
-
},
|
|
39
|
-
eur: {
|
|
40
|
-
name: "Euro",
|
|
41
|
-
symbol: "€"
|
|
42
|
-
},
|
|
43
|
-
gbp: {
|
|
44
|
-
name: "British Pound",
|
|
45
|
-
symbol: "£"
|
|
46
|
-
},
|
|
47
|
-
cad: {
|
|
48
|
-
name: "Canadian Dollar",
|
|
49
|
-
symbol: "C$"
|
|
50
|
-
},
|
|
51
|
-
aud: {
|
|
52
|
-
name: "Australian Dollar",
|
|
53
|
-
symbol: "A$"
|
|
54
|
-
},
|
|
55
|
-
nzd: {
|
|
56
|
-
name: "New Zealand Dollar",
|
|
57
|
-
symbol: "NZ$"
|
|
58
|
-
},
|
|
59
|
-
jpy: {
|
|
60
|
-
name: "Japanese Yen",
|
|
61
|
-
symbol: "¥"
|
|
62
|
-
},
|
|
63
|
-
rub: {
|
|
64
|
-
name: "Russian Ruble",
|
|
65
|
-
symbol: "₽"
|
|
66
|
-
},
|
|
67
|
-
krw: {
|
|
68
|
-
name: "South Korean Won",
|
|
69
|
-
symbol: "₩"
|
|
70
|
-
},
|
|
71
|
-
idr: {
|
|
72
|
-
name: "Indonesian Rupiah",
|
|
73
|
-
symbol: "Rp"
|
|
74
|
-
},
|
|
75
|
-
php: {
|
|
76
|
-
name: "Philippine Peso",
|
|
77
|
-
symbol: "₱"
|
|
78
|
-
},
|
|
79
|
-
thb: {
|
|
80
|
-
name: "Thai Baht",
|
|
81
|
-
symbol: "฿"
|
|
82
|
-
},
|
|
83
|
-
vnd: {
|
|
84
|
-
name: "Vietnamese Dong",
|
|
85
|
-
symbol: "₫"
|
|
86
|
-
},
|
|
87
|
-
inr: {
|
|
88
|
-
name: "Indian Rupee",
|
|
89
|
-
symbol: "₹"
|
|
90
|
-
},
|
|
91
|
-
try: {
|
|
92
|
-
name: "Turkish Lira",
|
|
93
|
-
symbol: "₺"
|
|
94
|
-
},
|
|
95
|
-
// hkd: { name: "Hong Kong Dollar", symbol: "HK$" },
|
|
96
|
-
sgd: {
|
|
97
|
-
name: "Singapore Dollar",
|
|
98
|
-
symbol: "S$"
|
|
99
|
-
}
|
|
100
|
-
// twd: { name: "Taiwanese Dollar", symbol: "NT$" },
|
|
101
|
-
};
|
|
102
|
-
const newTokenRates = () => ({
|
|
103
|
-
btc: null,
|
|
104
|
-
eth: null,
|
|
105
|
-
dot: null,
|
|
106
|
-
tao: null,
|
|
107
|
-
usd: null,
|
|
108
|
-
cny: null,
|
|
109
|
-
eur: null,
|
|
110
|
-
gbp: null,
|
|
111
|
-
cad: null,
|
|
112
|
-
aud: null,
|
|
113
|
-
nzd: null,
|
|
114
|
-
jpy: null,
|
|
115
|
-
rub: null,
|
|
116
|
-
krw: null,
|
|
117
|
-
idr: null,
|
|
118
|
-
php: null,
|
|
119
|
-
thb: null,
|
|
120
|
-
vnd: null,
|
|
121
|
-
inr: null,
|
|
122
|
-
try: null,
|
|
123
|
-
// hkd: null,
|
|
124
|
-
sgd: null
|
|
125
|
-
// twd: null,
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
class TokenRatesError extends Error {
|
|
129
|
-
constructor(message, response) {
|
|
130
|
-
super(message);
|
|
131
|
-
this.response = response;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
const ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES);
|
|
135
|
-
const DEFAULT_COINSAPI_CONFIG = {
|
|
136
|
-
apiUrl: "https://coins.talisman.xyz"
|
|
137
|
-
};
|
|
138
|
-
async function fetchTokenRates(tokens, currencyIds = ALL_CURRENCY_IDS, config = DEFAULT_COINSAPI_CONFIG) {
|
|
139
|
-
// create a map from `coingeckoId` -> `tokenId` for each token
|
|
140
|
-
const coingeckoIdToTokenIds = Object.values(tokens).flatMap(token => {
|
|
141
|
-
// BEGIN: LP tokens have a rate which is calculated later on, using the rates of two other tokens.
|
|
142
|
-
//
|
|
143
|
-
// This section contains the logic such that: if token is an LP token, then fetch the rates for the two underlying tokens.
|
|
144
|
-
if (token.type === "evm-uniswapv2") {
|
|
145
|
-
if (token.platform !== "ethereum") return [];
|
|
146
|
-
const getToken = (evmNetworkId, tokenAddress, coingeckoId) => ({
|
|
147
|
-
id: chaindataProvider.evmErc20TokenId(evmNetworkId, tokenAddress),
|
|
148
|
-
coingeckoId
|
|
149
|
-
});
|
|
150
|
-
const token0 = token.coingeckoId0 ? [getToken(token.networkId, token.tokenAddress0, token.coingeckoId0)] : [];
|
|
151
|
-
const token1 = token.coingeckoId1 ? [getToken(token.networkId, token.tokenAddress1, token.coingeckoId1)] : [];
|
|
152
|
-
return [...token0, ...token1];
|
|
153
|
-
}
|
|
154
|
-
// END: LP tokens have a rate which is calculated later on, using the rates of two other tokens.
|
|
155
|
-
|
|
156
|
-
// ignore tokens which don't have a coingeckoId
|
|
157
|
-
if (!token.coingeckoId) return [];
|
|
158
|
-
return [{
|
|
159
|
-
id: token.id,
|
|
160
|
-
coingeckoId: token.coingeckoId
|
|
161
|
-
}];
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
// get each token's coingeckoId
|
|
165
|
-
.reduce((coingeckoIdToTokenIds, {
|
|
166
|
-
id,
|
|
167
|
-
coingeckoId
|
|
168
|
-
}) => {
|
|
169
|
-
if (!coingeckoIdToTokenIds[coingeckoId]) coingeckoIdToTokenIds[coingeckoId] = [];
|
|
170
|
-
coingeckoIdToTokenIds[coingeckoId].push(id);
|
|
171
|
-
return coingeckoIdToTokenIds;
|
|
172
|
-
}, {});
|
|
173
|
-
|
|
174
|
-
// create a list of coingeckoIds we want to fetch
|
|
175
|
-
const coingeckoIds = Object.keys(coingeckoIdToTokenIds).sort();
|
|
176
|
-
|
|
177
|
-
// skip network request if there is nothing for us to fetch
|
|
178
|
-
if (coingeckoIds.length < 1) return {};
|
|
179
|
-
|
|
180
|
-
// If `currencyIds` includes `tao`, we need to always fetch the `bittensor` coingeckoId and the `usd` currency,
|
|
181
|
-
// we can use these to calculate the currency rate for TAO relative to all other tokens.
|
|
182
|
-
//
|
|
183
|
-
// We support showing balances in TAO just like we support BTC/ETH/DOT, but coingecko doesn't support TAO as a vs currency rate.
|
|
184
|
-
// We can macgyver our own TOKEN<>TAO rate by combining the TOKEN<>USD data with the TAO<>USD data.
|
|
185
|
-
const hasVsTao = currencyIds.includes("tao");
|
|
186
|
-
const [effectiveCoingeckoIds, effectiveCurrencyIds] = hasVsTao ? [[...new Set(coingeckoIds).add("bittensor")], [...new Set(
|
|
187
|
-
// don't request `tao` from coingecko (we calculate it from `usd`)
|
|
188
|
-
currencyIds.filter(c => c !== "tao"))
|
|
189
|
-
// always include `usd` (so we can calculate `tao`)
|
|
190
|
-
.add("usd")]] : [coingeckoIds, currencyIds];
|
|
191
|
-
const response = await fetch(`${config.apiUrl}/token-rates`, {
|
|
192
|
-
method: "POST",
|
|
193
|
-
body: JSON.stringify({
|
|
194
|
-
coingeckoIds: effectiveCoingeckoIds,
|
|
195
|
-
currencyIds: effectiveCurrencyIds
|
|
196
|
-
})
|
|
197
|
-
});
|
|
198
|
-
const rawTokenRates = await response.json();
|
|
199
|
-
if (hasVsTao) {
|
|
200
|
-
// calculate the TAO<>USD rate
|
|
201
|
-
const effectiveTaoIndex = effectiveCoingeckoIds.indexOf("bittensor");
|
|
202
|
-
const effectiveUsdIndex = effectiveCurrencyIds.indexOf("usd");
|
|
203
|
-
const taoUsdRate = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[0];
|
|
204
|
-
const taoUsdChange24h = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[2];
|
|
205
|
-
|
|
206
|
-
// insert TOKEN<>TAO rate (calculated based on TAO<>USD rate and TOKEN<>USD rate) into each TOKEN
|
|
207
|
-
const taoIndex = currencyIds.indexOf("tao");
|
|
208
|
-
rawTokenRates.forEach((rates, i) => {
|
|
209
|
-
// hardcoded rate for TAO<>TAO
|
|
210
|
-
if (i === effectiveTaoIndex) {
|
|
211
|
-
rates?.splice(taoIndex, 0, [1, null, null]);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// get TOKEN<>USD rate
|
|
216
|
-
const tokenUsdRate = rates?.[effectiveUsdIndex]?.[0];
|
|
217
|
-
// calculate TOKEN<>TAO rate
|
|
218
|
-
const tokenTaoRate = typeof tokenUsdRate === "number" && typeof taoUsdRate === "number" && taoUsdRate !== 0 ? tokenUsdRate / taoUsdRate : null;
|
|
219
|
-
const tokenUsdChange24h = rates?.[effectiveUsdIndex]?.[2];
|
|
220
|
-
const tokenTaoChange24h = typeof taoUsdChange24h === "number" && typeof tokenUsdChange24h === "number" ? (1 + tokenUsdChange24h) / (1 + taoUsdChange24h) - 1 : null;
|
|
221
|
-
|
|
222
|
-
// insert at the correct location (based on the index of `tao` in `currencyIds`)
|
|
223
|
-
rates?.splice(taoIndex, 0, [tokenTaoRate, null, tokenTaoChange24h]);
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds);
|
|
227
|
-
|
|
228
|
-
// build a TokenRatesList from the token prices result
|
|
229
|
-
const ratesList = Object.fromEntries(Object.entries(tokens).map(([tokenId, token]) => [tokenId, token.coingeckoId ? tokenRates[token.coingeckoId] ?? null : null]));
|
|
230
|
-
return ratesList;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// To save on bandwidth and work around response size limits, values are returned without json property names
|
|
234
|
-
// (e.g. [[[12, 12332, 0.5]]] instead of { dot : {usd: { value: 12, marketCap: 12332, change24h: 0.5 }} })
|
|
235
|
-
|
|
236
|
-
const parseTokenRatesFromApi = (rawTokenRates, coingeckoIds, currencyIds) => {
|
|
237
|
-
return Object.fromEntries(coingeckoIds.map((coingeckoId, idx) => {
|
|
238
|
-
const rates = rawTokenRates[idx];
|
|
239
|
-
if (!rates) return [coingeckoId, null];
|
|
240
|
-
return [coingeckoId, Object.fromEntries(currencyIds.map((currencyId, idx) => {
|
|
241
|
-
const curRate = rates[idx];
|
|
242
|
-
if (!curRate) return [currencyId, null];
|
|
243
|
-
const [price, marketCap, change24h] = rates[idx];
|
|
244
|
-
return [currencyId, {
|
|
245
|
-
price,
|
|
246
|
-
marketCap,
|
|
247
|
-
change24h
|
|
248
|
-
}];
|
|
249
|
-
}))];
|
|
250
|
-
}));
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
exports.ALL_CURRENCY_IDS = ALL_CURRENCY_IDS;
|
|
254
|
-
exports.DEFAULT_COINSAPI_CONFIG = DEFAULT_COINSAPI_CONFIG;
|
|
255
|
-
exports.SUPPORTED_CURRENCIES = SUPPORTED_CURRENCIES;
|
|
256
|
-
exports.TokenRatesError = TokenRatesError;
|
|
257
|
-
exports.fetchTokenRates = fetchTokenRates;
|
|
258
|
-
exports.newTokenRates = newTokenRates;
|
|
259
|
-
exports.tryToDeleteOldTokenRatesDb = tryToDeleteOldTokenRatesDb;
|