@hazbase/simplicity 0.0.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +140 -1038
  2. package/dist/cli.js +3341 -269
  3. package/dist/client/SimplicityClient.d.ts +91 -290
  4. package/dist/client/SimplicityClient.js +93 -23
  5. package/dist/core/executor.js +67 -92
  6. package/dist/core/lineage.d.ts +18 -0
  7. package/dist/core/lineage.js +30 -0
  8. package/dist/core/outputBinding.d.ts +61 -0
  9. package/dist/core/outputBinding.js +552 -0
  10. package/dist/core/reporting.d.ts +22 -0
  11. package/dist/core/reporting.js +40 -0
  12. package/dist/core/schnorr.d.ts +5 -0
  13. package/dist/core/schnorr.js +46 -0
  14. package/dist/core/types.d.ts +799 -1
  15. package/dist/docs/definitions/bond-anchor.simf +19 -0
  16. package/dist/docs/definitions/bond-definition.json +10 -0
  17. package/dist/docs/definitions/bond-descriptor-bound-settlement-machine.simf +144 -0
  18. package/dist/docs/definitions/bond-issuance-anchor.simf +26 -0
  19. package/dist/docs/definitions/bond-issuance-state-partial-redemption.json +18 -0
  20. package/dist/docs/definitions/bond-issuance-state-redeemed.json +18 -0
  21. package/dist/docs/definitions/bond-issuance-state.json +12 -0
  22. package/dist/docs/definitions/bond-redemption-state-machine.simf +118 -0
  23. package/dist/docs/definitions/bond-redemption-transition.simf +41 -0
  24. package/dist/docs/definitions/bond-script-bound-settlement-machine.simf +142 -0
  25. package/dist/docs/definitions/fund-capital-call-open.simf +82 -0
  26. package/dist/docs/definitions/fund-capital-call-refund-only.simf +67 -0
  27. package/dist/docs/definitions/fund-capital-call-state.json +11 -0
  28. package/dist/docs/definitions/fund-definition.json +8 -0
  29. package/dist/docs/definitions/fund-distribution-claim.simf +57 -0
  30. package/dist/docs/definitions/receivable-definition.json +9 -0
  31. package/dist/docs/definitions/receivable-funding-claim.json +13 -0
  32. package/dist/docs/definitions/receivable-funding-claim.simf +58 -0
  33. package/dist/docs/definitions/receivable-repayment-claim.json +13 -0
  34. package/dist/docs/definitions/receivable-repayment-claim.simf +59 -0
  35. package/dist/docs/definitions/receivable-state-funded.json +20 -0
  36. package/dist/docs/definitions/receivable-state-originated.json +19 -0
  37. package/dist/docs/definitions/receivable-state-repaid.json +20 -0
  38. package/dist/docs/definitions/recursive-delay-direct-next.simf +60 -0
  39. package/dist/docs/definitions/recursive-delay-optional.simf +88 -0
  40. package/dist/docs/definitions/recursive-delay-required.simf +72 -0
  41. package/dist/docs/definitions/recursive-delay.simf +83 -0
  42. package/dist/docs/definitions/recursive-policy-transfer-machine.simf +65 -0
  43. package/dist/domain/bond.d.ts +8649 -720
  44. package/dist/domain/bond.js +1398 -8
  45. package/dist/domain/bondSettlementValidation.d.ts +2 -0
  46. package/dist/domain/bondSettlementValidation.js +28 -0
  47. package/dist/domain/bondValidation.d.ts +37 -1
  48. package/dist/domain/bondValidation.js +137 -11
  49. package/dist/domain/fund.d.ts +2452 -0
  50. package/dist/domain/fund.js +1756 -0
  51. package/dist/domain/fundValidation.d.ts +152 -0
  52. package/dist/domain/fundValidation.js +767 -0
  53. package/dist/domain/policies.d.ts +1064 -0
  54. package/dist/domain/policies.js +1625 -0
  55. package/dist/domain/receivable.d.ts +824 -0
  56. package/dist/domain/receivable.js +1375 -0
  57. package/dist/domain/receivableValidation.d.ts +147 -0
  58. package/dist/domain/receivableValidation.js +831 -0
  59. package/dist/index.d.ts +8 -2
  60. package/dist/index.js +137 -21
  61. package/package.json +20 -2
