@node-dlc/messaging
Version:
DLC Messaging Protocol
393 lines • 16.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OracleInfoV0 = exports.OracleInfo = exports.MultiOracleInfo = exports.SingleOracleInfo = exports.OracleParams = void 0;
const bufio_1 = require("@node-dlc/bufio");
const MessageType_1 = require("../MessageType");
const getTlv_1 = require("../serialize/getTlv");
const OracleAnnouncement_1 = require("./OracleAnnouncement");
/**
* OracleParams describe allowed differences between oracles in
* numerical outcome contracts, as per rust-dlc specification.
*/
class OracleParams {
constructor() {
this.type = OracleParams.type;
}
/**
* Creates an OracleParams from JSON data
* @param json JSON object representing oracle params
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
static fromJSON(json) {
const instance = new OracleParams();
instance.maxErrorExp = json.maxErrorExp || json.max_error_exp || 0;
instance.minFailExp = json.minFailExp || json.min_fail_exp || 0;
instance.maximizeCoverage =
json.maximizeCoverage || json.maximize_coverage || false;
return instance;
}
/**
* Deserializes oracle_params_v0 message
*/
static deserialize(buf) {
const instance = new OracleParams();
const reader = new bufio_1.BufferReader(buf);
reader.readBigSize(); // read type
instance.length = reader.readBigSize();
instance.maxErrorExp = reader.readUInt16BE();
instance.minFailExp = reader.readUInt16BE();
instance.maximizeCoverage = reader.readUInt8() === 1;
return instance;
}
/**
* Deserializes oracle params body without TLV wrapper (for optional deserialization)
*/
static deserializeBody(buf) {
const instance = new OracleParams();
const reader = new bufio_1.BufferReader(buf);
// No type/length to read - just the body content
instance.maxErrorExp = reader.readUInt16BE();
instance.minFailExp = reader.readUInt16BE();
instance.maximizeCoverage = reader.readUInt8() === 1;
return instance;
}
validate() {
if (this.maxErrorExp < 0) {
throw new Error('maxErrorExp must be greater than or equal to 0');
}
if (this.minFailExp < 0) {
throw new Error('minFailExp must be greater than or equal to 0');
}
if (this.maxErrorExp >= this.minFailExp) {
throw new Error('maxErrorExp must be less than minFailExp');
}
}
toJSON() {
return {
maxErrorExp: this.maxErrorExp,
minFailExp: this.minFailExp,
maximizeCoverage: this.maximizeCoverage,
};
}
serialize() {
const writer = new bufio_1.BufferWriter();
writer.writeBigSize(this.type);
const dataWriter = new bufio_1.BufferWriter();
dataWriter.writeUInt16BE(this.maxErrorExp);
dataWriter.writeUInt16BE(this.minFailExp);
dataWriter.writeUInt8(this.maximizeCoverage ? 1 : 0);
writer.writeBigSize(dataWriter.size);
writer.writeBytes(dataWriter.toBuffer());
return writer.toBuffer();
}
/**
* Serializes the oracle params body without TLV wrapper (for optional serialization)
*/
serializeBody() {
const writer = new bufio_1.BufferWriter();
writer.writeUInt16BE(this.maxErrorExp);
writer.writeUInt16BE(this.minFailExp);
writer.writeUInt8(this.maximizeCoverage ? 1 : 0);
return writer.toBuffer();
}
}
exports.OracleParams = OracleParams;
OracleParams.type = MessageType_1.MessageType.OracleParamsV0;
/**
* SingleOracleInfo contains information about a single oracle.
*/
class SingleOracleInfo {
constructor() {
this.type = SingleOracleInfo.type;
}
/**
* Creates a SingleOracleInfo from JSON data
* @param json JSON object representing single oracle info
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
static fromJSON(json) {
const instance = new SingleOracleInfo();
const announcementData = json.announcement || json.oracleAnnouncement;
if (!announcementData) {
throw new Error('announcement or oracleAnnouncement is required for single oracle info');
}
// Parse announcement using proper fromJSON method
instance.announcement = OracleAnnouncement_1.OracleAnnouncement.fromJSON(announcementData);
return instance;
}
/**
* Deserializes single oracle info
*/
static deserialize(buf) {
const instance = new SingleOracleInfo();
const reader = new bufio_1.BufferReader(buf);
// Read the type and length that serialize() writes
reader.readBigSize(); // read type
instance.length = reader.readBigSize(); // read length
// Read the announcement data
instance.announcement = OracleAnnouncement_1.OracleAnnouncement.deserialize(reader.readBytes(Number(instance.length)));
return instance;
}
/**
* Returns the closest maturity date amongst all events
*/
getClosestMaturityDate() {
return this.announcement.oracleEvent.eventMaturityEpoch;
}
validate() {
this.announcement.validate();
}
toJSON() {
// Return enum variant format for Rust compatibility
return {
single: {
oracleAnnouncement: this.announcement.toJSON(),
},
};
}
serialize() {
const writer = new bufio_1.BufferWriter();
writer.writeBigSize(this.type);
const dataWriter = new bufio_1.BufferWriter();
dataWriter.writeBytes(this.announcement.serialize());
writer.writeBigSize(dataWriter.size);
writer.writeBytes(dataWriter.toBuffer());
return writer.toBuffer();
}
/**
* Serializes the body without TLV wrapper (for embedding in ContractInfo)
* This matches rust-dlc behavior where OracleInfo.write() doesn't add type_id
*/
serializeBody() {
return this.announcement.serialize();
}
/**
* Deserializes the body without TLV wrapper (for embedding in ContractInfo)
* This matches rust-dlc behavior where OracleInfo is read without type_id
*/
static deserializeBody(buf) {
const instance = new SingleOracleInfo();
// No type/length to read - just the announcement data directly
instance.announcement = OracleAnnouncement_1.OracleAnnouncement.deserialize(buf);
return instance;
}
}
exports.SingleOracleInfo = SingleOracleInfo;
SingleOracleInfo.type = MessageType_1.MessageType.SingleOracleInfo;
/**
* MultiOracleInfo contains information about multiple oracles used in multi-oracle based contracts.
*/
class MultiOracleInfo {
constructor() {
this.type = MultiOracleInfo.type;
/** The set of oracle announcements. */
this.announcements = [];
}
/**
* Creates a MultiOracleInfo from JSON data
* @param json JSON object representing multi oracle info
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
static fromJSON(json) {
const instance = new MultiOracleInfo();
instance.threshold = json.threshold || 1;
// Parse oracle announcements using proper fromJSON method
const announcements = json.oracleAnnouncements || json.oracle_announcements || [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
instance.announcements = announcements.map((announcementJson) => OracleAnnouncement_1.OracleAnnouncement.fromJSON(announcementJson));
// Parse oracle params if present (null means explicitly absent)
const oracleParamsData = json.oracleParams || json.oracle_params;
if (oracleParamsData !== null && oracleParamsData !== undefined) {
// Create OracleParams from JSON data using the fromJSON method
instance.oracleParams = OracleParams.fromJSON(oracleParamsData);
}
else {
// Explicitly null/undefined - will serialize as 00 (not present)
instance.oracleParams = undefined;
}
return instance;
}
/**
* Deserializes multi oracle info
*/
static deserialize(buf) {
const instance = new MultiOracleInfo();
const reader = new bufio_1.BufferReader(buf);
// Read the type and length that serialize() writes
reader.readBigSize(); // read type
instance.length = reader.readBigSize(); // read length
// In rust-dlc format, MultiOracleInfo body is: threshold + announcements + optional oracle params
instance.threshold = reader.readUInt16BE();
const numAnnouncements = Number(reader.readBigSize()); // Changed from readUInt16BE to readBigSize to match rust-dlc vec_cb
for (let i = 0; i < numAnnouncements; i++) {
instance.announcements.push(OracleAnnouncement_1.OracleAnnouncement.deserialize((0, getTlv_1.getTlv)(reader)));
}
// Optional oracle params using Optional sub-type format
const oracleParamsData = reader.readOptional();
if (oracleParamsData) {
instance.oracleParams = OracleParams.deserializeBody(oracleParamsData);
}
return instance;
}
validate() {
if (this.threshold <= 0) {
throw new Error('threshold must be greater than 0');
}
if (this.threshold > this.announcements.length) {
throw new Error('threshold cannot be greater than number of announcements');
}
if (this.announcements.length === 0) {
throw new Error('must have at least one announcement');
}
// Validate all announcements
this.announcements.forEach((announcement) => announcement.validate());
// Validate oracle params if present
if (this.oracleParams) {
this.oracleParams.validate();
}
}
/**
* Returns the closest maturity date amongst all events
*/
getClosestMaturityDate() {
return Math.min(...this.announcements.map((a) => a.oracleEvent.eventMaturityEpoch));
}
toJSON() {
// Return enum variant format for Rust compatibility
return {
multi: {
threshold: this.threshold,
oracleAnnouncements: this.announcements.map((a) => a.toJSON()),
oracleParams: this.oracleParams?.toJSON(),
},
};
}
serialize() {
const writer = new bufio_1.BufferWriter();
// writer.writeBigSize(this.type);
const dataWriter = new bufio_1.BufferWriter();
dataWriter.writeUInt16BE(this.threshold);
dataWriter.writeBigSize(this.announcements.length); // Changed from writeUInt16BE to writeBigSize to match rust-dlc vec_cb
for (const announcement of this.announcements) {
dataWriter.writeBytes(announcement.serialize());
}
// Use Optional serialization for oracle params (body content only, not TLV wrapped)
const oracleParamsData = this.oracleParams
? this.oracleParams.serializeBody()
: null;
dataWriter.writeOptional(oracleParamsData);
// writer.writeBigSize(dataWriter.size);
writer.writeBytes(dataWriter.toBuffer());
return writer.toBuffer();
}
/**
* Serializes the body without TLV wrapper (for embedding in ContractInfo)
* This matches rust-dlc behavior where OracleInfo.write() doesn't add type_id
*/
serializeBody() {
const writer = new bufio_1.BufferWriter();
writer.writeUInt16BE(this.threshold);
writer.writeBigSize(this.announcements.length); // Changed from writeUInt16BE to writeBigSize to match rust-dlc vec_cb
for (const announcement of this.announcements) {
writer.writeBytes(announcement.serialize());
}
// Use Optional serialization for oracle params (body content only, not TLV wrapped)
const oracleParamsData = this.oracleParams
? this.oracleParams.serializeBody()
: null;
writer.writeOptional(oracleParamsData);
return writer.toBuffer();
}
/**
* Deserializes the body without TLV wrapper (for embedding in ContractInfo)
* This matches rust-dlc behavior where OracleInfo is read without type_id
*/
static deserializeBody(buf) {
const instance = new MultiOracleInfo();
const reader = new bufio_1.BufferReader(buf);
// No type/length to read - directly read the multi-oracle body
instance.threshold = reader.readUInt16BE();
const numAnnouncements = Number(reader.readBigSize()); // BigSize for announcements count
for (let i = 0; i < numAnnouncements; i++) {
instance.announcements.push(OracleAnnouncement_1.OracleAnnouncement.deserialize((0, getTlv_1.getTlv)(reader)));
}
// Optional oracle params using Optional sub-type format
const oracleParamsData = reader.readOptional();
if (oracleParamsData) {
instance.oracleParams = OracleParams.deserializeBody(oracleParamsData);
}
return instance;
}
}
exports.MultiOracleInfo = MultiOracleInfo;
MultiOracleInfo.type = MessageType_1.MessageType.MultiOracleInfo;
/**
* OracleInfo contains information about the oracles to be used in
* executing a DLC. Updated to support both single and multi-oracle
* patterns as per rust-dlc specification.
*/
class OracleInfo {
static deserialize(buf) {
const reader = new bufio_1.BufferReader(buf);
const type = Number(reader.readBigSize());
switch (type) {
case MessageType_1.MessageType.SingleOracleInfo:
return SingleOracleInfo.deserialize(buf);
case MessageType_1.MessageType.MultiOracleInfo:
return MultiOracleInfo.deserialize(buf);
default:
throw new Error(`Unknown oracle info type: ${type}`);
}
}
/**
* Creates an OracleInfo from JSON data (e.g., from test vectors)
* @param json JSON object representing oracle info
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
static fromJSON(json) {
if (!json) {
throw new Error('oracleInfo is required');
}
// Handle direct single oracle (legacy format)
if (json.announcement) {
return SingleOracleInfo.fromJSON(json);
}
// Handle wrapped single oracle
else if (json.single) {
return SingleOracleInfo.fromJSON(json.single);
}
// Handle multi oracle
else if (json.multi) {
return MultiOracleInfo.fromJSON(json.multi);
}
else {
throw new Error('oracleInfo must have either announcement, single, or multi');
}
}
/**
* Returns the closest maturity date amongst all events
*/
getClosestMaturityDate() {
if (this instanceof SingleOracleInfo) {
return this.announcement.oracleEvent.eventMaturityEpoch;
}
else if (this instanceof MultiOracleInfo) {
return this.getClosestMaturityDate();
}
throw new Error('Unknown oracle info type');
}
}
exports.OracleInfo = OracleInfo;
// For backward compatibility, keep the V0 class as alias to SingleOracleInfo
class OracleInfoV0 extends SingleOracleInfo {
/**
* Creates an OracleInfoV0 from JSON data (alias for SingleOracleInfo.fromJSON)
* @param json JSON object representing oracle info
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
static fromJSON(json) {
return SingleOracleInfo.fromJSON(json);
}
}
exports.OracleInfoV0 = OracleInfoV0;
//# sourceMappingURL=OracleInfo.js.map