UNPKG

redstone-api

Version:

Javascript library for fetching trusted token pricing data from Redstone data ecosystem

461 lines (460 loc) 24.2 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var lodash_1 = __importDefault(require("lodash")); var arweave_proxy_1 = __importDefault(require("./proxies/arweave-proxy")); var cache_proxy_1 = __importDefault(require("./proxies/cache-proxy")); var signature_verifier_1 = __importDefault(require("./signature-verifier")); var price_not_found_1 = __importDefault(require("./errors/price-not-found")); var index_1 = __importDefault(require("./config/index")); var REDSTONE_API_DEFAULTS = { defaultProvider: "redstone", useCache: true, verifySignature: false, }; // Providers list is sorted by priority (redstone-rapid has the highest priority) var PROVIDERS_SORTED_BY_PRIORITY = [ "redstone-rapid", "redstone-stocks", "redstone" ]; var RedstoneApi = /** @class */ (function () { function RedstoneApi(redstoneConfig) { if (redstoneConfig === void 0) { redstoneConfig = {}; } this.arweaveProxy = new arweave_proxy_1.default(); this.cacheProxy = new cache_proxy_1.default(lodash_1.default.defaultTo(redstoneConfig.cacheApiUrl, index_1.default.cacheApiUrl)); this.signatureVerifier = new signature_verifier_1.default(this.arweaveProxy); this.version = lodash_1.default.defaultTo(redstoneConfig.version, index_1.default.version); this.verifySignature = lodash_1.default.defaultTo(redstoneConfig.verifySignature, REDSTONE_API_DEFAULTS.verifySignature); this.defaultProvider = lodash_1.default.defaultTo(redstoneConfig.defaultProvider, REDSTONE_API_DEFAULTS.defaultProvider); this.useCache = lodash_1.default.defaultTo(redstoneConfig.useCache, REDSTONE_API_DEFAULTS.useCache); } RedstoneApi.prototype.setCacheApiUrl = function (cacheApiUrl) { this.cacheProxy.setCacheApiUrl(cacheApiUrl); }; RedstoneApi.prototype.getPrice = function (symbolOrSymbols, opts) { if (opts === void 0) { opts = {}; } return __awaiter(this, void 0, void 0, function () { var shouldVerifySignature, symbols, provider, symbol, provider; return __generator(this, function (_a) { switch (_a.label) { case 0: shouldVerifySignature = lodash_1.default.defaultTo(opts.verifySignature, this.verifySignature); if (!lodash_1.default.isArray(symbolOrSymbols)) return [3 /*break*/, 2]; symbols = symbolOrSymbols; provider = this.getProviderForSymbols(symbols, opts.provider); return [4 /*yield*/, this.getPriceForManyTokens({ symbols: symbols, provider: provider, shouldVerifySignature: shouldVerifySignature, })]; case 1: return [2 /*return*/, _a.sent()]; case 2: if (!(typeof symbolOrSymbols === "string")) return [3 /*break*/, 4]; symbol = symbolOrSymbols; provider = this.getProviderForSymbol(symbol, opts.provider); return [4 /*yield*/, this.getLatestPriceForOneToken({ symbol: symbol, provider: provider, shouldVerifySignature: shouldVerifySignature, })]; case 3: return [2 /*return*/, _a.sent()]; case 4: return [2 /*return*/]; } }); }); }; RedstoneApi.prototype.getHistoricalPrice = function (symbolOrSymbols, opts) { return __awaiter(this, void 0, void 0, function () { var shouldVerifySignature, symbols, provider, symbol, provider; return __generator(this, function (_a) { switch (_a.label) { case 0: shouldVerifySignature = lodash_1.default.defaultTo(opts.verifySignature, this.verifySignature); if (!lodash_1.default.isArray(symbolOrSymbols)) return [3 /*break*/, 2]; symbols = symbolOrSymbols; provider = this.getProviderForSymbols(symbols, opts.provider); return [4 /*yield*/, this.getPriceForManyTokens({ symbols: symbols, timestamp: getTimestamp(opts.date), provider: provider, shouldVerifySignature: shouldVerifySignature, })]; case 1: return [2 /*return*/, _a.sent()]; case 2: if (!(typeof symbolOrSymbols === "string")) return [3 /*break*/, 6]; symbol = symbolOrSymbols; provider = this.getProviderForSymbol(symbol, opts.provider); if (!(opts.interval !== undefined || opts.limit !== undefined)) return [3 /*break*/, 4]; return [4 /*yield*/, this.getHistoricalPricesForOneSymbol({ symbol: symbol, fromTimestamp: getTimestamp(opts.startDate), toTimestamp: getTimestamp(opts.endDate), interval: opts.interval, offset: opts.offset, limit: opts.limit, provider: provider, shouldVerifySignature: shouldVerifySignature, })]; case 3: return [2 /*return*/, _a.sent()]; case 4: return [4 /*yield*/, this.getHistoricalPriceForOneSymbol({ symbol: symbol, timestamp: getTimestamp(opts.date), provider: provider, shouldVerifySignature: shouldVerifySignature, })]; case 5: return [2 /*return*/, _a.sent()]; case 6: return [2 /*return*/]; } }); }); }; /** * Returns the latest price for all the supported symbols * * @param opts - Optioanl options object. * * opts.provider: provider name (string) * * opts.verifySignature: enable signature verification (boolean) * @returns The latest price for all the supported tokens * */ RedstoneApi.prototype.getAllPrices = function (opts) { if (opts === void 0) { opts = {}; } return __awaiter(this, void 0, void 0, function () { var provider, pricesObj, _i, _a, symbol; return __generator(this, function (_b) { switch (_b.label) { case 0: provider = lodash_1.default.defaultTo(opts.provider, this.defaultProvider); if (!this.useCache) return [3 /*break*/, 2]; return [4 /*yield*/, this.cacheProxy.getPriceForManyTokens({ provider: provider, })]; case 1: pricesObj = _b.sent(); // Signature verification if (lodash_1.default.defaultTo(opts.verifySignature, this.verifySignature)) { for (_i = 0, _a = lodash_1.default.keys(pricesObj); _i < _a.length; _i++) { symbol = _a[_i]; this.signatureVerifier.assertValidSignature(pricesObj[symbol]); } } return [2 /*return*/, convertPricesToUserFacingFormat(pricesObj)]; case 2: return [4 /*yield*/, this.getPricesFromArweave(provider)]; case 3: return [2 /*return*/, _b.sent()]; } }); }); }; RedstoneApi.prototype.getLatestPriceForOneToken = function (args) { return __awaiter(this, void 0, void 0, function () { var price, price, prices, priceForSymbol; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.useCache) return [3 /*break*/, 4]; return [4 /*yield*/, this.cacheProxy.getPrice(lodash_1.default.pick(args, ["symbol", "provider"]))]; case 1: price = _a.sent(); if (!(args.shouldVerifySignature && price !== undefined)) return [3 /*break*/, 3]; return [4 /*yield*/, this.signatureVerifier.assertValidSignature(price)]; case 2: _a.sent(); _a.label = 3; case 3: if (price === undefined) { throw new price_not_found_1.default(args.symbol); } return [2 /*return*/, convertToUserFacingFormat(price)]; case 4: if (!(args.symbol === "AR")) return [3 /*break*/, 6]; return [4 /*yield*/, this.tryToGetPriceFromGQL(lodash_1.default.pick(args, ["provider", "symbol"]))]; case 5: price = _a.sent(); if (price !== undefined) { return [2 /*return*/, convertToUserFacingFormat(price)]; } _a.label = 6; case 6: return [4 /*yield*/, this.getPricesFromArweave(args.provider)]; case 7: prices = _a.sent(); priceForSymbol = prices[args.symbol]; return [2 /*return*/, convertToUserFacingFormat(priceForSymbol)]; } }); }); }; RedstoneApi.prototype.getPriceForManyTokens = function (args) { return __awaiter(this, void 0, void 0, function () { var pricesObj, _i, _a, symbol, allPrices, pricesObj; return __generator(this, function (_b) { switch (_b.label) { case 0: if (!this.useCache) return [3 /*break*/, 2]; return [4 /*yield*/, this.cacheProxy.getPriceForManyTokens(lodash_1.default.pick(args, ["symbols", "provider", "timestamp"]))]; case 1: pricesObj = _b.sent(); // Signature verification if (args.shouldVerifySignature) { for (_i = 0, _a = lodash_1.default.keys(pricesObj); _i < _a.length; _i++) { symbol = _a[_i]; this.signatureVerifier.assertValidSignature(pricesObj[symbol]); } } return [2 /*return*/, convertPricesToUserFacingFormat(pricesObj)]; case 2: if (args.timestamp !== undefined) { throw new Error("Getting historical prices from arweave is not supported"); } return [4 /*yield*/, this.getPricesFromArweave(args.provider)]; case 3: allPrices = _b.sent(); pricesObj = lodash_1.default.pick(allPrices, args.symbols); return [2 /*return*/, convertPricesToUserFacingFormat(pricesObj)]; } }); }); }; RedstoneApi.prototype.getPricesFromArweave = function (provider) { return __awaiter(this, void 0, void 0, function () { var address, gqlResponse, prices, pricesObj, _i, prices_1, price; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.arweaveProxy.getProviderDetails(provider)]; case 1: address = (_a.sent()).address; return [4 /*yield*/, this.arweaveProxy.findPricesInGraphQL({ type: "data", providerAddress: address, version: this.version, })]; case 2: gqlResponse = _a.sent(); if (gqlResponse === undefined) { return [2 /*return*/, {}]; } return [4 /*yield*/, this.arweaveProxy.getTxDataById(gqlResponse.permawebTx, { parseJSON: true })]; case 3: prices = _a.sent(); pricesObj = {}; for (_i = 0, prices_1 = prices; _i < prices_1.length; _i++) { price = prices_1[_i]; pricesObj[price.symbol] = __assign(__assign({}, price), { provider: address, permawebTx: gqlResponse.permawebTx }); } return [2 /*return*/, convertPricesToUserFacingFormat(pricesObj)]; } }); }); }; RedstoneApi.prototype.getHistoricalPriceForOneSymbol = function (args) { return __awaiter(this, void 0, void 0, function () { var price; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.useCache) return [3 /*break*/, 4]; return [4 /*yield*/, this.cacheProxy.getPrice(lodash_1.default.pick(args, ["symbol", "provider", "timestamp"]))]; case 1: price = _a.sent(); if (!(args.shouldVerifySignature && price !== undefined)) return [3 /*break*/, 3]; return [4 /*yield*/, this.signatureVerifier.assertValidSignature(price)]; case 2: _a.sent(); _a.label = 3; case 3: if (price === undefined) { throw new price_not_found_1.default(args.symbol); } return [2 /*return*/, convertToUserFacingFormat(price)]; case 4: // TODO: we cannot query ArGQL with timestamp comparators like timestamp_gt // But in future we can think of querying based on block numbers throw new Error("Fetching historical price from arweave is not supported"); } }); }); }; RedstoneApi.prototype.tryToGetPriceFromGQL = function (args) { return __awaiter(this, void 0, void 0, function () { var address, response; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.arweaveProxy.getProviderDetails(args.provider)]; case 1: address = (_a.sent()).address; return [4 /*yield*/, this.arweaveProxy.findPricesInGraphQL({ type: "data", providerAddress: address, version: this.version, })]; case 2: response = _a.sent(); if (response === undefined || response.tags[args.symbol] === undefined) { throw new price_not_found_1.default(args.symbol); } else { return [2 /*return*/, { symbol: args.symbol, value: Number(response.tags[args.symbol]), permawebTx: response.permawebTx, timestamp: Number(response.tags.timestamp), provider: address, }]; } return [2 /*return*/]; } }); }); }; RedstoneApi.prototype.getHistoricalPricesForOneSymbol = function (args) { return __awaiter(this, void 0, void 0, function () { var prices, _i, prices_2, price; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.useCache) return [3 /*break*/, 6]; return [4 /*yield*/, this.cacheProxy.getManyPrices(lodash_1.default.pick(args, [ "symbol", "provider", "fromTimestamp", "toTimestamp", "interval", "offset", "limit", ]))]; case 1: prices = _a.sent(); if (!args.shouldVerifySignature) return [3 /*break*/, 5]; _i = 0, prices_2 = prices; _a.label = 2; case 2: if (!(_i < prices_2.length)) return [3 /*break*/, 5]; price = prices_2[_i]; return [4 /*yield*/, this.signatureVerifier.assertValidSignature(price)]; case 3: _a.sent(); _a.label = 4; case 4: _i++; return [3 /*break*/, 2]; case 5: return [2 /*return*/, prices.map(convertToUserFacingFormat)]; case 6: // TODO: we cannot query ArGQL with timestamp comparators like timestamp_gt // But in future we can think of querying based on block numbers throw new Error("Fetching historical prices from arweave is not supported"); } }); }); }; RedstoneApi.prototype.getProviderForSymbol = function (symbol, provider) { return this.getProviderForSymbols([symbol], provider); }; RedstoneApi.prototype.getProviderForSymbols = function (symbols, passedProvider) { if (passedProvider !== undefined) { return passedProvider; } else { // Calculating a list of providers which support all symbols in the list var possibleProviders = Array.from(PROVIDERS_SORTED_BY_PRIORITY); for (var _i = 0, symbols_1 = symbols; _i < symbols_1.length; _i++) { var symbol = symbols_1[_i]; var providersForToken = index_1.default.tokens[symbol]; if (providersForToken && Array.isArray(providersForToken)) { var _loop_1 = function (provider) { // If any of symbols doesn't support the provider // it can not be used if (!providersForToken.includes(provider)) { possibleProviders = possibleProviders.filter(function (p) { return p !== provider; }); } }; for (var _a = 0, possibleProviders_1 = possibleProviders; _a < possibleProviders_1.length; _a++) { var provider = possibleProviders_1[_a]; _loop_1(provider); } } else { // If any symbol has no supported providers in redstone-node config // we break the loop and return the default provider possibleProviders = []; break; } } // Returning the best possible provider if (possibleProviders.length > 0) { return possibleProviders[0]; } else { return this.defaultProvider; } } }; return RedstoneApi; }()); exports.default = RedstoneApi; function getTimestamp(date) { if (lodash_1.default.isUndefined(date)) { return undefined; } return new Date(date).getTime(); } function convertToUserFacingFormat(price) { var result = lodash_1.default.omit(price, ["version", "signature", "providerPublicKey"]); return result; } function convertPricesToUserFacingFormat(prices) { var userFacingPricesObj = {}; for (var _i = 0, _a = lodash_1.default.keys(prices); _i < _a.length; _i++) { var symbol = _a[_i]; userFacingPricesObj[symbol] = convertToUserFacingFormat(prices[symbol]); } return userFacingPricesObj; }