@pythnetwork/price-pusher
Version:
Pyth Price Pusher
168 lines (167 loc) • 6.35 kB
JavaScript
;
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;