UNPKG

@pythnetwork/price-pusher

Version:
168 lines (167 loc) 6.35 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NearAccount = exports.NearPricePusher = exports.NearPriceListener = void 0; const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const interface_1 = require("../interface"); const near_api_js_1 = require("near-api-js"); const key_stores_1 = require("near-api-js/lib/key_stores"); class NearPriceListener extends interface_1.ChainPriceListener { account; logger; constructor(account, priceItems, logger, config) { super(config.pollingFrequency, priceItems); this.account = account; this.logger = logger; } async getOnChainPriceInfo(priceId) { try { const priceRaw = await this.account.getPriceUnsafe(priceId); this.logger.debug(`Polled a NEAR on chain price for feed ${this.priceIdToAlias.get(priceId)} (${priceId}) ${JSON.stringify(priceRaw)}.`); if (priceRaw) { return { conf: priceRaw.conf, price: priceRaw.price, publishTime: priceRaw.publish_time, }; } else { return undefined; } } catch (err) { this.logger.error(err, `Polling on-chain price for ${priceId} failed.:`); return undefined; } } } exports.NearPriceListener = NearPriceListener; class NearPricePusher { account; hermesClient; logger; constructor(account, hermesClient, logger) { this.account = account; this.hermesClient = hermesClient; this.logger = logger; } async updatePriceFeed(priceIds, pubTimesToPush) { if (priceIds.length === 0) { return; } if (priceIds.length !== pubTimesToPush.length) throw new Error("Invalid arguments"); let priceFeedUpdateData; try { priceFeedUpdateData = await this.getPriceFeedsUpdateData(priceIds); } catch (err) { this.logger.error(err, "getPriceFeedsUpdateData failed"); return; } for (const data of priceFeedUpdateData) { let updateFee; try { updateFee = await this.account.getUpdateFeeEstimate(data); this.logger.debug(`Update fee: ${updateFee}`); } catch (err) { this.logger.error(err, "getUpdateFeeEstimate failed"); continue; } try { const outcome = await this.account.updatePriceFeeds(data, updateFee); const failureMessages = []; const is_success = Object.values(outcome["receipts_outcome"]).reduce((is_success, receipt) => { if (Object.prototype.hasOwnProperty.call(receipt["outcome"]["status"], "Failure")) { failureMessages.push(receipt["outcome"]["status"]); return false; } return is_success; }, true); if (is_success) { this.logger.info({ hash: outcome["transaction"]["hash"] }, "updatePriceFeeds successful."); } else { this.logger.error({ failureMessages }, "updatePriceFeeds failed"); } } catch (err) { this.logger.error(err, "updatePriceFeeds failed"); } } } async getPriceFeedsUpdateData(priceIds) { const response = await this.hermesClient.getLatestPriceUpdates(priceIds, { encoding: "base64", }); return response.binary.data; } } exports.NearPricePusher = NearPricePusher; class NearAccount { pythAccountId; account; constructor(network, accountId, nodeUrl, privateKeyPath, pythAccountId) { this.pythAccountId = pythAccountId; const connection = this.getConnection(network, accountId, nodeUrl, privateKeyPath); this.account = new near_api_js_1.Account(connection, accountId); } async getPriceUnsafe(priceId) { return await this.account.viewFunction({ contractId: this.pythAccountId, methodName: "get_price_unsafe", args: { price_identifier: priceId, }, }); } async getUpdateFeeEstimate(data) { return await this.account.viewFunction({ contractId: this.pythAccountId, methodName: "get_update_fee_estimate", args: { data, }, }); } async updatePriceFeeds(data, updateFee) { return await this.account.functionCall({ contractId: this.pythAccountId, methodName: "update_price_feeds", args: { data, }, gas: "300000000000000", attachedDeposit: updateFee, }); } getConnection(network, accountId, nodeUrl, privateKeyPath) { const content = fs_1.default.readFileSync(privateKeyPath || path_1.default.join(os_1.default.homedir(), ".near-credentials", network, accountId + ".json")); const accountInfo = JSON.parse(content.toString()); let privateKey = accountInfo.private_key; if (!privateKey && accountInfo.secret_key) { privateKey = accountInfo.secret_key; } if (accountInfo.account_id && privateKey) { const keyPair = near_api_js_1.KeyPair.fromString(privateKey); const keyStore = new key_stores_1.InMemoryKeyStore(); keyStore.setKey(network, accountInfo.account_id, keyPair); return near_api_js_1.Connection.fromConfig({ networkId: network, provider: { type: "JsonRpcProvider", args: { url: nodeUrl } }, signer: { type: "InMemorySigner", keyStore }, jsvmAccountId: `jsvm.${network}`, }); } else { throw new Error("Invalid key file!"); } } } exports.NearAccount = NearAccount;