├── .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 | [![License](https://img.shields.io/badge/license-MIT-green)](https://choosealicense.com/licenses/mit/) 4 | [![Github activity](https://img.shields.io/github/commit-activity/m/redstone-finance/redstone-api)](https://github.com/redstone-finance/redstone-api) 5 | [![Discord](https://img.shields.io/discord/786251205008949258?logo=discord)](https://discord.gg/2CT6hN6C) 6 | [![NPM](https://img.shields.io/npm/v/redstone-api)](https://www.npmjs.com/package/redstone-api) 7 | 8 | [![Twitter](https://img.shields.io/twitter/follow/redstone_defi?style=flat&logo=twitter)](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 | ![redstone fluent interface example](docs/redstone-query-example.gif) 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 | --------------------------------------------------------------------------------