├── .gitignore
├── README.md
├── __tests__
├── arweave-signing.test.ts
├── fluent-interface.test.ts
├── get-all-prices.test.ts
├── get-historical-price.test.ts
└── get-price.test.ts
├── docs
├── ALL_SUPPORTED_TOKENS.md
├── FLUENT_INTERFACE.md
├── HTTP_API.md
├── QUICK_START.md
├── REDSTONE_DATA_ECOSYSTEM.md
├── redstone-query-example.gif
├── redstone-simple-example.gif
└── redstone-symbols-example.gif
├── examples
├── discord-bots
│ ├── ar-price-bot.js
│ ├── memory-price-bot.js
│ └── simple-discord-bot.js
└── telegram-bots
│ └── telegram-price-bot.js
├── jestconfig.json
├── package.json
├── src
├── api.ts
├── config
│ ├── index.ts
│ ├── providers.ts
│ └── tokens.ts
├── decs.d.ts
├── errors
│ └── price-not-found.ts
├── index.ts
├── proxies
│ ├── arweave-proxy.ts
│ └── cache-proxy.ts
├── query.ts
├── signature-verifier.ts
└── types.ts
├── tsconfig.json
├── tslint.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .idea
4 | lib
5 | coverage
6 | .exp
7 | tsconfig.tsbuildinfo
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Redstone API
2 |
3 | [](https://choosealicense.com/licenses/mit/)
4 | [](https://github.com/redstone-finance/redstone-api)
5 | [](https://discord.gg/2CT6hN6C)
6 | [](https://www.npmjs.com/package/redstone-api)
7 |
8 | [](https://twitter.com/intent/follow?screen_name=limestone_defi)
9 |
10 | Redstone API is a Javascript library for fetching trusted token pricing data from [Redstone data ecosystem](docs/REDSTONE_DATA_ECOSYSTEM.md).
11 |
12 | It is a Javascript wrapper for [Redstone HTTP Api](docs/HTTP_API.md).
13 |
14 | ## 🚀 Demo
15 | Try it directly in CodeSandbox: [demo link](https://codesandbox.io/s/redstone-api-demo-0h4oo?from-embed=&file=/src/index.ts)
16 |
17 | ## ✅ Why Redstone API
18 | ### ✓ Secure
19 | Redstone pricing data is secured on Arweave and protected by the provider's collateral.
20 | [Learn more](docs/REDSTONE_DATA_ECOSYSTEM.md)
21 |
22 | ### ✓ Easy to use
23 | You don't need any API keys. Just install the npm package and add a single line of code.
24 | [Quick start](docs/QUICK_START.md)
25 |
26 | ### ✓ 100+ tokens
27 | We support BTC, ETH, AR, EUR, and many other crypto and fiat currencies.
28 | [All supported tokens](docs/ALL_SUPPORTED_TOKENS.md)
29 |
30 | ### ✓ TypeScript Support
31 | Redstone API is fully written in Typescript and then compiled to JavaScript.
32 | [Source code](https://github.com/redstone-finance/redstone-api/)
33 |
34 | ## 📖 Documentation
35 | This readme should provide you with all the information you need to start using redstone api. If you want to see the full documentation, visit [api.docs.redstone.finance](https://api.docs.redstone.finance)
36 |
37 | ## 📦 Installation
38 |
39 | ### Using npm
40 | ```bash
41 | npm install redstone-api
42 | ```
43 |
44 | ### Using yarn
45 | ```bash
46 | yarn add redstone-api
47 | ```
48 |
49 | ## 🤖 Usage
50 |
51 | ### Importing
52 |
53 | ```js
54 | // Using Node.js `require()`
55 | const redstone = require('redstone-api');
56 |
57 | // Using ES6 imports
58 | import redstone from 'redstone-api';
59 |
60 | ```
61 |
62 | ### Get the latest price for a single token
63 | ```js
64 | const price = await redstone.getPrice("AR");
65 |
66 | console.log(price.value); // latest price value for AR token (in USD)
67 | console.log(price.timestamp); // the exact timestamp of the price
68 | ```
69 | 💡 Note: All the prices are denominated in USD. You can fetch price data for BTC, ETH, AR, EUR and any other of [ 100+ supported tokens.](docs/ALL_SUPPORTED_TOKENS.md)
70 |
71 | ### Available symbols
72 | You can use a `symbols` object to explore all available symbols right in the code.
73 | ```js
74 | import redstone from 'redstone-api';
75 |
76 | const price = await redstone.getPrice("AR");
77 | ```
78 |
79 |
80 |
81 |
82 |
83 | Price data format
84 |
85 | ```js
86 | {
87 | value: 123.23, // Number: Price value in USD
88 | timestamp: 1617146511173, // Number: Timestamp (ms) for price
89 | provider: "I-5rWUehEv-MjdK9gFw09RxfSLQX9DIHxG614Wf8qo0", // String: Provider arweave address
90 | permawebTx: "V8FUU0BG4kVOJwKWHzgkn1aEFm-eanhqqEXfPFY7pmI", // String: Arweave transaction id
91 | source: {"coingecko": 123,"sushiswap": 123.23,"uniswap": 123.35}, // Object: Prices from different sources
92 | }
93 | ```
94 |
95 |
96 |
97 |
98 |
99 | Fetch price using promises
100 |
101 | ```js
102 | // As async/await is only a syntactic sugar on Javascript
103 | // Promises you can use them in a "standard" way
104 | const price = redstone.getPrice("AR").then((price) => {
105 | console.log(price.value); // latest price value for AR token
106 | });
107 | ```
108 |
109 |
110 |
111 | ----------------------------------------------
112 |
113 | ### Get the latest prices for several tokens
114 | To fetch prices for several tokens use the `getPrice` method and pass an array with any subset of [supported tokens](docs/ALL_SUPPORTED_TOKENS.md).
115 | ```js
116 | const prices = await redstone.getPrice(["BTC", "ETH", "AR", "EUR"]);
117 |
118 | console.log(prices); // Example output below
119 | /*
120 | {
121 | "BTC": {
122 | value: 58953.39,
123 | timestamp: 1617152802779,
124 | ...
125 | },
126 | "ETH": {
127 | value: 1856.75,
128 | timestamp: 1617152802779,
129 | ...
130 | },
131 | ...
132 | }
133 | */
134 |
135 |
136 | console.log(prices["BTC"].value); // latest price value for BTC
137 | console.log(prices["ETH"].value); // latest price value for ETH
138 | console.log(prices["AR"].value); // latest price value for AR
139 |
140 | ```
141 |
142 | ----------------------------------------------
143 |
144 | ### Get prices for all available tokens
145 | To fetch the latest prices for all available tokens use the `getAllPrices` method.
146 | ```js
147 | const prices = await redstone.getAllPrices();
148 |
149 | console.log(prices); // Example output below
150 | /*
151 | {
152 | "BTC": {...},
153 | "ETH": {...},
154 | ...
155 | }
156 | */
157 |
158 | console.log(prices["AR"].value); // latest price value for AR
159 | console.log(prices["EUR"].value); // latest price value for EUR
160 | ```
161 |
162 | ----------------------------------------------
163 |
164 | ### Get the historical price for a single token
165 | To get the historical price use the `getHistoricalPrice` method.
166 | ```js
167 | const price = await redstone.getHistoricalPrice("AR", {
168 | date: "2021-03-30T12:35:09", // Any convertable to date type
169 | });
170 |
171 | console.log(price.value); // AR price for specific time
172 | ```
173 |
174 | 💡 Note: `date` argument must be convertable to Date type. You may pass date (e.g. `new Date(2021-04-01)`), timestamp (e.g. `1617709771289`), or just string (e.g. `2021-04-01` or `2021-04-01T12:30:58`).
175 |
176 | ----------------------------------------------
177 |
178 | ### Get the historical price for several tokens
179 | To fetch the historical price for several tokens pass an array of symbols to `getHistoricalPrice` method.
180 | ```js
181 | const symbols = ["AR", "BTC", "UNI", "ETH", "EUR"];
182 | const prices = await redstone.getHistoricalPrice(symbols, {
183 | date: "2021-03-30T12:35:09",
184 | });
185 |
186 | console.log(prices["BTC"].value); // BTC price for specific time
187 | ```
188 |
189 | ----------------------------------------------
190 |
191 | ### Get the historical prices in a time range
192 | To fetch the historical prices in a time range specify token symbol as the first argument of the `getHistoricalPrice` method, and `startDate`, `endDate` and `interval` as fields of the second argument.
193 |
194 | 💡 Note: currently Redstone API supports fetching historical prices in a time range only for a single token.
195 | ```js
196 | const prices = await redstone.getHistoricalPrice("AR", {
197 | startDate: "2021-03-29T12:35:09",
198 | endDate: "2021-03-30T12:35:09",
199 | interval: 3600 * 1000, // 1 hour
200 | });
201 |
202 | console.log(prices); // Example output below
203 | /*
204 | [
205 | {
206 | value: 28.8,
207 | timestamp: 1617016995624,
208 | ...
209 | },
210 | {
211 | value: 28.59,
212 | timestamp: 1617014111705,
213 | ...
214 | },
215 | ...
216 | ]
217 | */
218 | ```
219 |
220 | 💡 Note: `startDate` and `endDate` argument must be convertable to Date type.
221 |
222 | ### Get prices with pagination
223 | To fetch prices with pagination specify token symbol as the first argument of the `getHistoricalPrice` method, and `offset` with `limit` as properties of the second argument.
224 |
225 | 💡 Note: pagination is supported only for a single token.
226 |
227 | ```js
228 | const prices = await redstone.getHistoricalPrice("AR", {
229 | offset: 1000,
230 | limit: 100,
231 | });
232 | ```
233 |
234 | ----------------------------------------------
235 |
236 | ### Verify signature
237 | All prices saved in Redstone have a signature, thanks to which you always can verify if the price data has been submitted by the trusted provider.
238 |
239 | To do so you can set `verifySignature` option to `true` in `getPrice`, `getHistoricalPrice` or `getAllPrices` methods. If signature is invalid - error will be thrown.
240 | ```js
241 | const price = await redstone.getPrice("AR", {
242 | verifySignature: true,
243 | });
244 | console.log(price.value);
245 | ```
246 |
247 | ----------------------------------------------
248 |
249 | ### Fluent Interface
250 | Redstone implements a fluent interface to simplify query creation thanks to a human readable syntax. [Learn more](docs/FLUENT_INTERFACE.md)
251 |
252 | 
253 |
254 | ----------------------------------------------
255 |
256 | ### Using a custom cache api url
257 | #### Option 1. Using a setCacheApiUrl method
258 | ```js
259 | redstone.setCacheApiUrl("http://localhost:9000/prices");
260 | redstone.getPrice("AR").then(console.log);
261 | ```
262 |
263 | #### Option 2. Initialising a new redstone api instance with a cacheApiUrl param
264 | ```js
265 | const redstoneApi = new redstone.Api({
266 | cacheApiUrl: "http://localhost:9000/prices",
267 | });
268 | redstoneApi.getPrice("AR").then(console.log);
269 | ```
270 |
271 | 💡 Note: To use a custom cache api url with the redstone fluent interface you should pass a `cacheApiUrl` as an argument of the `exec` method each time you make a query.
272 | ```js
273 | redstone.query().symbol("AR").latest().exec({
274 | cacheApiUrl: "http://localhost:9000/prices",
275 | }).then(console.log);
276 | ```
277 |
278 | ----------------------------------------------
279 |
280 | ### Get prices from Arweave
281 | By default, Redstone API fetches data from the Redstone cache layer. It works way faster than fetching directly from Arweave Blockchain. Even so, thanks to signature verification prices data is still trusted and secure.
282 |
283 | We strongly recommend using the default fetching mechanism which leverages cache to speed up queries. But if you want to fetch data directly from Arweave you can do it by initialising a new `Api` client and setting `useCache` option to `false`.
284 |
285 | ```js
286 | const redstoneArweaveClient = new redstone.Api({ useCache: false });
287 |
288 | const price = await redstoneArweaveClient.getPrice("AR");
289 |
290 | console.log(price.value); // AR price value fetched directly from Arweave
291 | ```
292 |
293 | ## 🚀 Examples
294 | - [Discord bots](examples/discord-bots/)
295 | - [Web app](https://github.com/redstone-finance/redstone-app)
296 |
297 | ## 💬 Contributing
298 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
299 |
300 | Please make sure to update tests as appropriate.
301 |
302 | ## 📜 License
303 | This software is licensed under the [MIT](https://choosealicense.com/licenses/mit/) © [Redstone](https://github.com/redstone-finance)
304 |
--------------------------------------------------------------------------------
/__tests__/arweave-signing.test.ts:
--------------------------------------------------------------------------------
1 | import ArweaveMultihost from "arweave-multihost";
2 |
3 | describe("Test arweave signing and verification", () => {
4 | const arweaveClient = ArweaveMultihost.initWithDefaultHosts({
5 | timeout: 10000, // Network request timeouts in milliseconds
6 | logging: false, // Enable network request logging
7 | onError: console.error, // On request error callback
8 | });
9 |
10 | test("Should sign and verify signature", async () => {
11 | // Keys generation
12 | const jwk = await arweaveClient.wallets.generate();
13 | const publicKey = jwk.n;
14 |
15 | // Signing
16 | const strToSign = "This is a test string data";
17 | const dataToSign = new TextEncoder().encode(strToSign);
18 | const signature = await arweaveClient.crypto.sign(jwk, dataToSign);
19 |
20 | // Verification
21 | const validSignature = await arweaveClient.crypto.verify(
22 | publicKey,
23 | dataToSign,
24 | signature);
25 |
26 | expect(validSignature).toBeTruthy();
27 | });
28 |
29 | test("Should get address from owner", async () => {
30 | // Keys generation
31 | const jwk = await arweaveClient.wallets.generate();
32 | const publicKey = jwk.n;
33 |
34 | const address = await arweaveClient.wallets.jwkToAddress(jwk);
35 | const addressFromOwner = await arweaveClient.wallets.ownerToAddress(publicKey);
36 |
37 | expect(addressFromOwner).toBe(address);
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/__tests__/fluent-interface.test.ts:
--------------------------------------------------------------------------------
1 | import redstone from "../src/index";
2 |
3 | const MAX_TIME_DIFF = 150000; // 150s
4 |
5 | describe("Fluent interface tests ", () => {
6 |
7 | /********* SINGLE SYMBOL *********/
8 |
9 | test("Should get AR price", async () => {
10 | const price = await redstone.query()
11 | .symbol("AR")
12 | .latest()
13 | .exec();
14 |
15 | expect(price).toBeDefined();
16 | expect(price.symbol).toBe("AR");
17 | expect(price.value).toBeGreaterThan(1);
18 | expect(Date.now() - price.timestamp).toBeLessThan(MAX_TIME_DIFF);
19 | });
20 |
21 | test("Should get AAPL price with redstone-stocks provider", async () => {
22 | const price = await redstone.query()
23 | .symbol("AAPL")
24 | .latest()
25 | .exec({ provider: "redstone-stocks" });
26 |
27 | expect(price).toBeDefined();
28 | expect(price.symbol).toBe("AAPL");
29 | expect(price.value).toBeGreaterThan(50);
30 | expect(Date.now() - price.timestamp).toBeLessThan(MAX_TIME_DIFF);
31 | });
32 |
33 | test("Should get a single historical AR price", async () => {
34 | const price = await redstone.query()
35 | .symbol("AR")
36 | .atDate("2021-06-19")
37 | .exec({ provider: "redstone" });
38 |
39 | const timeDiff = new Date("2021-06-19").getTime() - price.timestamp;
40 |
41 | expect(price.symbol).toBe("AR");
42 | expect(timeDiff).toBeLessThan(MAX_TIME_DIFF);
43 | expect(price.value).toBeCloseTo(15.653773173625972, 15);
44 | });
45 |
46 | test("Should get historical AR price for the last 12 hours", async () => {
47 | const prices = await redstone.query()
48 | .symbol("AR")
49 | .forLastHours(12)
50 | .exec();
51 |
52 | expect(prices.length).toBeGreaterThan(70);
53 | expect(prices.length).toBeLessThan(74);
54 | for (const price of prices) {
55 | expect(price.timestamp).toBeLessThan(Date.now());
56 | expect(price.timestamp).toBeGreaterThan(Date.now() - 12.5 * 3600 * 1000);
57 | }
58 | });
59 |
60 | test("Should get single historical AR price for the 24 hours ago", async () => {
61 | const price = await redstone.query()
62 | .symbol("AR")
63 | .hoursAgo(24)
64 | .exec();
65 |
66 | const timeDiff = Date.now() - 24 * 3600 * 1000 - price.timestamp;
67 |
68 | expect(price.symbol).toBe("AR");
69 | expect(price.value).toBeGreaterThan(1);
70 | expect(timeDiff).toBeLessThan(MAX_TIME_DIFF);
71 | });
72 |
73 | test("Should get historical AR price for last 7 days", async () => {
74 | const prices = await redstone.query()
75 | .symbol("AR")
76 | .forLastDays(7)
77 | .exec();
78 |
79 | expect(prices.length).toBeGreaterThan(165);
80 | expect(prices.length).toBeLessThan(171);
81 | for (const price of prices) {
82 | expect(price.timestamp).toBeLessThan(Date.now());
83 | expect(price.timestamp).toBeGreaterThan(Date.now() - 7.2 * 24 * 3600 * 1000);
84 | }
85 | });
86 |
87 | test("Should get historical AR price for the last 1 day", async () => {
88 | const prices = await redstone.query()
89 | .symbol("AR")
90 | .forLastDays(1)
91 | .exec();
92 |
93 | expect(prices.length).toBeGreaterThanOrEqual(23);
94 | expect(prices.length).toBeLessThanOrEqual(25);
95 | for (const price of prices) {
96 | expect(price.timestamp).toBeLessThan(Date.now());
97 | expect(price.timestamp).toBeGreaterThan(Date.now() - 1.2 * 24 * 3600 * 1000);
98 | }
99 | });
100 |
101 | test("Should get AR price in time range", async () => {
102 | const prices = await redstone.query()
103 | .symbol("AR")
104 | .fromDate("2021-06-19")
105 | .toDate("2021-06-20")
106 | .exec({ provider: "redstone" });
107 |
108 | expect(prices.length).toBe(24);
109 | for (const price of prices) {
110 | expect(price.timestamp).toBeLessThanOrEqual(
111 | new Date("2021-06-20").getTime());
112 | expect(price.timestamp).toBeGreaterThanOrEqual(
113 | new Date("2021-06-19").getTime());
114 | }
115 | });
116 |
117 | // /********* SEVERAL SYMBOLS *********/
118 |
119 | test("Should get latest prices for AR, ETH and BTC", async () => {
120 | const prices = await redstone.query()
121 | .symbols(["AR", "ETH", "BTC"])
122 | .latest()
123 | .exec();
124 |
125 | expect(prices["AR"]).toBeDefined();
126 | expect(prices["ETH"]).toBeDefined();
127 | expect(prices["BTC"]).toBeDefined();
128 | expect(prices["AR"].value).toBeGreaterThan(0.1);
129 | expect(prices["ETH"].value).toBeGreaterThan(100);
130 | expect(prices["BTC"].value).toBeGreaterThan(1000);
131 | expect(Date.now() - prices["AR"].timestamp).toBeLessThan(MAX_TIME_DIFF);
132 | expect(Date.now() - prices["BTC"].timestamp).toBeLessThan(MAX_TIME_DIFF);
133 | });
134 |
135 | test("Should get the historical price for AR, ETH and BTC", async () => {
136 | const prices = await redstone.query()
137 | .symbols(["AR", "ETH", "BTC"])
138 | .atDate("2021-06-19")
139 | .exec({ provider: "redstone" });
140 |
141 | const timestamp = new Date("2021-06-19").getTime();
142 |
143 | expect(prices["AR"].value).toBeCloseTo(15.653773173625972, 15);
144 | expect(prices["ETH"].value).toBeCloseTo(2230.201106028155, 12);
145 | expect(prices["BTC"].value).toBeCloseTo(35774.50061802952, 11);
146 | expect(timestamp - prices["AR"].timestamp).toBeLessThan(MAX_TIME_DIFF);
147 | expect(timestamp - prices["ETH"].timestamp).toBeLessThan(MAX_TIME_DIFF);
148 | expect(timestamp - prices["BTC"].timestamp).toBeLessThan(MAX_TIME_DIFF);
149 | });
150 |
151 |
152 | // /********* ALL SYMBOLS *********/
153 |
154 | test("Should get the latest prices for all symbols", async () => {
155 | const prices = await redstone.query()
156 | .allSymbols()
157 | .latest()
158 | .exec();
159 |
160 | expect(Object.keys(prices)).toContain("BTC");
161 | expect(Object.keys(prices)).toContain("ETH");
162 | expect(Object.keys(prices)).toContain("AR");
163 | expect(Object.keys(prices).length).toBeGreaterThan(100);
164 | expect(Date.now() - prices["AR"].timestamp).toBeLessThan(MAX_TIME_DIFF);
165 | expect(Date.now() - prices["ETH"].timestamp).toBeLessThan(MAX_TIME_DIFF);
166 | expect(Date.now() - prices["BTC"].timestamp).toBeLessThan(MAX_TIME_DIFF);
167 | });
168 |
169 | });
170 |
--------------------------------------------------------------------------------
/__tests__/get-all-prices.test.ts:
--------------------------------------------------------------------------------
1 | import redstone from "../src/index";
2 |
3 | const MAX_TIME_DIFF = 150000; // 150s
4 | const MAX_TIME_DIFF_ARWEAVE = 7200 * 1000; // 2 hours
5 |
6 | const shouldNotHaveTechProps = (price: any) => {
7 | const technicalProps = ["signature", "version", "providerPublicKey"];
8 | for (const prop of technicalProps) {
9 | expect(price).not.toHaveProperty(prop);
10 | }
11 | };
12 |
13 | describe("Test getAllPrices method", () => {
14 | test("Should get all prices", async () => {
15 | const prices = await redstone.getAllPrices();
16 |
17 | expect(Object.keys(prices)).toContain("BTC");
18 | expect(Object.keys(prices)).toContain("ETH");
19 | expect(Object.keys(prices)).toContain("AR");
20 | expect(Object.keys(prices).length).toBeGreaterThan(100);
21 | expect(Date.now() - prices["AR"].timestamp).toBeLessThan(MAX_TIME_DIFF);
22 | expect(Date.now() - prices["BTC"].timestamp).toBeLessThan(MAX_TIME_DIFF);
23 | });
24 |
25 | test("Should get all prices and verify their signatures", async () => {
26 | const prices = await redstone.getAllPrices({
27 | verifySignature: true,
28 | });
29 |
30 | expect(Object.keys(prices).length).toBeGreaterThan(100);
31 | });
32 |
33 | test("Should not have technical props", async () => {
34 | const prices = await redstone.getAllPrices();
35 | for (const price of Object.values(prices)) {
36 | shouldNotHaveTechProps(price);
37 | }
38 | });
39 |
40 | });
41 |
--------------------------------------------------------------------------------
/__tests__/get-historical-price.test.ts:
--------------------------------------------------------------------------------
1 | import redstone from "../src/index";
2 |
3 | const shouldNotHaveTechProps = (price: any) => {
4 | const technicalProps = ["signature", "version", "providerPublicKey"];
5 | for (const prop of technicalProps) {
6 | expect(price).not.toHaveProperty(prop);
7 | }
8 | };
9 |
10 | describe("Test getHistoricalPrice method", () => {
11 | test("Should get AR price for 2021-04-17", async () => {
12 | const symbol = "AR";
13 | const date = new Date("2021-04-17");
14 | const price: any =
15 | await redstone.getHistoricalPrice(symbol, { date, provider: "redstone" });
16 |
17 | expect(price).toBeDefined();
18 | expect(price.symbol).toBe(symbol);
19 | expect(price.value).toBeCloseTo(25.923827794046517, 15);
20 | shouldNotHaveTechProps(price);
21 | });
22 |
23 | test("Should get ETH price for 2021-04-17", async () => {
24 | const symbol = "ETH";
25 | const date = new Date("2021-04-17");
26 | const price: any =
27 | await redstone.getHistoricalPrice(symbol, { date, provider: "redstone" });
28 |
29 | expect(price).toBeDefined();
30 | expect(price.symbol).toBe(symbol);
31 | expect(price.value).toBeCloseTo(2421.882615498678, 12);
32 | expect(price.timestamp).toBeLessThanOrEqual(date.getTime());
33 | expect(price.timestamp).toBeGreaterThan(date.getTime() - 2 * 60 * 1000);
34 | shouldNotHaveTechProps(price);
35 | });
36 |
37 | test("Should get ETH price for 2021-04-17 and verify signature", async () => {
38 | const symbol = "ETH";
39 | const date = new Date("2021-04-17");
40 |
41 | await redstone.getHistoricalPrice(symbol, {
42 | date,
43 | verifySignature: true,
44 | provider: "redstone",
45 | });
46 | });
47 |
48 | test("Should get AR, BTC and ETH price for 2021-04-17", async () => {
49 | const symbols = ["AR", "BTC", "ETH"];
50 | const prices: any =
51 | await redstone.getHistoricalPrice(symbols, {
52 | date: "2021-04-17",
53 | provider: "redstone",
54 | });
55 |
56 | for (const symbol of symbols) {
57 | expect(prices[symbol]).toBeDefined();
58 | shouldNotHaveTechProps(prices[symbol]);
59 | }
60 |
61 | expect(prices["AR"].value).toBeGreaterThan(0.1);
62 | expect(prices["ETH"].value).toBeGreaterThan(100);
63 | expect(prices["BTC"].value).toBeGreaterThan(1000);
64 | });
65 |
66 | test("Should get AR prices in a time range", async () => {
67 | const symbol = "AR";
68 |
69 | const prices: any = await redstone.getHistoricalPrice(symbol, {
70 | startDate: "2021-06-17",
71 | endDate: "2021-06-18",
72 | interval: 600000,
73 | provider: "redstone",
74 | });
75 |
76 | expect(prices).toBeDefined();
77 | expect(prices.length).toBe(144);
78 |
79 | for (const price of prices) {
80 | shouldNotHaveTechProps(price);
81 | }
82 | });
83 |
84 | test("Should get AR prices with hourly interval", async () => {
85 | const symbol = "AR";
86 |
87 |
88 | const endDate = new Date("2021-06-20T23:59:00+00:00").getTime();
89 | const startDate = endDate - 2 * 24 * 3600 * 1000; // 2 days before
90 |
91 | const prices: any = await redstone.getHistoricalPrice(symbol, {
92 | startDate,
93 | endDate,
94 | interval: 3600 * 1000, // 1 hour
95 | verifySignature: true,
96 | provider: "redstone",
97 | });
98 |
99 | expect(prices).toBeDefined();
100 | expect(prices.length).toBeGreaterThanOrEqual(48);
101 | expect(prices.length).toBeLessThanOrEqual(50);
102 | });
103 |
104 | test("Should get AR prices with paging", async () => {
105 | const symbol = "AR";
106 |
107 | const prices: any = await redstone.getHistoricalPrice(symbol, {
108 | offset: 1000,
109 | limit: 100,
110 | provider: "redstone",
111 | });
112 |
113 | expect(prices).toHaveLength(100);
114 |
115 | for (const price of prices) {
116 | shouldNotHaveTechProps(price);
117 | }
118 | });
119 |
120 | test("Should not found AR price for 2019-01-01", async () => {
121 | await redstone.getHistoricalPrice("AR", {
122 | date: "2019-01-01",
123 | provider: "redstone"
124 | }).catch(e => {
125 | const msg = e.toString();
126 | expect(msg.includes("Price not found for symbol: AR")).toBe(true);
127 | });
128 | });
129 | });
130 |
--------------------------------------------------------------------------------
/__tests__/get-price.test.ts:
--------------------------------------------------------------------------------
1 | import redstone from "../src/index";
2 | import config from "../src/config";
3 |
4 | const MAX_TIME_DIFF = 90000; // 90s
5 |
6 | const shouldNotHaveTechProps = (price: any) => {
7 | const technicalProps = ["signature", "version", "providerPublicKey"];
8 | for (const prop of technicalProps) {
9 | expect(price).not.toHaveProperty(prop);
10 | }
11 | };
12 |
13 | describe("Test getPrice method", () => {
14 | test("Should get AR price", async () => {
15 | const symbol = "AR";
16 | const price: any = await redstone.getPrice(symbol);
17 |
18 | expect(price).toBeDefined();
19 | expect(price.symbol).toBe(symbol);
20 | expect(price.provider).toBe(config.providers["redstone-rapid"].address);
21 | expect(price.value).toBeGreaterThan(0.1);
22 | expect(Date.now() - price.timestamp).toBeLessThan(MAX_TIME_DIFF);
23 | });
24 |
25 | test("Should get ETH price", async () => {
26 | const symbol = "ETH";
27 | const price: any = await redstone.getPrice(symbol);
28 |
29 | expect(price).toBeDefined();
30 | expect(price.symbol).toBe(symbol);
31 | expect(price.value).toBeGreaterThan(10);
32 | expect(price.provider).toBe(config.providers["redstone-rapid"].address);
33 | expect(Date.now() - price.timestamp).toBeLessThan(MAX_TIME_DIFF);
34 | });
35 |
36 | test("Should not have technical properties", async () => {
37 | const symbol = "ETH";
38 | const price: any = await redstone.getPrice(symbol);
39 |
40 | expect(price).toBeDefined();
41 | shouldNotHaveTechProps(price);
42 | });
43 |
44 | test("Should get prices for AR, ETH and BTC", async () => {
45 | const symbols = ["AR", "ETH", "BTC"];
46 | const prices: any = await redstone.getPrice(symbols);
47 |
48 | expect(prices["AR"]).toBeDefined();
49 | expect(prices["ETH"]).toBeDefined();
50 | expect(prices["BTC"]).toBeDefined();
51 | expect(prices["AR"].value).toBeGreaterThan(0.1);
52 | expect(prices["ETH"].value).toBeGreaterThan(100);
53 | expect(prices["BTC"].value).toBeGreaterThan(1000);
54 | expect(Date.now() - prices["AR"].timestamp).toBeLessThan(MAX_TIME_DIFF);
55 | expect(Date.now() - prices["BTC"].timestamp).toBeLessThan(MAX_TIME_DIFF);
56 | });
57 |
58 | test("Should fetch AAPl price without explicit provider setting", async () => {
59 | const price = await redstone.getPrice("AAPL");
60 |
61 | expect(price.provider).toBe(config.providers["redstone-stocks"].address);
62 | expect(price.value).toBeGreaterThan(10);
63 | });
64 |
65 | test("Should fetch LINK price without explicit provider setting", async () => {
66 | const price = await redstone.getPrice("LINK");
67 |
68 | expect(price.provider).toBe(config.providers["redstone"].address);
69 | expect(price.value).toBeGreaterThan(1);
70 | });
71 |
72 | test("Should not have technical properties", async () => {
73 | const symbols = ["AR", "ETH", "BTC"];
74 | const prices: any = await redstone.getPrice(symbols);
75 |
76 | for (const symbol of symbols) {
77 | expect(prices[symbol]).toBeDefined();
78 | shouldNotHaveTechProps(prices[symbol]);
79 | }
80 | });
81 |
82 | test("Should get prices for AR, ETH, BNB, BTC and verify signature", async () => {
83 | const symbols = ["AR", "ETH", "BNB", "BTC"];
84 | const prices: any = await redstone.getPrice(symbols, {
85 | verifySignature: true,
86 | });
87 |
88 | for (const symbol of symbols) {
89 | expect(prices[symbol]).toBeDefined();
90 | }
91 | });
92 |
93 | test("Should verify signature for latest ETH price", async () => {
94 | const symbol = "ETH";
95 | const price = await redstone.getPrice(symbol, {
96 | verifySignature: true,
97 | });
98 |
99 | expect(price).toBeDefined();
100 | });
101 |
102 | });
103 |
--------------------------------------------------------------------------------
/docs/ALL_SUPPORTED_TOKENS.md:
--------------------------------------------------------------------------------
1 | # Currencies supported by Redstone
2 | - ETH
3 | - BTC
4 | - LINK
5 | - LTC
6 | - UNI
7 | - XRP
8 | - BCH
9 | - DOGE
10 | - EOS
11 | - TRX
12 | - XLM
13 | - YFI
14 | - DOT
15 | - AAVE
16 | - COMP
17 | - ADA
18 | - ETC
19 | - MKR
20 | - FIL
21 | - MATIC
22 | - BAT
23 | - DASH
24 | - SUSHI
25 | - USDC
26 | - ZEC
27 | - NEO
28 | - OMG
29 | - XTZ
30 | - SNX
31 | - ATOM
32 | - QTUM
33 | - ZRX
34 | - BTT
35 | - REN
36 | - CRV
37 | - DAI
38 | - LRC
39 | - BNB
40 | - CHZ
41 | - ALGO
42 | - BAND
43 | - ENJ
44 | - SOL
45 | - XMR
46 | - GRT
47 | - 1INCH
48 | - SAND
49 | - MANA
50 | - BSV
51 | - UMA
52 | - SHIB
53 | - KSM
54 | - BAL
55 | - VET
56 | - ZIL
57 | - XEM
58 | - WAVES
59 | - KNC
60 | - ONT
61 | - RSR
62 | - BNT
63 | - YFII
64 | - STORJ
65 | - PAX
66 | - ICP
67 | - DGB
68 | - THETA
69 | - SRM
70 | - ZEN
71 | - AXS
72 | - FTM
73 | - JST
74 | - AVAX
75 | - MIR
76 | - OCEAN
77 | - NEAR
78 | - TUSD
79 | - REP
80 | - HT
81 | - IOST
82 | - ICX
83 | - RVN
84 | - CRO
85 | - HBAR
86 | - LUNA
87 | - LPT
88 | - IOTA
89 | - WBTC
90 | - MASK
91 | - CAKE
92 | - SUN
93 | - XCH
94 | - FORTH
95 | - EGLD
96 | - ANKR
97 | - ANT
98 | - PERP
99 | - USDT
100 | - FTT
101 | - DIA
102 | - LON
103 | - CSPR
104 | - NANO
105 | - ALPHA
106 | - REEF
107 | - OKB
108 | - SC
109 | - DODO
110 | - CELR
111 | - OGN
112 | - SKL
113 | - POLS
114 | - BTM
115 | - SWRV
116 | - OXT
117 | - BZZ
118 | - CHR
119 | - MLN
120 | - WNXM
121 | - TORN
122 | - XVS
123 | - CVC
124 | - AKRO
125 | - API3
126 | - BADGER
127 | - MDX
128 | - AKITA
129 | - RUNE
130 | - MINA
131 | - NFT
132 | - CKB
133 | - BTG
134 | - SXP
135 | - NULS
136 | - BUSD
137 | - DCR
138 | - UTK
139 | - SNT
140 | - CEL
141 | - ELF
142 | - CVP
143 | - KP3R
144 | - DORA
145 | - NKN
146 | - ALICE
147 | - GTC
148 | - KAVA
149 | - LINA
150 | - XYM
151 | - NEXO
152 | - TRB
153 | - HOT
154 | - TOMO
155 | - BTS
156 | - LSK
157 | - NMR
158 | - BZRX
159 | - CELO
160 | - KLAY
161 | - XDC
162 | - KLV
163 | - VSYS
164 | - LEO
165 | - ZKS
166 | - LAMB
167 | - PHA
168 | - INJ
169 | - POND
170 | - FLOW
171 | - ANC
172 | - STX
173 | - LAT
174 | - SLP
175 | - ONG
176 | - ONE
177 | - WIN
178 | - PUNDIX
179 | - GHST
180 | - GLM
181 | - GAS
182 | - PAXG
183 | - CTXC
184 | - FLM
185 | - NU
186 | - AMP
187 | - KEEP
188 | - RLC
189 | - TLM
190 | - VRA
191 | - FRONT
192 | - OM
193 | - ORBS
194 | - CFX
195 | - ARPA
196 | - BOSON
197 | - WAXP
198 | - UST
199 | - GUSD
200 | - ELA
201 | - CQT
202 | - HC
203 | - CREAM
204 | - DOCK
205 | - AR
206 | - KINE
207 | - BAKE
208 | - BOND
209 | - STAKE
210 | - GT
211 | - WOO
212 | - COTI
213 | - PICKLE
214 | - RAMP
215 | - DAO
216 | - IOTX
217 | - MDT
218 | - VTHO
219 | - HNT
220 | - XVG
221 | - KAN
222 | - VELO
223 | - REV
224 | - CMT
225 | - WXT
226 | - MTA
227 | - SWFTC
228 | - AUDIO
229 | - SOC
230 | - WTC
231 | - VALUE
232 | - JFI
233 | - BLZ
234 | - AUCTION
235 | - OXY
236 | - RLY
237 | - ALPACA
238 | - O3
239 | - CTSI
240 | - QNT
241 | - RFUEL
242 | - JULD
243 | - FET
244 | - FSN
245 | - IRIS
246 | - HMR
247 | - MXC
248 | - XEC
249 | - MITH
250 | - DENT
251 | - FUN
252 | - STMX
253 | - CTK
254 | - AVA
255 | - FIRO
256 | - SFP
257 | - FREE
258 | - RARI
259 | - RNDR
260 | - IQ
261 | - PNK
262 | - PROPS
263 | - NAS
264 | - HEGIC
265 | - PRQ
266 | - KAR
267 | - TFUEL
268 | - RAY
269 | - AE
270 | - RING
271 | - FCL
272 | - KISHU
273 | - ORN
274 | - ABBC
275 | - PLU
276 | - AKT
277 | - NEST
278 | - UNFI
279 | - ELON
280 | - EUR
281 | - WRX
282 | - DATA
283 | - PNT
284 | - GBP
285 | - KMD
286 | - WING
287 | - NBS
288 | - DEGO
289 | - FARM
290 | - CLO
291 | - AMPL
292 | - BHD
293 | - REVV
294 | - VLX
295 | - DEP
296 | - EXE
297 | - SKM
298 | - STEEM
299 | - STRK
300 | - XUC
301 | - DNA
302 | - KONO
303 | - PLF
304 | - ACT
305 | - ITC
306 | - EGT
307 | - AST
308 | - YEE
309 | - DMG
310 | - BCD
311 | - DHT
312 | - ARK
313 | - MEME
314 | - PSG
315 | - GXC
316 | - PEARL
317 | - GOF
318 | - CRU
319 | - YAM
320 | - BDP
321 | - TRIBE
322 | - QUICK
323 | - BCHA
324 | - BEAR
325 | - UOS
326 | - ETHBULL
327 | - TARA
328 | - STPT
329 | - CNS
330 | - EXRD
331 | - RNT
332 | - LTO
333 | - BULL
334 | - HOPR
335 | - AERGO
336 | - KCS
337 | - FIS
338 | - ALCX
339 | - GERA
340 | - GTO
341 | - MTL
342 | - KEY
343 | - BEAM
344 | - TCT
345 | - HIVE
346 | - ARDR
347 | - AUD
348 | - BEL
349 | - LIT
350 | - EPS
351 | - SENSO
352 | - SATT
353 | - TON
354 | - STC
355 | - INSUR
356 | - ETP
357 | - GNO
358 | - XAUT
359 | - ALBT
360 | - SUKU
361 | - MOB
362 | - DCN
363 | - FLY
364 | - EWT
365 | - LBC
366 | - CNTM
367 | - HDAO
368 | - INX
369 | - JOB
370 | - LOON
371 | - MOF
372 | - XWC
373 | - OKT
374 | - CLV
375 | - FESS
376 | - SERO
377 | - COVER
378 | - SEELE
379 | - DMD
380 | - NWC
381 | - BHP
382 | - FAIR
383 | - CONV
384 | - XPR
385 | - CVT
386 | - VIB
387 | - ABT
388 | - BETH
389 | - KCASH
390 | - CFG
391 | - XSR
392 | - CHAT
393 | - TRADE
394 | - TAI
395 | - LOOM
396 | - ACH
397 | - NSURE
398 | - JGN
399 | - ATA
400 | - ERN
401 | - FEAR
402 | - MAPS
403 | - HEZ
404 | - SWINGBY
405 | - ZEE
406 | - CWS
407 | - WAN
408 | - FLEX
409 | - OLT
410 | - BVOL
411 | - DIVI
412 | - XRPBULL
413 | - CUDOS
414 | - HARD
415 | - ASD
416 | - XRPBEAR
417 | - HGET
418 | - HAPI
419 | - LTCBEAR
420 | - FIO
421 | - ETHBEAR
422 | - IBVOL
423 | - LTCBULL
424 | - PROM
425 | - EOSBULL
426 | - MFT
427 | - BIX
428 | - FOR
429 | - LHB
430 | - TBCC
431 | - GNX
432 | - PANDO
433 | - GHD
434 | - TNC
435 | - GMT
436 | - TKO
437 | - DUSK
438 | - TROY
439 | - DNT
440 | - ROSE
441 | - JUV
442 | - RIF
443 | - BTCST
444 | - ACM
445 | - SUPER
446 | - LOC
447 | - BCVT
448 | - HYDRA
449 | - RSV
450 | - QASH
451 | - LYM
452 | - EURS
453 | - XSN
454 | - BEST
455 | - BMI
456 | - ECELL
457 | - MATH
458 | - BIFI
459 | - SAFEMOON
460 | - SRK
461 | - NDAU
462 | - AMC
463 | - AAPL
464 | - ABNB
465 | - ADABULL
466 | - AMD
467 | - AMZN
468 | - ARKK
469 | - BABA
470 | - BB
471 | - BILI
472 | - BNTX
473 | - BRZ
474 | - BYND
475 | - CBC
476 | - COIN
477 | - CRON
478 | - CUSD
479 | - DEXA
480 | - DFI
481 | - FB
482 | - GLD
483 | - GME
484 | - GOOGL
485 | - KDA
486 | - MONA
487 | - MRNA
488 | - NFLX
489 | - NIO
490 | - NOK
491 | - PFE
492 | - PHNX
493 | - PYPL
494 | - RFOX
495 | - ROOK
496 | - SPY
497 | - SQ
498 | - TSLA
499 | - TSM
500 | - TWTR
501 | - UBER
502 | - WICC
503 | - ZM
504 | - HTDF
505 | - YFV
506 | - USDJ
507 | - BAGS
508 | - LBY
509 | - BANANA
510 | - TOKAU
511 | - SMT
512 | - PAI
513 | - HPT
514 | - RUFF
515 | - HBC
516 | - TT
517 | - VIDY
518 | - CRE
519 | - ZAP
520 | - WEST
521 | - BCN
522 | - ETN
523 | - PRE
524 | - DRS
525 | - DDRT
526 | - LIKE
527 | - TRR
528 | - MNC
529 | - SMART
530 | - CARR
531 | - ELCASH
532 | - MCO
533 | - APM
534 | - YOU
535 | - EM
536 | - AET
537 | - RIO
538 | - XPO
539 | - UBTC
540 | - LBA
541 | - AAC
542 | - BLOC
543 | - TMTG
544 | - ORS
545 | - TRIO
546 | - APIX
547 | - EC
548 | - NDN
549 | - TRA
550 | - ANW
551 | - PLG
552 | - MXT
553 | - SFG
554 | - DefiBox
555 | - ZYRO
556 | - TOPC
557 | - INT
558 | - WGRT
559 | - TRUE
560 | - PAY
561 | - QUN
562 | - CTC
563 | - HYC
564 | - ALV
565 | - MDA
566 | - ROAD
567 | - PST
568 | - PPT
569 | - GAL
570 | - ETM
571 | - YOYOW
572 | - STEP
573 | - AAB
574 | - OGT
575 | - XRT
576 | - SHOPX
577 | - FEI
578 | - ETHA
579 | - BUNNY
580 | - APN
581 | - DOGGY
582 | - AXIS
583 | - DOM
584 | - BABYDOGE
585 | - HF
586 | - DPET
587 | - DFD
588 | - FIDA
589 | - GEEQ
590 | - EXCHBEAR
591 | - XCAD
592 | - DOS
593 | - HPB
594 | - TVK
595 | - TOKO
596 | - UMB
597 | - CET
598 | - ROUTE
599 | - NORD
600 | - EZ
601 | - NIM
602 | - ALTBEAR
603 | - BNBBEAR
604 | - DEC
605 | - JRT
606 | - SMTY
607 | - HTR
608 | - BOLT
609 | - EXCHBULL
610 | - MAHA
611 | - BNBBULL
612 | - FRM
613 | - ALTBULL
614 | - SWAP
615 | - MITX
616 | - MARSH
617 | - PHX
618 | - UNN
619 | - BEPRO
620 | - LUSD
621 | - NGM
622 | - DOG
623 | - PEAK
624 | - HAKKA
625 | - BNF
626 | - PLEX
627 | - ARMOR
628 | - FILDA
629 | - YFDAI
630 | - G999
631 | - AG8
632 | - CORX
633 | - COOP
634 | - BLAST
635 | - BAAS
636 | - DS
637 | - ROCKS
638 | - TYB
639 | - LEND
640 | - DREP
641 | - USDS
642 | - VITE
643 | - MBL
644 | - BTCUP
645 | - BTCDOWN
646 | - ETHUP
647 | - ETHDOWN
648 | - DOTUP
649 | - SUSD
650 | - ATM
651 | - AUTO
652 | - BAR
653 | - PERL
654 | - HUB
655 | - ILV
656 | - RFR
657 | - STAK
658 | - GASP
659 | - NUT
660 | - CCE
661 | - SUPERBID
662 | - Cube
663 | - BOR
664 | - ASP
665 | - HEDG
666 | - IDEA
667 | - RRT
668 | - SAN
669 | - GNT
670 | - SNGLS
671 | - NEC
672 | - REQ
673 | - ODE
674 | - VEE
675 | - Group
676 | - ZCN
677 | - XRA
678 | - GOT
679 | - YGG
680 | - OMN
681 | - RBTC
682 | - EURT
683 | - PASS
684 | - WBT
685 | - XCHF
686 | - GTX
687 | - RRB
688 | - RINGX
689 | - BTSE
690 | - MDOGE
691 | - EOSDT
692 | - UOP
693 | - B21X
694 | - ETH2X
695 | - ICE
696 | - CHEX
697 | - QTF
698 | - PLANETS
699 | - ANY
700 | - FINE
701 | - BUY
702 | - HERO
703 | - ELONGATE
704 | - ORE
705 | - CBY
706 | - ULT
707 | - BBT
708 | - LABS
709 | - NCT
710 | - WSB
711 | - BSP
712 | - SAFEMARS
713 | - AMA
714 | - BEC
715 | - POLK
716 | - SCONEX
717 | - TERN
718 | - LAYER
719 | - AXC
720 | - OCC
721 | - EPK
722 | - AQUAGOAT
723 | - GMEE
724 | - LANC
725 | - PNIXS
726 | - STA
727 | - PORNROCKET
728 | - APL
729 | - XDNA
730 | - SG
731 | - SCRT
732 | - WHIRL
733 | - RDD
734 | - STARS
735 | - SATOZ
736 | - ACB
737 | - APHA
738 | - BAX
739 | - BITW
740 | - BOA
741 | - CAMP
742 | - CGC
743 | - CLT
744 | - DOGEBULL
745 | - FLIXX
746 | - GDXJ
747 | - GLXY
748 | - GOLD
749 | - HNS
750 | - HXRO
751 | - IGNIS
752 | - KAI
753 | - KOK
754 | - MSTR
755 | - NVT
756 | - PENN
757 | - PMA
758 | - PYR
759 | - RENBTC
760 | - ROC
761 | - SDT
762 | - SHR
763 | - SLV
764 | - SMBSWAP
765 | - SUTER
766 | - SYLO
767 | - TRYB
768 | - UBQ
769 | - UCT
770 | - UQC
771 | - USDN
772 | - USO
773 | - XDB
774 | - XDN
775 | - XEP
776 | - YFL
777 | - ZUSD
778 | - ILC
779 | - VEX
780 | - CAN
781 | - YFX
782 | - HGOLD
783 | - SYS
784 | - ZIK
785 | - TORI
786 | - BWB
787 | - OGO
788 | - ATP
789 | - EKT
790 | - NODE
791 | - MX
792 | - NEW
793 | - PVT
794 | - MDS
795 | - OCN
796 | - DTA
797 | - CNNS
798 | - TOP
799 | - LOL
800 | - MHC
801 | - AWG
802 | - AWX
803 | - EOSC
804 | - POLY
805 | - YUSRA
806 | - CHI
807 | - DERO
808 | - COMBO
809 | - HYDRO
810 | - EMC
811 | - AYA
812 | - BKK
813 | - FLUX
814 | - PIVX
815 | - DGTX
816 | - ARRR
817 | - ERG
818 | - KTON
819 | - CHESS
820 | - INNBC
821 | - NVDA
822 | - GDX
823 | - TLRY
824 | - DKNG
825 | - ETZ
826 | - ZLW
827 | - LCMS
828 | - ZUPI
829 | - SEMI
830 | - CFXQ
831 | - BFIC
832 | - PFI
833 | - IQN
834 | - ZB
835 | - ADABEAR
836 | - CUSDT
837 | - KIN
838 | - UBXT
839 | - BCHBEAR
840 | - BCHBULL
841 | - BSVBEAR
842 | - BSVBULL
843 | - LINKBEAR
844 | - LINKBULL
845 | - TRXBEAR
846 | - TRXBULL
847 | - XLMBEAR
848 | - XLMBULL
849 | - SRM3L
850 | - YFI3S
851 | - DF
852 | - LEASH
853 | - MAN
854 | - GOLDR
855 | - IPX
856 | - ERK
857 | - NXT
858 | - SOLO
859 | - CENNZ
860 | - NTK
861 | - CRPT
862 | - XHT
863 | - MASS
864 | - TLOS
865 | - MAP
866 | - SKEY
867 | - FKX
868 | - DSLA
869 | - ETHO
870 | - OOE
871 | - SHA
872 | - GLCH
873 | - LTX
874 | - ERSDL
875 | - BNS
876 | - POL
877 | - ENQ
878 | - UBT
879 | - EFK
880 | - TTT
881 | - BEER
882 | - RWN
883 | - EVY
884 | - HVE2
885 | - WHX
886 | - USDU
887 | - UCOIN
888 | - DFM
889 | - DUKE
890 | - INVESTEL
891 | - HOKK
892 | - KABOSU
893 | - GDT
894 | - SMC
895 | - MARX
896 | - MVL
897 | - BID
898 | - GNBT
899 | - FOY
900 | - CEEK
901 | - BLCT
902 | - BSBT
903 | - LONG
904 | - DPI
905 | - ESD
906 | - BAO
907 | - TRU
908 | - sUSD
909 | - MPH
910 | - SFI
911 | - GRIN
912 | - MIOTA
913 | - VGX
914 | - CHSB
915 | - BTMX
916 | - JPY
917 | - CHF
918 | - PIG
919 | - BASE
920 | - wNXM
921 | - FXS
922 | - BAC
923 | - XOR
924 | - WHITE
925 | - CORE
926 | - FRAX
927 | - APY
928 | - SPI
929 | - ALC
930 | - ARDRIVE
931 | - ARGO
932 | - ARVERIFY
933 | - DLT
934 | - EDST
935 | - EMD_PST_TOKEN
936 | - OPENBITS
937 | - PBT
938 | - QUAD
939 | - SAM
940 | - SUNNY
941 | - VRT
942 | - XYZ
943 |
--------------------------------------------------------------------------------
/docs/FLUENT_INTERFACE.md:
--------------------------------------------------------------------------------
1 | # Fluent interface for redstone
2 |
3 | Redstone implements a fluent interface to simplify query creation thanks to a human readable syntax.
4 |
5 | ### Importing
6 | To use the fluent interface you should import the redstone-api in a standard way and initialise a query calling `redstone.query()`;
7 | ```js
8 | // Using Node.js `require()`
9 | const redstone = require('redstone-api');
10 |
11 | // Using ES6 imports
12 | import redstone from 'redstone-api';
13 |
14 | ```
15 |
16 | ## Usage
17 | All redstone queries consits of 4 parts:
18 | - Query initialization (`redstone.query()`)
19 | - What to fetch (`symbol` or `symbols`)
20 | - For which date/dates (`latest`, `atDate`, `forLastHours`, `hoursAgo`, `fromDate`, `toDate`)
21 | - Query execution (`exec`)
22 |
23 | ## Examples
24 |
25 | ### Get the latest price for a single token
26 | ```js
27 | const price = await redstone.query()
28 | .symbol("AR")
29 | .latest()
30 | .exec();
31 |
32 | console.log(price.value); // latest price value for AR token (in USD)
33 | console.log(price.timestamp); // the exact timestamp of the price
34 | ```
35 |
36 | ### Get the historical price for a single token
37 | ```js
38 | const price = await redstone.query()
39 | .symbol("AR")
40 | .atDate("2021-04-19")
41 | .exec();
42 | ```
43 | 💡 Note: The argument passed to `atDate` must be convertable to date. You may pass date (e.g. `new Date(2021-04-01)`), timestamp (e.g. `1617709771289`), or just string (e.g. `2021-04-01` or `2021-04-01T12:30:58`)
44 |
45 | ### Get the historical price for the last X hours
46 | ```js
47 | // Returns an array of prices with ~10 minutes interval
48 | const prices = await redstone.query()
49 | .symbol("AR")
50 | .forLastHours(12)
51 | .exec();
52 | ```
53 |
54 | ### Get the historical price for X hours ago
55 | ```js
56 | const price = await redstone.query()
57 | .symbol("AR")
58 | .hoursAgo(24)
59 | .exec();
60 | ```
61 |
62 | ### Get the historical price for the last X days
63 | ```js
64 | // Returns an array of prices with ~1h minutes interval
65 | const prices = await redstone.query()
66 | .symbol("AR")
67 | .forLastDays(7)
68 | .exec();
69 | ```
70 |
71 | ### Get the historical prices in a time range
72 | ```js
73 | // Returns an array of prices
74 | // Interval depends on the time range
75 | // For ranges more than 24 hours interval is 1h
76 | const prices = await redstone.query()
77 | .symbol("AR")
78 | .fromDate("2021-04-19")
79 | .toDate("2021-04-20")
80 | .exec();
81 | ```
82 |
83 | ### Get the latest prices for several tokens
84 | ```js
85 | const prices = await redstone.query()
86 | .symbols(["AR", "BTC", "ETH"])
87 | .latest()
88 | .exec();
89 |
90 | console.log(prices); // Example output below
91 | /*
92 | {
93 | "BTC": {
94 | value: 58953.39,
95 | timestamp: 1617152802779,
96 | ...
97 | },
98 | "ETH": {
99 | value: 1856.75,
100 | timestamp: 1617152802779,
101 | ...
102 | },
103 | ...
104 | }
105 | */
106 | ```
107 |
108 | ### Get the historical prices for several tokens
109 | ```js
110 | const prices: any = await redstone.query()
111 | .symbols(["AR", "ETH", "BTC"])
112 | .atDate("2021-04-19")
113 | .exec();
114 | ```
115 |
116 |
117 | ### Get prices for all available tokens
118 | ```js
119 | const prices = await redstone.query()
120 | .allSymbols()
121 | .latest()
122 | .exec();
123 |
124 | console.log(prices); // Example output below
125 | /*
126 | {
127 | "BTC": {...},
128 | "ETH": {...},
129 | ...
130 | }
131 | */
132 |
133 | console.log(prices["AR"].value); // latest price value for AR
134 | console.log(prices["EUR"].value); // latest price value for EUR
135 | ```
136 |
137 |
--------------------------------------------------------------------------------
/docs/HTTP_API.md:
--------------------------------------------------------------------------------
1 | # Redstone HTTP Api
2 |
3 | Redstone HTTP Api is accessible at https://api.redstone.finance/prices.
4 |
5 | It allows to fetch financial data from [Redstone data ecosystem](https://github.com/redstone-finance/redstone-api/blob/main/docs/REDSTONE_DATA_ECOSYSTEM.md).
6 |
7 | ## Usage
8 |
9 | ### Using curl
10 |
11 | #### ⦿ Fetch latest price for a single currency
12 | ```bash
13 | curl "https://api.redstone.finance/prices/?symbol=ETH&provider=redstone&limit=1"
14 | ```
15 |
16 | 💡 Note: You can replace symbol query param with a currency symbol of [any supported token](ALL_SUPPORTED_TOKENS.md)
17 |
18 | #### ⦿ Fetch latest price for several currencies
19 | ```bash
20 | curl "https://api.redstone.finance/prices/?symbols=ETH,BTC,AR,EUR,CHF,BNB&provider=redstone"
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/QUICK_START.md:
--------------------------------------------------------------------------------
1 | # Quick start for Redstone API
2 |
3 | ## Install npm module
4 | ```bash
5 | npm install redstone-api
6 | ```
7 | ## Fetch price for a single token
8 | ```js
9 | const redstone = require("redstone-api");
10 |
11 | // Prints the latest price for AR token in USD
12 | redstone.getPrice("AR").then((price) => {
13 | console.log(price.value);
14 | });
15 | ```
16 |
17 | Explore [redstone api](../README.md#-usage)
18 |
--------------------------------------------------------------------------------
/docs/REDSTONE_DATA_ECOSYSTEM.md:
--------------------------------------------------------------------------------
1 |
Redstone - data ecosystem for Decentralised Finance
2 |
3 | The Problem
4 |
5 |
6 | Financial data is the backbone of decentralised finance.
7 | It enables assets valuation and collateral management.
8 | Without access to the reliable pricing feeds, it's impossible to create instruments like derivatives, options and futures.
9 |
10 |
11 |
12 | The awareness of the importance of data quality raised with the growth of decentralised finance.
13 | This sector (aka DeFi) is experiencing an exponential growth surpassing 5bn valuation
14 | and attracting professional investors and thousands of retail users.
15 | At the beginning, different protocols used their own, in-house price feeds.
16 | The naive approach to getting financial data lead to many multiple security incidents
17 | resulting in millions of dollars being lost and posing an existential threat to the further growth of the sector.
18 | Let's just mention the few costly incidents from the last year:
19 |
20 |
21 |
22 |
23 | The hard-learned lesson about the data quality importance led to the explosive growth of data-providing services.
24 | The Chainlink, being the market leader, experienced a massive growth reaching a valuation that exceed the whole defi space.
25 | This led many experts to the conclusion, that the whole decentralised finance is as valuable as the data that powers it.
26 |
27 |
28 | There are however a few problems with how the financial data is being collected right now.
29 | The most important is the extreme cost of storing pricing data on-chain.
30 | The Chainlink pays daily about ~$100k to cover the Ethereum storage fees.
31 | This model is currently heavily subsidised and is not sustainable in the future
32 | when we are expecting more and more financial instruments to enter the market.
33 |
34 |
35 | The Solution
36 |
37 |
38 | Redstone uses Arweave as a base blockchain.
39 | Arweave with its affordable permanent storage blockchain appears to be a perfect remedy for the pains of the DeFi sector.
40 | Moreover, the Smartweave contracts may help to maintain data integrity managing the reputation of data providers.
41 | Arweave offers also a neat monetisation model with the Profit Sharing Token rewarding providers for high-quality data.
42 |
43 |
44 | Architecute
45 |
46 | Data miners
47 | Data miners are agents (system deamons) that periodically upload data to the network. The content of the uploaded dataset is specified by the data order that contains:
48 |
49 | * Asset name
50 | * Period
51 | * Probing interval
52 | * Data source
53 |
54 | The orders are also stored on the Arweave chain and periodically queried by the miners.
55 |
56 | Data miners could be started by executing the following scipt from the main directory:
57 |
58 | `
59 | node ./backend/provider/data-miners/mine-data.js
60 | `
61 |
62 |
63 | Data processors
64 | Data processors can transform one data set into another. It allows producing meaningful metrics and insights based on a combination of base pricing and traffic data.
65 |
66 | Balancer rewards
67 | An example of data processor is the code calculating rewards for providing liquidity to the Balancer protocol.
68 |
69 | The sample processor could be started by executing the following scipt from the main directory:
70 |
71 | `
72 | node ./backend/provider/data-miners/mine-balancer-rewards.js
73 | `
74 |
75 | Licensing
76 |
77 |
85 |
--------------------------------------------------------------------------------
/docs/redstone-query-example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redstone-finance/redstone-api/2d9a549acaa57d5dc64de05c2e80735d09aede48/docs/redstone-query-example.gif
--------------------------------------------------------------------------------
/docs/redstone-simple-example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redstone-finance/redstone-api/2d9a549acaa57d5dc64de05c2e80735d09aede48/docs/redstone-simple-example.gif
--------------------------------------------------------------------------------
/docs/redstone-symbols-example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/redstone-finance/redstone-api/2d9a549acaa57d5dc64de05c2e80735d09aede48/docs/redstone-symbols-example.gif
--------------------------------------------------------------------------------
/examples/discord-bots/ar-price-bot.js:
--------------------------------------------------------------------------------
1 | const redstone = require("../../lib/index.js");
2 | const { runSimpleDiscordBot } = require("./simple-discord-bot");
3 |
4 | runSimpleDiscordBot({
5 | titleGetter: async () => {
6 | const priceFeed = await redstone.getPrice("AR");
7 | return `$${priceFeed.value.toFixed(2)} = 1 AR`;
8 | },
9 | botToken: process.env.BOT_TOKEN,
10 | });
11 |
--------------------------------------------------------------------------------
/examples/discord-bots/memory-price-bot.js:
--------------------------------------------------------------------------------
1 | const axios = require("axios");
2 | const redstone = require("../../lib/index.js");
3 | const { runSimpleDiscordBot } = require("./simple-discord-bot");
4 |
5 | async function getCurrentARPricePerGB() {
6 | const response = await axios.get("https://arweave.net/price/1073741824");
7 | return response.data * 0.000000000001;
8 | }
9 |
10 | runSimpleDiscordBot({
11 | titleGetter: async () => {
12 | const priceFeed = await redstone.getPrice("AR");
13 | const arPricePerGB = await getCurrentARPricePerGB();
14 | const usdPricePerGB = arPricePerGB * priceFeed.value;
15 | const usdPricePerGBFormatted = +usdPricePerGB.toFixed(2);
16 | return `$${usdPricePerGBFormatted} = 1 GB`;
17 | },
18 | botToken: process.env.BOT_TOKEN,
19 | });
20 |
--------------------------------------------------------------------------------
/examples/discord-bots/simple-discord-bot.js:
--------------------------------------------------------------------------------
1 | // Format logs
2 | require("console-stamp")(console, "[HH:MM:ss.l]");
3 | const Discord = require("discord.js");
4 |
5 | function runSimpleDiscordBot({ titleGetter, botToken, interval = 5000 }) {
6 | if (!botToken) {
7 | throw new Error("Bot token is required to run discord bot");
8 | }
9 |
10 | const Client = new Discord.Client();
11 |
12 | Client.on("ready", () => {
13 | // Setting subtitle (as an activity)
14 | Client.user.setActivity("Redstone", {
15 | type: "LISTENING",
16 | });
17 |
18 | setInterval(async () => {
19 | try {
20 | const title = await titleGetter();
21 |
22 | console.log(`Updating title: ${title}`);
23 |
24 | // Displaying title (as a nickname)
25 | Client.guilds.cache.forEach((guild) => {
26 | guild.me.setNickname(title);
27 | });
28 | } catch (err) {
29 | console.log("Error fetching title for discord bot:");
30 | console.log(err);
31 | }
32 | }, interval);
33 | });
34 |
35 | Client.login(botToken);
36 | }
37 |
38 | module.exports = { runSimpleDiscordBot };
39 |
--------------------------------------------------------------------------------
/examples/telegram-bots/telegram-price-bot.js:
--------------------------------------------------------------------------------
1 | const { Telegraf } = require("telegraf");
2 | const Markup = require("telegraf/markup");
3 | const Redstone = require("@redstonefi/api");
4 |
5 | const bot = new Telegraf("YOUR TELEGRAM BOT TOKEN");
6 |
7 | // initialize keyboard command list with the /start command
8 | bot.command("start", ({ reply }) => {
9 | return reply("Hello Arweaver!", Markup
10 | .keyboard([
11 | ["📈 AR Price 📉"],
12 | ])
13 | .oneTime()
14 | .resize()
15 | .extra()
16 | )
17 | })
18 |
19 |
20 | // price command
21 | bot.hears("📈 AR Price 📉" , async(ctx) => {
22 |
23 | const fetch = await Redstone.getPrice("AR");
24 |
25 | let update = new Date - Date.parse(fetch["updated"]); //last update timestamp
26 | let price = fetch["price"];
27 |
28 | let message = (`AR/USDT` +
29 | `\n\nprice: $ ${price} ` +
30 | `\nLast update: ${Math.round(update / 1000 / 60)} min ago` +
31 | `\n\nData provided by redstone`
32 | );
33 |
34 | ctx.replyWithHTML(message, {disable_web_page_preview: true});
35 | });
36 |
37 |
38 | // custom auto-reply on any sticker
39 | bot.on("sticker", (ctx) => ctx.reply("i like stickers!"));
40 |
41 |
42 | bot.launch();
43 |
--------------------------------------------------------------------------------
/jestconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "transform": {
3 | "^.+\\.(t|j)sx?$": "ts-jest"
4 | },
5 | "testEnvironment": "node",
6 | "collectCoverage": true,
7 | "testTimeout": 20000,
8 | "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
9 | "moduleFileExtensions": [
10 | "ts",
11 | "tsx",
12 | "js",
13 | "jsx",
14 | "json",
15 | "node"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redstone-api",
3 | "version": "0.4.12",
4 | "description": "Javascript library for fetching trusted token pricing data from Redstone data ecosystem",
5 | "main": "lib/index.js",
6 | "types": "lib/index.d.ts",
7 | "scripts": {
8 | "build": "tsc",
9 | "test": "jest --config jestconfig.json",
10 | "format": "prettier --write \"src/**/*.ts\"",
11 | "prepare": "yarn build",
12 | "lint": "tslint -p ./",
13 | "prepublishOnly": "yarn lint",
14 | "preversion": "yarn lint",
15 | "version": "git add -A src",
16 | "postversion": "git push && git push --tags",
17 | "generate-docs": "typedoc ./src/** --out .exp/docs-typedoc",
18 | "pack-package": "yarn build && npm pack"
19 | },
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/redstone-finance/redstone-api.git"
23 | },
24 | "keywords": [
25 | "price",
26 | "tokens",
27 | "arweave",
28 | "api",
29 | "oracle",
30 | "market",
31 | "crypto",
32 | "data",
33 | "finance",
34 | "defi",
35 | "pricing",
36 | "usd",
37 | "AR",
38 | "BTC",
39 | "USDT",
40 | "ETH",
41 | "WETH",
42 | "WBTC",
43 | "coin",
44 | "Bitcoin",
45 | "Ethereum",
46 | "currency",
47 | "cryptocurrency"
48 | ],
49 | "author": "Redstone Team ",
50 | "contributors": [
51 | "Jakub Wojciechowski ",
52 | "Alex Suvorov ",
53 | "Peter Pedziwiatr "
54 | ],
55 | "license": "MIT",
56 | "bugs": {
57 | "url": "https://github.com/redstone-finance/redstone-api/issues"
58 | },
59 | "homepage": "https://api.docs.redstone.finance/",
60 | "devDependencies": {
61 | "@types/jest": "^26.0.20",
62 | "@types/lodash": "^4.14.168",
63 | "@types/node": "^14.14.33",
64 | "@types/pako": "^1.0.1",
65 | "console-stamp": "^3.0.2",
66 | "discord.js": "^12.5.1",
67 | "documentation": "^13.2.5",
68 | "jest": "^26.6.3",
69 | "prettier": "^2.2.1",
70 | "ts-jest": "^26.5.3",
71 | "ts-node": "^9.1.1",
72 | "tslint": "^6.1.3",
73 | "tslint-config-prettier": "^1.18.0",
74 | "typedoc": "^0.20.36",
75 | "typedoc-plugin-markdown": "^3.7.2",
76 | "typescript": "^4.2.3"
77 | },
78 | "dependencies": {
79 | "ar-gql": "^0.0.6",
80 | "arweave": "^1.10.16",
81 | "arweave-multihost": "^0.1.0",
82 | "axios": "^1.6.0",
83 | "deep-sort-object": "^1.0.2",
84 | "lodash": "^4.17.21",
85 | "pako": "^2.0.3"
86 | },
87 | "files": [
88 | "lib/**/*"
89 | ]
90 | }
91 |
--------------------------------------------------------------------------------
/src/api.ts:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 | import ArweaveProxy from "./proxies/arweave-proxy";
3 | import CacheProxy from "./proxies/cache-proxy";
4 | import SignatureVerifier from "./signature-verifier";
5 | import PriceNotFoundError from "./errors/price-not-found";
6 | import {
7 | PriceData,
8 | GetPriceOptions,
9 | ConvertableToDate,
10 | RedstoneApiConfig,
11 | PriceDataWithSignature,
12 | GetHistoricalPriceOptions,
13 | GetHistoricalPriceForSingleTokenOptions,
14 | } from "./types";
15 | import config from "./config/index";
16 |
17 | const REDSTONE_API_DEFAULTS = {
18 | defaultProvider: "redstone",
19 | useCache: true,
20 | verifySignature: false,
21 | };
22 |
23 | // Providers list is sorted by priority (redstone-rapid has the highest priority)
24 | const PROVIDERS_SORTED_BY_PRIORITY = [
25 | "redstone-rapid",
26 | "redstone-stocks",
27 | "redstone"];
28 |
29 | export default class RedstoneApi {
30 | private defaultProvider: string;
31 | private useCache: boolean;
32 | private version: string;
33 | private verifySignature: boolean;
34 | private arweaveProxy: ArweaveProxy;
35 | private cacheProxy: CacheProxy;
36 | private signatureVerifier: SignatureVerifier;
37 |
38 | constructor(redstoneConfig: RedstoneApiConfig = {}) {
39 | this.arweaveProxy = new ArweaveProxy();
40 | this.cacheProxy = new CacheProxy(
41 | _.defaultTo(redstoneConfig.cacheApiUrl, config.cacheApiUrl),
42 | );
43 | this.signatureVerifier = new SignatureVerifier(this.arweaveProxy);
44 | this.version = _.defaultTo(redstoneConfig.version, config.version);
45 | this.verifySignature = _.defaultTo(
46 | redstoneConfig.verifySignature,
47 | REDSTONE_API_DEFAULTS.verifySignature,
48 | );
49 | this.defaultProvider = _.defaultTo(
50 | redstoneConfig.defaultProvider,
51 | REDSTONE_API_DEFAULTS.defaultProvider,
52 | );
53 | this.useCache = _.defaultTo(
54 | redstoneConfig.useCache,
55 | REDSTONE_API_DEFAULTS.useCache,
56 | );
57 | }
58 |
59 | setCacheApiUrl(cacheApiUrl: string) {
60 | this.cacheProxy.setCacheApiUrl(cacheApiUrl);
61 | }
62 |
63 | /**
64 | * Returns the latest price for a single symbol
65 | *
66 | * @param symbol - Token symbol (string)
67 | * @param opts - Optional params (object)
68 | * * opts.provider: provider name (string)
69 | * * opts.verifySignature: enable signature verification (boolean)
70 | * @returns The latest price for the token
71 | *
72 | */
73 | async getPrice(symbol: string, opts?: GetPriceOptions): Promise;
74 | /**
75 | * Returns the latest price for several symbols
76 | *
77 | * @param symbols - Token symbols (array of strings)
78 | * @param opts - Optional params (object)
79 | * * opts.provider: provider name (string)
80 | * * opts.verifySignature: enable signature verification (boolean)
81 | * @returns The latest price for the tokens
82 | *
83 | */
84 | async getPrice(
85 | symbols: string[],
86 | opts?: GetPriceOptions,
87 | ): Promise<{ [token: string]: PriceData }>;
88 | async getPrice(
89 | symbolOrSymbols: any,
90 | opts: GetPriceOptions = {},
91 | ): Promise {
92 | const shouldVerifySignature = _.defaultTo(
93 | opts.verifySignature,
94 | this.verifySignature,
95 | );
96 |
97 | if (_.isArray(symbolOrSymbols)) {
98 | // Getting latest price for many tokens
99 | const symbols = symbolOrSymbols;
100 | const provider = this.getProviderForSymbols(symbols, opts.provider);
101 | return await this.getPriceForManyTokens({
102 | symbols,
103 | provider,
104 | shouldVerifySignature,
105 | });
106 | } else if (typeof symbolOrSymbols === "string") {
107 | // Getting latest price for one token
108 | const symbol = symbolOrSymbols;
109 | const provider = this.getProviderForSymbol(symbol, opts.provider);
110 | return await this.getLatestPriceForOneToken({
111 | symbol,
112 | provider,
113 | shouldVerifySignature,
114 | });
115 | }
116 | }
117 |
118 | /**
119 | * Returns the historical price for a single token
120 | *
121 | * @remarks
122 | * Full list of supported tokens is available at
123 | * {@link https://github.com/redstone-finance/redstone-api/blob/main/ALL_SUPPORTED_TOKENS.md}
124 | *
125 | * @param symbol - Token symbol (string)
126 | * @param opts - Optional params (object)
127 | * * opts.date: Date for the historical price
128 | * * opts.provider: provider name (string)
129 | * * opts.verifySignature: enable signature verification (boolean)
130 | * @returns The historical price for token
131 | *
132 | */
133 | async getHistoricalPrice(
134 | symbol: string,
135 | opts: GetHistoricalPriceOptions,
136 | ): Promise;
137 | /**
138 | * Returns the historical prices for a token in a time range with the specified interval
139 | *
140 | * @remarks
141 | * This method can be used to display charts with historical prices.
142 | * Full list of supported tokens is available at
143 | * {@link https://github.com/redstone-finance/redstone-api/blob/main/ALL_SUPPORTED_TOKENS.md}
144 | *
145 | * @param symbol - Token symbol
146 | * @param opts - Options object.
147 | * It must contain either a startDate, an endDate, and interval properties
148 | * or an offset and a limit (used for pagination) properties.
149 | * * opts.startDate: Start time for the time range (date | timestamp | string)
150 | * * opts.endDate: End time for the time range (date | timestamp | string)
151 | * * opts.interval: Interval in milliseconds (number)
152 | * * opts.provider: provider name (string)
153 | * * opts.offset: query offset (number)
154 | * * opts.limit: query limit (number)
155 | * * opts.verifySignature: enable signature verification (boolean)
156 | * @returns The historical prices for the symbol with the passed interval
157 | *
158 | */
159 | async getHistoricalPrice(
160 | symbol: string,
161 | opts: GetHistoricalPriceForSingleTokenOptions,
162 | ): Promise;
163 | /**
164 | * Returns the historical prices for several tokens
165 | *
166 | * @param symbols - Array of token symbols
167 | * @param opts - Options object. It must contain the date property.
168 | * * opts.date: Date for the historical price (date | timestamp | string)
169 | * * opts.provider: provider name (string)
170 | * * opts.verifySignature: enable signature verification (boolean)
171 | * @returns The historical prices for several tokens
172 | *
173 | */
174 | async getHistoricalPrice(
175 | symbols: string[],
176 | opts: GetHistoricalPriceOptions,
177 | ): Promise<{ [token: string]: PriceData }>;
178 | async getHistoricalPrice(symbolOrSymbols: any, opts: any): Promise {
179 | const shouldVerifySignature = _.defaultTo(
180 | opts.verifySignature,
181 | this.verifySignature,
182 | );
183 |
184 | if (_.isArray(symbolOrSymbols)) {
185 | // Getting historical price for many tokens
186 | const symbols = symbolOrSymbols;
187 | const provider = this.getProviderForSymbols(symbols, opts.provider);
188 | return await this.getPriceForManyTokens({
189 | symbols,
190 | timestamp: getTimestamp(opts.date),
191 | provider,
192 | shouldVerifySignature,
193 | });
194 | } else if (typeof symbolOrSymbols === "string") {
195 | const symbol = symbolOrSymbols;
196 | const provider = this.getProviderForSymbol(symbol, opts.provider);
197 | if (opts.interval !== undefined || opts.limit !== undefined) {
198 | return await this.getHistoricalPricesForOneSymbol({
199 | symbol,
200 | fromTimestamp: getTimestamp(opts.startDate),
201 | toTimestamp: getTimestamp(opts.endDate),
202 | interval: opts.interval,
203 | offset: opts.offset,
204 | limit: opts.limit,
205 | provider,
206 | shouldVerifySignature,
207 | });
208 | } else {
209 | return await this.getHistoricalPriceForOneSymbol({
210 | symbol,
211 | timestamp: getTimestamp(opts.date) as number,
212 | provider,
213 | shouldVerifySignature,
214 | });
215 | }
216 | }
217 | }
218 |
219 | /**
220 | * Returns the latest price for all the supported symbols
221 | *
222 | * @param opts - Optioanl options object.
223 | * * opts.provider: provider name (string)
224 | * * opts.verifySignature: enable signature verification (boolean)
225 | * @returns The latest price for all the supported tokens
226 | *
227 | */
228 | async getAllPrices(
229 | opts: GetPriceOptions = {},
230 | ): Promise<{ [symbol: string]: PriceData }> {
231 | const provider = _.defaultTo(opts.provider, this.defaultProvider);
232 |
233 | if (this.useCache) {
234 | const pricesObj = await this.cacheProxy.getPriceForManyTokens({
235 | provider,
236 | });
237 |
238 | // Signature verification
239 | if (_.defaultTo(opts.verifySignature, this.verifySignature)) {
240 | for (const symbol of _.keys(pricesObj)) {
241 | this.signatureVerifier.assertValidSignature(pricesObj[symbol]);
242 | }
243 | }
244 |
245 | return convertPricesToUserFacingFormat(pricesObj);
246 | } else {
247 | return await this.getPricesFromArweave(provider);
248 | }
249 | }
250 |
251 | private async getLatestPriceForOneToken(args: {
252 | symbol: string;
253 | provider: string;
254 | shouldVerifySignature: boolean;
255 | }): Promise {
256 | if (this.useCache) {
257 | // Getting price from cache
258 | const price = await this.cacheProxy.getPrice(
259 | _.pick(args, ["symbol", "provider"]),
260 | );
261 | if (args.shouldVerifySignature && price !== undefined) {
262 | await this.signatureVerifier.assertValidSignature(price);
263 | }
264 |
265 | if (price === undefined) {
266 | throw new PriceNotFoundError(args.symbol);
267 | }
268 |
269 | return convertToUserFacingFormat(price);
270 | } else {
271 | // Getting price from arweave
272 |
273 | // Try to get price from graphql if possible
274 | if (args.symbol === "AR") {
275 | const price = await this.tryToGetPriceFromGQL(
276 | _.pick(args, ["provider", "symbol"]),
277 | );
278 | if (price !== undefined) {
279 | return convertToUserFacingFormat(price);
280 | }
281 | }
282 |
283 | // Getting price from arweave in a "standard" way (from data)
284 | const prices = await this.getPricesFromArweave(args.provider);
285 | const priceForSymbol = prices[args.symbol];
286 | return convertToUserFacingFormat(priceForSymbol);
287 | }
288 | }
289 |
290 | private async getPriceForManyTokens(args: {
291 | symbols: string[];
292 | provider: string;
293 | timestamp?: number;
294 | shouldVerifySignature: boolean;
295 | }): Promise<{ [token: string]: PriceData }> {
296 | // Fetching prices
297 | if (this.useCache) {
298 | const pricesObj = await this.cacheProxy.getPriceForManyTokens(
299 | _.pick(args, ["symbols", "provider", "timestamp"]),
300 | );
301 |
302 | // Signature verification
303 | if (args.shouldVerifySignature) {
304 | for (const symbol of _.keys(pricesObj)) {
305 | this.signatureVerifier.assertValidSignature(pricesObj[symbol]);
306 | }
307 | }
308 |
309 | return convertPricesToUserFacingFormat(pricesObj);
310 | } else {
311 | if (args.timestamp !== undefined) {
312 | throw new Error(
313 | "Getting historical prices from arweave is not supported",
314 | );
315 | }
316 | const allPrices = await this.getPricesFromArweave(args.provider);
317 | const pricesObj = _.pick(allPrices, args.symbols);
318 |
319 | return convertPricesToUserFacingFormat(pricesObj);
320 | }
321 | }
322 |
323 | private async getPricesFromArweave(
324 | provider: string,
325 | ): Promise<{ [symbol: string]: PriceData }> {
326 | const { address } = await this.arweaveProxy.getProviderDetails(provider);
327 |
328 | const gqlResponse = await this.arweaveProxy.findPricesInGraphQL({
329 | type: "data",
330 | providerAddress: address,
331 | version: this.version,
332 | });
333 |
334 | if (gqlResponse === undefined) {
335 | return {};
336 | }
337 |
338 | const prices = await this.arweaveProxy.getTxDataById(
339 | gqlResponse.permawebTx,
340 | { parseJSON: true },
341 | );
342 |
343 | // Building prices object
344 | const pricesObj: any = {};
345 | for (const price of prices) {
346 | pricesObj[price.symbol] = {
347 | ...price,
348 | provider: address,
349 | permawebTx: gqlResponse.permawebTx,
350 | };
351 | }
352 |
353 | return convertPricesToUserFacingFormat(pricesObj);
354 | }
355 |
356 | private async getHistoricalPriceForOneSymbol(args: {
357 | symbol: string;
358 | provider: string;
359 | timestamp: number;
360 | shouldVerifySignature: boolean;
361 | }): Promise {
362 | if (this.useCache) {
363 | const price = await this.cacheProxy.getPrice(
364 | _.pick(args, ["symbol", "provider", "timestamp"]),
365 | );
366 |
367 | // Signature verification
368 | if (args.shouldVerifySignature && price !== undefined) {
369 | await this.signatureVerifier.assertValidSignature(price);
370 | }
371 |
372 | if (price === undefined) {
373 | throw new PriceNotFoundError(args.symbol);
374 | }
375 |
376 | return convertToUserFacingFormat(price);
377 | } else {
378 | // TODO: we cannot query ArGQL with timestamp comparators like timestamp_gt
379 | // But in future we can think of querying based on block numbers
380 | throw new Error(
381 | "Fetching historical price from arweave is not supported",
382 | );
383 | }
384 | }
385 |
386 | private async tryToGetPriceFromGQL(args: {
387 | symbol: string;
388 | provider: string;
389 | }): Promise {
390 | const { address } = await this.arweaveProxy.getProviderDetails(
391 | args.provider,
392 | );
393 |
394 | const response = await this.arweaveProxy.findPricesInGraphQL({
395 | type: "data",
396 | providerAddress: address,
397 | version: this.version,
398 | });
399 |
400 | if (response === undefined || response.tags[args.symbol] === undefined) {
401 | throw new PriceNotFoundError(args.symbol);
402 | } else {
403 | return {
404 | symbol: args.symbol,
405 | value: Number(response.tags[args.symbol]),
406 | permawebTx: response.permawebTx,
407 | timestamp: Number(response.tags.timestamp),
408 | provider: address,
409 | };
410 | }
411 | }
412 |
413 | private async getHistoricalPricesForOneSymbol(args: {
414 | symbol: string;
415 | provider: string;
416 | fromTimestamp?: number;
417 | toTimestamp?: number;
418 | interval?: number;
419 | shouldVerifySignature: boolean;
420 | limit?: number;
421 | offset?: number;
422 | }): Promise {
423 | if (this.useCache) {
424 | const prices = await this.cacheProxy.getManyPrices(
425 | _.pick(args, [
426 | "symbol",
427 | "provider",
428 | "fromTimestamp",
429 | "toTimestamp",
430 | "interval",
431 | "offset",
432 | "limit",
433 | ]),
434 | );
435 |
436 | // Signature verification for all prices
437 | if (args.shouldVerifySignature) {
438 | for (const price of prices) {
439 | await this.signatureVerifier.assertValidSignature(price);
440 | }
441 | }
442 |
443 | return prices.map(convertToUserFacingFormat);
444 | } else {
445 | // TODO: we cannot query ArGQL with timestamp comparators like timestamp_gt
446 | // But in future we can think of querying based on block numbers
447 | throw new Error(
448 | "Fetching historical prices from arweave is not supported",
449 | );
450 | }
451 | }
452 |
453 | private getProviderForSymbol(symbol: string, provider?: string): string {
454 | return this.getProviderForSymbols([symbol], provider);
455 | }
456 |
457 | private getProviderForSymbols(symbols: string[], passedProvider?: string): string {
458 | if (passedProvider !== undefined) {
459 | return passedProvider;
460 | } else {
461 | // Calculating a list of providers which support all symbols in the list
462 | let possibleProviders = Array.from(PROVIDERS_SORTED_BY_PRIORITY);
463 | for (const symbol of symbols) {
464 | const providersForToken = (config.tokens as any)[symbol];
465 | if (providersForToken && Array.isArray(providersForToken)) {
466 | for (const provider of possibleProviders) {
467 | // If any of symbols doesn't support the provider
468 | // it can not be used
469 | if (!providersForToken.includes(provider)) {
470 | possibleProviders = possibleProviders.filter(p => p !== provider);
471 | }
472 | }
473 | } else {
474 | // If any symbol has no supported providers in redstone-node config
475 | // we break the loop and return the default provider
476 | possibleProviders = [];
477 | break;
478 | }
479 | }
480 |
481 | // Returning the best possible provider
482 | if (possibleProviders.length > 0) {
483 | return possibleProviders[0];
484 | } else {
485 | return this.defaultProvider;
486 | }
487 | }
488 | }
489 |
490 | }
491 |
492 | function getTimestamp(date?: ConvertableToDate): number | undefined {
493 | if (_.isUndefined(date)) {
494 | return undefined;
495 | }
496 | return new Date(date).getTime();
497 | }
498 |
499 | function convertToUserFacingFormat(
500 | price: PriceDataWithSignature | PriceData,
501 | ): PriceData {
502 | const result = _.omit(price, ["version", "signature", "providerPublicKey"]);
503 | return result as PriceData;
504 | }
505 |
506 | function convertPricesToUserFacingFormat(prices: {
507 | [symbol: string]: PriceDataWithSignature | PriceData;
508 | }): { [symbol: string]: PriceData } {
509 | const userFacingPricesObj: { [symbol: string]: PriceData } = {};
510 | for (const symbol of _.keys(prices)) {
511 | userFacingPricesObj[symbol] = convertToUserFacingFormat(prices[symbol]);
512 | }
513 |
514 | return userFacingPricesObj;
515 | }
516 |
--------------------------------------------------------------------------------
/src/config/index.ts:
--------------------------------------------------------------------------------
1 | import providers from "./providers";
2 | import tokens from "./tokens";
3 |
4 | export default {
5 | version: "0.4",
6 | cacheApiUrl: "https://api.redstone.finance/prices",
7 | providers,
8 | tokens,
9 | };
10 |
--------------------------------------------------------------------------------
/src/config/providers.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | "redstone": {
3 | "address": "I-5rWUehEv-MjdK9gFw09RxfSLQX9DIHxG614Wf8qo0",
4 | "publicKey": "xyTvKiCST8bAT6sxrgkLh8UCX2N1eKvawODuxwq4qOHIdDAZFU_3N2m59rkZ0E7m77GsJuf1I8u0oEJEbxAdT7uD2JTwoYEHauXSxyJYvF0RCcZOhl5P1PJwImd44SJYa_9My7L84D5KXB9SKs8_VThe7ZyOb5HSGLNvMIK6A8IJ4Hr_tg9GYm65CRmtcu18S9mhun8vgw2wi7Gw6oR6mc4vU1I-hrU66Fi7YlXwFieP6YSy01JqoLPhU84EunPQzXPouVSbXjgRU5kFVxtdRy4GK2fzEBFYsQwCQgFrySCrFKHV8AInu9jerfof_DxNKiXkBzlB8nc22CrYnvvio_BWyh-gN0hQHZT0gwMR-A7sbXNCQJfReaIZzX_jP6XoB82PnpzmL_j1mJ2lnv2Rn001flBAx9AYxtGXd9s07pA-FggTbEG3Y2UnlWW6l3EJ93E0IfxL0PqGEUlp217mxUHvmTw9fkGDWa8rT9RPmsTyji-kMFSefclw80cBm_iOsIEutGP4S3LDbP-ZVJWDeJOBQQpSgwbisl8qbjl2sMQLQihoG2TQyNbmLwfyq-XSULkXjUi1_6BH36wnDBLWBKF-bS2bLKcGtn3Vjet72lNHxJJilcj8vpauwJG0078S_lO5uGt6oicdGR6eh_NSn6_8za_tXg0G_fohz4Yb1z8",
5 | "evmAddress": "0x0C39486f770B26F5527BBBf942726537986Cd7eb"
6 | },
7 | "redstone-rapid": {
8 | "address": "zYqPZuALSPa_f5Agvf8g2JHv94cqMn9aBtnH7GFHbuA",
9 | "publicKey": "tfkkt6lHR3lSEBNvjistpdGb8pR9UJoOVO-IuXRXD9PckAqY7TAVuDVhrcQDM56GZ_EUh6Eg_NRYd-EGW8SEQLHXtY_CM4P8563xUpw0XcZJbpOeScFcN5JdN47gq8vllOheO6-v4nRPLVabRVJqkXEqzdEwxQNYDkmPL-gxE0ziZcQRQZdJUzL5mI9DzwpPC86JBVwsBK71iuRlstABciIu8u77qyArkNu0pPig9OFQvT3Vg4OPuWXd83EhqEuN5gqVufyomkmL8X7agiEjDf-UQIfZrSYqgiJsWiVJ2aKHRhLZN17wdX51L21Cg2Sbyb3B1Roy5EgUUTdJ2MY7LnI-CTbBBJLKUHSvN67MDhj1OSBwUULc8bgVCzmfVQryIFmb4tucKvz7TRAWseXNO2MtMlggXa42Hx0sOTopbFTmT_r9glRLYw3QLzyJVH7Ltqr8QldoU-VMWtpo5cmOGh8jwVknSHqWNURbRCoDfAuwh8lpWXBjL_V8haaY0OKFT9Lpi1VW8o4Kfx7ED1VAnLcpVIoV5CkPs_L0Qy_G7XpgA02OAbJH2KvwxZPeSSymdupmr1KMc8iGz5B4a1HEcKggk5ETFfeGz5r0hDha3dwDj-dOv1jbADcdgk7e2xaLgw1CpS1XEHhAnhBJgAzJDJpcHKrxPkD6cUc7FbnsBCE",
10 | "evmAddress": "0xf786a909D559F5Dee2dc6706d8e5A81728a39aE9"
11 | },
12 | "redstone-stocks": {
13 | "address": "Yba8IVc_01bFxutKNJAZ7CmTD5AVi2GcWXf1NajPAsc",
14 | "publicKey": "wOoClMPZ_maAoE6OhCC8svn_9gu8rjuWPi3Grf05zpQnCzeFjzUocJUr0ta-muJDqfiTd4t5KR2qNxn3nkN2_kXsn5AcXnlI4UgnRdcEvsDh6yPMoMlAiAD6c0_CIgdiMFku5OTijNMtFYOybPKrd9Xdf_7_lnRZhU9qQXeBclVjmIzftwNaTFYI1sgApWKMxJ6301CY7VN9SOD9KBrFQJ3QjWqLrLwAyAu_Q6aBeQF_L-cZSFCiTfwyDptiMCdLAjzU9s55LNAg65pZ_OfFsvqylwiXEVIEDLUI3nmcM6OUHfyAG2TxoeXeem9nCpA96MiM8MyTWtOApGA3GW__j3fxutD0wt3_uGnVRZoypPqNuPjb5BraF0owTDYJQNML9Ddx1Hy6gMXjvjxn-MbAV7tI4KnDynrbtXPSZYjRpTpxcH9Rlr-bQAZWGpYF7OWpsZlPuE4MdxpQYhqLj6wfGCxTyzIjyt_t4zR28gWIGT5SfM-etcuXyy_chspsIqisSj781AP7YMTIxzyrY_LAAm-u5gz5VwnD8xDKIrYcsxLpC4V30HFWRc5vYtBQ0cAZYnp9AGGFD-6A7PnqQ1EYSSEHzSAqoYHaqxuAvxJEhG5AECdXBS3-FpgbGgVFEeQxIH7v9Js6-Uk85t23MjRUJFNo2JIlvyolmtkd3mf88qc",
15 | "evmAddress": "0x926E370fD53c23f8B71ad2B3217b227E41A92b12"
16 | },
17 | "redstone-avalanche": {
18 | "address": "f1Ipos2fVPbxPVO65GBygkMyW0tkAhp2hdprRPPBBN8",
19 | "publicKey": "nehHT9-CMWscteR3COOU455AmDI8VihUPG05kDryVl8wjv4iE_7MttV2rz3MTIicL1BNDQ246EnyIL_4qsqu8a_B3dZT8sVdfOtHqh48pDgA0MJ8ifFVUtyabr3D2ZSc2M4re79wm2SQ_S74ZNHV7zBIBS8pUHB20TfrKhrOWi-W0hfkxrqz4jIngXIPmE1ycYbaj9OHSVRFIB-EZN-1LmLZuy2EAAN0nOqJ-aYoblcGOu4WfZDpTXTfj-6-GRRBgeHen3MYnQuDneEYYrtzoCnimWGYrqRnpM654mJTW7gp_NIqLnXn3yG2aS2pbhn1_I0CavtKuE4PSACoP0UwZaGXEtanIaSuMDkeRdu-zhrr8yXZSW1inWdC0lhWPxAhzTYWQ00_OD6oj2FUtbXbAk_F-aPnT1Teb-LcNn6q-HbHSeZ_VV0ge5nn3zvBCc77z-Qfbgyd_a7dNwfy8sVkQc4-gvDJIftzxVXbUWnc3ipvL-PHfLsajg5Jkhteuvf8bIS0cNFS5u9pu3qnutskQxSymPzhkVmSyMWNe48Xp3ok4mUA9SuWTb6AskyoDVR9APh7JNDgeYINctgduqawzJQFxcVDuoW-1b8Gy79EYXW8yHowpAaDm6eYGTPQjnMpttThZwFiC_81JTXHXmDneKm677F2kO-1f6o4UWTBOEk",
20 | "evmAddress": "0x3a7d971De367FE15D164CDD952F64205F2D9f10c"
21 | },
22 | "redstone-dev": {
23 | "address": "33F0QHcb22W7LwWR1iRC8Az1ntZG09XQ03YWuw2ABqA",
24 | "publicKey": "omNGUhh5ar4WnqNxSjDDHBfhK5nMshxyRXUW8uObuxxKELKQjUeEsUdtg5UNuGnUh4AYb5rmGsx_KrLmuqbxl4-OLQrDNnj_LYcP78RLCA7wwonzi5HZVi6vXZ765JKQ-f5SJtFR7hhjj8W8f6PFQ_F2AjtNBkC6basAP4RM3GYIKQGUdVfvAJECZYcjtkZBGZtBxTzSm3SsvcdyVZkiEnvFzg1xLgwsGNuq8dHFciUHaa_ItNzM8GZ_Esjx77qgRLUwKNGvpU8nbao07YxcC7vj-opQQTAWNt1spxLLPqRTRJM_JGDgOEhEN_Tr0qWmjz61LhFW15NJiY1D5sBpOUM29IEpqhMUjj2r2TWp8-gjus_1u3g-KCB6ydfWbjsW9W5oZUfXE8lBKpmQFQ7jOzfev94-uVkxvnK4lY-FeP0fOD6hL_3EPAWLR1oWUqwXPs38A_vvys_ySTgQ11VWEtW-F8s3BRdeHFNZ4XalWAF-BM81HoyC2TwcbLJx3pLJS4FjqbQXcjPCzGE-eUztwKH73luqYXyG-S6sfBWRlqwcwsB9vNp7SnIDs2VLNUmxXG5PpwWG7GlO3HHj-WSchVfFqge0jHlDQeukIIy6fYIqnXaR6cSpiAfqHyzazOnOGgjBUBYDky5LlnrO8bctggYDlAyzJAn997AjeLTlmLM"
25 | },
26 | "redstone-avalanche-prod-1": {
27 | "address": "TEHhCDWy-vGmPSZsYJyM0aP_MM4xESgyIZdf5mVODzg",
28 | "publicKey": "01vc-Uxo9LabpCzQBGLdtlsaoJh9ee5rl-AcOG6dwmXoUDHRUzwexCmIItDOJ7yqus-XMxHeTlks8Re_q7oiKvKAU9KGoYnhoP5hiymeqFMwCIta0Qne92Za9Nm2R2tBW5cUqFHeXJuEutDnU0PUkBF5W7Ut3x3gd0xyVe9j69Sqd-kIQL_E47_ost0-pwHd76z6MZm_u7zmxbnTq0OsZTP4OQrvsnlRLpjyRsNzQomsKjuakM4RcYwfpueLKLwQa4OOJ9uWxsrSmtwdwdHZzQY9F2-l2LOllu5L-dOeDYCw4q9KZ0gmkKM6333pPo8DB-r5qe-gKY4_vNgOLRmBJqCF-G_ifWNptM3fhbkGwokfGw0ELCDqXwZ9Ka2QbqM11xViRhUiVPUJLiYwdhydPv9z0Mu5hPOR13GU-sNCf8Zt6GRyZ3ZMIo40GWrGbowJgTvSxU-mAxXwZ3hs3HRxA8wKkgUwUzTJ9UD6tYhctrG-SLUJfDVdEOGmih3FUbayt6OXN4p_I0WP_8OB8FEMV1Y7Ln-fxhErNonO3HDco4sgRiJvCP-Rcey94fK7fOQXAJ2ZLBh00uEjOWPsf0QjWpDp1qjKFkLAGlMoGDCa_SrTikOZtLAf8QKWap2wye3FeBp5UVwedi2vlF1HJuV8u_C-hoMxI1n0FKoyGJEyLrM",
29 | "evmAddress": "0x981bdA8276ae93F567922497153de7A5683708d3"
30 | },
31 | "redstone-avalanche-prod-2": {
32 | "address": "ll8DlO4xMwHK7gIMsbnOnN7Jg8Sl674Ls4G0aBfHCyk",
33 | "publicKey": "s0H0bHuIK0X-FptNPBvZGBHDbTLj0y-P6PV33sE2xW8WLx0DUtYz7jzlqc1brQ76SiO7ER-B9v80uLkaf5RwRQG-NL3Cm79FOxksz1oA0_wvK5oI-uZRWHDH4uTjhsnc3K2rg6N1X5J0GrSmB-puwiVt-1_fYrCyyzv2rE135bG61IA0i_4_cREr9t84WAYIkSbH003vbuI1KssDJtzqwVidC-DtYuzSnp1oqd4WPBDWSRSM_AswYFCk4A0xROhSmRf--140th5soz7FEJ92QRd3UYGgZ7HpPnedz-xCDgRi1-h12eMzCQM-HtipEJDl2DeTjekcTqPk_2BfnclpeDHO8myd49ikqwlHpkv0rerB3kJgDWYXcP499cZfXetEOrAiVlbPsmkJvpT7u1swaWyG5m78AjBAjZ1RrlSMLXuEHEPb1H31w16oPO6W-cqsZLZXjnxom5GXCbBOZ2Sdo7eIoTt7WMW653yzwZ97tjyMTbM5IqJZLyAFJgq6CFFnwAiKZlrtnCNHWFpfCm4uxsAzAyoW2l0FMZ4lgp7cUCHnl7IEWKSpkfsZy5FLQ0kInfeabZXPs12d5ufnL3upRSlL3pbi0OGrRQyojhOwIkasuVe0MiSSJW1QoD7oK07GWQhSke40vg3GrKW8V57IKSd1pU8owj1k7R9lADYAWLk",
34 | "evmAddress": "0x3BEFDd935b50F172e696A5187DBaCfEf0D208e48"
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/src/config/tokens.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | "TSLA": [
3 | "redstone-stocks",
4 | "redstone"
5 | ],
6 | "AAPL": [
7 | "redstone-stocks",
8 | "redstone"
9 | ],
10 | "IBM": [
11 | "redstone-stocks"
12 | ],
13 | "AMZN": [
14 | "redstone-stocks",
15 | "redstone"
16 | ],
17 | "GOOG": [
18 | "redstone-stocks"
19 | ],
20 | "COST": [
21 | "redstone-stocks"
22 | ],
23 | "DIS": [
24 | "redstone-stocks"
25 | ],
26 | "FB": [
27 | "redstone-stocks",
28 | "redstone"
29 | ],
30 | "MA": [
31 | "redstone-stocks"
32 | ],
33 | "MSFT": [
34 | "redstone-stocks"
35 | ],
36 | "NFLX": [
37 | "redstone-stocks",
38 | "redstone"
39 | ],
40 | "NKE": [
41 | "redstone-stocks"
42 | ],
43 | "PINS": [
44 | "redstone-stocks"
45 | ],
46 | "SHOP": [
47 | "redstone-stocks"
48 | ],
49 | "SPOT": [
50 | "redstone-stocks"
51 | ],
52 | "TDOC": [
53 | "redstone-stocks"
54 | ],
55 | "SPY": [
56 | "redstone-stocks",
57 | "redstone"
58 | ],
59 | "QQQ": [
60 | "redstone-stocks"
61 | ],
62 | "ONEQ": [
63 | "redstone-stocks"
64 | ],
65 | "IWM": [
66 | "redstone-stocks"
67 | ],
68 | "EFA": [
69 | "redstone-stocks"
70 | ],
71 | "VGK": [
72 | "redstone-stocks"
73 | ],
74 | "INDA": [
75 | "redstone-stocks"
76 | ],
77 | "RSX": [
78 | "redstone-stocks"
79 | ],
80 | "ZC=F": [
81 | "redstone-stocks"
82 | ],
83 | "ZS=F": [
84 | "redstone-stocks"
85 | ],
86 | "ZM=F": [
87 | "redstone-stocks"
88 | ],
89 | "ZW=F": [
90 | "redstone-stocks"
91 | ],
92 | "KE=F": [
93 | "redstone-stocks"
94 | ],
95 | "ZO=F": [
96 | "redstone-stocks"
97 | ],
98 | "ZR=F": [
99 | "redstone-stocks"
100 | ],
101 | "CL=F": [
102 | "redstone-stocks"
103 | ],
104 | "RB=F": [
105 | "redstone-stocks"
106 | ],
107 | "NG=F": [
108 | "redstone-stocks"
109 | ],
110 | "QA=F": [
111 | "redstone-stocks"
112 | ],
113 | "EH=F": [
114 | "redstone-stocks"
115 | ],
116 | "GC=F": [
117 | "redstone-stocks"
118 | ],
119 | "SI=F": [
120 | "redstone-stocks"
121 | ],
122 | "HG=F": [
123 | "redstone-stocks"
124 | ],
125 | "PL=F": [
126 | "redstone-stocks"
127 | ],
128 | "PA=F": [
129 | "redstone-stocks"
130 | ],
131 | "LE=F": [
132 | "redstone-stocks"
133 | ],
134 | "GF=F": [
135 | "redstone-stocks"
136 | ],
137 | "HE=F": [
138 | "redstone-stocks"
139 | ],
140 | "PRK=F": [
141 | "redstone-stocks"
142 | ],
143 | "DC=F": [
144 | "redstone-stocks"
145 | ],
146 | "GNF=F": [
147 | "redstone-stocks"
148 | ],
149 | "CB=F": [
150 | "redstone-stocks"
151 | ],
152 | "CSC=F": [
153 | "redstone-stocks"
154 | ],
155 | "BTC": [
156 | "redstone-rapid",
157 | "redstone",
158 | "redstone-avalanche"
159 | ],
160 | "ETH": [
161 | "redstone-rapid",
162 | "redstone",
163 | "redstone-avalanche"
164 | ],
165 | "BNB": [
166 | "redstone-rapid",
167 | "redstone"
168 | ],
169 | "ADA": [
170 | "redstone-rapid",
171 | "redstone"
172 | ],
173 | "USDT": [
174 | "redstone-rapid",
175 | "redstone",
176 | "redstone-avalanche"
177 | ],
178 | "DOT": [
179 | "redstone-rapid",
180 | "redstone"
181 | ],
182 | "XRP": [
183 | "redstone-rapid",
184 | "redstone"
185 | ],
186 | "LTC": [
187 | "redstone"
188 | ],
189 | "LINK": [
190 | "redstone",
191 | "redstone-avalanche"
192 | ],
193 | "BCH": [
194 | "redstone"
195 | ],
196 | "XLM": [
197 | "redstone-rapid",
198 | "redstone"
199 | ],
200 | "USDC": [
201 | "redstone"
202 | ],
203 | "UNI": [
204 | "redstone"
205 | ],
206 | "DOGE": [
207 | "redstone-rapid",
208 | "redstone"
209 | ],
210 | "WBTC": [
211 | "redstone"
212 | ],
213 | "OKB": [
214 | "redstone"
215 | ],
216 | "XEM": [
217 | "redstone"
218 | ],
219 | "ATOM": [
220 | "redstone"
221 | ],
222 | "AAVE": [
223 | "redstone"
224 | ],
225 | "SOL": [
226 | "redstone"
227 | ],
228 | "CRO": [
229 | "redstone"
230 | ],
231 | "EOS": [
232 | "redstone"
233 | ],
234 | "XMR": [
235 | "redstone"
236 | ],
237 | "BSV": [
238 | "redstone"
239 | ],
240 | "TRX": [
241 | "redstone"
242 | ],
243 | "HT": [
244 | "redstone"
245 | ],
246 | "MIOTA": [
247 | "redstone"
248 | ],
249 | "THETA": [
250 | "redstone"
251 | ],
252 | "SNX": [
253 | "redstone"
254 | ],
255 | "NEO": [
256 | "redstone"
257 | ],
258 | "XTZ": [
259 | "redstone"
260 | ],
261 | "VET": [
262 | "redstone"
263 | ],
264 | "LUNA": [
265 | "redstone"
266 | ],
267 | "FTT": [
268 | "redstone"
269 | ],
270 | "DASH": [
271 | "redstone"
272 | ],
273 | "DAI": [
274 | "redstone"
275 | ],
276 | "GRT": [
277 | "redstone"
278 | ],
279 | "BUSD": [
280 | "redstone"
281 | ],
282 | "AVAX": [
283 | "redstone",
284 | "redstone-avalanche"
285 | ],
286 | "KSM": [
287 | "redstone"
288 | ],
289 | "EGLD": [
290 | "redstone"
291 | ],
292 | "SUSHI": [
293 | "redstone"
294 | ],
295 | "LEO": [
296 | "redstone"
297 | ],
298 | "MKR": [
299 | "redstone"
300 | ],
301 | "FIL": [
302 | "redstone"
303 | ],
304 | "CEL": [
305 | "redstone"
306 | ],
307 | "COMP": [
308 | "redstone"
309 | ],
310 | "DCR": [
311 | "redstone"
312 | ],
313 | "FTM": [
314 | "redstone"
315 | ],
316 | "CAKE": [
317 | "redstone"
318 | ],
319 | "VGX": [
320 | "redstone"
321 | ],
322 | "RVN": [
323 | "redstone"
324 | ],
325 | "ZEC": [
326 | "redstone"
327 | ],
328 | "ZIL": [
329 | "redstone"
330 | ],
331 | "NEAR": [
332 | "redstone"
333 | ],
334 | "ETC": [
335 | "redstone"
336 | ],
337 | "UMA": [
338 | "redstone"
339 | ],
340 | "NEXO": [
341 | "redstone"
342 | ],
343 | "YFI": [
344 | "redstone"
345 | ],
346 | "RUNE": [
347 | "redstone"
348 | ],
349 | "ZRX": [
350 | "redstone"
351 | ],
352 | "REN": [
353 | "redstone"
354 | ],
355 | "WAVES": [
356 | "redstone"
357 | ],
358 | "MATIC": [
359 | "redstone"
360 | ],
361 | "ICX": [
362 | "redstone"
363 | ],
364 | "HBAR": [
365 | "redstone"
366 | ],
367 | "STX": [
368 | "redstone"
369 | ],
370 | "AMP": [
371 | "redstone"
372 | ],
373 | "BTT": [
374 | "redstone"
375 | ],
376 | "RENBTC": [
377 | "redstone"
378 | ],
379 | "MDX": [
380 | "redstone"
381 | ],
382 | "CHSB": [
383 | "redstone"
384 | ],
385 | "ALGO": [
386 | "redstone"
387 | ],
388 | "PAX": [
389 | "redstone"
390 | ],
391 | "IOST": [
392 | "redstone"
393 | ],
394 | "DGB": [
395 | "redstone"
396 | ],
397 | "ONT": [
398 | "redstone"
399 | ],
400 | "ZKS": [
401 | "redstone"
402 | ],
403 | "BAT": [
404 | "redstone"
405 | ],
406 | "NANO": [
407 | "redstone"
408 | ],
409 | "LRC": [
410 | "redstone"
411 | ],
412 | "OMG": [
413 | "redstone"
414 | ],
415 | "UST": [
416 | "redstone"
417 | ],
418 | "BNT": [
419 | "redstone"
420 | ],
421 | "ZEN": [
422 | "redstone"
423 | ],
424 | "ENJ": [
425 | "redstone"
426 | ],
427 | "QTUM": [
428 | "redstone"
429 | ],
430 | "HOT": [
431 | "redstone"
432 | ],
433 | "XVS": [
434 | "redstone"
435 | ],
436 | "SC": [
437 | "redstone"
438 | ],
439 | "CRV": [
440 | "redstone"
441 | ],
442 | "BTG": [
443 | "redstone"
444 | ],
445 | "FLOW": [
446 | "redstone"
447 | ],
448 | "EWT": [
449 | "redstone"
450 | ],
451 | "LSK": [
452 | "redstone"
453 | ],
454 | "DODO": [
455 | "redstone"
456 | ],
457 | "QNT": [
458 | "redstone"
459 | ],
460 | "AR": [
461 | "redstone-rapid",
462 | "redstone"
463 | ],
464 | "RSR": [
465 | "redstone"
466 | ],
467 | "1INCH": [
468 | "redstone"
469 | ],
470 | "KCS": [
471 | "redstone"
472 | ],
473 | "CELO": [
474 | "redstone"
475 | ],
476 | "BAL": [
477 | "redstone"
478 | ],
479 | "ALPHA": [
480 | "redstone"
481 | ],
482 | "OCEAN": [
483 | "redstone"
484 | ],
485 | "GLM": [
486 | "redstone"
487 | ],
488 | "CKB": [
489 | "redstone"
490 | ],
491 | "KNC": [
492 | "redstone"
493 | ],
494 | "AMPL": [
495 | "redstone"
496 | ],
497 | "SRM": [
498 | "redstone"
499 | ],
500 | "MANA": [
501 | "redstone"
502 | ],
503 | "TUSD": [
504 | "redstone"
505 | ],
506 | "CUSDT": [
507 | "redstone"
508 | ],
509 | "SNT": [
510 | "redstone"
511 | ],
512 | "XVG": [
513 | "redstone"
514 | ],
515 | "BAND": [
516 | "redstone"
517 | ],
518 | "XDC": [
519 | "redstone"
520 | ],
521 | "HNT": [
522 | "redstone"
523 | ],
524 | "CHZ": [
525 | "redstone"
526 | ],
527 | "ORN": [
528 | "redstone"
529 | ],
530 | "BADGER": [
531 | "redstone"
532 | ],
533 | "NU": [
534 | "redstone"
535 | ],
536 | "POLS": [
537 | "redstone"
538 | ],
539 | "GT": [
540 | "redstone"
541 | ],
542 | "SWAP": [
543 | "redstone"
544 | ],
545 | "SCRT": [
546 | "redstone"
547 | ],
548 | "POLY": [
549 | "redstone"
550 | ],
551 | "CVC": [
552 | "redstone"
553 | ],
554 | "BTCST": [
555 | "redstone"
556 | ],
557 | "AKT": [
558 | "redstone"
559 | ],
560 | "ONE": [
561 | "redstone"
562 | ],
563 | "SUSD": [
564 | "redstone"
565 | ],
566 | "SFP": [
567 | "redstone"
568 | ],
569 | "SFI": [
570 | "redstone"
571 | ],
572 | "MIR": [
573 | "redstone"
574 | ],
575 | "KAVA": [
576 | "redstone"
577 | ],
578 | "UBT": [
579 | "redstone"
580 | ],
581 | "SXP": [
582 | "redstone"
583 | ],
584 | "NMR": [
585 | "redstone"
586 | ],
587 | "GALA": [
588 | "redstone"
589 | ],
590 | "GNO": [
591 | "redstone"
592 | ],
593 | "BCD": [
594 | "redstone"
595 | ],
596 | "DNT": [
597 | "redstone"
598 | ],
599 | "RFOX": [
600 | "redstone"
601 | ],
602 | "FUN": [
603 | "redstone"
604 | ],
605 | "ARK": [
606 | "redstone"
607 | ],
608 | "ANKR": [
609 | "redstone"
610 | ],
611 | "MATH": [
612 | "redstone"
613 | ],
614 | "RIF": [
615 | "redstone"
616 | ],
617 | "INJ": [
618 | "redstone"
619 | ],
620 | "DENT": [
621 | "redstone"
622 | ],
623 | "TOMO": [
624 | "redstone"
625 | ],
626 | "GAS": [
627 | "redstone"
628 | ],
629 | "FET": [
630 | "redstone"
631 | ],
632 | "ETN": [
633 | "redstone"
634 | ],
635 | "KMD": [
636 | "redstone"
637 | ],
638 | "SAND": [
639 | "redstone"
640 | ],
641 | "ARDR": [
642 | "redstone"
643 | ],
644 | "LIT": [
645 | "redstone"
646 | ],
647 | "USDN": [
648 | "redstone"
649 | ],
650 | "UTK": [
651 | "redstone"
652 | ],
653 | "REP": [
654 | "redstone"
655 | ],
656 | "ANT": [
657 | "redstone"
658 | ],
659 | "WIN": [
660 | "redstone"
661 | ],
662 | "VTHO": [
663 | "redstone"
664 | ],
665 | "XOR": [
666 | "redstone"
667 | ],
668 | "UOS": [
669 | "redstone"
670 | ],
671 | "MONA": [
672 | "redstone"
673 | ],
674 | "MASK": [
675 | "redstone"
676 | ],
677 | "STEEM": [
678 | "redstone"
679 | ],
680 | "BTM": [
681 | "redstone"
682 | ],
683 | "ROSE": [
684 | "redstone"
685 | ],
686 | "COTI": [
687 | "redstone"
688 | ],
689 | "HTR": [
690 | "redstone"
691 | ],
692 | "PRQ": [
693 | "redstone"
694 | ],
695 | "WAN": [
696 | "redstone"
697 | ],
698 | "PROM": [
699 | "redstone"
700 | ],
701 | "ROOK": [
702 | "redstone"
703 | ],
704 | "IOTX": [
705 | "redstone"
706 | ],
707 | "EDG": [
708 | "redstone"
709 | ],
710 | "SKL": [
711 | "redstone"
712 | ],
713 | "MFT": [
714 | "redstone"
715 | ],
716 | "RDD": [
717 | "redstone"
718 | ],
719 | "XAUT": [
720 | "redstone"
721 | ],
722 | "LON": [
723 | "redstone"
724 | ],
725 | "KEEP": [
726 | "redstone"
727 | ],
728 | "SUPER": [
729 | "redstone"
730 | ],
731 | "WOO": [
732 | "redstone"
733 | ],
734 | "BTS": [
735 | "redstone"
736 | ],
737 | "PERP": [
738 | "redstone"
739 | ],
740 | "PAXG": [
741 | "redstone"
742 | ],
743 | "WAXP": [
744 | "redstone"
745 | ],
746 | "FARM": [
747 | "redstone"
748 | ],
749 | "AVA": [
750 | "redstone"
751 | ],
752 | "GUSD": [
753 | "redstone"
754 | ],
755 | "PHA": [
756 | "redstone"
757 | ],
758 | "HNS": [
759 | "redstone"
760 | ],
761 | "DPI": [
762 | "redstone"
763 | ],
764 | "IRIS": [
765 | "redstone"
766 | ],
767 | "STAKE": [
768 | "redstone"
769 | ],
770 | "ELF": [
771 | "redstone"
772 | ],
773 | "RAY": [
774 | "redstone"
775 | ],
776 | "HEGIC": [
777 | "redstone"
778 | ],
779 | "RLC": [
780 | "redstone"
781 | ],
782 | "ERG": [
783 | "redstone"
784 | ],
785 | "PIVX": [
786 | "redstone"
787 | ],
788 | "LOOM": [
789 | "redstone"
790 | ],
791 | "JULD": [
792 | "redstone"
793 | ],
794 | "API3": [
795 | "redstone"
796 | ],
797 | "EXRD": [
798 | "redstone"
799 | ],
800 | "HIVE": [
801 | "redstone"
802 | ],
803 | "MX": [
804 | "redstone"
805 | ],
806 | "SYS": [
807 | "redstone"
808 | ],
809 | "AKRO": [
810 | "redstone"
811 | ],
812 | "SHR": [
813 | "redstone"
814 | ],
815 | "YFII": [
816 | "redstone"
817 | ],
818 | "TORN": [
819 | "redstone"
820 | ],
821 | "STORJ": [
822 | "redstone"
823 | ],
824 | "AION": [
825 | "redstone"
826 | ],
827 | "UQC": [
828 | "redstone"
829 | ],
830 | "ANY": [
831 | "redstone"
832 | ],
833 | "DUSK": [
834 | "redstone"
835 | ],
836 | "WHALE": [
837 | "redstone"
838 | ],
839 | "BIFI": [
840 | "redstone"
841 | ],
842 | "CREAM": [
843 | "redstone"
844 | ],
845 | "LTO": [
846 | "redstone"
847 | ],
848 | "UNFI": [
849 | "redstone"
850 | ],
851 | "LBC": [
852 | "redstone"
853 | ],
854 | "AXS": [
855 | "redstone"
856 | ],
857 | "LINA": [
858 | "redstone"
859 | ],
860 | "ALBT": [
861 | "redstone"
862 | ],
863 | "BCN": [
864 | "redstone"
865 | ],
866 | "HARD": [
867 | "redstone"
868 | ],
869 | "LPT": [
870 | "redstone"
871 | ],
872 | "SWINGBY": [
873 | "redstone"
874 | ],
875 | "FRAX": [
876 | "redstone",
877 | "redstone-avalanche"
878 | ],
879 | "STMX": [
880 | "redstone"
881 | ],
882 | "HXRO": [
883 | "redstone"
884 | ],
885 | "WNXM": [
886 | "redstone"
887 | ],
888 | "NEST": [
889 | "redstone"
890 | ],
891 | "AUTO": [
892 | "redstone"
893 | ],
894 | "OM": [
895 | "redstone"
896 | ],
897 | "TRU": [
898 | "redstone"
899 | ],
900 | "AUDIO": [
901 | "redstone"
902 | ],
903 | "WRX": [
904 | "redstone"
905 | ],
906 | "REQ": [
907 | "redstone"
908 | ],
909 | "FRONT": [
910 | "redstone"
911 | ],
912 | "DF": [
913 | "redstone"
914 | ],
915 | "PPT": [
916 | "redstone"
917 | ],
918 | "DAO": [
919 | "redstone"
920 | ],
921 | "CELR": [
922 | "redstone"
923 | ],
924 | "WICC": [
925 | "redstone"
926 | ],
927 | "ESD": [
928 | "redstone"
929 | ],
930 | "JST": [
931 | "redstone"
932 | ],
933 | "TRB": [
934 | "redstone"
935 | ],
936 | "KAI": [
937 | "redstone"
938 | ],
939 | "DIVI": [
940 | "redstone"
941 | ],
942 | "MAPS": [
943 | "redstone"
944 | ],
945 | "OGN": [
946 | "redstone"
947 | ],
948 | "KP3R": [
949 | "redstone"
950 | ],
951 | "ORBS": [
952 | "redstone"
953 | ],
954 | "BOND": [
955 | "redstone"
956 | ],
957 | "CTK": [
958 | "redstone"
959 | ],
960 | "COVER": [
961 | "redstone"
962 | ],
963 | "BZRX": [
964 | "redstone"
965 | ],
966 | "FIRO": [
967 | "redstone"
968 | ],
969 | "SKEY": [
970 | "redstone"
971 | ],
972 | "BAO": [
973 | "redstone"
974 | ],
975 | "ELA": [
976 | "redstone"
977 | ],
978 | "GHST": [
979 | "redstone"
980 | ],
981 | "TT": [
982 | "redstone"
983 | ],
984 | "AE": [
985 | "redstone"
986 | ],
987 | "ARRR": [
988 | "redstone"
989 | ],
990 | "RING": [
991 | "redstone"
992 | ],
993 | "DATA": [
994 | "redstone"
995 | ],
996 | "CTSI": [
997 | "redstone"
998 | ],
999 | "DIA": [
1000 | "redstone"
1001 | ],
1002 | "FRM": [
1003 | "redstone"
1004 | ],
1005 | "IGNIS": [
1006 | "redstone"
1007 | ],
1008 | "NULS": [
1009 | "redstone"
1010 | ],
1011 | "FSN": [
1012 | "redstone"
1013 | ],
1014 | "AUCTION": [
1015 | "redstone"
1016 | ],
1017 | "TON": [
1018 | "redstone"
1019 | ],
1020 | "ZB": [
1021 | "redstone"
1022 | ],
1023 | "POND": [
1024 | "redstone"
1025 | ],
1026 | "LAMB": [
1027 | "redstone"
1028 | ],
1029 | "MTL": [
1030 | "redstone"
1031 | ],
1032 | "VLX": [
1033 | "redstone"
1034 | ],
1035 | "CRE": [
1036 | "redstone"
1037 | ],
1038 | "NIM": [
1039 | "redstone"
1040 | ],
1041 | "MXC": [
1042 | "redstone"
1043 | ],
1044 | "ZAP": [
1045 | "redstone"
1046 | ],
1047 | "FXS": [
1048 | "redstone"
1049 | ],
1050 | "PNT": [
1051 | "redstone"
1052 | ],
1053 | "BLZ": [
1054 | "redstone"
1055 | ],
1056 | "MLN": [
1057 | "redstone"
1058 | ],
1059 | "APY": [
1060 | "redstone"
1061 | ],
1062 | "BEL": [
1063 | "redstone"
1064 | ],
1065 | "BEAM": [
1066 | "redstone"
1067 | ],
1068 | "VSYS": [
1069 | "redstone"
1070 | ],
1071 | "YAM": [
1072 | "redstone"
1073 | ],
1074 | "RARI": [
1075 | "redstone"
1076 | ],
1077 | "FIDA": [
1078 | "redstone"
1079 | ],
1080 | "CORE": [
1081 | "redstone"
1082 | ],
1083 | "TVK": [
1084 | "redstone"
1085 | ],
1086 | "HC": [
1087 | "redstone"
1088 | ],
1089 | "MTA": [
1090 | "redstone"
1091 | ],
1092 | "KDA": [
1093 | "redstone"
1094 | ],
1095 | "RNDR": [
1096 | "redstone"
1097 | ],
1098 | "CUSD": [
1099 | "redstone"
1100 | ],
1101 | "EURS": [
1102 | "redstone"
1103 | ],
1104 | "MEME": [
1105 | "redstone"
1106 | ],
1107 | "STPT": [
1108 | "redstone"
1109 | ],
1110 | "TLOS": [
1111 | "redstone"
1112 | ],
1113 | "DHT": [
1114 | "redstone"
1115 | ],
1116 | "GXC": [
1117 | "redstone"
1118 | ],
1119 | "BOR": [
1120 | "redstone"
1121 | ],
1122 | "ARPA": [
1123 | "redstone"
1124 | ],
1125 | "AST": [
1126 | "redstone"
1127 | ],
1128 | "XSN": [
1129 | "redstone"
1130 | ],
1131 | "VALUE": [
1132 | "redstone"
1133 | ],
1134 | "FILDA": [
1135 | "redstone"
1136 | ],
1137 | "PNK": [
1138 | "redstone"
1139 | ],
1140 | "RAMP": [
1141 | "redstone"
1142 | ],
1143 | "SPI": [
1144 | "redstone"
1145 | ],
1146 | "NXT": [
1147 | "redstone"
1148 | ],
1149 | "HEZ": [
1150 | "redstone"
1151 | ],
1152 | "FIS": [
1153 | "redstone"
1154 | ],
1155 | "COMBO": [
1156 | "redstone"
1157 | ],
1158 | "NWC": [
1159 | "redstone"
1160 | ],
1161 | "MPH": [
1162 | "redstone"
1163 | ],
1164 | "RFUEL": [
1165 | "redstone"
1166 | ],
1167 | "ALEPH": [
1168 | "redstone"
1169 | ],
1170 | "XRT": [
1171 | "redstone"
1172 | ],
1173 | "SERO": [
1174 | "redstone"
1175 | ],
1176 | "NKN": [
1177 | "redstone"
1178 | ],
1179 | "FOR": [
1180 | "redstone"
1181 | ],
1182 | "FIO": [
1183 | "redstone"
1184 | ],
1185 | "REVV": [
1186 | "redstone"
1187 | ],
1188 | "RFR": [
1189 | "redstone"
1190 | ],
1191 | "AERGO": [
1192 | "redstone"
1193 | ],
1194 | "TRYB": [
1195 | "redstone"
1196 | ],
1197 | "NEC": [
1198 | "redstone"
1199 | ],
1200 | "ZEE": [
1201 | "redstone"
1202 | ],
1203 | "UNN": [
1204 | "redstone"
1205 | ],
1206 | "ZCN": [
1207 | "redstone"
1208 | ],
1209 | "BAC": [
1210 | "redstone"
1211 | ],
1212 | "UMB": [
1213 | "redstone"
1214 | ],
1215 | "DERO": [
1216 | "redstone"
1217 | ],
1218 | "PEAK": [
1219 | "redstone"
1220 | ],
1221 | "CRU": [
1222 | "redstone"
1223 | ],
1224 | "APL": [
1225 | "redstone"
1226 | ],
1227 | "PHX": [
1228 | "redstone"
1229 | ],
1230 | "BMI": [
1231 | "redstone"
1232 | ],
1233 | "WTC": [
1234 | "redstone"
1235 | ],
1236 | "MDT": [
1237 | "redstone"
1238 | ],
1239 | "KEY": [
1240 | "redstone"
1241 | ],
1242 | "WHITE": [
1243 | "redstone"
1244 | ],
1245 | "CVP": [
1246 | "redstone"
1247 | ],
1248 | "CHR": [
1249 | "redstone"
1250 | ],
1251 | "ULT": [
1252 | "redstone"
1253 | ],
1254 | "PRE": [
1255 | "redstone"
1256 | ],
1257 | "WING": [
1258 | "redstone"
1259 | ],
1260 | "NAS": [
1261 | "redstone"
1262 | ],
1263 | "WXT": [
1264 | "redstone"
1265 | ],
1266 | "XDB": [
1267 | "redstone"
1268 | ],
1269 | "CRPT": [
1270 | "redstone"
1271 | ],
1272 | "RLY": [
1273 | "redstone"
1274 | ],
1275 | "DEC": [
1276 | "redstone"
1277 | ],
1278 | "QASH": [
1279 | "redstone"
1280 | ],
1281 | "SDT": [
1282 | "redstone"
1283 | ],
1284 | "DEGO": [
1285 | "redstone"
1286 | ],
1287 | "RUFF": [
1288 | "redstone"
1289 | ],
1290 | "MDA": [
1291 | "redstone"
1292 | ],
1293 | "DOCK": [
1294 | "redstone"
1295 | ],
1296 | "CUDOS": [
1297 | "redstone"
1298 | ],
1299 | "SUKU": [
1300 | "redstone"
1301 | ],
1302 | "LOC": [
1303 | "redstone"
1304 | ],
1305 | "AITRA": [
1306 | "redstone"
1307 | ],
1308 | "PAY": [
1309 | "redstone"
1310 | ],
1311 | "VITE": [
1312 | "redstone"
1313 | ],
1314 | "CBC": [
1315 | "redstone"
1316 | ],
1317 | "MITX": [
1318 | "redstone"
1319 | ],
1320 | "ERSDL": [
1321 | "redstone"
1322 | ],
1323 | "SMART": [
1324 | "redstone"
1325 | ],
1326 | "KAN": [
1327 | "redstone"
1328 | ],
1329 | "UBQ": [
1330 | "redstone"
1331 | ],
1332 | "SHA": [
1333 | "redstone"
1334 | ],
1335 | "COIN": [
1336 | "redstone"
1337 | ],
1338 | "JRT": [
1339 | "redstone"
1340 | ],
1341 | "HAKKA": [
1342 | "redstone"
1343 | ],
1344 | "PROPS": [
1345 | "redstone"
1346 | ],
1347 | "TROY": [
1348 | "redstone"
1349 | ],
1350 | "DSLA": [
1351 | "redstone"
1352 | ],
1353 | "PNG": [
1354 | "redstone",
1355 | "redstone-avalanche"
1356 | ],
1357 | "CLO": [
1358 | "redstone"
1359 | ],
1360 | "TOP": [
1361 | "redstone"
1362 | ],
1363 | "ETP": [
1364 | "redstone"
1365 | ],
1366 | "HGET": [
1367 | "redstone"
1368 | ],
1369 | "DOS": [
1370 | "redstone"
1371 | ],
1372 | "YFL": [
1373 | "redstone"
1374 | ],
1375 | "MHC": [
1376 | "redstone"
1377 | ],
1378 | "IQN": [
1379 | "redstone"
1380 | ],
1381 | "BTSE": [
1382 | "redstone"
1383 | ],
1384 | "ABT": [
1385 | "redstone"
1386 | ],
1387 | "GTO": [
1388 | "redstone"
1389 | ],
1390 | "LAYER": [
1391 | "redstone"
1392 | ],
1393 | "ACH": [
1394 | "redstone"
1395 | ],
1396 | "NSURE": [
1397 | "redstone"
1398 | ],
1399 | "PICKLE": [
1400 | "redstone"
1401 | ],
1402 | "BIX": [
1403 | "redstone"
1404 | ],
1405 | "SWRV": [
1406 | "redstone"
1407 | ],
1408 | "NGM": [
1409 | "redstone"
1410 | ],
1411 | "HYDRA": [
1412 | "redstone"
1413 | ],
1414 | "DCN": [
1415 | "redstone"
1416 | ],
1417 | "PLU": [
1418 | "redstone"
1419 | ],
1420 | "EGT": [
1421 | "redstone"
1422 | ],
1423 | "ESS": [
1424 | "redstone"
1425 | ],
1426 | "TCT": [
1427 | "redstone"
1428 | ],
1429 | "MITH": [
1430 | "redstone"
1431 | ],
1432 | "VEE": [
1433 | "redstone"
1434 | ],
1435 | "DYP": [
1436 | "redstone"
1437 | ],
1438 | "SAN": [
1439 | "redstone"
1440 | ],
1441 | "MBL": [
1442 | "redstone"
1443 | ],
1444 | "CMT": [
1445 | "redstone"
1446 | ],
1447 | "SLP": [
1448 | "redstone"
1449 | ],
1450 | "EMC": [
1451 | "redstone"
1452 | ],
1453 | "MAHA": [
1454 | "redstone"
1455 | ],
1456 | "HYDRO": [
1457 | "redstone"
1458 | ],
1459 | "DTA": [
1460 | "redstone"
1461 | ],
1462 | "SWFTC": [
1463 | "redstone"
1464 | ],
1465 | "TRADE": [
1466 | "redstone"
1467 | ],
1468 | "BFT": [
1469 | "redstone"
1470 | ],
1471 | "CHI": [
1472 | "redstone"
1473 | ],
1474 | "SNGLS": [
1475 | "redstone"
1476 | ],
1477 | "ACT": [
1478 | "redstone"
1479 | ],
1480 | "UBXT": [
1481 | "redstone"
1482 | ],
1483 | "DFD": [
1484 | "redstone"
1485 | ],
1486 | "DLT": [
1487 | "redstone"
1488 | ],
1489 | "VIB": [
1490 | "redstone"
1491 | ],
1492 | "LIKE": [
1493 | "redstone"
1494 | ],
1495 | "LYM": [
1496 | "redstone"
1497 | ],
1498 | "APM": [
1499 | "redstone"
1500 | ],
1501 | "GNX": [
1502 | "redstone"
1503 | ],
1504 | "AGA": [
1505 | "redstone"
1506 | ],
1507 | "SMT": [
1508 | "redstone"
1509 | ],
1510 | "GEEQ": [
1511 | "redstone"
1512 | ],
1513 | "SATT": [
1514 | "redstone"
1515 | ],
1516 | "MDS": [
1517 | "redstone"
1518 | ],
1519 | "YOU": [
1520 | "redstone"
1521 | ],
1522 | "XDN": [
1523 | "redstone"
1524 | ],
1525 | "ANJ": [
1526 | "redstone"
1527 | ],
1528 | "BOLT": [
1529 | "redstone"
1530 | ],
1531 | "CNNS": [
1532 | "redstone"
1533 | ],
1534 | "VEO": [
1535 | "redstone"
1536 | ],
1537 | "XCUR": [
1538 | "redstone"
1539 | ],
1540 | "BEPRO": [
1541 | "redstone"
1542 | ],
1543 | "ENQ": [
1544 | "redstone"
1545 | ],
1546 | "FLEX": [
1547 | "redstone"
1548 | ],
1549 | "LTX": [
1550 | "redstone"
1551 | ],
1552 | "STA": [
1553 | "redstone"
1554 | ],
1555 | "BULL": [
1556 | "redstone"
1557 | ],
1558 | "DMD": [
1559 | "redstone"
1560 | ],
1561 | "SKM": [
1562 | "redstone"
1563 | ],
1564 | "OCN": [
1565 | "redstone"
1566 | ],
1567 | "ODE": [
1568 | "redstone"
1569 | ],
1570 | "NCT": [
1571 | "redstone"
1572 | ],
1573 | "NORD": [
1574 | "redstone"
1575 | ],
1576 | "ITC": [
1577 | "redstone"
1578 | ],
1579 | "RIO": [
1580 | "redstone"
1581 | ],
1582 | "YEE": [
1583 | "redstone"
1584 | ],
1585 | "REV": [
1586 | "redstone"
1587 | ],
1588 | "AMB": [
1589 | "redstone"
1590 | ],
1591 | "NFT": [
1592 | "redstone"
1593 | ],
1594 | "SOC": [
1595 | "redstone"
1596 | ],
1597 | "VEX": [
1598 | "redstone"
1599 | ],
1600 | "EOSDT": [
1601 | "redstone"
1602 | ],
1603 | "ASP": [
1604 | "redstone"
1605 | ],
1606 | "TERN": [
1607 | "redstone"
1608 | ],
1609 | "$ANRX": [
1610 | "redstone"
1611 | ],
1612 | "TRUE": [
1613 | "redstone"
1614 | ],
1615 | "HPB": [
1616 | "redstone"
1617 | ],
1618 | "INT": [
1619 | "redstone"
1620 | ],
1621 | "RNT": [
1622 | "redstone"
1623 | ],
1624 | "XLQ": [
1625 | "redstone"
1626 | ],
1627 | "FAIR": [
1628 | "redstone"
1629 | ],
1630 | "DMG": [
1631 | "redstone"
1632 | ],
1633 | "UOP": [
1634 | "redstone"
1635 | ],
1636 | "YOYOW": [
1637 | "redstone"
1638 | ],
1639 | "DGTX": [
1640 | "redstone"
1641 | ],
1642 | "ALPA": [
1643 | "redstone"
1644 | ],
1645 | "IDEA": [
1646 | "redstone"
1647 | ],
1648 | "AMN": [
1649 | "redstone"
1650 | ],
1651 | "VRA": [
1652 | "redstone"
1653 | ],
1654 | "AXIS": [
1655 | "redstone"
1656 | ],
1657 | "ABL": [
1658 | "redstone"
1659 | ],
1660 | "BASE": [
1661 | "redstone"
1662 | ],
1663 | "PHNX": [
1664 | "redstone"
1665 | ],
1666 | "BNF": [
1667 | "redstone"
1668 | ],
1669 | "HYC": [
1670 | "redstone"
1671 | ],
1672 | "TRIO": [
1673 | "redstone"
1674 | ],
1675 | "UCT": [
1676 | "redstone"
1677 | ],
1678 | "EOSC": [
1679 | "redstone"
1680 | ],
1681 | "AIT": [
1682 | "redstone"
1683 | ],
1684 | "ETHO": [
1685 | "redstone"
1686 | ],
1687 | "QUN": [
1688 | "redstone"
1689 | ],
1690 | "LBA": [
1691 | "redstone"
1692 | ],
1693 | "CAN": [
1694 | "redstone"
1695 | ],
1696 | "BAX": [
1697 | "redstone"
1698 | ],
1699 | "AVS": [
1700 | "redstone"
1701 | ],
1702 | "BEAR": [
1703 | "redstone"
1704 | ],
1705 | "XPR": [
1706 | "redstone"
1707 | ],
1708 | "ADEL": [
1709 | "redstone"
1710 | ],
1711 | "CHAT": [
1712 | "redstone"
1713 | ],
1714 | "OLT": [
1715 | "redstone"
1716 | ],
1717 | "OGO": [
1718 | "redstone"
1719 | ],
1720 | "PVT": [
1721 | "redstone"
1722 | ],
1723 | "RSV": [
1724 | "redstone"
1725 | ],
1726 | "CTXC": [
1727 | "redstone"
1728 | ],
1729 | "PST": [
1730 | "redstone"
1731 | ],
1732 | "ONG": [
1733 | "redstone"
1734 | ],
1735 | "ACAT": [
1736 | "redstone"
1737 | ],
1738 | "NTK": [
1739 | "redstone"
1740 | ],
1741 | "CUR": [
1742 | "redstone"
1743 | ],
1744 | "ROCKS": [
1745 | "redstone"
1746 | ],
1747 | "XDNA": [
1748 | "redstone"
1749 | ],
1750 | "AID": [
1751 | "redstone"
1752 | ],
1753 | "AIDOC": [
1754 | "redstone"
1755 | ],
1756 | "ACM": [
1757 | "redstone"
1758 | ],
1759 | "ALEX": [
1760 | "redstone"
1761 | ],
1762 | "ORS": [
1763 | "redstone"
1764 | ],
1765 | "SDS": [
1766 | "redstone"
1767 | ],
1768 | "CORX": [
1769 | "redstone"
1770 | ],
1771 | "ASAFE": [
1772 | "redstone"
1773 | ],
1774 | "ALI": [
1775 | "redstone"
1776 | ],
1777 | "TNC": [
1778 | "redstone"
1779 | ],
1780 | "ALLBI": [
1781 | "redstone"
1782 | ],
1783 | "AG8": [
1784 | "redstone"
1785 | ],
1786 | "ATM": [
1787 | "redstone"
1788 | ],
1789 | "AGS": [
1790 | "redstone"
1791 | ],
1792 | "CNS": [
1793 | "redstone"
1794 | ],
1795 | "IQ": [
1796 | "redstone"
1797 | ],
1798 | "ETZ": [
1799 | "redstone"
1800 | ],
1801 | "ALMX": [
1802 | "redstone"
1803 | ],
1804 | "ALT": [
1805 | "redstone"
1806 | ],
1807 | "FLIXX": [
1808 | "redstone"
1809 | ],
1810 | "FESS": [
1811 | "redstone"
1812 | ],
1813 | "ALV": [
1814 | "redstone"
1815 | ],
1816 | "FLUX": [
1817 | "redstone"
1818 | ],
1819 | "ETM": [
1820 | "redstone"
1821 | ],
1822 | "DEX": [
1823 | "redstone"
1824 | ],
1825 | "GOT": [
1826 | "redstone"
1827 | ],
1828 | "BLOC": [
1829 | "redstone"
1830 | ],
1831 | "INX": [
1832 | "redstone"
1833 | ],
1834 | "A": [
1835 | "redstone"
1836 | ],
1837 | "MXT": [
1838 | "redstone"
1839 | ],
1840 | "ANON": [
1841 | "redstone"
1842 | ],
1843 | "XSR": [
1844 | "redstone"
1845 | ],
1846 | "UCM": [
1847 | "redstone"
1848 | ],
1849 | "GTX": [
1850 | "redstone"
1851 | ],
1852 | "WBT": [
1853 | "redstone"
1854 | ],
1855 | "STAK": [
1856 | "redstone"
1857 | ],
1858 | "SMC": [
1859 | "redstone"
1860 | ],
1861 | "ABET": [
1862 | "redstone"
1863 | ],
1864 | "AMS": [
1865 | "redstone"
1866 | ],
1867 | "BUNNY": [
1868 | "redstone"
1869 | ],
1870 | "APC": [
1871 | "redstone"
1872 | ],
1873 | "AGU": [
1874 | "redstone"
1875 | ],
1876 | "AET": [
1877 | "redstone"
1878 | ],
1879 | "AIAS": [
1880 | "redstone"
1881 | ],
1882 | "SSX": [
1883 | "redstone"
1884 | ],
1885 | "NIO": [
1886 | "redstone"
1887 | ],
1888 | "FCL": [
1889 | "redstone"
1890 | ],
1891 | "LBY": [
1892 | "redstone"
1893 | ],
1894 | "HOPR": [
1895 | "redstone"
1896 | ],
1897 | "ALOHA": [
1898 | "redstone"
1899 | ],
1900 | "QTF": [
1901 | "redstone"
1902 | ],
1903 | "SPORE": [
1904 | "redstone"
1905 | ],
1906 | "RAD": [
1907 | "redstone"
1908 | ],
1909 | "QUICK": [
1910 | "redstone"
1911 | ],
1912 | "ZUSD": [
1913 | "redstone"
1914 | ],
1915 | "LHB": [
1916 | "redstone"
1917 | ],
1918 | "ELCASH": [
1919 | "redstone"
1920 | ],
1921 | "ALL": [
1922 | "redstone"
1923 | ],
1924 | "A5T": [
1925 | "redstone"
1926 | ],
1927 | "CWS": [
1928 | "redstone"
1929 | ],
1930 | "DFM": [
1931 | "redstone"
1932 | ],
1933 | "CVT": [
1934 | "redstone"
1935 | ],
1936 | "GME": [
1937 | "redstone"
1938 | ],
1939 | "BETH": [
1940 | "redstone"
1941 | ],
1942 | "GLCH": [
1943 | "redstone"
1944 | ],
1945 | "ROUTE": [
1946 | "redstone"
1947 | ],
1948 | "OKT": [
1949 | "redstone"
1950 | ],
1951 | "GOLD": [
1952 | "redstone"
1953 | ],
1954 | "BAGS": [
1955 | "redstone"
1956 | ],
1957 | "GERA": [
1958 | "redstone"
1959 | ],
1960 | "HGOLD": [
1961 | "redstone"
1962 | ],
1963 | "UCOIN": [
1964 | "redstone"
1965 | ],
1966 | "AIM": [
1967 | "redstone"
1968 | ],
1969 | "EC": [
1970 | "redstone"
1971 | ],
1972 | "XEP": [
1973 | "redstone"
1974 | ],
1975 | "AME": [
1976 | "redstone"
1977 | ],
1978 | "REEF": [
1979 | "redstone"
1980 | ],
1981 | "GUSDT": [
1982 | "redstone"
1983 | ],
1984 | "G999": [
1985 | "redstone"
1986 | ],
1987 | "BANANA": [
1988 | "redstone"
1989 | ],
1990 | "CTC": [
1991 | "redstone"
1992 | ],
1993 | "PLEX": [
1994 | "redstone"
1995 | ],
1996 | "TBCC": [
1997 | "redstone"
1998 | ],
1999 | "HUB": [
2000 | "redstone"
2001 | ],
2002 | "CAMP": [
2003 | "redstone"
2004 | ],
2005 | "XLMBEAR": [
2006 | "redstone"
2007 | ],
2008 | "XLMBULL": [
2009 | "redstone"
2010 | ],
2011 | "MOB": [
2012 | "redstone"
2013 | ],
2014 | "AIN": [
2015 | "redstone"
2016 | ],
2017 | "CLT": [
2018 | "redstone"
2019 | ],
2020 | "AKN": [
2021 | "redstone"
2022 | ],
2023 | "CFX": [
2024 | "redstone"
2025 | ],
2026 | "LONG": [
2027 | "redstone"
2028 | ],
2029 | "TRA": [
2030 | "redstone"
2031 | ],
2032 | "GASP": [
2033 | "redstone"
2034 | ],
2035 | "UBX": [
2036 | "redstone"
2037 | ],
2038 | "PASS": [
2039 | "redstone"
2040 | ],
2041 | "CHESS": [
2042 | "redstone"
2043 | ],
2044 | "AICO": [
2045 | "redstone"
2046 | ],
2047 | "CLV": [
2048 | "redstone"
2049 | ],
2050 | "AGT": [
2051 | "redstone"
2052 | ],
2053 | "JGN": [
2054 | "redstone"
2055 | ],
2056 | "AWX": [
2057 | "redstone"
2058 | ],
2059 | "APE": [
2060 | "redstone"
2061 | ],
2062 | "AHF": [
2063 | "redstone"
2064 | ],
2065 | "ECELL": [
2066 | "redstone"
2067 | ],
2068 | "FLM": [
2069 | "redstone"
2070 | ],
2071 | "ZOMB": [
2072 | "redstone"
2073 | ],
2074 | "SFG": [
2075 | "redstone"
2076 | ],
2077 | "BID": [
2078 | "redstone"
2079 | ],
2080 | "BAKE": [
2081 | "redstone"
2082 | ],
2083 | "BURGER": [
2084 | "redstone"
2085 | ],
2086 | "VELO": [
2087 | "redstone"
2088 | ],
2089 | "NBS": [
2090 | "redstone"
2091 | ],
2092 | "SAM": [
2093 | "redstone"
2094 | ],
2095 | "HBC": [
2096 | "redstone"
2097 | ],
2098 | "ZYRO": [
2099 | "redstone"
2100 | ],
2101 | "GOF": [
2102 | "redstone"
2103 | ],
2104 | "SUN": [
2105 | "redstone"
2106 | ],
2107 | "JFI": [
2108 | "redstone"
2109 | ],
2110 | "GBP": [
2111 | "redstone"
2112 | ],
2113 | "USDU": [
2114 | "redstone"
2115 | ],
2116 | "ALA": [
2117 | "redstone"
2118 | ],
2119 | "TAI": [
2120 | "redstone"
2121 | ],
2122 | "PEARL": [
2123 | "redstone"
2124 | ],
2125 | "EPS": [
2126 | "redstone"
2127 | ],
2128 | "EFK": [
2129 | "redstone"
2130 | ],
2131 | "ANC": [
2132 | "redstone"
2133 | ],
2134 | "KLV": [
2135 | "redstone"
2136 | ],
2137 | "BUY": [
2138 | "redstone"
2139 | ],
2140 | "BSY": [
2141 | "redstone"
2142 | ],
2143 | "AHT": [
2144 | "redstone"
2145 | ],
2146 | "AMIX": [
2147 | "redstone"
2148 | ],
2149 | "WGRT": [
2150 | "redstone"
2151 | ],
2152 | "BNTX": [
2153 | "redstone"
2154 | ],
2155 | "ALD": [
2156 | "redstone"
2157 | ],
2158 | "CGC": [
2159 | "redstone"
2160 | ],
2161 | "SHIB": [
2162 | "redstone"
2163 | ],
2164 | "XAMP": [
2165 | "redstone"
2166 | ],
2167 | "KTON": [
2168 | "redstone"
2169 | ],
2170 | "NVT": [
2171 | "redstone"
2172 | ],
2173 | "BBT": [
2174 | "redstone"
2175 | ],
2176 | "RINGX": [
2177 | "redstone"
2178 | ],
2179 | "RWN": [
2180 | "redstone"
2181 | ],
2182 | "DFI": [
2183 | "redstone"
2184 | ],
2185 | "POL": [
2186 | "redstone"
2187 | ],
2188 | "BAR": [
2189 | "redstone"
2190 | ],
2191 | "LOON": [
2192 | "redstone"
2193 | ],
2194 | "AKC": [
2195 | "redstone"
2196 | ],
2197 | "GATOR": [
2198 | "redstone"
2199 | ],
2200 | "ALC": [
2201 | "redstone"
2202 | ],
2203 | "AWG": [
2204 | "redstone"
2205 | ],
2206 | "GAL": [
2207 | "redstone"
2208 | ],
2209 | "NDN": [
2210 | "redstone"
2211 | ],
2212 | "ZLW": [
2213 | "redstone"
2214 | ],
2215 | "AXT": [
2216 | "redstone"
2217 | ],
2218 | "ANW": [
2219 | "redstone"
2220 | ],
2221 | "AOS": [
2222 | "redstone"
2223 | ],
2224 | "TTT": [
2225 | "redstone"
2226 | ],
2227 | "AAB": [
2228 | "redstone"
2229 | ],
2230 | "SENSO": [
2231 | "redstone"
2232 | ],
2233 | "IBVOL": [
2234 | "redstone"
2235 | ],
2236 | "BVOL": [
2237 | "redstone"
2238 | ],
2239 | "HMR": [
2240 | "redstone"
2241 | ],
2242 | "MASS": [
2243 | "redstone"
2244 | ],
2245 | "RBTC": [
2246 | "redstone"
2247 | ],
2248 | "AMA": [
2249 | "redstone"
2250 | ],
2251 | "USDJ": [
2252 | "redstone"
2253 | ],
2254 | "DEP": [
2255 | "redstone"
2256 | ],
2257 | "BLCT": [
2258 | "redstone"
2259 | ],
2260 | "HDAO": [
2261 | "redstone"
2262 | ],
2263 | "SOLO": [
2264 | "redstone"
2265 | ],
2266 | "BEER": [
2267 | "redstone"
2268 | ],
2269 | "DNA": [
2270 | "redstone"
2271 | ],
2272 | "KOK": [
2273 | "redstone"
2274 | ],
2275 | "WEST": [
2276 | "redstone"
2277 | ],
2278 | "EXCHBULL": [
2279 | "redstone"
2280 | ],
2281 | "HTDF": [
2282 | "redstone"
2283 | ],
2284 | "DDRT": [
2285 | "redstone"
2286 | ],
2287 | "NODE": [
2288 | "redstone"
2289 | ],
2290 | "GOLDR": [
2291 | "redstone"
2292 | ],
2293 | "CHEX": [
2294 | "redstone"
2295 | ],
2296 | "BSVBEAR": [
2297 | "redstone"
2298 | ],
2299 | "EXCHBEAR": [
2300 | "redstone"
2301 | ],
2302 | "PLF": [
2303 | "redstone"
2304 | ],
2305 | "BCHBULL": [
2306 | "redstone"
2307 | ],
2308 | "BNA": [
2309 | "redstone"
2310 | ],
2311 | "DOGEBULL": [
2312 | "redstone"
2313 | ],
2314 | "BCHBEAR": [
2315 | "redstone"
2316 | ],
2317 | "BSVBULL": [
2318 | "redstone"
2319 | ],
2320 | "TRXBULL": [
2321 | "redstone"
2322 | ],
2323 | "AAT": [
2324 | "redstone"
2325 | ],
2326 | "BNBBEAR": [
2327 | "redstone"
2328 | ],
2329 | "LINKBULL": [
2330 | "redstone"
2331 | ],
2332 | "ADABEAR": [
2333 | "redstone"
2334 | ],
2335 | "TRXBEAR": [
2336 | "redstone"
2337 | ],
2338 | "LINKBEAR": [
2339 | "redstone"
2340 | ],
2341 | "LTCBULL": [
2342 | "redstone"
2343 | ],
2344 | "LTCBEAR": [
2345 | "redstone"
2346 | ],
2347 | "BNBBULL": [
2348 | "redstone"
2349 | ],
2350 | "XRPBULL": [
2351 | "redstone"
2352 | ],
2353 | "XRPBEAR": [
2354 | "redstone"
2355 | ],
2356 | "ALTBULL": [
2357 | "redstone"
2358 | ],
2359 | "ADABULL": [
2360 | "redstone"
2361 | ],
2362 | "ALTBEAR": [
2363 | "redstone"
2364 | ],
2365 | "EOSBEAR": [
2366 | "redstone"
2367 | ],
2368 | "ETHBULL": [
2369 | "redstone"
2370 | ],
2371 | "APIX": [
2372 | "redstone"
2373 | ],
2374 | "ROAD": [
2375 | "redstone"
2376 | ],
2377 | "JUV": [
2378 | "redstone"
2379 | ],
2380 | "AMC": [
2381 | "redstone"
2382 | ],
2383 | "ANCT": [
2384 | "redstone"
2385 | ],
2386 | "SUTER": [
2387 | "redstone"
2388 | ],
2389 | "BYND": [
2390 | "redstone"
2391 | ],
2392 | "DEXA": [
2393 | "redstone"
2394 | ],
2395 | "XPO": [
2396 | "redstone"
2397 | ],
2398 | "ALP": [
2399 | "redstone"
2400 | ],
2401 | "KLAY": [
2402 | "redstone"
2403 | ],
2404 | "NDAU": [
2405 | "redstone"
2406 | ],
2407 | "ALY": [
2408 | "redstone"
2409 | ],
2410 | "RRB": [
2411 | "redstone"
2412 | ],
2413 | "TORI": [
2414 | "redstone"
2415 | ],
2416 | "NUT": [
2417 | "redstone"
2418 | ],
2419 | "EXE": [
2420 | "redstone"
2421 | ],
2422 | "AXA": [
2423 | "redstone"
2424 | ],
2425 | "ARDX": [
2426 | "redstone"
2427 | ],
2428 | "XYZ": [
2429 | "redstone"
2430 | ],
2431 | "JOB": [
2432 | "redstone"
2433 | ],
2434 | "LOL": [
2435 | "redstone"
2436 | ],
2437 | "TRIBE": [
2438 | "redstone"
2439 | ],
2440 | "AIRX": [
2441 | "redstone"
2442 | ],
2443 | "ANTR": [
2444 | "redstone"
2445 | ],
2446 | "ZIK": [
2447 | "redstone"
2448 | ],
2449 | "ERK": [
2450 | "redstone"
2451 | ],
2452 | "AMON": [
2453 | "redstone"
2454 | ],
2455 | "SLV": [
2456 | "redstone"
2457 | ],
2458 | "TOKO": [
2459 | "redstone"
2460 | ],
2461 | "BEST": [
2462 | "redstone"
2463 | ],
2464 | "AMIO": [
2465 | "redstone"
2466 | ],
2467 | "ANDES": [
2468 | "redstone"
2469 | ],
2470 | "AFO": [
2471 | "redstone"
2472 | ],
2473 | "CNTM": [
2474 | "redstone"
2475 | ],
2476 | "BRZ": [
2477 | "redstone"
2478 | ],
2479 | "XCHF": [
2480 | "redstone"
2481 | ],
2482 | "AMIS": [
2483 | "redstone"
2484 | ],
2485 | "SRK": [
2486 | "redstone"
2487 | ],
2488 | "AIPE": [
2489 | "redstone"
2490 | ],
2491 | "TFUEL": [
2492 | "redstone"
2493 | ],
2494 | "ALTM": [
2495 | "redstone"
2496 | ],
2497 | "GTC": [
2498 | "redstone"
2499 | ],
2500 | "BKK": [
2501 | "redstone"
2502 | ],
2503 | "ACD": [
2504 | "redstone"
2505 | ],
2506 | "ALB": [
2507 | "redstone"
2508 | ],
2509 | "BHP": [
2510 | "redstone"
2511 | ],
2512 | "BWB": [
2513 | "redstone"
2514 | ],
2515 | "AIDUS": [
2516 | "redstone"
2517 | ],
2518 | "SONO": [
2519 | "redstone"
2520 | ],
2521 | "HEDG": [
2522 | "redstone"
2523 | ],
2524 | "PLG": [
2525 | "redstone"
2526 | ],
2527 | "ABAO": [
2528 | "redstone"
2529 | ],
2530 | "AGVC": [
2531 | "redstone"
2532 | ],
2533 | "BHD": [
2534 | "redstone"
2535 | ],
2536 | "HPT": [
2537 | "redstone"
2538 | ],
2539 | "RRT": [
2540 | "redstone"
2541 | ],
2542 | "ANTY": [
2543 | "redstone"
2544 | ],
2545 | "STC": [
2546 | "redstone"
2547 | ],
2548 | "TMTG": [
2549 | "redstone"
2550 | ],
2551 | "MARX": [
2552 | "redstone"
2553 | ],
2554 | "ADK": [
2555 | "redstone"
2556 | ],
2557 | "XRA": [
2558 | "redstone"
2559 | ],
2560 | "EVY": [
2561 | "redstone"
2562 | ],
2563 | "ME": [
2564 | "redstone"
2565 | ],
2566 | "VIDY": [
2567 | "redstone"
2568 | ],
2569 | "FKX": [
2570 | "redstone"
2571 | ],
2572 | "ATP": [
2573 | "redstone"
2574 | ],
2575 | "PAI": [
2576 | "redstone"
2577 | ],
2578 | "CET": [
2579 | "redstone"
2580 | ],
2581 | "PERL": [
2582 | "redstone"
2583 | ],
2584 | "DREP": [
2585 | "redstone"
2586 | ],
2587 | "AGLT": [
2588 | "redstone"
2589 | ],
2590 | "AKA": [
2591 | "redstone"
2592 | ],
2593 | "NEW": [
2594 | "redstone"
2595 | ],
2596 | "CRON": [
2597 | "redstone"
2598 | ],
2599 | "AGN": [
2600 | "redstone"
2601 | ],
2602 | "INNBC": [
2603 | "redstone"
2604 | ],
2605 | "AGET": [
2606 | "redstone"
2607 | ],
2608 | "SG": [
2609 | "redstone"
2610 | ],
2611 | "OXT": [
2612 | "redstone"
2613 | ],
2614 | "XAO": [
2615 | "redstone"
2616 | ],
2617 | "MVL": [
2618 | "redstone"
2619 | ],
2620 | "CENNZ": [
2621 | "redstone"
2622 | ],
2623 | "SEELE": [
2624 | "redstone"
2625 | ],
2626 | "ICP": [
2627 | "redstone"
2628 | ],
2629 | "ABBC": [
2630 | "redstone"
2631 | ],
2632 | "MOF": [
2633 | "redstone"
2634 | ],
2635 | "EKT": [
2636 | "redstone"
2637 | ],
2638 | "AAC": [
2639 | "redstone"
2640 | ],
2641 | "KCASH": [
2642 | "redstone"
2643 | ],
2644 | "TOPC": [
2645 | "redstone"
2646 | ],
2647 | "AMR": [
2648 | "redstone"
2649 | ],
2650 | "UBTC": [
2651 | "redstone"
2652 | ],
2653 | "XUC": [
2654 | "redstone"
2655 | ],
2656 | "ALTOM": [
2657 | "redstone"
2658 | ],
2659 | "ALIS": [
2660 | "redstone"
2661 | ],
2662 | "KICK": [
2663 | "redstone"
2664 | ],
2665 | "KIN": [
2666 | "redstone"
2667 | ],
2668 | "AGRS": [
2669 | "redstone"
2670 | ],
2671 | "XWC": [
2672 | "redstone"
2673 | ],
2674 | "AGVE": [
2675 | "redstone"
2676 | ],
2677 | "AIOZ": [
2678 | "redstone"
2679 | ],
2680 | "AWT": [
2681 | "redstone"
2682 | ],
2683 | "AKITA": [
2684 | "redstone"
2685 | ],
2686 | "ALR": [
2687 | "redstone"
2688 | ],
2689 | "ALCX": [
2690 | "redstone"
2691 | ],
2692 | "ALUSD": [
2693 | "redstone"
2694 | ],
2695 | "ALCH": [
2696 | "redstone"
2697 | ],
2698 | "AGOL": [
2699 | "redstone"
2700 | ],
2701 | "TLM": [
2702 | "redstone"
2703 | ],
2704 | "ALH": [
2705 | "redstone"
2706 | ],
2707 | "ALPACA": [
2708 | "redstone"
2709 | ],
2710 | "ALPHR": [
2711 | "redstone"
2712 | ],
2713 | "ALN": [
2714 | "redstone"
2715 | ],
2716 | "AMI": [
2717 | "redstone"
2718 | ],
2719 | "FORTH": [
2720 | "redstone"
2721 | ],
2722 | "AAPX": [
2723 | "redstone"
2724 | ],
2725 | "ANS": [
2726 | "redstone"
2727 | ],
2728 | "AGOV": [
2729 | "redstone"
2730 | ],
2731 | "MATTER": [
2732 | "redstone"
2733 | ],
2734 | "ABLOCK": [
2735 | "redstone"
2736 | ],
2737 | "AP3": [
2738 | "redstone"
2739 | ],
2740 | "APE$": [
2741 | "redstone"
2742 | ],
2743 | "NANA": [
2744 | "redstone"
2745 | ],
2746 | "APN": [
2747 | "redstone"
2748 | ],
2749 | "BCVT": [
2750 | "redstone"
2751 | ],
2752 | "BSBT": [
2753 | "redstone"
2754 | ],
2755 | "BOSON": [
2756 | "redstone"
2757 | ],
2758 | "CARR": [
2759 | "redstone"
2760 | ],
2761 | "CONV": [
2762 | "redstone"
2763 | ],
2764 | "CQT": [
2765 | "redstone"
2766 | ],
2767 | "DPR": [
2768 | "redstone"
2769 | ],
2770 | "ELON": [
2771 | "redstone"
2772 | ],
2773 | "DORA": [
2774 | "redstone"
2775 | ],
2776 | "ELONGATE": [
2777 | "redstone"
2778 | ],
2779 | "ETHA": [
2780 | "redstone"
2781 | ],
2782 | "ERN": [
2783 | "redstone"
2784 | ],
2785 | "FEI": [
2786 | "redstone"
2787 | ],
2788 | "FLY": [
2789 | "redstone"
2790 | ],
2791 | "GMT": [
2792 | "redstone"
2793 | ],
2794 | "GMEE": [
2795 | "redstone"
2796 | ],
2797 | "HAPI": [
2798 | "redstone"
2799 | ],
2800 | "HOKK": [
2801 | "redstone"
2802 | ],
2803 | "ICE": [
2804 | "redstone"
2805 | ],
2806 | "ILV": [
2807 | "redstone"
2808 | ],
2809 | "INSUR": [
2810 | "redstone"
2811 | ],
2812 | "IQQ": [
2813 | "redstone"
2814 | ],
2815 | "KINE": [
2816 | "redstone"
2817 | ],
2818 | "KISHU": [
2819 | "redstone"
2820 | ],
2821 | "KONO": [
2822 | "redstone"
2823 | ],
2824 | "LABS": [
2825 | "redstone"
2826 | ],
2827 | "LQTY": [
2828 | "redstone"
2829 | ],
2830 | "LUSD": [
2831 | "redstone"
2832 | ],
2833 | "MINA": [
2834 | "redstone"
2835 | ],
2836 | "MMAON": [
2837 | "redstone"
2838 | ],
2839 | "ALICE": [
2840 | "redstone"
2841 | ],
2842 | "OCC": [
2843 | "redstone"
2844 | ],
2845 | "OXY": [
2846 | "redstone"
2847 | ],
2848 | "PANDO": [
2849 | "redstone"
2850 | ],
2851 | "PSL": [
2852 | "redstone"
2853 | ],
2854 | "PIG": [
2855 | "redstone"
2856 | ],
2857 | "PUNDIX": [
2858 | "redstone"
2859 | ],
2860 | "FINE": [
2861 | "redstone"
2862 | ],
2863 | "ROC": [
2864 | "redstone"
2865 | ],
2866 | "SAFEMOON": [
2867 | "redstone"
2868 | ],
2869 | "SATOZ": [
2870 | "redstone"
2871 | ],
2872 | "SHFT": [
2873 | "redstone"
2874 | ],
2875 | "SMTY": [
2876 | "redstone"
2877 | ],
2878 | "SNOB": [
2879 | "redstone"
2880 | ],
2881 | "SHOPX": [
2882 | "redstone"
2883 | ],
2884 | "STEP": [
2885 | "redstone"
2886 | ],
2887 | "STRK": [
2888 | "redstone"
2889 | ],
2890 | "SUPERBID": [
2891 | "redstone"
2892 | ],
2893 | "XYM": [
2894 | "redstone"
2895 | ],
2896 | "TARA": [
2897 | "redstone"
2898 | ],
2899 | "TKO": [
2900 | "redstone"
2901 | ],
2902 | "HVE2": [
2903 | "redstone"
2904 | ],
2905 | "MARSH": [
2906 | "redstone"
2907 | ],
2908 | "PYR": [
2909 | "redstone"
2910 | ],
2911 | "WSB": [
2912 | "redstone"
2913 | ],
2914 | "XELS": [
2915 | "redstone"
2916 | ],
2917 | "sUSD": [
2918 | "redstone"
2919 | ],
2920 | "wNXM": [
2921 | "redstone"
2922 | ],
2923 | "AMD": [
2924 | "redstone"
2925 | ],
2926 | "AUD": [
2927 | "redstone"
2928 | ],
2929 | "CHF": [
2930 | "redstone"
2931 | ],
2932 | "EUR": [
2933 | "redstone"
2934 | ],
2935 | "JPY": [
2936 | "redstone"
2937 | ],
2938 | "NOK": [
2939 | "redstone"
2940 | ],
2941 | "CSPR": [
2942 | "redstone"
2943 | ],
2944 | "YFV": [
2945 | "redstone"
2946 | ],
2947 | "ARDRIVE": [
2948 | "redstone"
2949 | ],
2950 | "ARGO": [
2951 | "redstone"
2952 | ],
2953 | "ARVERIFY": [
2954 | "redstone"
2955 | ],
2956 | "EDST": [
2957 | "redstone"
2958 | ],
2959 | "EMD_PST_TOKEN": [
2960 | "redstone"
2961 | ],
2962 | "OPENBITS": [
2963 | "redstone"
2964 | ],
2965 | "PBT": [
2966 | "redstone"
2967 | ],
2968 | "QUAD": [
2969 | "redstone"
2970 | ],
2971 | "SUNNY": [
2972 | "redstone"
2973 | ],
2974 | "VRT": [
2975 | "redstone"
2976 | ],
2977 | "IOTA": [
2978 | "redstone"
2979 | ],
2980 | "XCH": [
2981 | "redstone"
2982 | ],
2983 | "LAT": [
2984 | "redstone"
2985 | ],
2986 | "DOGGY": [
2987 | "redstone"
2988 | ],
2989 | "BZZ": [
2990 | "redstone"
2991 | ],
2992 | "O3": [
2993 | "redstone"
2994 | ],
2995 | "DOM": [
2996 | "redstone"
2997 | ],
2998 | "ATA": [
2999 | "redstone"
3000 | ],
3001 | "BABYDOGE": [
3002 | "redstone"
3003 | ],
3004 | "HF": [
3005 | "redstone"
3006 | ],
3007 | "FEAR": [
3008 | "redstone"
3009 | ],
3010 | "DPET": [
3011 | "redstone"
3012 | ],
3013 | "C98": [
3014 | "redstone"
3015 | ],
3016 | "SKILL": [
3017 | "redstone"
3018 | ],
3019 | "XCAD": [
3020 | "redstone"
3021 | ],
3022 | "ASD": [
3023 | "redstone"
3024 | ],
3025 | "YFDAI": [
3026 | "redstone"
3027 | ],
3028 | "COOP": [
3029 | "redstone"
3030 | ],
3031 | "SAFELIGHT": [
3032 | "redstone"
3033 | ],
3034 | "DS": [
3035 | "redstone"
3036 | ],
3037 | "ETL": [
3038 | "redstone"
3039 | ],
3040 | "XEC": [
3041 | "redstone"
3042 | ],
3043 | "BTCUP": [
3044 | "redstone"
3045 | ],
3046 | "BTCDOWN": [
3047 | "redstone"
3048 | ],
3049 | "ETHUP": [
3050 | "redstone"
3051 | ],
3052 | "ETHDOWN": [
3053 | "redstone"
3054 | ],
3055 | "DOTUP": [
3056 | "redstone"
3057 | ],
3058 | "CCE": [
3059 | "redstone"
3060 | ],
3061 | "Cube": [
3062 | "redstone"
3063 | ],
3064 | "GNT": [
3065 | "redstone"
3066 | ],
3067 | "ORS Group": [
3068 | "redstone"
3069 | ],
3070 | "OMN": [
3071 | "redstone"
3072 | ],
3073 | "EURT": [
3074 | "redstone"
3075 | ],
3076 | "MDOGE": [
3077 | "redstone"
3078 | ],
3079 | "B21X": [
3080 | "redstone"
3081 | ],
3082 | "ETH2X": [
3083 | "redstone"
3084 | ],
3085 | "PLANETS": [
3086 | "redstone"
3087 | ],
3088 | "ABNB": [
3089 | "redstone"
3090 | ],
3091 | "ACB": [
3092 | "redstone"
3093 | ],
3094 | "ARKK": [
3095 | "redstone"
3096 | ],
3097 | "BABA": [
3098 | "redstone"
3099 | ],
3100 | "BB": [
3101 | "redstone"
3102 | ],
3103 | "BILI": [
3104 | "redstone"
3105 | ],
3106 | "BITW": [
3107 | "redstone"
3108 | ],
3109 | "GDXJ": [
3110 | "redstone"
3111 | ],
3112 | "GLD": [
3113 | "redstone"
3114 | ],
3115 | "GLXY": [
3116 | "redstone"
3117 | ],
3118 | "GOOGL": [
3119 | "redstone"
3120 | ],
3121 | "MRNA": [
3122 | "redstone"
3123 | ],
3124 | "PENN": [
3125 | "redstone"
3126 | ],
3127 | "PFE": [
3128 | "redstone"
3129 | ],
3130 | "PYPL": [
3131 | "redstone"
3132 | ],
3133 | "SMBSWAP": [
3134 | "redstone"
3135 | ],
3136 | "SQ": [
3137 | "redstone"
3138 | ],
3139 | "TSM": [
3140 | "redstone"
3141 | ],
3142 | "TWTR": [
3143 | "redstone"
3144 | ],
3145 | "UBER": [
3146 | "redstone"
3147 | ],
3148 | "USO": [
3149 | "redstone"
3150 | ],
3151 | "ZM": [
3152 | "redstone"
3153 | ],
3154 | "YFX": [
3155 | "redstone"
3156 | ],
3157 | "CFG": [
3158 | "redstone"
3159 | ],
3160 | "OOE": [
3161 | "redstone"
3162 | ],
3163 | "DRS": [
3164 | "redstone"
3165 | ],
3166 | "NVDA": [
3167 | "redstone"
3168 | ],
3169 | "GDX": [
3170 | "redstone"
3171 | ],
3172 | "TLRY": [
3173 | "redstone"
3174 | ],
3175 | "DKNG": [
3176 | "redstone"
3177 | ],
3178 | "XHT": [
3179 | "redstone"
3180 | ],
3181 | "KAR": [
3182 | "redstone"
3183 | ],
3184 | "AXC": [
3185 | "redstone"
3186 | ],
3187 | "HERO": [
3188 | "redstone"
3189 | ],
3190 | "AQUAGOAT": [
3191 | "redstone"
3192 | ],
3193 | "PORNROCKET": [
3194 | "redstone"
3195 | ],
3196 | "TOKAU": [
3197 | "redstone"
3198 | ],
3199 | "SRM3L": [
3200 | "redstone"
3201 | ],
3202 | "ALICE3L": [
3203 | "redstone"
3204 | ],
3205 | "YFI3S": [
3206 | "redstone"
3207 | ],
3208 | "ZEC3S": [
3209 | "redstone"
3210 | ],
3211 | "DUKE": [
3212 | "redstone"
3213 | ],
3214 | "DefiBox": [
3215 | "redstone"
3216 | ],
3217 | "ARTEX": [
3218 | "redstone"
3219 | ],
3220 | "CBY": [
3221 | "redstone"
3222 | ],
3223 | "STRI": [
3224 | "redstone"
3225 | ],
3226 | "SCONEX": [
3227 | "redstone"
3228 | ],
3229 | "PNIXS": [
3230 | "redstone"
3231 | ],
3232 | "PYE": [
3233 | "redstone"
3234 | ],
3235 | "TRR": [
3236 | "redstone"
3237 | ],
3238 | "WACO": [
3239 | "redstone"
3240 | ],
3241 | "INVESTEL": [
3242 | "redstone"
3243 | ],
3244 | "KABOSU": [
3245 | "redstone"
3246 | ],
3247 | "GNBT": [
3248 | "redstone"
3249 | ],
3250 | "MW=F": [
3251 | "redstone-stocks"
3252 | ],
3253 | "DLM21": [
3254 | "redstone-stocks"
3255 | ],
3256 | "DFQ21": [
3257 | "redstone-stocks"
3258 | ],
3259 | "BD=F": [
3260 | "redstone-stocks"
3261 | ],
3262 | "BJ=F": [
3263 | "redstone-stocks"
3264 | ],
3265 | "CTZ21": [
3266 | "redstone-stocks"
3267 | ],
3268 | "KC=F": [
3269 | "redstone-stocks"
3270 | ],
3271 | "SB=F": [
3272 | "redstone-stocks"
3273 | ],
3274 | "CC=F": [
3275 | "redstone-stocks"
3276 | ],
3277 | "LS=F": [
3278 | "redstone-stocks"
3279 | ],
3280 | "OJ=F": [
3281 | "redstone-stocks"
3282 | ],
3283 | "XAVA": [
3284 | "redstone",
3285 | "redstone-avalanche"
3286 | ],
3287 | "SHERPA": [
3288 | "redstone"
3289 | ],
3290 | "AVME": [
3291 | "redstone"
3292 | ],
3293 | "WAVAX": [
3294 | "redstone"
3295 | ],
3296 | "JOE": [
3297 | "redstone"
3298 | ],
3299 | "YAK": [
3300 | "redstone",
3301 | "redstone-avalanche"
3302 | ],
3303 | "QI": [
3304 | "redstone",
3305 | "redstone-avalanche"
3306 | ],
3307 | "MXNUSD=X": [
3308 | "redstone",
3309 | "redstone-stocks"
3310 | ],
3311 | "BRLUSD=X": [
3312 | "redstone",
3313 | "redstone-stocks"
3314 | ],
3315 | "COPUSD=X": [
3316 | "redstone",
3317 | "redstone-stocks"
3318 | ],
3319 | "ARSUSD=X": [
3320 | "redstone",
3321 | "redstone-stocks"
3322 | ],
3323 | "PENUSD=X": [
3324 | "redstone",
3325 | "redstone-stocks"
3326 | ],
3327 | "SOS": [
3328 | "redstone"
3329 | ]
3330 | };
3331 |
--------------------------------------------------------------------------------
/src/decs.d.ts:
--------------------------------------------------------------------------------
1 | declare module "deep-sort-object";
2 |
--------------------------------------------------------------------------------
/src/errors/price-not-found.ts:
--------------------------------------------------------------------------------
1 | export default class PriceNotFoundError extends Error {
2 | constructor(symbol: string) {
3 | super(`Price not found for symbol: ${symbol}`);
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import Api from "./api";
2 | import Query from "./query";
3 |
4 | class Redstone extends Api {
5 | query = Query;
6 | Api = Api;
7 |
8 | constructor(opts?: any) {
9 | super(opts);
10 | }
11 | }
12 |
13 | export = new Redstone();
14 |
--------------------------------------------------------------------------------
/src/proxies/arweave-proxy.ts:
--------------------------------------------------------------------------------
1 | import ArweaveMultihost from "arweave-multihost";
2 | import Arweave from "arweave";
3 | import { run } from "ar-gql";
4 | import pako from "pako";
5 | import config from "../config";
6 |
7 | interface GraphQLParams {
8 | type: string;
9 | version: string;
10 | providerAddress: string;
11 | }
12 |
13 | interface GraphQLResponse {
14 | permawebTx: string;
15 | tags: TagsObj;
16 | }
17 |
18 | interface TagsObj {
19 | [name: string]: string;
20 | }
21 |
22 | interface GetTxDataOpts {
23 | parseJSON: boolean;
24 | }
25 |
26 | interface ProviderNameToDetailsMapping {
27 | [providerName: string]: ProviderDetails;
28 | }
29 |
30 | interface ProviderDetails {
31 | address: string;
32 | publicKey: string;
33 | }
34 |
35 | // TODO: revert LAST_BLOCKS_TO_CHECK to 50
36 | const LAST_BLOCKS_TO_CHECK = 5000;
37 |
38 | export default class ArweaveProxy {
39 | arweaveClient: any;
40 |
41 | constructor() {
42 | this.arweaveClient = ArweaveMultihost.initWithDefaultHosts({
43 | timeout: 10000, // Network request timeouts in milliseconds
44 | logging: false, // Enable network request logging
45 | });
46 | }
47 |
48 | async findPricesInGraphQL(
49 | parameters: GraphQLParams,
50 | ): Promise {
51 | const networkInfo = await this.arweaveClient.network.getInfo();
52 | const minBlock = networkInfo.height - LAST_BLOCKS_TO_CHECK;
53 |
54 | const query = `
55 | {
56 | transactions(
57 | tags: [
58 | { name: "app", values: "Redstone" }
59 | { name: "type", values: "${parameters.type}" }
60 | { name: "version", values: "${parameters.version}" }
61 | ]
62 | block: { min: ${minBlock} }
63 | owners: ["${parameters.providerAddress}"]
64 | first: 1
65 | ) {
66 | edges {
67 | node {
68 | tags {
69 | name
70 | value
71 | }
72 | id
73 | }
74 | }
75 | }
76 | }`;
77 |
78 | const res = (await run(query)).data.transactions.edges;
79 |
80 | if (res.length > 0) {
81 | const node = res[0].node;
82 |
83 | // Converting name, value array to tags object
84 | const tags: TagsObj = {};
85 | for (const { name, value } of node.tags) {
86 | tags[name] = value;
87 | }
88 |
89 | return {
90 | permawebTx: node.id,
91 | tags,
92 | };
93 | } else {
94 | return undefined;
95 | }
96 | }
97 |
98 | async getTxDataById(txId: string, opts?: GetTxDataOpts): Promise {
99 | const data = await this.arweaveClient.transactions.getData(txId, {
100 | decode: true,
101 | });
102 | const gunzippedData = pako.ungzip(Buffer.from(data));
103 | const strData = Arweave.utils.bufferToString(gunzippedData);
104 |
105 | if (opts !== undefined && opts.parseJSON) {
106 | return JSON.parse(strData);
107 | } else {
108 | return strData;
109 | }
110 | }
111 |
112 | async getProviderDetails(providerName: string): Promise {
113 | const mapping: ProviderNameToDetailsMapping = config.providers as ProviderNameToDetailsMapping;
114 |
115 | if (mapping[providerName] === undefined) {
116 | throw new Error(`Provider details not found: ${providerName}`);
117 | } else {
118 | return mapping[providerName];
119 | }
120 | }
121 |
122 | async verifySignature(args: {
123 | signedData: string;
124 | signature: string;
125 | signerPublicKey: string;
126 | }): Promise {
127 | const signedBytes: Uint8Array = new TextEncoder().encode(args.signedData);
128 | const signatureBytes: Uint8Array = Uint8Array.from(
129 | Buffer.from(args.signature, "base64"),
130 | );
131 |
132 | return await this.arweaveClient.crypto.verify(
133 | args.signerPublicKey,
134 | signedBytes,
135 | signatureBytes,
136 | );
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/proxies/cache-proxy.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import _ from "lodash";
3 | import { PriceDataWithSignature } from "../types";
4 |
5 | export default class CacheProxy {
6 | cacheApiUrl: string;
7 |
8 | constructor(cacheApiUrl: string) {
9 | this.cacheApiUrl = cacheApiUrl;
10 | }
11 |
12 | setCacheApiUrl(cacheApiUrl: string) {
13 | this.cacheApiUrl = cacheApiUrl;
14 | }
15 |
16 | async getPrice(args: {
17 | symbol: string;
18 | provider: string;
19 | timestamp?: number;
20 | }): Promise {
21 | const params: any = {
22 | symbol: args.symbol,
23 | provider: args.provider,
24 | limit: 1,
25 | };
26 |
27 | // If timestamp is passed we fetch the latest price
28 | // with timestamp which is less or equal to the passed one
29 | if (args.timestamp !== undefined) {
30 | params.toTimestamp = args.timestamp;
31 | }
32 |
33 | const { data } = await axios.get(this.cacheApiUrl, { params });
34 |
35 | if (Array.isArray(data) && data.length === 1) {
36 | return data[0];
37 | } else {
38 | return undefined;
39 | }
40 | }
41 |
42 | // If 'symbols' is not passed it will fetch prices for all tokens
43 | async getPriceForManyTokens(args: {
44 | provider: string;
45 | timestamp?: number;
46 | symbols?: string[];
47 | }): Promise<{ [symbol: string]: PriceDataWithSignature }> {
48 | const params: any = {
49 | provider: args.provider,
50 | ...(args.timestamp != null ? { toTimestamp: args.timestamp } : {})
51 | };
52 |
53 | if (args.symbols !== undefined) {
54 | params.symbols = args.symbols.join(",");
55 | }
56 |
57 | const { data } = await axios.get(this.cacheApiUrl, { params });
58 |
59 | return data;
60 | }
61 |
62 | async getManyPrices(args: {
63 | symbol: string;
64 | provider: string;
65 | fromTimestamp?: number;
66 | toTimestamp?: number;
67 | interval?: number;
68 | offset?: number;
69 | limit?: number;
70 | }): Promise {
71 | const params = _.pickBy(args, (prop) => !_.isUndefined(prop));
72 | const { data } = await axios.get(this.cacheApiUrl, { params });
73 | return data;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/query.ts:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 | import {
3 | ConvertableToDate,
4 | GetPriceOptions,
5 | RedstoneApiConfigForQueryExec,
6 | PriceData,
7 | } from "./types";
8 | import RedstoneApi from "./api";
9 |
10 | type QueryParams = {
11 | symbols: string[];
12 | startDate?: ConvertableToDate;
13 | endDate?: ConvertableToDate;
14 | date?: ConvertableToDate;
15 | interval?: number;
16 | latest?: boolean;
17 | };
18 |
19 | export class RedstoneQuery {
20 | protected params: QueryParams;
21 |
22 | constructor(params = {}) {
23 | this.params = {
24 | symbols: [],
25 | ...params,
26 | };
27 | }
28 |
29 | /**
30 | * Sets a token symbol to fetch
31 | * @param symbol - Token symbol string
32 | * @returns query object
33 | */
34 | symbol(symbol: string): RedstoneQueryForSingleSymbol {
35 | return new RedstoneQueryForSingleSymbol({
36 | symbols: [symbol],
37 | });
38 | }
39 |
40 | /**
41 | * Sets token symbols to fetch
42 | * @param symbols - Array of strings (token symbols)
43 | * @returns query object
44 | */
45 | symbols(symbols: string[]): RedstoneQueryForSeveralSymbols {
46 | return new RedstoneQueryForSeveralSymbols({ symbols });
47 | }
48 |
49 | /**
50 | * Configures query to fetch prices for all supported tokens.
51 | * It doesn't support any params
52 | * @returns query object
53 | */
54 | allSymbols(): RedstoneQueryForSeveralSymbols {
55 | // return this.getQueryWithUpdatedSymbols<{ [symbol: string]: PriceData }>([]);
56 | return new RedstoneQueryForSeveralSymbols({
57 | symbols: [],
58 | });
59 | }
60 | }
61 |
62 | export class RedstoneQueryForSingleOrSeveralSymbols {
63 | protected params: QueryParams;
64 |
65 | constructor(params: QueryParams) {
66 | this.params = params;
67 | }
68 |
69 | // TODO: Maybe improve the type later (replace any with more detailed one)
70 | protected getExecutableQuery(update: any): RedstoneQueryExecutable {
71 | return new RedstoneQueryExecutable({
72 | ...this.params,
73 | ...update,
74 | });
75 | }
76 |
77 | /**
78 | * Configures query to fetch the latest price/prices
79 | * It doesn't support any params
80 | * @returns query object
81 | */
82 | latest(): RedstoneQueryExecutable {
83 | return this.getExecutableQuery({});
84 | }
85 |
86 | /**
87 | * Configures query to fetch the price for X hours ago.
88 | * @param hoursCount - Number of hours ago
89 | * @returns query object
90 | */
91 | hoursAgo(hoursCount: number): RedstoneQueryExecutable {
92 | return this.getExecutableQuery({
93 | date: Date.now() - hoursCount * 3600 * 1000,
94 | });
95 | }
96 |
97 | /**
98 | * Configures query to fetch the price for a specific date.
99 | * @param date - Date for the historical price (date | timestamp | string)
100 | * @returns query object
101 | */
102 | atDate(date: ConvertableToDate): RedstoneQueryExecutable {
103 | return this.getExecutableQuery({ date });
104 | }
105 | }
106 |
107 | export class RedstoneQueryForSingleSymbol extends RedstoneQueryForSingleOrSeveralSymbols {
108 | constructor(params: QueryParams) {
109 | super(params);
110 | }
111 |
112 | /**
113 | * Configures query to fetch the price in a time range. It is important to use fromDate with toDate query methods
114 | * @param date - Start date/time for the time range
115 | *
116 | * @returns query object
117 | */
118 | fromDate(date: ConvertableToDate): RedstoneQueryForSingleSymbol {
119 | return new RedstoneQueryForSingleSymbol({
120 | ...this.params,
121 | startDate: date,
122 | });
123 | }
124 |
125 | /**
126 | * Configures query to fetch the price in a time range. toDate method should go after the fromDate
127 | * @param date - End date/time for the time range
128 | *
129 | * @returns query object
130 | */
131 | toDate(date: ConvertableToDate): RedstoneQueryExecutable {
132 | if (this.params.startDate === undefined) {
133 | throw new Error("Please specify fromDate before using toDate");
134 | }
135 | return this.getExecutableQuery({ endDate: date });
136 | }
137 |
138 | /**
139 | * Configures query to fetch the price for the last few hours
140 | * @param hoursCount - Number of hours in the time range
141 | *
142 | * @returns query object
143 | */
144 | forLastHours(hoursCount: number): RedstoneQueryExecutable {
145 | const endDate = Date.now();
146 | return this.getExecutableQuery({
147 | endDate,
148 | startDate: endDate - hoursCount * 3600 * 1000,
149 | interval: 600 * 1000,
150 | });
151 | }
152 |
153 | /**
154 | * Configures query to fetch the price for the last few days
155 | * @param daysCount - Number of days in the time range
156 | *
157 | * @returns query object
158 | */
159 | forLastDays(daysCount: number): RedstoneQueryExecutable {
160 | const endDate = Date.now();
161 | return this.getExecutableQuery({
162 | endDate,
163 | startDate: endDate - daysCount * 24 * 3600 * 1000,
164 | interval: 3600 * 1000,
165 | });
166 | }
167 | }
168 |
169 | export class RedstoneQueryForSeveralSymbols extends RedstoneQueryForSingleOrSeveralSymbols<{
170 | [symbol: string]: PriceData;
171 | }> {
172 | constructor(params: QueryParams) {
173 | super(params);
174 | }
175 | }
176 |
177 | export class RedstoneQueryExecutable {
178 | private params: QueryParams;
179 |
180 | constructor(params = {}) {
181 | this.params = {
182 | symbols: [],
183 | ...params,
184 | };
185 | }
186 |
187 | /**
188 | * Executes the query
189 | *
190 | * @returns Promise resolving the query result (result type depends on the query)
191 | */
192 | async exec(
193 | redstoneApiConfig?: RedstoneApiConfigForQueryExec,
194 | ): Promise {
195 | const redstone = new RedstoneApi({
196 | ...redstoneApiConfig,
197 | defaultProvider: redstoneApiConfig?.provider,
198 | });
199 | const symbols = this.params.symbols;
200 | if (symbols.length > 0) {
201 | const symbolOrSymbols = symbols.length === 1 ? symbols[0] : symbols;
202 | const { startDate, endDate, date, interval } = this.params;
203 |
204 | if ([startDate, endDate, date].every((el) => el === undefined)) {
205 | // Fetch the latest price
206 | return (await redstone.getPrice(
207 | symbolOrSymbols as any,
208 | this.params as any,
209 | )) as any;
210 | } else {
211 | // Fetch the historical price
212 | if (
213 | startDate !== undefined &&
214 | endDate !== undefined &&
215 | interval === undefined
216 | ) {
217 | const diff = getTimeDiff(startDate, endDate);
218 | if (diff >= 24 * 3600 * 1000) {
219 | this.params.interval = 3600 * 1000;
220 | } else {
221 | this.params.interval = 1;
222 | }
223 | }
224 |
225 | return (await redstone.getHistoricalPrice(
226 | symbolOrSymbols as any,
227 | this.params as any,
228 | )) as any;
229 | }
230 | } else {
231 | // Fetch prices for all tokens
232 | return (await redstone.getAllPrices(
233 | this.params as GetPriceOptions,
234 | )) as any;
235 | }
236 | }
237 | }
238 |
239 | function getTimeDiff(
240 | date1: ConvertableToDate,
241 | date2: ConvertableToDate,
242 | ): number {
243 | const timestamp1 = new Date(date1).getTime();
244 | const timestamp2 = new Date(date2).getTime();
245 | return Math.abs(timestamp2 - timestamp1);
246 | }
247 |
248 | /**
249 | * Initializes and returns an empty query.
250 | * It doesn't support any params
251 | * @returns query object
252 | */
253 | const query = () => new RedstoneQuery();
254 |
255 | export default query;
256 |
--------------------------------------------------------------------------------
/src/signature-verifier.ts:
--------------------------------------------------------------------------------
1 | import deepSortObject from "deep-sort-object";
2 | import _ from "lodash";
3 | import ArweaveProxy from "./proxies/arweave-proxy";
4 | import { PriceDataWithSignature } from "./types";
5 |
6 | export default class SignatureVerifier {
7 | private arweaveProxy: ArweaveProxy;
8 |
9 | constructor(arweaveProxy: ArweaveProxy) {
10 | this.arweaveProxy = arweaveProxy;
11 | }
12 |
13 | public async assertValidSignature(
14 | _price: PriceDataWithSignature,
15 | ): Promise {
16 | // We've decided to disable signing with Arweave signature
17 | // and use only EVM compatible signatures instead
18 |
19 | // const signedData = this.getPriceSignedData(price);
20 | // const publicKey = price.providerPublicKey;
21 |
22 | // const validSignature = await this.arweaveProxy.verifySignature({
23 | // signedData,
24 | // signature: price.signature,
25 | // signerPublicKey: publicKey,
26 | // });
27 |
28 | // const addressFromPublicKey = await this.arweaveProxy.arweaveClient.wallets.ownerToAddress(
29 | // publicKey,
30 | // );
31 |
32 | // if (!validSignature) {
33 | // throw new Error("Signature verification failed for price: " + signedData);
34 | // }
35 |
36 | // if (addressFromPublicKey !== price.provider) {
37 | // throw new Error(
38 | // `Provider address doesn't match the public key.` +
39 | // ` Address: ${price.provider}.` +
40 | // ` Public key: ${publicKey}.`,
41 | // );
42 | // }
43 | }
44 |
45 | private getPriceSignedData(price: PriceDataWithSignature) {
46 | const priceWithPickedProps = _.pick(price, [
47 | "id",
48 | "source",
49 | "symbol",
50 | "timestamp",
51 | "version",
52 | "value",
53 | "permawebTx",
54 | "provider",
55 | ]);
56 |
57 | if (price.version === "3" || price.version?.includes(".")) {
58 | return JSON.stringify(deepSortObject(priceWithPickedProps));
59 | } else {
60 | return JSON.stringify(priceWithPickedProps);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | export type ConvertableToDate = Date | number | string;
2 |
3 | export interface RedstoneApiConfig {
4 | useCache?: boolean;
5 | defaultProvider?: string; // Name of default provider
6 | version?: string;
7 | verifySignature?: boolean;
8 | cacheApiUrl?: string;
9 | }
10 |
11 | export interface RedstoneApiConfigForQueryExec extends RedstoneApiConfig {
12 | provider?: string;
13 | }
14 |
15 | export interface PriceData {
16 | id?: string;
17 | symbol: string;
18 | provider: string;
19 | value: number;
20 | permawebTx: string;
21 | source?: string;
22 | timestamp: number;
23 | }
24 |
25 | export interface PriceDataWithSignature extends PriceData {
26 | signature: string;
27 | version?: string;
28 | providerPublicKey: string;
29 | }
30 |
31 | export interface ProviderNameToAddressMapping {
32 | [name: string]: string;
33 | }
34 |
35 | export interface GetPriceOptions {
36 | provider?: string;
37 | verifySignature?: boolean;
38 | }
39 |
40 | export interface GetHistoricalPriceOptions extends GetPriceOptions {
41 | date: ConvertableToDate;
42 | }
43 |
44 | export type GetHistoricalPriceForSingleTokenOptions =
45 | | GetHistoricalPriceInTimeRangeOptions
46 | | GetHistoricalPriceWithPaginationOptions;
47 |
48 | interface GetHistoricalPriceWithPaginationOptions extends GetPriceOptions {
49 | offset: number;
50 | limit: number;
51 | startDate?: ConvertableToDate;
52 | endDate?: ConvertableToDate;
53 | interval?: number; // ms
54 | }
55 |
56 | interface GetHistoricalPriceInTimeRangeOptions extends GetPriceOptions {
57 | startDate: ConvertableToDate;
58 | endDate: ConvertableToDate;
59 | interval: number; // ms
60 | }
61 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "target": "es5",
5 | "module": "commonjs",
6 | "declaration": true,
7 | "outDir": "./lib",
8 | "resolveJsonModule": true,
9 | "esModuleInterop": true
10 | },
11 | "include": ["src/**/*"],
12 | "exclude": ["node_modules", "__tests__", "examples", "coverage"]
13 | }
14 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["tslint:recommended", "tslint-config-prettier"],
3 | "rules": {
4 | "max-classes-per-file": [true, 5]
5 | }
6 | }
7 |
--------------------------------------------------------------------------------