@node-dlc/messaging
Version:
DLC Messaging Protocol
420 lines (362 loc) • 17.9 kB
text/typescript
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {
EnumeratedDescriptor,
NumericalDescriptor,
} from '../../lib/messages/ContractDescriptor';
import {
ContractInfo,
SingleContractInfo,
} from '../../lib/messages/ContractInfo';
import {
DigitDecompositionEventDescriptor,
EnumEventDescriptor,
} from '../../lib/messages/EventDescriptor';
import { OracleAnnouncement } from '../../lib/messages/OracleAnnouncement';
import { OracleEvent } from '../../lib/messages/OracleEvent';
import { SingleOracleInfo } from '../../lib/messages/OracleInfo';
import { HyperbolaPayoutCurvePiece } from '../../lib/messages/PayoutCurvePiece';
import { PayoutFunction } from '../../lib/messages/PayoutFunction';
import { RoundingIntervals } from '../../lib/messages/RoundingIntervals';
import { F64 } from '../../lib/serialize/F64';
chai.use(chaiAsPromised);
const expect = chai.expect;
describe('ContractInfo', () => {
describe('SingleContractInfo', () => {
describe('serialize', () => {
it('serializes with enumerated contract descriptor', () => {
const instance = new SingleContractInfo();
instance.totalCollateral = BigInt(200000000);
// Create contract descriptor programmatically to match new format
const contractDescriptor = new EnumeratedDescriptor();
contractDescriptor.outcomes = [
{ outcome: 'outcome1', localPayout: BigInt(0) },
{ outcome: 'outcome2', localPayout: BigInt(150000000) },
{ outcome: 'outcome3', localPayout: BigInt(200000000) },
];
instance.contractDescriptor = contractDescriptor;
// Create oracle info programmatically to match new format
const oracleAnnouncement = new OracleAnnouncement();
oracleAnnouncement.announcementSig = Buffer.from(
'fab22628f6e2602e1671c286a2f63a9246794008627a1749639217f4214cb4a9494c93d1a852221080f44f697adb4355df59eb339f6ba0f9b01ba661a8b108d4',
'hex',
);
oracleAnnouncement.oraclePublicKey = Buffer.from(
'da078bbb1d34e7729e38e2ae34236e776da121af442626fa31e31ae55a279a0b',
'hex',
);
const oracleEvent = new OracleEvent();
oracleEvent.oracleNonces = [
Buffer.from(
'3cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b',
'hex',
),
];
oracleEvent.eventMaturityEpoch = 0;
const eventDescriptor = new EnumEventDescriptor();
eventDescriptor.outcomes = ['dummy1', 'dummy2'];
oracleEvent.eventDescriptor = eventDescriptor;
oracleEvent.eventId = 'dummy';
oracleAnnouncement.oracleEvent = oracleEvent;
const oracleInfo = new SingleOracleInfo();
oracleInfo.announcement = oracleAnnouncement;
instance.oracleInfo = oracleInfo;
// Test that it serializes without errors
const serialized = instance.serialize();
expect(serialized).to.be.instanceof(Buffer);
expect(serialized.length).to.be.greaterThan(0);
});
it('serializes with numerical contract descriptor', () => {
const instance = new SingleContractInfo();
instance.totalCollateral = BigInt(969482);
// Create numerical contract descriptor with payout function
const contractDescriptor = new NumericalDescriptor();
contractDescriptor.numDigits = 18;
// Create a proper PayoutFunction with the new structure
const payoutFunction = new PayoutFunction();
// Create hyperbola payout curve piece
const hyperbolaPayoutCurvePiece = new HyperbolaPayoutCurvePiece();
hyperbolaPayoutCurvePiece.usePositivePiece = true;
hyperbolaPayoutCurvePiece.translateOutcome = F64.fromNumber(0);
hyperbolaPayoutCurvePiece.translatePayout = F64.fromNumber(30518);
hyperbolaPayoutCurvePiece.a = F64.fromNumber(1);
hyperbolaPayoutCurvePiece.b = F64.fromNumber(0);
hyperbolaPayoutCurvePiece.c = F64.fromNumber(0);
hyperbolaPayoutCurvePiece.d = F64.fromNumber(4000000000);
payoutFunction.payoutFunctionPieces = [
{
endPoint: {
eventOutcome: BigInt(131071),
outcomePayout: BigInt(0),
extraPrecision: 0,
},
payoutCurvePiece: hyperbolaPayoutCurvePiece,
},
];
payoutFunction.lastEndpoint = {
eventOutcome: BigInt(0),
outcomePayout: BigInt(969482),
extraPrecision: 0,
};
contractDescriptor.payoutFunction = payoutFunction;
// Create rounding intervals
const roundingIntervals = new RoundingIntervals();
roundingIntervals.intervals = [
{
beginInterval: BigInt(0),
roundingMod: BigInt(500),
},
];
contractDescriptor.roundingIntervals = roundingIntervals;
instance.contractDescriptor = contractDescriptor;
// Create oracle info for numerical contract
const eventDescriptor = new DigitDecompositionEventDescriptor();
eventDescriptor.base = 2;
eventDescriptor.isSigned = false;
eventDescriptor.unit = 'BTCUSD';
eventDescriptor.precision = 0;
eventDescriptor.nbDigits = 18;
const oracleEvent = new OracleEvent();
oracleEvent.oracleNonces = [
'c3347aa29db9f5e619483a92e746be91982bc66127e856bee62daeb91148cb92',
'fbdffe731b410c1d735ad7ce868f40d5b4a44c29131b0feca0f9c28f3a5c2fee',
'4326fc9c19013d4ca30f9f5dc184a6dab417acd4486528f14dbae2d2279bd29e',
'dd88db0628df7e17ddcafe3a274518d0a4f6baa100308b5522ad099b27305b5b',
'448774d5544b0151f112dd6f1cfc28df0e575e38bb8acb5c164357ebdb8bd364',
'5b8dd47e80fe9c12741190a515ff7d8a801911ab64f8c6117303cf0f065ae055',
'4b1f14efbba6e3bf7e5f4c40d5735ff126f23ac9a41659a05bebdd3596eb8d56',
'd8d7524476c8f5e014a536cf513de7136fac9d3925d1f479c2f68b09ea3f43cd',
'3719a5d31121084fd9f25f77bf042d2da23075102acf59e3e592e8a3a2220940',
'64cf5b50bbc405c14078f82240aa38403fafcbee3c74c3e8ca361e60001cbc74',
'149631c02f1f26ead45f9560ffe6cb1684c8c068d6eb1f03fdd655827b9f2531',
'2669158073d4c5ebe4855e724b6650f8214697b17eeac1ef33a582d4efb73c90',
'6abaac5f261322a5097115aaadaeca025e4eae0cdfb26fc591bc05820606929e',
'b437c474dff5ed40fb7dcf298f5a1a70e95658135cb72008c3fbd515d86e59c3',
'f85acb02775200421eda2c99c4a12975c9ac0d460ae7c9340e814745df4ebb54',
'5a719fe0b7e44bab56448a4007f40a09f3adc57fcee18089d6334655c407a63a',
'27540f4887299b36646b7c2202aef86bb0e7e68efc508583039f74c41cb57d66',
'ed5b9a675d77506b71f597dc59e6777cbba054c82d3ba139e82d74ca717b0d0c',
].map((hex) => Buffer.from(hex, 'hex'));
oracleEvent.eventMaturityEpoch = 1635321600;
oracleEvent.eventDescriptor = eventDescriptor;
oracleEvent.eventId = 'Deribit-BTC-27OCT21';
const oracleAnnouncement = new OracleAnnouncement();
oracleAnnouncement.announcementSig = Buffer.from(
'c3347aa29db9f5e619483a92e746be91982bc66127e856bee62daeb91148cb92a1074a802a481c008ba44143c752e519c28d906658e5257d9d82e80ef66cbf0f',
'hex',
);
oracleAnnouncement.oraclePublicKey = Buffer.from(
'4f15b5e4b8000c33a8e5bbdbc6802375a7d6e7cefcfbf098aa51ce7da0f910c1',
'hex',
);
oracleAnnouncement.oracleEvent = oracleEvent;
const oracleInfo = new SingleOracleInfo();
oracleInfo.announcement = oracleAnnouncement;
instance.oracleInfo = oracleInfo;
// Test that it serializes without errors
const serialized = instance.serialize();
expect(serialized).to.be.instanceof(Buffer);
expect(serialized.length).to.be.greaterThan(0);
});
});
describe('deserialize', () => {
it('deserializes enumerated contract', () => {
// Create a test instance and serialize it first
const originalInstance = new SingleContractInfo();
originalInstance.totalCollateral = BigInt(200000000);
// Create contract descriptor
const contractDescriptor = new EnumeratedDescriptor();
contractDescriptor.outcomes = [
{ outcome: 'outcome1', localPayout: BigInt(0) },
{ outcome: 'outcome2', localPayout: BigInt(100000000) },
{ outcome: 'outcome3', localPayout: BigInt(200000000) },
];
originalInstance.contractDescriptor = contractDescriptor;
// Create oracle info
const oracleAnnouncement = new OracleAnnouncement();
oracleAnnouncement.announcementSig = Buffer.from(
'fab22628f6e2602e1671c286a2f63a9246794008627a1749639217f4214cb4a9494c93d1a852221080f44f697adb4355df59eb339f6ba0f9b01ba661a8b108d4',
'hex',
);
oracleAnnouncement.oraclePublicKey = Buffer.from(
'da078bbb1d34e7729e38e2ae34236e776da121af442626fa31e31ae55a279a0b',
'hex',
);
const oracleEvent = new OracleEvent();
oracleEvent.oracleNonces = [
Buffer.from(
'3cfba011378411b20a5ab773cb95daab93e9bcd1e4cce44986a7dda84e01841b',
'hex',
),
];
oracleEvent.eventMaturityEpoch = 0;
const eventDescriptor = new EnumEventDescriptor();
eventDescriptor.outcomes = ['dummy1', 'dummy2'];
oracleEvent.eventDescriptor = eventDescriptor;
oracleEvent.eventId = 'dummy';
oracleAnnouncement.oracleEvent = oracleEvent;
const oracleInfo = new SingleOracleInfo();
oracleInfo.announcement = oracleAnnouncement;
originalInstance.oracleInfo = oracleInfo;
// Serialize and then deserialize to ensure round-trip consistency
const serialized = originalInstance.serialize();
const unknownInstance = ContractInfo.deserialize(serialized);
if (unknownInstance instanceof SingleContractInfo) {
const instance = unknownInstance as SingleContractInfo;
expect(Number(instance.totalCollateral)).to.equal(200000000);
expect(instance.contractDescriptor).to.be.instanceof(
EnumeratedDescriptor,
);
expect(instance.oracleInfo).to.be.instanceof(SingleOracleInfo);
// Verify the contract descriptor was properly deserialized
const enumDescriptor = instance.contractDescriptor as EnumeratedDescriptor;
expect(enumDescriptor.outcomes.length).to.equal(3);
expect(enumDescriptor.outcomes[0].outcome).to.equal('outcome1');
expect(enumDescriptor.outcomes[1].outcome).to.equal('outcome2');
expect(enumDescriptor.outcomes[2].outcome).to.equal('outcome3');
}
});
});
describe('validate', () => {
let eventDescriptorNumDigits;
let contractDescriptorNumDigits;
let oracleInfo: SingleOracleInfo;
let oracleAnnouncement: OracleAnnouncement;
let hyperbolaPayoutCurvePiece: HyperbolaPayoutCurvePiece;
let payoutFunction: PayoutFunction;
beforeEach(() => {
eventDescriptorNumDigits = 18;
contractDescriptorNumDigits = 18;
const eventDescriptor = new DigitDecompositionEventDescriptor();
eventDescriptor.base = 2;
eventDescriptor.isSigned = false;
eventDescriptor.unit = 'BTCUSD';
eventDescriptor.precision = 0;
eventDescriptor.nbDigits = eventDescriptorNumDigits;
const oracleEvent = new OracleEvent();
oracleEvent.oracleNonces = [
'c3347aa29db9f5e619483a92e746be91982bc66127e856bee62daeb91148cb92',
'fbdffe731b410c1d735ad7ce868f40d5b4a44c29131b0feca0f9c28f3a5c2fee',
'4326fc9c19013d4ca30f9f5dc184a6dab417acd4486528f14dbae2d2279bd29e',
'dd88db0628df7e17ddcafe3a274518d0a4f6baa100308b5522ad099b27305b5b',
'448774d5544b0151f112dd6f1cfc28df0e575e38bb8acb5c164357ebdb8bd364',
'5b8dd47e80fe9c12741190a515ff7d8a801911ab64f8c6117303cf0f065ae055',
'4b1f14efbba6e3bf7e5f4c40d5735ff126f23ac9a41659a05bebdd3596eb8d56',
'd8d7524476c8f5e014a536cf513de7136fac9d3925d1f479c2f68b09ea3f43cd',
'3719a5d31121084fd9f25f77bf042d2da23075102acf59e3e592e8a3a2220940',
'64cf5b50bbc405c14078f82240aa38403fafcbee3c74c3e8ca361e60001cbc74',
'149631c02f1f26ead45f9560ffe6cb1684c8c068d6eb1f03fdd655827b9f2531',
'2669158073d4c5ebe4855e724b6650f8214697b17eeac1ef33a582d4efb73c90',
'6abaac5f261322a5097115aaadaeca025e4eae0cdfb26fc591bc05820606929e',
'b437c474dff5ed40fb7dcf298f5a1a70e95658135cb72008c3fbd515d86e59c3',
'f85acb02775200421eda2c99c4a12975c9ac0d460ae7c9340e814745df4ebb54',
'5a719fe0b7e44bab56448a4007f40a09f3adc57fcee18089d6334655c407a63a',
'27540f4887299b36646b7c2202aef86bb0e7e68efc508583039f74c41cb57d66',
'ed5b9a675d77506b71f597dc59e6777cbba054c82d3ba139e82d74ca717b0d0c',
].map((hex) => Buffer.from(hex, 'hex'));
oracleEvent.eventMaturityEpoch = 1635321600;
oracleEvent.eventDescriptor = eventDescriptor;
oracleEvent.eventId = 'Deribit-BTC-27OCT21';
oracleAnnouncement = new OracleAnnouncement();
oracleAnnouncement.announcementSig = Buffer.from(
'c3347aa29db9f5e619483a92e746be91982bc66127e856bee62daeb91148cb92a1074a802a481c008ba44143c752e519c28d906658e5257d9d82e80ef66cbf0f',
'hex',
);
oracleAnnouncement.oraclePublicKey = Buffer.from(
'4f15b5e4b8000c33a8e5bbdbc6802375a7d6e7cefcfbf098aa51ce7da0f910c1',
'hex',
);
oracleAnnouncement.oracleEvent = oracleEvent;
oracleInfo = new SingleOracleInfo();
oracleInfo.announcement = oracleAnnouncement;
// Create PayoutFunction with new structure
hyperbolaPayoutCurvePiece = new HyperbolaPayoutCurvePiece();
hyperbolaPayoutCurvePiece.usePositivePiece = true;
hyperbolaPayoutCurvePiece.translateOutcome = F64.fromNumber(0);
hyperbolaPayoutCurvePiece.translatePayout = F64.fromNumber(30518);
hyperbolaPayoutCurvePiece.a = F64.fromNumber(1);
hyperbolaPayoutCurvePiece.b = F64.fromNumber(0);
hyperbolaPayoutCurvePiece.c = F64.fromNumber(0);
hyperbolaPayoutCurvePiece.d = F64.fromNumber(4000000000);
payoutFunction = new PayoutFunction();
payoutFunction.lastEndpoint = {
eventOutcome: BigInt(0),
outcomePayout: BigInt(969482),
extraPrecision: 0,
};
payoutFunction.payoutFunctionPieces = [
{
endPoint: {
eventOutcome: BigInt(131071),
outcomePayout: BigInt(0),
extraPrecision: 0,
},
payoutCurvePiece: hyperbolaPayoutCurvePiece,
},
];
});
it('should throw Error if oracle nonces length different than nbDigits', () => {
oracleInfo.announcement.oracleEvent.oracleNonces.pop();
const roundingIntervals = new RoundingIntervals();
roundingIntervals.intervals = [
{
beginInterval: BigInt(0),
roundingMod: BigInt(500),
},
];
const contractDescriptor = new NumericalDescriptor();
contractDescriptor.numDigits = contractDescriptorNumDigits;
contractDescriptor.payoutFunction = payoutFunction;
contractDescriptor.roundingIntervals = roundingIntervals;
const contractInfo = new SingleContractInfo();
contractInfo.totalCollateral = BigInt(969482);
contractInfo.contractDescriptor = contractDescriptor;
contractInfo.oracleInfo = oracleInfo;
expect(function () {
contractInfo.validate();
}).to.throw('OracleEvent nonce count mismatch');
});
it('should validate successfully with correct oracle nonces count', () => {
const roundingIntervals = new RoundingIntervals();
roundingIntervals.intervals = [
{
beginInterval: BigInt(0),
roundingMod: BigInt(500),
},
];
const contractDescriptor = new NumericalDescriptor();
contractDescriptor.numDigits = contractDescriptorNumDigits;
contractDescriptor.payoutFunction = payoutFunction;
contractDescriptor.roundingIntervals = roundingIntervals;
const contractInfo = new SingleContractInfo();
contractInfo.totalCollateral = BigInt(969482);
contractInfo.contractDescriptor = contractDescriptor;
contractInfo.oracleInfo = oracleInfo;
// Should not throw an error when oracle nonces match nbDigits
expect(function () {
contractInfo.validate();
}).to.not.throw();
});
it('should create and serialize PayoutFunction with HyperbolaPayoutCurvePiece', () => {
// Test that we can create and serialize the payout function structure
expect(payoutFunction.payoutFunctionPieces.length).to.equal(1);
expect(
payoutFunction.payoutFunctionPieces[0].payoutCurvePiece,
).to.be.instanceof(HyperbolaPayoutCurvePiece);
const serialized = payoutFunction.serialize();
expect(serialized).to.be.instanceof(Buffer);
expect(serialized.length).to.be.greaterThan(0);
// Test round-trip serialization
const deserialized = PayoutFunction.deserialize(serialized);
expect(deserialized.payoutFunctionPieces.length).to.equal(1);
expect(
deserialized.payoutFunctionPieces[0].payoutCurvePiece,
).to.be.instanceof(HyperbolaPayoutCurvePiece);
expect(deserialized.lastEndpoint.eventOutcome).to.equal(BigInt(0));
expect(deserialized.lastEndpoint.outcomePayout).to.equal(
BigInt(969482),
);
});
});
});
});