@@ -15,4 +15,6 @@ export declare function validateBondSettlementMatchesExpected(actual: BondSettle
15
15
  assetIdMatch: boolean;
16
16
  nextAmountSatMatch: boolean;
17
17
  maxFeeSatMatch: boolean;
18
+ expectedOutputDescriptorHashMatch: boolean;
19
+ outputBindingModeMatch: boolean;
18
20
  };
@@ -33,6 +33,17 @@ function validateBondSettlementDescriptor(value) {
33
33
  assertFiniteNumber(descriptor?.redeemAmount, "BOND_SETTLEMENT_REDEEM_AMOUNT_INVALID", "redeemAmount must be a finite number");
34
34
  assertFiniteNumber(descriptor?.nextAmountSat, "BOND_SETTLEMENT_AMOUNT_INVALID", "nextAmountSat must be a finite number");
35
35
  assertFiniteNumber(descriptor?.maxFeeSat, "BOND_SETTLEMENT_MAX_FEE_INVALID", "maxFeeSat must be a finite number");
36
+ if (descriptor.expectedOutputDescriptorHash !== undefined && descriptor.expectedOutputDescriptorHash !== null) {
37
+ assertNonEmptyString(descriptor.expectedOutputDescriptorHash, "BOND_SETTLEMENT_EXPECTED_OUTPUT_DESCRIPTOR_HASH_INVALID", "expectedOutputDescriptorHash must be a non-empty string");
38
+ }
39
+ if (descriptor.outputBindingMode !== undefined
40
+ && descriptor.outputBindingMode !== "none"
41
+ && descriptor.outputBindingMode !== "script-bound"
42
+ && descriptor.outputBindingMode !== "descriptor-bound") {
43
+ throw new errors_1.ValidationError("outputBindingMode must be none, script-bound, or descriptor-bound", {
44
+ code: "BOND_SETTLEMENT_OUTPUT_BINDING_MODE_INVALID",
45
+ });
46
+ }
36
47
  assertFiniteNumber(descriptor?.principal?.issued, "BOND_SETTLEMENT_PRINCIPAL_INVALID", "principal.issued must be a finite number");
37
48
  assertFiniteNumber(descriptor?.principal?.previousOutstanding, "BOND_SETTLEMENT_PRINCIPAL_INVALID", "principal.previousOutstanding must be a finite number");
38
49
  assertFiniteNumber(descriptor?.principal?.nextOutstanding, "BOND_SETTLEMENT_PRINCIPAL_INVALID", "principal.nextOutstanding must be a finite number");
@@ -58,6 +69,11 @@ function validateBondSettlementDescriptor(value) {
58
69
  code: "BOND_SETTLEMENT_MAX_FEE_INVALID",
59
70
  });
60
71
  }
