@pythnetwork/price-pusher
Version:
Pyth Price Pusher
114 lines (113 loc) • 5.48 kB
JavaScript
;
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;
}
}