All files / lib/messages ContractDescriptor.ts

94.94% Statements 75/79
60.71% Branches 17/28
93.33% Functions 14/15
94.81% Lines 73/77

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 3181x   1x   1x 1x   1x       69x 69x   69x   35x   34x                           41x         41x       18x           23x       23x                                           1x     1x               18x   18x   72x         18x               35x 35x   35x 35x   35x 125x 125x 125x   125x           35x           102x         102x   102x             15x   52x                       120x   120x 120x   120x 427x 427x 427x     120x                 1x     1x               23x   23x     23x         23x       23x               34x 34x   34x 34x     34x 34x     34x     34x 34x     34x       34x           62x         62x                                           13x                         113x   113x 113x 113x 113x   113x         1x 1x                                                              
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 {
    Iif (!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 Eif (
      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(
      Buffer.from(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(
      Buffer.from(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;