72
+ if (descriptor.expectedOutputDescriptorHash && !/^[0-9a-f]{64}$/i.test(descriptor.expectedOutputDescriptorHash)) {
73
+ throw new errors_1.ValidationError("expectedOutputDescriptorHash must be a 64-character hex string", {
74
+ code: "BOND_SETTLEMENT_EXPECTED_OUTPUT_DESCRIPTOR_HASH_INVALID",
75
+ });
76
+ }
61
77
  if (Number.isNaN(Date.parse(descriptor.transitionAt))) {
62
78
  throw new errors_1.ValidationError("transitionAt must be an ISO8601 string", {
63
79
  code: "BOND_SETTLEMENT_TRANSITION_AT_INVALID",
@@ -77,6 +93,8 @@ function validateBondSettlementMatchesExpected(actual, expected) {
77
93
  assetIdMatch: actual.assetId === expected.assetId,
78
94
  nextAmountSatMatch: actual.nextAmountSat === expected.nextAmountSat,
79
95
  maxFeeSatMatch: actual.maxFeeSat === expected.maxFeeSat,
96
+ expectedOutputDescriptorHashMatch: (actual.expectedOutputDescriptorHash ?? null) === (expected.expectedOutputDescriptorHash ?? null),
97
+ outputBindingModeMatch: (actual.outputBindingMode ?? "none") === (expected.outputBindingMode ?? "none"),
80
98
  };
81
99
  if (!result.bondIdMatch) {
82
100
  throw new errors_1.ValidationError("Bond settlement bondId does not match expected value", {
@@ -118,5 +136,15 @@ function validateBondSettlementMatchesExpected(actual, expected) {
118
136
  code: "BOND_SETTLEMENT_AMOUNT_INVALID",
119
137
  });
120
138
  }
139
+ if (!result.expectedOutputDescriptorHashMatch) {
140
+ throw new errors_1.ValidationError("Bond settlement expectedOutputDescriptorHash does not match expected value", {
141
+ code: "BOND_SETTLEMENT_EXPECTED_OUTPUT_DESCRIPTOR_HASH_MISMATCH",
142
+ });
143
+ }
144
+ if (!result.outputBindingModeMatch) {
145
+ throw new errors_1.ValidationError("Bond settlement outputBindingMode does not match expected value", {
146
+ code: "BOND_SETTLEMENT_OUTPUT_BINDING_MODE_MISMATCH",
147
+ });
148
+ }
121
149
  return result;
122
150
  }
@@ -1,4 +1,4 @@
1
- import { BondDefinition, BondIssuanceState } from "../core/types";
1
+ import { BondDefinition, BondIssuanceState, BondIssuanceStatus } from "../core/types";
2
2
  export declare function summarizeBondIssuanceState(state: BondIssuanceState): {
3
3
  canonicalJson: string;
4
4
  hash: string;
@@ -27,3 +27,39 @@ export declare function buildRedeemedBondIssuanceState(input: {
27
27
  amount: number;
28
28
  redeemedAt: string;
29
29
  }): BondIssuanceState;
30
+ export declare function buildClosedBondIssuanceState(input: {
31
+ previous: BondIssuanceState;
32
+ closedAt: string;
33
+ closingReason: "REDEEMED" | "CANCELLED" | "MATURED_OUT";
34
+ finalSettlementDescriptorHash: string;
35
+ }): BondIssuanceState;
36
+ export declare function verifyBondIssuanceHistory(input: {
37
+ history: BondIssuanceState[];
38
+ }): {
39
+ chainLength: number;
40
+ latestStatus: BondIssuanceStatus | undefined;
41
+ startsAtGenesis: boolean;
42
+ previousStateHashLinked: boolean;
43
+ issuanceIdConsistent: boolean;
44
+ bondIdConsistent: boolean;
45
+ currencyConsistent: boolean;
46
+ controllerConsistent: boolean;
47
+ issuerEntityConsistent: boolean;
48
+ issuedPrincipalConsistent: boolean;
49
+ allPreviousStateHashMatch: boolean;
50
+ allRedemptionArithmeticValid: boolean;
51
+ allStatusProgressionValid: boolean;
52
+ allTransitionIdentitiesMatch: boolean;
53
+ fullHistoryVerified: boolean;
54
+ transitionChecks: {
55
+ issuanceIdMatch: boolean;
56
+ bondIdMatch: boolean;
57
+ currencyMatch: boolean;
58
+ controllerMatch: boolean;
59
+ issuerEntityMatch: boolean;
60
+ issuedPrincipalMatch: boolean;
61
+ previousStateHashMatch: boolean;
62
+ redemptionArithmeticValid: boolean;
63
+ statusProgressionValid: boolean;
64
+ }[];
65
+ };
@@ -6,8 +6,11 @@ exports.validateBondIssuanceState = validateBondIssuanceState;
6
6
  exports.validateBondCrossChecks = validateBondCrossChecks;
7
7
  exports.validateBondStateTransition = validateBondStateTransition;
8
8
  exports.buildRedeemedBondIssuanceState = buildRedeemedBondIssuanceState;
9
- const summary_1 = require("../core/summary");
9
+ exports.buildClosedBondIssuanceState = buildClosedBondIssuanceState;
10
+ exports.verifyBondIssuanceHistory = verifyBondIssuanceHistory;
10
11
  const errors_1 = require("../core/errors");
12
+ const lineage_1 = require("../core/lineage");
13
+ const summary_1 = require("../core/summary");
11
14
  function assertNonEmptyString(value, code, message) {
12
15
  if (typeof value !== "string" || value.trim().length === 0) {
13
16
  throw new errors_1.ValidationError(message, { code });
@@ -19,8 +22,8 @@ function assertFiniteNumber(value, code, message) {
19
22
  }
20
23
  }
21
24
  function assertValidStatus(status) {
22
- if (status !== "ISSUED" && status !== "PARTIALLY_REDEEMED" && status !== "REDEEMED") {
23
- throw new errors_1.ValidationError("status must be ISSUED, PARTIALLY_REDEEMED, or REDEEMED", {
25
+ if (status !== "ISSUED" && status !== "PARTIALLY_REDEEMED" && status !== "REDEEMED" && status !== "CLOSED") {
26
+ throw new errors_1.ValidationError("status must be ISSUED, PARTIALLY_REDEEMED, REDEEMED, or CLOSED", {
24
27
  code: "BOND_STATUS_INVALID",
25
28
  });
26
29
  }
@@ -153,6 +156,40 @@ function validateBondIssuanceState(value) {
153
156
  });
154
157
  }
155
158
  }
159
+ if (issuance.status === "CLOSED") {
160
+ if (issuance.outstandingPrincipal !== 0 || issuance.redeemedPrincipal !== issuance.issuedPrincipal) {
161
+ throw new errors_1.ValidationError("CLOSED state must have zero outstanding principal and full redeemed principal", {
162
+ code: "BOND_STATUS_TRANSITION_INVALID",
163
+ });
164
+ }
165
+ if (!issuance.previousStateHash) {
166
+ throw new errors_1.ValidationError("CLOSED state must set previousStateHash", {
167
+ code: "BOND_PREVIOUS_STATE_HASH_INVALID",
168
+ });
169
+ }
170
+ if (!issuance.closedAt || Number.isNaN(Date.parse(issuance.closedAt))) {
171
+ throw new errors_1.ValidationError("CLOSED state must set closedAt as an ISO8601 string", {
172
+ code: "BOND_CLOSED_AT_INVALID",
173
+ });
174
+ }
175
+ if (issuance.closingReason !== "REDEEMED"
176
+ && issuance.closingReason !== "CANCELLED"
177
+ && issuance.closingReason !== "MATURED_OUT") {
178
+ throw new errors_1.ValidationError("CLOSED state must set a valid closingReason", {
179
+ code: "BOND_CLOSING_REASON_INVALID",
180
+ });
181
+ }
182
+ if (!issuance.finalSettlementDescriptorHash || !/^[0-9a-f]{64}$/i.test(issuance.finalSettlementDescriptorHash)) {
183
+ throw new errors_1.ValidationError("CLOSED state must set finalSettlementDescriptorHash as a 64-character hex string", {
184
+ code: "BOND_FINAL_SETTLEMENT_DESCRIPTOR_HASH_INVALID",
185
+ });
186
+ }
187
+ if (!transition || transition.type !== "REDEEM") {
188
+ throw new errors_1.ValidationError("CLOSED state must carry a REDEEM transition", {
189
+ code: "BOND_TRANSITION_TYPE_INVALID",
190
+ });
191
+ }
192
+ }
156
193
  return issuance;
157
194
  }
158
195
  function validateBondCrossChecks(definition, issuance) {
@@ -241,21 +278,29 @@ function validateBondStateTransition(previous, next) {
241
278
  code: "BOND_TRANSITION_TYPE_INVALID",
242
279
  });
243
280
  }
244
- const expectedOutstanding = previous.outstandingPrincipal - transition.amount;
245
- const expectedRedeemed = previous.redeemedPrincipal + transition.amount;
246
- result.redemptionArithmeticValid =
247
- expectedOutstanding >= 0 &&
248
- next.outstandingPrincipal === expectedOutstanding &&
249
- next.redeemedPrincipal === expectedRedeemed;
281
+ if (previous.status === "REDEEMED" && next.status === "CLOSED") {
282
+ result.redemptionArithmeticValid =
283
+ next.outstandingPrincipal === previous.outstandingPrincipal &&
284
+ next.redeemedPrincipal === previous.redeemedPrincipal;
285
+ }
286
+ else {
287
+ const expectedOutstanding = previous.outstandingPrincipal - transition.amount;
288
+ const expectedRedeemed = previous.redeemedPrincipal + transition.amount;
289
+ result.redemptionArithmeticValid =
290
+ expectedOutstanding >= 0 &&
291
+ next.outstandingPrincipal === expectedOutstanding &&
292
+ next.redeemedPrincipal === expectedRedeemed;
293
+ }
250
294
  if (!result.redemptionArithmeticValid) {
251
295
  throw new errors_1.ValidationError("Bond issuance redemption arithmetic is invalid", {
252
296
  code: "BOND_REDEMPTION_ARITHMETIC_INVALID",
253
297
  });
254
298
  }
255
299
  result.statusProgressionValid =
256
- (previous.status === "ISSUED" || previous.status === "PARTIALLY_REDEEMED") &&
300
+ (((previous.status === "ISSUED" || previous.status === "PARTIALLY_REDEEMED") &&
257
301
  ((next.status === "PARTIALLY_REDEEMED" && next.outstandingPrincipal > 0) ||
258
- (next.status === "REDEEMED" && next.outstandingPrincipal === 0));
302
+ (next.status === "REDEEMED" && next.outstandingPrincipal === 0))) ||
303
+ (previous.status === "REDEEMED" && next.status === "CLOSED" && next.outstandingPrincipal === 0));
259
304
  if (!result.statusProgressionValid) {
260
305
  throw new errors_1.ValidationError("Bond issuance status progression is invalid", {
261
306
  code: "BOND_STATUS_TRANSITION_INVALID",
@@ -301,3 +346,84 @@ function buildRedeemedBondIssuanceState(input) {
301
346
  };
302
347
  return validateBondIssuanceState(next);
303
348
  }
349
+ function buildClosedBondIssuanceState(input) {
350
+ const previous = validateBondIssuanceState(input.previous);
351
+ if (previous.status !== "REDEEMED") {
352
+ throw new errors_1.ValidationError("Only a REDEEMED issuance state can be closed", {
353
+ code: "BOND_CLOSING_PRECONDITION_INVALID",
354
+ });
355
+ }
356
+ assertNonEmptyString(input.closedAt, "BOND_CLOSED_AT_INVALID", "closedAt must be a non-empty string");
357
+ if (Number.isNaN(Date.parse(input.closedAt))) {
358
+ throw new errors_1.ValidationError("closedAt must be an ISO8601 string", {
359
+ code: "BOND_CLOSED_AT_INVALID",
360
+ });
361
+ }
362
+ if (!/^[0-9a-f]{64}$/i.test(input.finalSettlementDescriptorHash)) {
363
+ throw new errors_1.ValidationError("finalSettlementDescriptorHash must be a 64-character hex string", {
364
+ code: "BOND_FINAL_SETTLEMENT_DESCRIPTOR_HASH_INVALID",
365
+ });
366
+ }
367
+ return validateBondIssuanceState({
368
+ ...previous,
369
+ status: "CLOSED",
370
+ previousStateHash: summarizeBondIssuanceState(previous).hash,
371
+ closedAt: input.closedAt,
372
+ closingReason: input.closingReason,
373
+ finalSettlementDescriptorHash: input.finalSettlementDescriptorHash,
374
+ });
375
+ }
376
+ function verifyBondIssuanceHistory(input) {
377
+ if (input.history.length === 0) {
378
+ throw new errors_1.ValidationError("bond issuance history must include at least one state", {
379
+ code: "BOND_ISSUANCE_HISTORY_REQUIRED",
380
+ });
381
+ }
382
+ const history = input.history.map((state) => validateBondIssuanceState(state));
383
+ const lineage = (0, lineage_1.verifyHashLinkedLineage)({
384
+ entries: history,
385
+ summarize: summarizeBondIssuanceState,
386
+ getPreviousHash: (state) => state.previousStateHash,
387
+ isGenesis: (state) => state.status === "ISSUED" && !state.previousStateHash,
388
+ consistencyChecks: {
389
+ issuanceIdConsistent: (state, first) => state.issuanceId === first.issuanceId,
390
+ bondIdConsistent: (state, first) => state.bondId === first.bondId,
391
+ currencyConsistent: (state, first) => state.currencyAssetId === first.currencyAssetId,
392
+ controllerConsistent: (state, first) => state.controllerXonly === first.controllerXonly,
393
+ issuerEntityConsistent: (state, first) => state.issuerEntityId === first.issuerEntityId,
394
+ issuedPrincipalConsistent: (state, first) => state.issuedPrincipal === first.issuedPrincipal,
395
+ },
396
+ });
397
+ const transitionChecks = history.slice(1).map((state, index) => validateBondStateTransition(history[index], state));
398
+ const allPreviousStateHashMatch = transitionChecks.every((check) => check.previousStateHashMatch);
399
+ const allRedemptionArithmeticValid = transitionChecks.every((check) => check.redemptionArithmeticValid);
400
+ const allStatusProgressionValid = transitionChecks.every((check) => check.statusProgressionValid);
401
+ const allTransitionIdentitiesMatch = transitionChecks.every((check) => (check.issuanceIdMatch
402
+ && check.bondIdMatch
403
+ && check.currencyMatch
404
+ && check.controllerMatch
405
+ && check.issuerEntityMatch
406
+ && check.issuedPrincipalMatch));
407
+ return {
408
+ chainLength: history.length,
409
+ latestStatus: history.at(-1)?.status,
410
+ startsAtGenesis: lineage.startsAtGenesis,
411
+ previousStateHashLinked: lineage.previousHashLinked,
412
+ issuanceIdConsistent: lineage.consistency.issuanceIdConsistent,
413
+ bondIdConsistent: lineage.consistency.bondIdConsistent,
414
+ currencyConsistent: lineage.consistency.currencyConsistent,
415
+ controllerConsistent: lineage.consistency.controllerConsistent,
416
+ issuerEntityConsistent: lineage.consistency.issuerEntityConsistent,
417
+ issuedPrincipalConsistent: lineage.consistency.issuedPrincipalConsistent,
418
+ allPreviousStateHashMatch,
419
+ allRedemptionArithmeticValid,
420
+ allStatusProgressionValid,
421
+ allTransitionIdentitiesMatch,
422
+ fullHistoryVerified: lineage.fullLineageVerified
423
+ && allPreviousStateHashMatch
424
+ && allRedemptionArithmeticValid
425
+ && allStatusProgressionValid
426
+ && allTransitionIdentitiesMatch,
427
+ transitionChecks,
428
+ };
429
+ }