UNPKG

@pythnetwork/price-pusher

Version:
114 lines (113 loc) 5.48 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UpdateCondition = void 0; exports.readPriceConfigFile = readPriceConfigFile; exports.shouldUpdate = shouldUpdate; const joi_1 = __importDefault(require("joi")); const yaml_1 = __importDefault(require("yaml")); const fs_1 = __importDefault(require("fs")); const utils_1 = require("./utils"); const PriceConfigFileSchema = joi_1.default.array() .items(joi_1.default.object({ alias: joi_1.default.string().required(), id: joi_1.default.string() .regex(/^(0x)?[a-f0-9]{64}$/) .required(), time_difference: joi_1.default.number().required(), price_deviation: joi_1.default.number().required(), confidence_ratio: joi_1.default.number().required(), early_update: joi_1.default.object({ time_difference: joi_1.default.number().optional(), price_deviation: joi_1.default.number().optional(), confidence_ratio: joi_1.default.number().optional(), }).optional(), })) .unique("id") .unique("alias") .required(); function readPriceConfigFile(path) { const priceConfigs = yaml_1.default.parse(fs_1.default.readFileSync(path, "utf-8")); const validationResult = PriceConfigFileSchema.validate(priceConfigs); if (validationResult.error !== undefined) { throw validationResult.error; } return priceConfigs.map((priceConfigRaw) => { const priceConfig = { alias: priceConfigRaw.alias, id: (0, utils_1.removeLeading0x)(priceConfigRaw.id), timeDifference: priceConfigRaw.time_difference, priceDeviation: priceConfigRaw.price_deviation, confidenceRatio: priceConfigRaw.confidence_ratio, customEarlyUpdate: priceConfigRaw.early_update !== undefined, earlyUpdateTimeDifference: priceConfigRaw.early_update?.time_difference, earlyUpdatePriceDeviation: priceConfigRaw.early_update?.price_deviation, earlyUpdateConfidenceRatio: priceConfigRaw.early_update?.confidence_ratio, }; return priceConfig; }); } var UpdateCondition; (function (UpdateCondition) { // This price feed must be updated UpdateCondition[UpdateCondition["YES"] = 0] = "YES"; // This price feed may be updated as part of a larger batch UpdateCondition[UpdateCondition["EARLY"] = 1] = "EARLY"; // This price feed shouldn't be updated UpdateCondition[UpdateCondition["NO"] = 2] = "NO"; })(UpdateCondition || (exports.UpdateCondition = UpdateCondition = {})); /** * Checks whether on-chain price needs to be updated with the latest pyth price information. * * @param priceConfig Config of the price feed to check * @returns True if the on-chain price needs to be updated. */ function shouldUpdate(priceConfig, sourceLatestPrice, targetLatestPrice, logger) { const priceId = priceConfig.id; // There is no price to update the target with. if (sourceLatestPrice === undefined) { return UpdateCondition.YES; } // It means that price never existed there. So we should push the latest price feed. if (targetLatestPrice === undefined) { logger.info(`${priceConfig.alias} (${priceId}) is not available on the target network. Pushing the price.`); return UpdateCondition.YES; } // The current price is not newer than the price onchain if (sourceLatestPrice.publishTime < targetLatestPrice.publishTime) { return UpdateCondition.NO; } const timeDifference = sourceLatestPrice.publishTime - targetLatestPrice.publishTime; const priceDeviationPct = (Math.abs(Number(sourceLatestPrice.price) - Number(targetLatestPrice.price)) / Number(targetLatestPrice.price)) * 100; const confidenceRatioPct = Math.abs((Number(sourceLatestPrice.conf) / Number(sourceLatestPrice.price)) * 100); logger.info({ sourcePrice: sourceLatestPrice, targetPrice: targetLatestPrice, symbol: priceConfig.alias, }, `Analyzing price ${priceConfig.alias} (${priceId}). ` + `Time difference: ${timeDifference} (< ${priceConfig.timeDifference}? / early: < ${priceConfig.earlyUpdateTimeDifference}) OR ` + `Price deviation: ${priceDeviationPct.toFixed(5)}% (< ${priceConfig.priceDeviation}%? / early: < ${priceConfig.earlyUpdatePriceDeviation}%?) OR ` + `Confidence ratio: ${confidenceRatioPct.toFixed(5)}% (< ${priceConfig.confidenceRatio}%? / early: < ${priceConfig.earlyUpdatePriceDeviation}%?)`); if (timeDifference >= priceConfig.timeDifference || priceDeviationPct >= priceConfig.priceDeviation || confidenceRatioPct >= priceConfig.confidenceRatio) { return UpdateCondition.YES; } else if (priceConfig.customEarlyUpdate === undefined || !priceConfig.customEarlyUpdate || (priceConfig.earlyUpdateTimeDifference !== undefined && timeDifference >= priceConfig.earlyUpdateTimeDifference) || (priceConfig.earlyUpdatePriceDeviation !== undefined && priceDeviationPct >= priceConfig.earlyUpdatePriceDeviation) || (priceConfig.earlyUpdateConfidenceRatio !== undefined && confidenceRatioPct >= priceConfig.earlyUpdateConfidenceRatio)) { return UpdateCondition.EARLY; } else { return UpdateCondition.NO; } }