@talismn/token-rates 3.0.15 → 3.0.16

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,5 +1,8 @@
1
- import { TokenId } from "@talismn/chaindata-provider";
2
- export declare const SUPPORTED_CURRENCIES: {
1
+ import { TokenId, Token } from '@talismn/chaindata-provider';
2
+
3
+ declare const tryToDeleteOldTokenRatesDb: () => void;
4
+
5
+ declare const SUPPORTED_CURRENCIES: {
3
6
  readonly btc: {
4
7
  readonly name: "Bitcoin";
5
8
  readonly symbol: "₿";
@@ -85,15 +88,28 @@ export declare const SUPPORTED_CURRENCIES: {
85
88
  readonly symbol: "S$";
86
89
  };
87
90
  };
88
- export type TokenRatesStorage = {
91
+ type TokenRatesStorage = {
89
92
  tokenRates: TokenRatesList;
90
93
  };
91
- export type TokenRatesList = Record<TokenId, TokenRates>;
92
- export type TokenRates = Record<TokenRateCurrency, TokenRateData | null>;
93
- export type TokenRateData = {
94
+ type TokenRatesList = Record<TokenId, TokenRates>;
95
+ type TokenRates = Record<TokenRateCurrency, TokenRateData | null>;
96
+ type TokenRateData = {
94
97
  price: number;
95
98
  marketCap?: number;
96
99
  change24h?: number;
97
100
  };
98
- export type TokenRateCurrency = keyof typeof SUPPORTED_CURRENCIES;
99
- export declare const newTokenRates: () => TokenRates;
101
+ type TokenRateCurrency = keyof typeof SUPPORTED_CURRENCIES;
102
+ declare const newTokenRates: () => TokenRates;
103
+
104
+ declare class TokenRatesError extends Error {
105
+ response?: Response;
106
+ constructor(message: string, response?: Response);
107
+ }
108
+ declare const ALL_CURRENCY_IDS: TokenRateCurrency[];
109
+ type CoinsApiConfig = {
110
+ apiUrl: string;
111
+ };
112
+ declare const DEFAULT_COINSAPI_CONFIG: CoinsApiConfig;
113
+ declare function fetchTokenRates(tokens: Record<TokenId, Token>, currencyIds?: TokenRateCurrency[], config?: CoinsApiConfig): Promise<TokenRatesList>;
114
+
115
+ export { ALL_CURRENCY_IDS, type CoinsApiConfig, DEFAULT_COINSAPI_CONFIG, SUPPORTED_CURRENCIES, type TokenRateCurrency, type TokenRateData, type TokenRates, TokenRatesError, type TokenRatesList, type TokenRatesStorage, fetchTokenRates, newTokenRates, tryToDeleteOldTokenRatesDb };
@@ -0,0 +1,115 @@
1
+ import { TokenId, Token } from '@talismn/chaindata-provider';
2
+
3
+ declare const tryToDeleteOldTokenRatesDb: () => void;
4
+
5
+ declare const SUPPORTED_CURRENCIES: {
6
+ readonly btc: {
7
+ readonly name: "Bitcoin";
8
+ readonly symbol: "₿";
9
+ };
10
+ readonly eth: {
11
+ readonly name: "Ethereum";
12
+ readonly symbol: "Ξ";
13
+ };
14
+ readonly dot: {
15
+ readonly name: "Polkadot";
16
+ readonly symbol: "D";
17
+ };
18
+ readonly tao: {
19
+ readonly name: "Bittensor";
20
+ readonly symbol: "τ";
21
+ };
22
+ readonly usd: {
23
+ readonly name: "US Dollar";
24
+ readonly symbol: "$";
25
+ };
26
+ readonly cny: {
27
+ readonly name: "Chinese Yuan";
28
+ readonly symbol: "¥";
29
+ };
30
+ readonly eur: {
31
+ readonly name: "Euro";
32
+ readonly symbol: "€";
33
+ };
34
+ readonly gbp: {
35
+ readonly name: "British Pound";
36
+ readonly symbol: "£";
37
+ };
38
+ readonly cad: {
39
+ readonly name: "Canadian Dollar";
40
+ readonly symbol: "C$";
41
+ };
42
+ readonly aud: {
43
+ readonly name: "Australian Dollar";
44
+ readonly symbol: "A$";
45
+ };
46
+ readonly nzd: {
47
+ readonly name: "New Zealand Dollar";
48
+ readonly symbol: "NZ$";
49
+ };
50
+ readonly jpy: {
51
+ readonly name: "Japanese Yen";
52
+ readonly symbol: "¥";
53
+ };
54
+ readonly rub: {
55
+ readonly name: "Russian Ruble";
56
+ readonly symbol: "₽";
57
+ };
58
+ readonly krw: {
59
+ readonly name: "South Korean Won";
60
+ readonly symbol: "₩";
61
+ };
62
+ readonly idr: {
63
+ readonly name: "Indonesian Rupiah";
64
+ readonly symbol: "Rp";
65
+ };
66
+ readonly php: {
67
+ readonly name: "Philippine Peso";
68
+ readonly symbol: "₱";
69
+ };
70
+ readonly thb: {
71
+ readonly name: "Thai Baht";
72
+ readonly symbol: "฿";
73
+ };
74
+ readonly vnd: {
75
+ readonly name: "Vietnamese Dong";
76
+ readonly symbol: "₫";
77
+ };
78
+ readonly inr: {
79
+ readonly name: "Indian Rupee";
80
+ readonly symbol: "₹";
81
+ };
82
+ readonly try: {
83
+ readonly name: "Turkish Lira";
84
+ readonly symbol: "₺";
85
+ };
86
+ readonly sgd: {
87
+ readonly name: "Singapore Dollar";
88
+ readonly symbol: "S$";
89
+ };
90
+ };
91
+ type TokenRatesStorage = {
92
+ tokenRates: TokenRatesList;
93
+ };
94
+ type TokenRatesList = Record<TokenId, TokenRates>;
95
+ type TokenRates = Record<TokenRateCurrency, TokenRateData | null>;
96
+ type TokenRateData = {
97
+ price: number;
98
+ marketCap?: number;
99
+ change24h?: number;
100
+ };
101
+ type TokenRateCurrency = keyof typeof SUPPORTED_CURRENCIES;
102
+ declare const newTokenRates: () => TokenRates;
103
+
104
+ declare class TokenRatesError extends Error {
105
+ response?: Response;
106
+ constructor(message: string, response?: Response);
107
+ }
108
+ declare const ALL_CURRENCY_IDS: TokenRateCurrency[];
109
+ type CoinsApiConfig = {
110
+ apiUrl: string;
111
+ };
112
+ declare const DEFAULT_COINSAPI_CONFIG: CoinsApiConfig;
113
+ declare function fetchTokenRates(tokens: Record<TokenId, Token>, currencyIds?: TokenRateCurrency[], config?: CoinsApiConfig): Promise<TokenRatesList>;
114
+
115
+ export { ALL_CURRENCY_IDS, type CoinsApiConfig, DEFAULT_COINSAPI_CONFIG, SUPPORTED_CURRENCIES, type TokenRateCurrency, type TokenRateData, type TokenRates, TokenRatesError, type TokenRatesList, type TokenRatesStorage, fetchTokenRates, newTokenRates, tryToDeleteOldTokenRatesDb };
package/dist/index.js ADDED
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ALL_CURRENCY_IDS: () => ALL_CURRENCY_IDS,
24
+ DEFAULT_COINSAPI_CONFIG: () => DEFAULT_COINSAPI_CONFIG,
25
+ SUPPORTED_CURRENCIES: () => SUPPORTED_CURRENCIES,
26
+ TokenRatesError: () => TokenRatesError,
27
+ fetchTokenRates: () => fetchTokenRates,
28
+ newTokenRates: () => newTokenRates,
29
+ tryToDeleteOldTokenRatesDb: () => tryToDeleteOldTokenRatesDb
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/TalismanTokenRatesDatabase.ts
34
+ var tryToDeleteOldTokenRatesDb = () => {
35
+ try {
36
+ indexedDB.deleteDatabase("TalismanTokenRates");
37
+ } catch {
38
+ }
39
+ };
40
+
41
+ // src/TokenRates.ts
42
+ var import_chaindata_provider = require("@talismn/chaindata-provider");
43
+
44
+ // src/types.ts
45
+ var SUPPORTED_CURRENCIES = {
46
+ btc: { name: "Bitcoin", symbol: "\u20BF" },
47
+ eth: { name: "Ethereum", symbol: "\u039E" },
48
+ dot: { name: "Polkadot", symbol: "D" },
49
+ tao: { name: "Bittensor", symbol: "\u03C4" },
50
+ usd: { name: "US Dollar", symbol: "$" },
51
+ cny: { name: "Chinese Yuan", symbol: "\xA5" },
52
+ eur: { name: "Euro", symbol: "\u20AC" },
53
+ gbp: { name: "British Pound", symbol: "\xA3" },
54
+ cad: { name: "Canadian Dollar", symbol: "C$" },
55
+ aud: { name: "Australian Dollar", symbol: "A$" },
56
+ nzd: { name: "New Zealand Dollar", symbol: "NZ$" },
57
+ jpy: { name: "Japanese Yen", symbol: "\xA5" },
58
+ rub: { name: "Russian Ruble", symbol: "\u20BD" },
59
+ krw: { name: "South Korean Won", symbol: "\u20A9" },
60
+ idr: { name: "Indonesian Rupiah", symbol: "Rp" },
61
+ php: { name: "Philippine Peso", symbol: "\u20B1" },
62
+ thb: { name: "Thai Baht", symbol: "\u0E3F" },
63
+ vnd: { name: "Vietnamese Dong", symbol: "\u20AB" },
64
+ inr: { name: "Indian Rupee", symbol: "\u20B9" },
65
+ try: { name: "Turkish Lira", symbol: "\u20BA" },
66
+ // hkd: { name: "Hong Kong Dollar", symbol: "HK$" },
67
+ sgd: { name: "Singapore Dollar", symbol: "S$" }
68
+ // twd: { name: "Taiwanese Dollar", symbol: "NT$" },
69
+ };
70
+ var newTokenRates = () => ({
71
+ btc: null,
72
+ eth: null,
73
+ dot: null,
74
+ tao: null,
75
+ usd: null,
76
+ cny: null,
77
+ eur: null,
78
+ gbp: null,
79
+ cad: null,
80
+ aud: null,
81
+ nzd: null,
82
+ jpy: null,
83
+ rub: null,
84
+ krw: null,
85
+ idr: null,
86
+ php: null,
87
+ thb: null,
88
+ vnd: null,
89
+ inr: null,
90
+ try: null,
91
+ // hkd: null,
92
+ sgd: null
93
+ // twd: null,
94
+ });
95
+
96
+ // src/TokenRates.ts
97
+ var TokenRatesError = class extends Error {
98
+ response;
99
+ constructor(message, response) {
100
+ super(message);
101
+ this.response = response;
102
+ }
103
+ };
104
+ var ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES);
105
+ var DEFAULT_COINSAPI_CONFIG = {
106
+ apiUrl: "https://coins.talisman.xyz"
107
+ };
108
+ async function fetchTokenRates(tokens, currencyIds = ALL_CURRENCY_IDS, config = DEFAULT_COINSAPI_CONFIG) {
109
+ const coingeckoIdToTokenIds = Object.values(tokens).flatMap((token) => {
110
+ if (token.type === "evm-uniswapv2") {
111
+ if (token.platform !== "ethereum") return [];
112
+ const getToken = (evmNetworkId, tokenAddress, coingeckoId) => ({
113
+ id: (0, import_chaindata_provider.evmErc20TokenId)(evmNetworkId, tokenAddress),
114
+ coingeckoId
115
+ });
116
+ const token0 = token.coingeckoId0 ? [getToken(token.networkId, token.tokenAddress0, token.coingeckoId0)] : [];
117
+ const token1 = token.coingeckoId1 ? [getToken(token.networkId, token.tokenAddress1, token.coingeckoId1)] : [];
118
+ return [...token0, ...token1];
119
+ }
120
+ if (!token.coingeckoId) return [];
121
+ return [{ id: token.id, coingeckoId: token.coingeckoId }];
122
+ }).reduce(
123
+ (coingeckoIdToTokenIds2, { id, coingeckoId }) => {
124
+ if (!coingeckoIdToTokenIds2[coingeckoId]) coingeckoIdToTokenIds2[coingeckoId] = [];
125
+ coingeckoIdToTokenIds2[coingeckoId].push(id);
126
+ return coingeckoIdToTokenIds2;
127
+ },
128
+ {}
129
+ );
130
+ const coingeckoIds = Object.keys(coingeckoIdToTokenIds).sort();
131
+ if (coingeckoIds.length < 1) return {};
132
+ const hasVsTao = currencyIds.includes("tao");
133
+ const [effectiveCoingeckoIds, effectiveCurrencyIds] = hasVsTao ? [
134
+ [...new Set(coingeckoIds).add("bittensor")],
135
+ [
136
+ ...new Set(
137
+ // don't request `tao` from coingecko (we calculate it from `usd`)
138
+ currencyIds.filter((c) => c !== "tao")
139
+ ).add("usd")
140
+ ]
141
+ ] : [coingeckoIds, currencyIds];
142
+ const response = await fetch(`${config.apiUrl}/token-rates`, {
143
+ method: "POST",
144
+ body: JSON.stringify({
145
+ coingeckoIds: effectiveCoingeckoIds,
146
+ currencyIds: effectiveCurrencyIds
147
+ })
148
+ });
149
+ const rawTokenRates = await response.json();
150
+ if (hasVsTao) {
151
+ const effectiveTaoIndex = effectiveCoingeckoIds.indexOf("bittensor");
152
+ const effectiveUsdIndex = effectiveCurrencyIds.indexOf("usd");
153
+ const taoUsdRate = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[0];
154
+ const taoUsdChange24h = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[2];
155
+ const taoIndex = currencyIds.indexOf("tao");
156
+ rawTokenRates.forEach((rates, i) => {
157
+ if (i === effectiveTaoIndex) {
158
+ rates?.splice(taoIndex, 0, [1, null, null]);
159
+ return;
160
+ }
161
+ const tokenUsdRate = rates?.[effectiveUsdIndex]?.[0];
162
+ const tokenTaoRate = typeof tokenUsdRate === "number" && typeof taoUsdRate === "number" && taoUsdRate !== 0 ? tokenUsdRate / taoUsdRate : null;
163
+ const tokenUsdChange24h = rates?.[effectiveUsdIndex]?.[2];
164
+ const tokenTaoChange24h = typeof taoUsdChange24h === "number" && typeof tokenUsdChange24h === "number" ? (1 + tokenUsdChange24h) / (1 + taoUsdChange24h) - 1 : null;
165
+ rates?.splice(taoIndex, 0, [tokenTaoRate, null, tokenTaoChange24h]);
166
+ });
167
+ }
168
+ const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds);
169
+ const ratesList = Object.fromEntries(
170
+ Object.entries(tokens).map(([tokenId, token]) => [
171
+ tokenId,
172
+ token.coingeckoId ? tokenRates[token.coingeckoId] ?? null : null
173
+ ])
174
+ );
175
+ return ratesList;
176
+ }
177
+ var parseTokenRatesFromApi = (rawTokenRates, coingeckoIds, currencyIds) => {
178
+ return Object.fromEntries(
179
+ coingeckoIds.map((coingeckoId, idx) => {
180
+ const rates = rawTokenRates[idx];
181
+ if (!rates) return [coingeckoId, null];
182
+ return [
183
+ coingeckoId,
184
+ Object.fromEntries(
185
+ currencyIds.map((currencyId, idx2) => {
186
+ const curRate = rates[idx2];
187
+ if (!curRate) return [currencyId, null];
188
+ const [price, marketCap, change24h] = rates[idx2];
189
+ return [currencyId, { price, marketCap, change24h }];
190
+ })
191
+ )
192
+ ];
193
+ })
194
+ );
195
+ };
196
+ // Annotate the CommonJS export names for ESM import in node:
197
+ 0 && (module.exports = {
198
+ ALL_CURRENCY_IDS,
199
+ DEFAULT_COINSAPI_CONFIG,
200
+ SUPPORTED_CURRENCIES,
201
+ TokenRatesError,
202
+ fetchTokenRates,
203
+ newTokenRates,
204
+ tryToDeleteOldTokenRatesDb
205
+ });
206
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/TalismanTokenRatesDatabase.ts","../src/TokenRates.ts","../src/types.ts"],"sourcesContent":["export * from \"./TalismanTokenRatesDatabase\"\nexport * from \"./TokenRates\"\nexport * from \"./types\"\n","export const tryToDeleteOldTokenRatesDb = () => {\n try {\n // try and delete it if it's still there\n indexedDB.deleteDatabase(\"TalismanTokenRates\")\n } catch {\n // dont care if it fails\n }\n}\n","import { evmErc20TokenId, type Token, type TokenId } from \"@talismn/chaindata-provider\"\n\nimport {\n SUPPORTED_CURRENCIES,\n type TokenRateCurrency,\n type TokenRateData,\n type TokenRates,\n type TokenRatesList,\n} from \"./types\"\n\nexport class TokenRatesError extends Error {\n response?: Response\n constructor(message: string, response?: Response) {\n super(message)\n this.response = response\n }\n}\n\nexport const ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES) as TokenRateCurrency[]\nexport type CoinsApiConfig = {\n apiUrl: string\n}\n\nexport const DEFAULT_COINSAPI_CONFIG: CoinsApiConfig = {\n apiUrl: \"https://coins.talisman.xyz\",\n}\nexport async function fetchTokenRates(\n tokens: Record<TokenId, Token>,\n currencyIds: TokenRateCurrency[] = ALL_CURRENCY_IDS,\n config: CoinsApiConfig = DEFAULT_COINSAPI_CONFIG\n): Promise<TokenRatesList> {\n // create a map from `coingeckoId` -> `tokenId` for each token\n const coingeckoIdToTokenIds = Object.values(tokens)\n .flatMap((token) => {\n // BEGIN: LP tokens have a rate which is calculated later on, using the rates of two other tokens.\n //\n // This section contains the logic such that: if token is an LP token, then fetch the rates for the two underlying tokens.\n if (token.type === \"evm-uniswapv2\") {\n if (token.platform !== \"ethereum\") return []\n\n const getToken = (\n evmNetworkId: string,\n tokenAddress: `0x${string}`,\n coingeckoId: string\n ) => ({\n id: evmErc20TokenId(evmNetworkId, tokenAddress),\n coingeckoId,\n })\n\n const token0 = token.coingeckoId0\n ? [getToken(token.networkId, token.tokenAddress0, token.coingeckoId0)]\n : []\n const token1 = token.coingeckoId1\n ? [getToken(token.networkId, token.tokenAddress1, token.coingeckoId1)]\n : []\n\n return [...token0, ...token1]\n }\n // END: LP tokens have a rate which is calculated later on, using the rates of two other tokens.\n\n // ignore tokens which don't have a coingeckoId\n if (!token.coingeckoId) return []\n\n return [{ id: token.id, coingeckoId: token.coingeckoId }]\n })\n\n // get each token's coingeckoId\n .reduce(\n (coingeckoIdToTokenIds, { id, coingeckoId }) => {\n if (!coingeckoIdToTokenIds[coingeckoId]) coingeckoIdToTokenIds[coingeckoId] = []\n coingeckoIdToTokenIds[coingeckoId].push(id)\n return coingeckoIdToTokenIds\n },\n {} as Record<string, string[]>\n )\n\n // create a list of coingeckoIds we want to fetch\n const coingeckoIds = Object.keys(coingeckoIdToTokenIds).sort()\n\n // skip network request if there is nothing for us to fetch\n if (coingeckoIds.length < 1) return {}\n\n // If `currencyIds` includes `tao`, we need to always fetch the `bittensor` coingeckoId and the `usd` currency,\n // we can use these to calculate the currency rate for TAO relative to all other tokens.\n //\n // We support showing balances in TAO just like we support BTC/ETH/DOT, but coingecko doesn't support TAO as a vs currency rate.\n // We can macgyver our own TOKEN<>TAO rate by combining the TOKEN<>USD data with the TAO<>USD data.\n const hasVsTao = currencyIds.includes(\"tao\")\n const [effectiveCoingeckoIds, effectiveCurrencyIds] = hasVsTao\n ? [\n [...new Set(coingeckoIds).add(\"bittensor\")],\n [\n ...new Set(\n // don't request `tao` from coingecko (we calculate it from `usd`)\n currencyIds.filter((c) => c !== \"tao\")\n )\n // always include `usd` (so we can calculate `tao`)\n .add(\"usd\"),\n ],\n ]\n : [coingeckoIds, currencyIds]\n\n const response = await fetch(`${config.apiUrl}/token-rates`, {\n method: \"POST\",\n body: JSON.stringify({\n coingeckoIds: effectiveCoingeckoIds,\n currencyIds: effectiveCurrencyIds,\n }),\n })\n\n const rawTokenRates = (await response.json()) as RawTokenRates\n\n if (hasVsTao) {\n // calculate the TAO<>USD rate\n const effectiveTaoIndex = effectiveCoingeckoIds.indexOf(\"bittensor\")\n const effectiveUsdIndex = effectiveCurrencyIds.indexOf(\"usd\")\n const taoUsdRate = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[0]\n const taoUsdChange24h = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[2]\n\n // insert TOKEN<>TAO rate (calculated based on TAO<>USD rate and TOKEN<>USD rate) into each TOKEN\n const taoIndex = currencyIds.indexOf(\"tao\")\n rawTokenRates.forEach((rates, i) => {\n // hardcoded rate for TAO<>TAO\n if (i === effectiveTaoIndex) {\n rates?.splice(taoIndex, 0, [1, null, null])\n return\n }\n\n // get TOKEN<>USD rate\n const tokenUsdRate = rates?.[effectiveUsdIndex]?.[0]\n // calculate TOKEN<>TAO rate\n const tokenTaoRate =\n typeof tokenUsdRate === \"number\" && typeof taoUsdRate === \"number\" && taoUsdRate !== 0\n ? tokenUsdRate / taoUsdRate\n : null\n\n const tokenUsdChange24h = rates?.[effectiveUsdIndex]?.[2]\n const tokenTaoChange24h =\n typeof taoUsdChange24h === \"number\" && typeof tokenUsdChange24h === \"number\"\n ? (1 + tokenUsdChange24h) / (1 + taoUsdChange24h) - 1\n : null\n\n // insert at the correct location (based on the index of `tao` in `currencyIds`)\n rates?.splice(taoIndex, 0, [tokenTaoRate, null, tokenTaoChange24h])\n })\n }\n\n const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds)\n\n // build a TokenRatesList from the token prices result\n const ratesList: TokenRatesList = Object.fromEntries(\n Object.entries(tokens).map(([tokenId, token]) => [\n tokenId,\n token.coingeckoId ? (tokenRates[token.coingeckoId] ?? null) : null,\n ])\n ) as TokenRatesList\n\n return ratesList\n}\n\n// To save on bandwidth and work around response size limits, values are returned without json property names\n// (e.g. [[[12, 12332, 0.5]]] instead of { dot : {usd: { value: 12, marketCap: 12332, change24h: 0.5 }} })\ntype RawTokenRates = [number | null, number | null, number | null][][]\n\nconst parseTokenRatesFromApi = (\n rawTokenRates: RawTokenRates,\n coingeckoIds: string[],\n currencyIds: TokenRateCurrency[]\n): TokenRatesList => {\n return Object.fromEntries(\n coingeckoIds.map((coingeckoId, idx) => {\n const rates = rawTokenRates[idx]\n if (!rates) return [coingeckoId, null]\n\n return [\n coingeckoId,\n Object.fromEntries(\n currencyIds.map((currencyId, idx) => {\n const curRate = rates[idx]\n if (!curRate) return [currencyId, null]\n\n const [price, marketCap, change24h] = rates[idx]\n return [currencyId, { price, marketCap, change24h } as TokenRateData]\n })\n ) as TokenRates,\n ]\n })\n ) as TokenRatesList\n}\n","import type { TokenId } from \"@talismn/chaindata-provider\"\n\nexport const SUPPORTED_CURRENCIES = {\n btc: { name: \"Bitcoin\", symbol: \"₿\" },\n eth: { name: \"Ethereum\", symbol: \"Ξ\" },\n dot: { name: \"Polkadot\", symbol: \"D\" },\n tao: { name: \"Bittensor\", symbol: \"τ\" },\n\n usd: { name: \"US Dollar\", symbol: \"$\" },\n cny: { name: \"Chinese Yuan\", symbol: \"¥\" },\n eur: { name: \"Euro\", symbol: \"€\" },\n gbp: { name: \"British Pound\", symbol: \"£\" },\n cad: { name: \"Canadian Dollar\", symbol: \"C$\" },\n aud: { name: \"Australian Dollar\", symbol: \"A$\" },\n nzd: { name: \"New Zealand Dollar\", symbol: \"NZ$\" },\n jpy: { name: \"Japanese Yen\", symbol: \"¥\" },\n rub: { name: \"Russian Ruble\", symbol: \"₽\" },\n krw: { name: \"South Korean Won\", symbol: \"₩\" },\n idr: { name: \"Indonesian Rupiah\", symbol: \"Rp\" },\n php: { name: \"Philippine Peso\", symbol: \"₱\" },\n thb: { name: \"Thai Baht\", symbol: \"฿\" },\n vnd: { name: \"Vietnamese Dong\", symbol: \"₫\" },\n inr: { name: \"Indian Rupee\", symbol: \"₹\" },\n try: { name: \"Turkish Lira\", symbol: \"₺\" },\n // hkd: { name: \"Hong Kong Dollar\", symbol: \"HK$\" },\n sgd: { name: \"Singapore Dollar\", symbol: \"S$\" },\n // twd: { name: \"Taiwanese Dollar\", symbol: \"NT$\" },\n} as const\n\nexport type TokenRatesStorage = { tokenRates: TokenRatesList }\n\nexport type TokenRatesList = Record<TokenId, TokenRates>\nexport type TokenRates = Record<TokenRateCurrency, TokenRateData | null>\n\nexport type TokenRateData = { price: number; marketCap?: number; change24h?: number }\nexport type TokenRateCurrency = keyof typeof SUPPORTED_CURRENCIES\n\nexport const newTokenRates = (): TokenRates => ({\n btc: null,\n eth: null,\n dot: null,\n tao: null,\n\n usd: null,\n cny: null,\n eur: null,\n gbp: null,\n cad: null,\n aud: null,\n nzd: null,\n jpy: null,\n rub: null,\n krw: null,\n idr: null,\n php: null,\n thb: null,\n vnd: null,\n inr: null,\n try: null,\n // hkd: null,\n sgd: null,\n // twd: null,\n})\n"],"mappings":"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","names":["coingeckoIdToTokenIds","idx"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,173 @@
1
+ // src/TalismanTokenRatesDatabase.ts
2
+ var tryToDeleteOldTokenRatesDb = () => {
3
+ try {
4
+ indexedDB.deleteDatabase("TalismanTokenRates");
5
+ } catch {
6
+ }
7
+ };
8
+
9
+ // src/TokenRates.ts
10
+ import { evmErc20TokenId } from "@talismn/chaindata-provider";
11
+
12
+ // src/types.ts
13
+ var SUPPORTED_CURRENCIES = {
14
+ btc: { name: "Bitcoin", symbol: "\u20BF" },
15
+ eth: { name: "Ethereum", symbol: "\u039E" },
16
+ dot: { name: "Polkadot", symbol: "D" },
17
+ tao: { name: "Bittensor", symbol: "\u03C4" },
18
+ usd: { name: "US Dollar", symbol: "$" },
19
+ cny: { name: "Chinese Yuan", symbol: "\xA5" },
20
+ eur: { name: "Euro", symbol: "\u20AC" },
21
+ gbp: { name: "British Pound", symbol: "\xA3" },
22
+ cad: { name: "Canadian Dollar", symbol: "C$" },
23
+ aud: { name: "Australian Dollar", symbol: "A$" },
24
+ nzd: { name: "New Zealand Dollar", symbol: "NZ$" },
25
+ jpy: { name: "Japanese Yen", symbol: "\xA5" },
26
+ rub: { name: "Russian Ruble", symbol: "\u20BD" },
27
+ krw: { name: "South Korean Won", symbol: "\u20A9" },
28
+ idr: { name: "Indonesian Rupiah", symbol: "Rp" },
29
+ php: { name: "Philippine Peso", symbol: "\u20B1" },
30
+ thb: { name: "Thai Baht", symbol: "\u0E3F" },
31
+ vnd: { name: "Vietnamese Dong", symbol: "\u20AB" },
32
+ inr: { name: "Indian Rupee", symbol: "\u20B9" },
33
+ try: { name: "Turkish Lira", symbol: "\u20BA" },
34
+ // hkd: { name: "Hong Kong Dollar", symbol: "HK$" },
35
+ sgd: { name: "Singapore Dollar", symbol: "S$" }
36
+ // twd: { name: "Taiwanese Dollar", symbol: "NT$" },
37
+ };
38
+ var newTokenRates = () => ({
39
+ btc: null,
40
+ eth: null,
41
+ dot: null,
42
+ tao: null,
43
+ usd: null,
44
+ cny: null,
45
+ eur: null,
46
+ gbp: null,
47
+ cad: null,
48
+ aud: null,
49
+ nzd: null,
50
+ jpy: null,
51
+ rub: null,
52
+ krw: null,
53
+ idr: null,
54
+ php: null,
55
+ thb: null,
56
+ vnd: null,
57
+ inr: null,
58
+ try: null,
59
+ // hkd: null,
60
+ sgd: null
61
+ // twd: null,
62
+ });
63
+
64
+ // src/TokenRates.ts
65
+ var TokenRatesError = class extends Error {
66
+ response;
67
+ constructor(message, response) {
68
+ super(message);
69
+ this.response = response;
70
+ }
71
+ };
72
+ var ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES);
73
+ var DEFAULT_COINSAPI_CONFIG = {
74
+ apiUrl: "https://coins.talisman.xyz"
75
+ };
76
+ async function fetchTokenRates(tokens, currencyIds = ALL_CURRENCY_IDS, config = DEFAULT_COINSAPI_CONFIG) {
77
+ const coingeckoIdToTokenIds = Object.values(tokens).flatMap((token) => {
78
+ if (token.type === "evm-uniswapv2") {
79
+ if (token.platform !== "ethereum") return [];
80
+ const getToken = (evmNetworkId, tokenAddress, coingeckoId) => ({
81
+ id: evmErc20TokenId(evmNetworkId, tokenAddress),
82
+ coingeckoId
83
+ });
84
+ const token0 = token.coingeckoId0 ? [getToken(token.networkId, token.tokenAddress0, token.coingeckoId0)] : [];
85
+ const token1 = token.coingeckoId1 ? [getToken(token.networkId, token.tokenAddress1, token.coingeckoId1)] : [];
86
+ return [...token0, ...token1];
87
+ }
88
+ if (!token.coingeckoId) return [];
89
+ return [{ id: token.id, coingeckoId: token.coingeckoId }];
90
+ }).reduce(
91
+ (coingeckoIdToTokenIds2, { id, coingeckoId }) => {
92
+ if (!coingeckoIdToTokenIds2[coingeckoId]) coingeckoIdToTokenIds2[coingeckoId] = [];
93
+ coingeckoIdToTokenIds2[coingeckoId].push(id);
94
+ return coingeckoIdToTokenIds2;
95
+ },
96
+ {}
97
+ );
98
+ const coingeckoIds = Object.keys(coingeckoIdToTokenIds).sort();
99
+ if (coingeckoIds.length < 1) return {};
100
+ const hasVsTao = currencyIds.includes("tao");
101
+ const [effectiveCoingeckoIds, effectiveCurrencyIds] = hasVsTao ? [
102
+ [...new Set(coingeckoIds).add("bittensor")],
103
+ [
104
+ ...new Set(
105
+ // don't request `tao` from coingecko (we calculate it from `usd`)
106
+ currencyIds.filter((c) => c !== "tao")
107
+ ).add("usd")
108
+ ]
109
+ ] : [coingeckoIds, currencyIds];
110
+ const response = await fetch(`${config.apiUrl}/token-rates`, {
111
+ method: "POST",
112
+ body: JSON.stringify({
113
+ coingeckoIds: effectiveCoingeckoIds,
114
+ currencyIds: effectiveCurrencyIds
115
+ })
116
+ });
117
+ const rawTokenRates = await response.json();
118
+ if (hasVsTao) {
119
+ const effectiveTaoIndex = effectiveCoingeckoIds.indexOf("bittensor");
120
+ const effectiveUsdIndex = effectiveCurrencyIds.indexOf("usd");
121
+ const taoUsdRate = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[0];
122
+ const taoUsdChange24h = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[2];
123
+ const taoIndex = currencyIds.indexOf("tao");
124
+ rawTokenRates.forEach((rates, i) => {
125
+ if (i === effectiveTaoIndex) {
126
+ rates?.splice(taoIndex, 0, [1, null, null]);
127
+ return;
128
+ }
129
+ const tokenUsdRate = rates?.[effectiveUsdIndex]?.[0];
130
+ const tokenTaoRate = typeof tokenUsdRate === "number" && typeof taoUsdRate === "number" && taoUsdRate !== 0 ? tokenUsdRate / taoUsdRate : null;
131
+ const tokenUsdChange24h = rates?.[effectiveUsdIndex]?.[2];
132
+ const tokenTaoChange24h = typeof taoUsdChange24h === "number" && typeof tokenUsdChange24h === "number" ? (1 + tokenUsdChange24h) / (1 + taoUsdChange24h) - 1 : null;
133
+ rates?.splice(taoIndex, 0, [tokenTaoRate, null, tokenTaoChange24h]);
134
+ });
135
+ }
136
+ const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds);
137
+ const ratesList = Object.fromEntries(
138
+ Object.entries(tokens).map(([tokenId, token]) => [
139
+ tokenId,
140
+ token.coingeckoId ? tokenRates[token.coingeckoId] ?? null : null
141
+ ])
142
+ );
143
+ return ratesList;
144
+ }
145
+ var parseTokenRatesFromApi = (rawTokenRates, coingeckoIds, currencyIds) => {
146
+ return Object.fromEntries(
147
+ coingeckoIds.map((coingeckoId, idx) => {
148
+ const rates = rawTokenRates[idx];
149
+ if (!rates) return [coingeckoId, null];
150
+ return [
151
+ coingeckoId,
152
+ Object.fromEntries(
153
+ currencyIds.map((currencyId, idx2) => {
154
+ const curRate = rates[idx2];
155
+ if (!curRate) return [currencyId, null];
156
+ const [price, marketCap, change24h] = rates[idx2];
157
+ return [currencyId, { price, marketCap, change24h }];
158
+ })
159
+ )
160
+ ];
161
+ })
162
+ );
163
+ };
164
+ export {
165
+ ALL_CURRENCY_IDS,
166
+ DEFAULT_COINSAPI_CONFIG,
167
+ SUPPORTED_CURRENCIES,
168
+ TokenRatesError,
169
+ fetchTokenRates,
170
+ newTokenRates,
171
+ tryToDeleteOldTokenRatesDb
172
+ };
173
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/TalismanTokenRatesDatabase.ts","../src/TokenRates.ts","../src/types.ts"],"sourcesContent":["export const tryToDeleteOldTokenRatesDb = () => {\n try {\n // try and delete it if it's still there\n indexedDB.deleteDatabase(\"TalismanTokenRates\")\n } catch {\n // dont care if it fails\n }\n}\n","import { evmErc20TokenId, type Token, type TokenId } from \"@talismn/chaindata-provider\"\n\nimport {\n SUPPORTED_CURRENCIES,\n type TokenRateCurrency,\n type TokenRateData,\n type TokenRates,\n type TokenRatesList,\n} from \"./types\"\n\nexport class TokenRatesError extends Error {\n response?: Response\n constructor(message: string, response?: Response) {\n super(message)\n this.response = response\n }\n}\n\nexport const ALL_CURRENCY_IDS = Object.keys(SUPPORTED_CURRENCIES) as TokenRateCurrency[]\nexport type CoinsApiConfig = {\n apiUrl: string\n}\n\nexport const DEFAULT_COINSAPI_CONFIG: CoinsApiConfig = {\n apiUrl: \"https://coins.talisman.xyz\",\n}\nexport async function fetchTokenRates(\n tokens: Record<TokenId, Token>,\n currencyIds: TokenRateCurrency[] = ALL_CURRENCY_IDS,\n config: CoinsApiConfig = DEFAULT_COINSAPI_CONFIG\n): Promise<TokenRatesList> {\n // create a map from `coingeckoId` -> `tokenId` for each token\n const coingeckoIdToTokenIds = Object.values(tokens)\n .flatMap((token) => {\n // BEGIN: LP tokens have a rate which is calculated later on, using the rates of two other tokens.\n //\n // This section contains the logic such that: if token is an LP token, then fetch the rates for the two underlying tokens.\n if (token.type === \"evm-uniswapv2\") {\n if (token.platform !== \"ethereum\") return []\n\n const getToken = (\n evmNetworkId: string,\n tokenAddress: `0x${string}`,\n coingeckoId: string\n ) => ({\n id: evmErc20TokenId(evmNetworkId, tokenAddress),\n coingeckoId,\n })\n\n const token0 = token.coingeckoId0\n ? [getToken(token.networkId, token.tokenAddress0, token.coingeckoId0)]\n : []\n const token1 = token.coingeckoId1\n ? [getToken(token.networkId, token.tokenAddress1, token.coingeckoId1)]\n : []\n\n return [...token0, ...token1]\n }\n // END: LP tokens have a rate which is calculated later on, using the rates of two other tokens.\n\n // ignore tokens which don't have a coingeckoId\n if (!token.coingeckoId) return []\n\n return [{ id: token.id, coingeckoId: token.coingeckoId }]\n })\n\n // get each token's coingeckoId\n .reduce(\n (coingeckoIdToTokenIds, { id, coingeckoId }) => {\n if (!coingeckoIdToTokenIds[coingeckoId]) coingeckoIdToTokenIds[coingeckoId] = []\n coingeckoIdToTokenIds[coingeckoId].push(id)\n return coingeckoIdToTokenIds\n },\n {} as Record<string, string[]>\n )\n\n // create a list of coingeckoIds we want to fetch\n const coingeckoIds = Object.keys(coingeckoIdToTokenIds).sort()\n\n // skip network request if there is nothing for us to fetch\n if (coingeckoIds.length < 1) return {}\n\n // If `currencyIds` includes `tao`, we need to always fetch the `bittensor` coingeckoId and the `usd` currency,\n // we can use these to calculate the currency rate for TAO relative to all other tokens.\n //\n // We support showing balances in TAO just like we support BTC/ETH/DOT, but coingecko doesn't support TAO as a vs currency rate.\n // We can macgyver our own TOKEN<>TAO rate by combining the TOKEN<>USD data with the TAO<>USD data.\n const hasVsTao = currencyIds.includes(\"tao\")\n const [effectiveCoingeckoIds, effectiveCurrencyIds] = hasVsTao\n ? [\n [...new Set(coingeckoIds).add(\"bittensor\")],\n [\n ...new Set(\n // don't request `tao` from coingecko (we calculate it from `usd`)\n currencyIds.filter((c) => c !== \"tao\")\n )\n // always include `usd` (so we can calculate `tao`)\n .add(\"usd\"),\n ],\n ]\n : [coingeckoIds, currencyIds]\n\n const response = await fetch(`${config.apiUrl}/token-rates`, {\n method: \"POST\",\n body: JSON.stringify({\n coingeckoIds: effectiveCoingeckoIds,\n currencyIds: effectiveCurrencyIds,\n }),\n })\n\n const rawTokenRates = (await response.json()) as RawTokenRates\n\n if (hasVsTao) {\n // calculate the TAO<>USD rate\n const effectiveTaoIndex = effectiveCoingeckoIds.indexOf(\"bittensor\")\n const effectiveUsdIndex = effectiveCurrencyIds.indexOf(\"usd\")\n const taoUsdRate = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[0]\n const taoUsdChange24h = rawTokenRates[effectiveTaoIndex]?.[effectiveUsdIndex]?.[2]\n\n // insert TOKEN<>TAO rate (calculated based on TAO<>USD rate and TOKEN<>USD rate) into each TOKEN\n const taoIndex = currencyIds.indexOf(\"tao\")\n rawTokenRates.forEach((rates, i) => {\n // hardcoded rate for TAO<>TAO\n if (i === effectiveTaoIndex) {\n rates?.splice(taoIndex, 0, [1, null, null])\n return\n }\n\n // get TOKEN<>USD rate\n const tokenUsdRate = rates?.[effectiveUsdIndex]?.[0]\n // calculate TOKEN<>TAO rate\n const tokenTaoRate =\n typeof tokenUsdRate === \"number\" && typeof taoUsdRate === \"number\" && taoUsdRate !== 0\n ? tokenUsdRate / taoUsdRate\n : null\n\n const tokenUsdChange24h = rates?.[effectiveUsdIndex]?.[2]\n const tokenTaoChange24h =\n typeof taoUsdChange24h === \"number\" && typeof tokenUsdChange24h === \"number\"\n ? (1 + tokenUsdChange24h) / (1 + taoUsdChange24h) - 1\n : null\n\n // insert at the correct location (based on the index of `tao` in `currencyIds`)\n rates?.splice(taoIndex, 0, [tokenTaoRate, null, tokenTaoChange24h])\n })\n }\n\n const tokenRates = parseTokenRatesFromApi(rawTokenRates, coingeckoIds, currencyIds)\n\n // build a TokenRatesList from the token prices result\n const ratesList: TokenRatesList = Object.fromEntries(\n Object.entries(tokens).map(([tokenId, token]) => [\n tokenId,\n token.coingeckoId ? (tokenRates[token.coingeckoId] ?? null) : null,\n ])\n ) as TokenRatesList\n\n return ratesList\n}\n\n// To save on bandwidth and work around response size limits, values are returned without json property names\n// (e.g. [[[12, 12332, 0.5]]] instead of { dot : {usd: { value: 12, marketCap: 12332, change24h: 0.5 }} })\ntype RawTokenRates = [number | null, number | null, number | null][][]\n\nconst parseTokenRatesFromApi = (\n rawTokenRates: RawTokenRates,\n coingeckoIds: string[],\n currencyIds: TokenRateCurrency[]\n): TokenRatesList => {\n return Object.fromEntries(\n coingeckoIds.map((coingeckoId, idx) => {\n const rates = rawTokenRates[idx]\n if (!rates) return [coingeckoId, null]\n\n return [\n coingeckoId,\n Object.fromEntries(\n currencyIds.map((currencyId, idx) => {\n const curRate = rates[idx]\n if (!curRate) return [currencyId, null]\n\n const [price, marketCap, change24h] = rates[idx]\n return [currencyId, { price, marketCap, change24h } as TokenRateData]\n })\n ) as TokenRates,\n ]\n })\n ) as TokenRatesList\n}\n","import type { TokenId } from \"@talismn/chaindata-provider\"\n\nexport const SUPPORTED_CURRENCIES = {\n btc: { name: \"Bitcoin\", symbol: \"₿\" },\n eth: { name: \"Ethereum\", symbol: \"Ξ\" },\n dot: { name: \"Polkadot\", symbol: \"D\" },\n tao: { name: \"Bittensor\", symbol: \"τ\" },\n\n usd: { name: \"US Dollar\", symbol: \"$\" },\n cny: { name: \"Chinese Yuan\", symbol: \"¥\" },\n eur: { name: \"Euro\", symbol: \"€\" },\n gbp: { name: \"British Pound\", symbol: \"£\" },\n cad: { name: \"Canadian Dollar\", symbol: \"C$\" },\n aud: { name: \"Australian Dollar\", symbol: \"A$\" },\n nzd: { name: \"New Zealand Dollar\", symbol: \"NZ$\" },\n jpy: { name: \"Japanese Yen\", symbol: \"¥\" },\n rub: { name: \"Russian Ruble\", symbol: \"₽\" },\n krw: { name: \"South Korean Won\", symbol: \"₩\" },\n idr: { name: \"Indonesian Rupiah\", symbol: \"Rp\" },\n php: { name: \"Philippine Peso\", symbol: \"₱\" },\n thb: { name: \"Thai Baht\", symbol: \"฿\" },\n vnd: { name: \"Vietnamese Dong\", symbol: \"₫\" },\n inr: { name: \"Indian Rupee\", symbol: \"₹\" },\n try: { name: \"Turkish Lira\", symbol: \"₺\" },\n // hkd: { name: \"Hong Kong Dollar\", symbol: \"HK$\" },\n sgd: { name: \"Singapore Dollar\", symbol: \"S$\" },\n // twd: { name: \"Taiwanese Dollar\", symbol: \"NT$\" },\n} as const\n\nexport type TokenRatesStorage = { tokenRates: TokenRatesList }\n\nexport type TokenRatesList = Record<TokenId, TokenRates>\nexport type TokenRates = Record<TokenRateCurrency, TokenRateData | null>\n\nexport type TokenRateData = { price: number; marketCap?: number; change24h?: number }\nexport type TokenRateCurrency = keyof typeof SUPPORTED_CURRENCIES\n\nexport const newTokenRates = (): TokenRates => ({\n btc: null,\n eth: null,\n dot: null,\n tao: null,\n\n usd: null,\n cny: null,\n eur: null,\n gbp: null,\n cad: null,\n aud: null,\n nzd: null,\n jpy: null,\n rub: null,\n krw: null,\n idr: null,\n php: null,\n thb: null,\n vnd: null,\n inr: null,\n try: null,\n // hkd: null,\n sgd: null,\n // twd: null,\n})\n"],"mappings":"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","names":["coingeckoIdToTokenIds","idx"]}