UNPKG

@node-dlc/core

Version:
346 lines (303 loc) 10 kB
import { MessageType, PayoutFunction, PolynomialPayoutCurvePiece, RoundingIntervals, } from '@node-dlc/messaging'; import BigNumber from 'bignumber.js'; import { expect } from 'chai'; import { HyperbolaPayoutCurve } from '../../lib'; import { LinearPayout } from '../../lib/dlc/finance/LinearPayout'; import { PolynomialPayoutCurve } from '../../lib/dlc/PolynomialPayoutCurve'; import { fromPrecision } from '../../lib/utils/Precision'; describe('PolynomialPayoutCurve', () => { const points = [ { outcome: new BigNumber(1), payout: new BigNumber(1), }, { outcome: new BigNumber(2), payout: new BigNumber(3), }, ]; describe('#constructor', () => { it('should create a PolynomialPayoutCurve', () => { const curve = new PolynomialPayoutCurve(points); expect(curve).to.be.instanceOf(PolynomialPayoutCurve); }); it('should fail if not 2 points', () => { // 1 point expect( () => new PolynomialPayoutCurve([ { outcome: new BigNumber(1), payout: new BigNumber(1), }, ]), ).to.throw('Must have two points'); // 3 points expect( () => new PolynomialPayoutCurve([ { outcome: new BigNumber(1), payout: new BigNumber(1), }, { outcome: new BigNumber(2), payout: new BigNumber(2), }, { outcome: new BigNumber(3), payout: new BigNumber(3), }, ]), ).to.throw; }); }); describe('#getPayout', () => { it('should get the payout for an outcome', () => { const curve = new PolynomialPayoutCurve(points); const payout = curve.getPayout(BigInt(1)); expect(payout).to.deep.equal(new BigNumber(1)); }); it('should get the payout for an outcome', () => { const curve = new PolynomialPayoutCurve(points); const payout = curve.getPayout(BigInt(2)); expect(payout).to.deep.equal(new BigNumber(3)); }); }); describe('#getOutcomeForPayout', () => { it('should get the outcome for a payout', () => { const curve = new PolynomialPayoutCurve(points); const outcome = curve.getOutcomeForPayout(new BigNumber(1)); expect(outcome).to.deep.equal(BigInt(1)); }); it('should get the outcome for a payout', () => { const curve = new PolynomialPayoutCurve(points); const outcome = curve.getOutcomeForPayout(new BigNumber(3)); expect(outcome).to.deep.equal(BigInt(2)); }); }); describe('#toPayoutCurvePiece', () => { it('should serialize to a PolynomialPayoutCurvePiece', () => { const curve = new PolynomialPayoutCurve(points); const piece = curve.toPayoutCurvePiece(); expect(piece).to.deep.equal({ type: MessageType.PolynomialPayoutCurvePiece, payoutCurvePieceType: 0, // PayoutCurvePieceType.Polynomial points: [ { eventOutcome: BigInt(1), outcomePayout: BigInt(1), extraPrecision: 0, }, { eventOutcome: BigInt(2), outcomePayout: BigInt(3), extraPrecision: 0, }, ], }); }); }); describe('#fromPayoutCurvePiece', () => { const points = [ { eventOutcome: BigInt(1), outcomePayout: BigInt(1), extraPrecision: 0, }, { eventOutcome: BigInt(2), outcomePayout: BigInt(3), extraPrecision: 0, }, ]; it('should deserialize from a PolynomialPayoutCurvePiece', () => { const piece = new PolynomialPayoutCurvePiece(); piece.points = points; const curve = PolynomialPayoutCurve.fromPayoutCurvePiece(piece); expect(curve).to.be.instanceOf(PolynomialPayoutCurve); expect(curve['points']).to.deep.eq( points.map((p) => ({ outcome: new BigNumber(p.eventOutcome.toString()), payout: new BigNumber(p.outcomePayout.toString()).plus( fromPrecision(p.extraPrecision), ), })), ); }); }); describe('#equals', () => { it('should return true if the curves are equal', () => { const curve1 = new PolynomialPayoutCurve(points); const curve2 = new PolynomialPayoutCurve(points); expect(curve1.equals(curve2)).to.be.true; }); it('should return false if the curves are not equal', () => { const curve1 = new PolynomialPayoutCurve(points); const curve2 = new PolynomialPayoutCurve([ { outcome: new BigNumber(1), payout: new BigNumber(1), }, { outcome: new BigNumber(3), payout: new BigNumber(3), }, ]); expect(curve1.equals(curve2)).to.be.false; }); }); describe('#computePayouts', () => { it('should compute the correct payouts for simple interval', () => { const numDigits = 18; const oracleBase = 2; const minPayout = 40n; const maxPayout = 60n; const startOutcome = 40n; const endOutcome = 60n; const { payoutFunction } = LinearPayout.buildPayoutFunction( minPayout, maxPayout, startOutcome, endOutcome, oracleBase, numDigits, ); const intervals = [{ beginInterval: 0n, roundingMod: 1n }]; const roundingIntervals = new RoundingIntervals(); roundingIntervals.intervals = intervals; const payouts = PolynomialPayoutCurve.computePayouts( payoutFunction, maxPayout, roundingIntervals, ); const n = payouts.length - 1; const maxOutcome = BigInt( new BigNumber(oracleBase).pow(numDigits).minus(1).toString(10), ); expect(n).to.eq(20); // max loss CETS expect(payouts[0].indexFrom).to.eq(0n); expect(payouts[0].indexTo).to.eq(startOutcome); expect(payouts[0].payout).to.eq(minPayout); // payout curve CETS for (let i = 1; i < n - 1; ++i) { expect(payouts[i].indexFrom).to.eq(startOutcome + BigInt(i)); expect(payouts[i].indexTo).to.eq(startOutcome + BigInt(i)); expect(payouts[i].payout).to.eq(minPayout + BigInt(i)); } // max gain CETS expect(payouts[n].indexFrom).to.eq(endOutcome); expect(payouts[n].indexTo).to.eq(maxOutcome); expect(payouts[n].payout).to.eq(maxPayout); }); it('should compute the correct payouts for advanced interval', () => { const numDigits = 18; const oracleBase = 2; const minPayout = 40n; const maxPayout = 60n; const startOutcome = 40n; const endOutcome = 60n; const { payoutFunction } = LinearPayout.buildPayoutFunction( minPayout, maxPayout, startOutcome, endOutcome, oracleBase, numDigits, ); const intervals = [{ beginInterval: 0n, roundingMod: 2n }]; const roundingIntervals = new RoundingIntervals(); roundingIntervals.intervals = intervals; const payouts = PolynomialPayoutCurve.computePayouts( payoutFunction, maxPayout, roundingIntervals, ); const n = payouts.length - 1; const maxOutcome = BigInt( new BigNumber(oracleBase).pow(numDigits).minus(1).toString(10), ); expect(n).to.eq(10); // max loss CETS expect(payouts[0].indexFrom).to.eq(0n); expect(payouts[0].indexTo).to.eq(startOutcome); expect(payouts[0].payout).to.eq(minPayout); // payout curve CETS for (let i = 1; i < n; ++i) { expect(payouts[i].indexFrom).to.eq( startOutcome + 2n * BigInt(i - 1) + 1n, ); expect(payouts[i].indexTo).to.eq(startOutcome + 2n * BigInt(i)); expect(payouts[i].payout).to.eq(minPayout + 2n * BigInt(i)); } // max gain CETS expect(payouts[n].indexFrom).to.eq(endOutcome - 1n); expect(payouts[n].indexTo).to.eq(maxOutcome); expect(payouts[n].payout).to.eq(maxPayout); }); it('should fail if < 1 payout function pieces', () => { const maxPayout = 60n; const payoutFunction = new PayoutFunction(); payoutFunction.payoutFunctionPieces = []; payoutFunction.lastEndpoint = { eventOutcome: BigInt(0), outcomePayout: BigInt(0), extraPrecision: 0, }; const intervals = [{ beginInterval: 0n, roundingMod: 1n }]; const roundingIntervals = new RoundingIntervals(); roundingIntervals.intervals = intervals; expect(() => { PolynomialPayoutCurve.computePayouts( payoutFunction, maxPayout, roundingIntervals, ); }).to.throw('Must have at least one piece'); }); it('should fail if there is a non-polynomial payout curve piece', () => { const maxPayout = 60n; const hyperbola = new HyperbolaPayoutCurve( new BigNumber(1), new BigNumber(0), new BigNumber(0), new BigNumber(500000), new BigNumber(0), new BigNumber(0), true, ).toPayoutCurvePiece(); const payoutFunction = new PayoutFunction(); payoutFunction.payoutFunctionPieces = [ { endPoint: { eventOutcome: 2n, outcomePayout: 2n, extraPrecision: 0, }, payoutCurvePiece: hyperbola, }, ]; payoutFunction.lastEndpoint = { eventOutcome: 0n, outcomePayout: 0n, extraPrecision: 0, }; const intervals = [{ beginInterval: 0n, roundingMod: 1n }]; const roundingIntervals = new RoundingIntervals(); roundingIntervals.intervals = intervals; expect(() => { PolynomialPayoutCurve.computePayouts( payoutFunction, maxPayout, roundingIntervals, ); }).to.throw('Payout curve piece must be a polynomial'); }); }); });