UNPKG

@node-dlc/core

Version:
173 lines 8.69 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PolynomialPayoutCurve = void 0; const messaging_1 = require("@node-dlc/messaging"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const Precision_1 = require("../utils/Precision"); const CETCalculator_1 = require("./CETCalculator"); class PolynomialPayoutCurve { constructor(points) { if (points.length !== 2) throw new Error('Must have two points'); this.points = points; this.left = points[0]; this.right = points[points.length - 1]; // m = (y2 - y1) / (x2 - x1) this.slope = this.right.payout .minus(this.left.payout) .dividedBy(this.right.outcome.minus(this.left.outcome)); } /** * Get the payout for a given outcome * @param outcome The outcome to get the payout for * @returns The payout for the outcome */ getPayout(outcome) { const { left, slope } = this; const x = new bignumber_js_1.default(Number(outcome)); // y = mx + b const payout = slope.times(x.minus(left.outcome)).plus(left.payout); return payout; } /** * Get the outcome for a given payout * @param payout The payout to get the outcome for * @returns The outcome for the payout */ getOutcomeForPayout(payout) { const { left, right, slope } = this; const y = new bignumber_js_1.default(Number(payout)); // Handle constant payout curve (slope = 0) if (slope.isZero()) { // For constant curves, if the payout matches, return any outcome in the range // If it doesn't match, this is an invalid request if (y.eq(left.payout)) { return BigInt(left.outcome.toString()); } else { // For constant curves, if payout doesn't match, return left outcome // This prevents Infinity errors in calculations return BigInt(left.outcome.toString()); } } // Find the x value for the given y // slope = (y2 - y1) / (x2 - x1) // x1 = (y2 - y1) / slope + x2 const outcome = y .minus(left.payout) .dividedBy(slope) .plus(left.outcome) .integerValue(); // Clamp the outcome to the valid range const clampedOutcome = bignumber_js_1.default.max(left.outcome, bignumber_js_1.default.min(outcome, right.outcome)); return BigInt(clampedOutcome.toString()); } /** * Serializes PolynomialPayoutCurve to a PolynomialPayoutCurvePiece (for transport) * @returns A PolynomialPayoutCurvePiece */ toPayoutCurvePiece() { const { points } = this; const piece = new messaging_1.PolynomialPayoutCurvePiece(); piece.points = points.map((point) => { const eventOutcome = BigInt(point.outcome.toString()); const outcomePayout = BigInt(point.payout.toString()); const extraPrecision = (0, Precision_1.getPrecision)(point.payout); return { eventOutcome, outcomePayout, extraPrecision }; }); return piece; } /** * Determine if the payout curve is equal to another * @param curve A PolynomialPayoutCurve * @returns True if the curves are the same */ equals(curve) { return this.points.every((point, i) => { const otherPoint = curve.points[i]; return (point.outcome.eq(otherPoint.outcome) && point.payout.eq(otherPoint.payout)); }); } /** * Creates a PolynomialPayoutCurve from a PolynomialPayoutCurvePiece * @param piece * @returns A PolynomialPayoutCurve */ static fromPayoutCurvePiece(piece) { const points = piece.points.map((point) => { const outcome = new bignumber_js_1.default(point.eventOutcome.toString()); const payout = new bignumber_js_1.default(point.outcomePayout.toString()).plus((0, Precision_1.fromPrecision)(point.extraPrecision)); return { outcome, payout }; }); return new PolynomialPayoutCurve(points); } /** * Computes all CETs for a given payout curve * @param payoutFunction The payout function * @param totalCollateral The total collateral * @param roundingIntervals The rounding intervals * @returns A list of CETs */ static computePayouts(payoutFunction, totalCollateral, roundingIntervals) { if (payoutFunction.payoutFunctionPieces.length < 1) throw new Error('Must have at least one piece'); payoutFunction.payoutFunctionPieces.forEach((piece) => { if (piece.payoutCurvePiece.payoutCurvePieceType !== messaging_1.PayoutCurvePieceType.Polynomial && piece.payoutCurvePiece.type !== messaging_1.MessageType.PolynomialPayoutCurvePiece) throw new Error('Payout curve piece must be a polynomial'); }); const CETS = []; // 1. Add the first piece to the list const { payoutCurvePiece } = payoutFunction.payoutFunctionPieces[0]; const curve = this.fromPayoutCurvePiece(payoutCurvePiece); // For the first piece, start from 0 and go to the first endpoint const firstPiece = payoutFunction.payoutFunctionPieces[0]; // Calculate the start payout by evaluating the curve at outcome 0 const startPayout = curve.getPayout(BigInt(0)); const startPayoutBigInt = BigInt(startPayout.integerValue().toString()); // Only add ranges if there's actually a range to cover if (firstPiece.endPoint.eventOutcome > 0) { CETS.push(...(0, CETCalculator_1.splitIntoRanges)(BigInt(0), // Start from 0 firstPiece.endPoint.eventOutcome, startPayoutBigInt, // Start payout calculated from curve firstPiece.endPoint.outcomePayout, // End payout from endpoint totalCollateral, curve, roundingIntervals.intervals)); } // 2. If there are subsequent pieces, add them to the list for (let i = 1; i < payoutFunction.payoutFunctionPieces.length; i++) { const { payoutCurvePiece } = payoutFunction.payoutFunctionPieces[i]; const curve = this.fromPayoutCurvePiece(payoutCurvePiece); CETS.push(...(0, CETCalculator_1.splitIntoRanges)(payoutFunction.payoutFunctionPieces[i - 1].endPoint.eventOutcome, payoutFunction.payoutFunctionPieces[i].endPoint.eventOutcome, payoutFunction.payoutFunctionPieces[i - 1].endPoint.outcomePayout, payoutFunction.payoutFunctionPieces[i].endPoint.outcomePayout, totalCollateral, curve, roundingIntervals.intervals)); } // 3. Handle the final range from the last piece to lastEndpoint if it exists if (payoutFunction.lastEndpoint && payoutFunction.payoutFunctionPieces.length > 0) { const lastPieceIndex = payoutFunction.payoutFunctionPieces.length - 1; const lastPiece = payoutFunction.payoutFunctionPieces[lastPieceIndex]; // Check if there's a range to cover if (payoutFunction.lastEndpoint.eventOutcome > lastPiece.endPoint.eventOutcome) { // For the final range, we'll assume a constant payout from the last piece endpoint to lastEndpoint // This is a common pattern for DLC payout functions const finalCurve = new PolynomialPayoutCurve([ { outcome: new bignumber_js_1.default(lastPiece.endPoint.eventOutcome.toString()), payout: new bignumber_js_1.default(lastPiece.endPoint.outcomePayout.toString()), }, { outcome: new bignumber_js_1.default(payoutFunction.lastEndpoint.eventOutcome.toString()), payout: new bignumber_js_1.default(payoutFunction.lastEndpoint.outcomePayout.toString()), }, ]); CETS.push(...(0, CETCalculator_1.splitIntoRanges)(lastPiece.endPoint.eventOutcome, payoutFunction.lastEndpoint.eventOutcome, lastPiece.endPoint.outcomePayout, payoutFunction.lastEndpoint.outcomePayout, totalCollateral, finalCurve, roundingIntervals.intervals)); } } return (0, CETCalculator_1.mergePayouts)(CETS); } } exports.PolynomialPayoutCurve = PolynomialPayoutCurve; //# sourceMappingURL=PolynomialPayoutCurve.js.map