UNPKG

@node-dlc/messaging

Version:
318 lines (267 loc) 10.2 kB
import { BufferReader, BufferWriter } from '@node-dlc/bufio'; import { ContractDescriptorType, MessageType } from '../MessageType'; import { IDlcMessage } from './DlcMessage'; import { PayoutFunction, PayoutFunctionV0JSON } from './PayoutFunction'; import { IRoundingIntervalsJSON, RoundingIntervals } from './RoundingIntervals'; export abstract class ContractDescriptor { public static deserialize( buf: Buffer, ): EnumeratedDescriptor | NumericalDescriptor { const reader = new BufferReader(buf); const typeId = Number(reader.readBigSize()); switch (typeId) { case ContractDescriptorType.Enumerated: return EnumeratedDescriptor.deserialize(buf); case ContractDescriptorType.NumericOutcome: return NumericalDescriptor.deserialize(buf); default: throw new Error( `Contract descriptor type must be Enumerated (0) or NumericOutcome (1), got ${typeId}`, ); } } /** * Creates a ContractDescriptor from JSON data (e.g., from test vectors) * @param json JSON object representing a contract descriptor */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any public static fromJSON(json: any): ContractDescriptor { if (!json) { throw new Error('contractDescriptor is required'); } // Handle enumerated contract descriptor if ( json.enumeratedContractDescriptor || json.enumerated_contract_descriptor ) { return EnumeratedDescriptor.fromJSON( json.enumeratedContractDescriptor || json.enumerated_contract_descriptor, ); } // Handle numeric outcome contract descriptor else if ( json.numericOutcomeContractDescriptor || json.numeric_outcome_contract_descriptor ) { return NumericalDescriptor.fromJSON( json.numericOutcomeContractDescriptor || json.numeric_outcome_contract_descriptor, ); } else { throw new Error( 'contractDescriptor must have either enumeratedContractDescriptor or numericOutcomeContractDescriptor', ); } } public abstract contractDescriptorType: ContractDescriptorType; public abstract type: number; // For backward compatibility public abstract toJSON(): EnumeratedDescriptorJSON | NumericalDescriptorJSON; public abstract serialize(): Buffer; } /** * EnumeratedContractDescriptor contains information about a contract's outcomes * and their corresponding payouts (for enumerated outcomes). * This corresponds to the previous ContractDescriptorV0. */ export class EnumeratedDescriptor extends ContractDescriptor implements IDlcMessage { public static contractDescriptorType = ContractDescriptorType.Enumerated; /** * Creates an EnumeratedContractDescriptor from JSON data * @param json JSON object representing an enumerated contract descriptor */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any public static fromJSON(json: any): EnumeratedDescriptor { const instance = new EnumeratedDescriptor(); const payouts = json.payouts || []; // eslint-disable-next-line @typescript-eslint/no-explicit-any instance.outcomes = payouts.map((payout: any) => ({ outcome: payout.outcome, localPayout: BigInt(payout.offerPayout || 0), // Use canonical offerPayout field })); return instance; } /** * Deserializes an enumerated_contract_descriptor message * @param buf */ public static deserialize(buf: Buffer): EnumeratedDescriptor { const instance = new EnumeratedDescriptor(); const reader = new BufferReader(buf); reader.readBigSize(); // read type (0) const numOutcomes = Number(reader.readBigSize()); for (let i = 0; i < numOutcomes; i++) { const outcomeLen = Number(reader.readBigSize()); const outcome = reader.readBytes(outcomeLen).toString(); const localPayout = reader.readUInt64BE(); instance.outcomes.push({ outcome, localPayout, }); } return instance; } /** * The type for enumerated_contract_descriptor message - using MessageType for IDlcMessage compatibility */ public type = MessageType.ContractDescriptorV0; // For IDlcMessage compatibility /** * The contract descriptor type for new format */ public contractDescriptorType = ContractDescriptorType.Enumerated; public outcomes: IOutcome[] = []; /** * Converts enumerated_contract_descriptor to JSON */ public toJSON(): EnumeratedDescriptorJSON { // Return enum variant format for Rust compatibility return { enumeratedContractDescriptor: { payouts: this.outcomes.map((outcome) => ({ outcome: outcome.outcome, offerPayout: Number(outcome.localPayout), // Use offerPayout to match Rust })), }, }; } /** * Serializes the enumerated_contract_descriptor message into a Buffer */ public serialize(): Buffer { const writer = new BufferWriter(); writer.writeBigSize(this.contractDescriptorType); writer.writeBigSize(this.outcomes.length); for (const outcome of this.outcomes) { writer.writeBigSize(outcome.outcome.length); writer.writeBytes(Buffer.from(outcome.outcome)); writer.writeUInt64BE(outcome.localPayout); } return writer.toBuffer(); } } /** * NumericOutcomeContractDescriptor contains information about a contract's outcomes * and their corresponding payouts (for numeric outcomes). * This corresponds to the previous ContractDescriptorV1. */ export class NumericalDescriptor extends ContractDescriptor implements IDlcMessage { public static contractDescriptorType = ContractDescriptorType.NumericOutcome; /** * Creates a NumericOutcomeContractDescriptor from JSON data * @param json JSON object representing a numeric outcome contract descriptor */ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any public static fromJSON(json: any): NumericalDescriptor { const instance = new NumericalDescriptor(); instance.numDigits = json.numDigits || json.num_digits || 0; // Parse payout function using proper fromJSON method instance.payoutFunction = PayoutFunction.fromJSON( json.payoutFunction || json.payout_function, ); // Parse rounding intervals using proper fromJSON method instance.roundingIntervals = RoundingIntervals.fromJSON( json.roundingIntervals || json.rounding_intervals, ); return instance; } /** * Deserializes a numeric_outcome_contract_descriptor message * @param buf */ public static deserialize(buf: Buffer): NumericalDescriptor { const instance = new NumericalDescriptor(); const reader = new BufferReader(buf); reader.readBigSize(); // read type (1) instance.numDigits = reader.readUInt16BE(); // Parse payout function - need to calculate its size to avoid consuming all bytes const payoutFunctionStartPos = reader.position; const tempPayoutFunction = PayoutFunction.deserialize( reader.buffer.subarray(reader.position), ); instance.payoutFunction = tempPayoutFunction; // Skip past the payout function bytes const payoutFunctionSize = tempPayoutFunction.serialize().length; reader.position = payoutFunctionStartPos + payoutFunctionSize; // Parse remaining bytes as rounding intervals instance.roundingIntervals = RoundingIntervals.deserialize( reader.buffer.subarray(reader.position), ); return instance; } /** * The type for numeric_outcome_contract_descriptor message - using MessageType for IDlcMessage compatibility */ public type = MessageType.ContractDescriptorV1; // For IDlcMessage compatibility /** * The contract descriptor type for new format */ public contractDescriptorType = ContractDescriptorType.NumericOutcome; public numDigits: number; public payoutFunction: PayoutFunction; public roundingIntervals: RoundingIntervals; /** * Validates correctness of all fields in the message * https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md#the-contract_descriptor-type * @throws Will throw an error if validation fails */ public validate(): void { this.roundingIntervals.validate(); } /** * Converts numeric_outcome_contract_descriptor to JSON */ public toJSON(): NumericalDescriptorJSON { // Return enum variant format for Rust compatibility return { numericOutcomeContractDescriptor: { numDigits: this.numDigits, payoutFunction: this.payoutFunction.toJSON(), roundingIntervals: this.roundingIntervals.toJSON(), }, }; } /** * Serializes the numeric_outcome_contract_descriptor message into a Buffer */ public serialize(): Buffer { const writer = new BufferWriter(); writer.writeBigSize(this.contractDescriptorType); writer.writeUInt16BE(this.numDigits); writer.writeBytes(this.payoutFunction.serialize()); writer.writeBytes(this.roundingIntervals.serialize()); return writer.toBuffer(); } } // Legacy support - keeping old class names as aliases export const ContractDescriptorV0 = EnumeratedDescriptor; export const ContractDescriptorV1 = NumericalDescriptor; export type ContractDescriptorV0 = EnumeratedDescriptor; export type ContractDescriptorV1 = NumericalDescriptor; interface IOutcome { outcome: string; localPayout: bigint; } // Rust-dlc enum variant format for EnumeratedDescriptor export interface EnumeratedDescriptorJSON { enumeratedContractDescriptor: { payouts: Array<{ outcome: string; offerPayout: number; }>; }; } // Rust-dlc enum variant format for NumericalDescriptor export interface NumericalDescriptorJSON { numericOutcomeContractDescriptor: { numDigits: number; payoutFunction: PayoutFunctionV0JSON; roundingIntervals: IRoundingIntervalsJSON; }; } // Legacy interfaces for backward compatibility export type ContractDescriptorV0JSON = EnumeratedDescriptorJSON; export type ContractDescriptorV1JSON = NumericalDescriptorJSON;