UNPKG

@node-dlc/core

Version:
190 lines (162 loc) 5.41 kB
import { MessageType, PayoutFunctionV0, PolynomialPayoutCurvePiece, RoundingIntervalsV0, } from '@node-dlc/messaging'; import BigNumber from 'bignumber.js'; import { fromPrecision, getPrecision } from '../utils/Precision'; import { CETPayout, mergePayouts, splitIntoRanges } from './CETCalculator'; interface DlcPoint { outcome: BigNumber; payout: BigNumber; } export class PolynomialPayoutCurve { private readonly points: DlcPoint[]; private readonly slope: BigNumber; private readonly left: DlcPoint; private readonly right: DlcPoint; constructor(points: DlcPoint[]) { 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: bigint): BigNumber { const { left, slope } = this; const x = new BigNumber(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: BigNumber): bigint { const { left, slope } = this; const y = new BigNumber(Number(payout)); // 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(); return BigInt(outcome.toString()); } /** * Serializes PolynomialPayoutCurve to a PolynomialPayoutCurvePiece (for transport) * @returns A PolynomialPayoutCurvePiece */ toPayoutCurvePiece(): PolynomialPayoutCurvePiece { const { points } = this; const piece = new PolynomialPayoutCurvePiece(); piece.points = points.map((point) => { const eventOutcome = BigInt(point.outcome.toString()); const outcomePayout = BigInt(point.payout.toString()); const extraPrecision = 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: PolynomialPayoutCurve): boolean { 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: PolynomialPayoutCurvePiece, ): PolynomialPayoutCurve { const points = piece.points.map((point) => { const outcome = new BigNumber(point.eventOutcome.toString()); const payout = new BigNumber(point.outcomePayout.toString()).plus( 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: PayoutFunctionV0, totalCollateral: bigint, roundingIntervals: RoundingIntervalsV0, ): CETPayout[] { if (payoutFunction.pieces.length < 1) throw new Error('Must have at least one piece'); payoutFunction.pieces.forEach((piece) => { if ( piece.payoutCurvePiece.type !== MessageType.PolynomialPayoutCurvePiece ) throw new Error('Payout curve piece must be a polynomial'); }); const CETS: CETPayout[] = []; // 1. Add the first piece to the list const { payoutCurvePiece } = payoutFunction.pieces[0]; const curve = this.fromPayoutCurvePiece( payoutCurvePiece as PolynomialPayoutCurvePiece, ); CETS.push( ...splitIntoRanges( payoutFunction.endpoint0, payoutFunction.pieces[0].endpoint, payoutFunction.endpointPayout0, payoutFunction.pieces[0].endpointPayout, totalCollateral, curve, roundingIntervals.intervals, ), ); // 2. If there are subsequent pieces, add them to the list for (let i = 1; i < payoutFunction.pieces.length; i++) { const { payoutCurvePiece } = payoutFunction.pieces[i]; const curve = this.fromPayoutCurvePiece( payoutCurvePiece as PolynomialPayoutCurvePiece, ); CETS.push( ...splitIntoRanges( payoutFunction.pieces[i - 1].endpoint, payoutFunction.pieces[i].endpoint, payoutFunction.pieces[i - 1].endpointPayout, payoutFunction.pieces[i].endpointPayout, totalCollateral, curve, roundingIntervals.intervals, ), ); } return mergePayouts(CETS); } }