@hazbase/simplicity 0.0.3 → 0.0.5
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.
- package/README.md +229 -0
- package/dist/cli.js +388 -1
- package/dist/client/ContractFactory.d.ts +2 -1
- package/dist/client/ContractFactory.js +3 -0
- package/dist/client/DeployedContract.d.ts +15 -1
- package/dist/client/DeployedContract.js +22 -0
- package/dist/client/SimplicityClient.d.ts +304 -1
- package/dist/client/SimplicityClient.js +49 -0
- package/dist/core/artifact.js +8 -0
- package/dist/core/compiler.js +60 -5
- package/dist/core/executor.js +24 -0
- package/dist/core/state.d.ts +18 -0
- package/dist/core/state.js +278 -0
- package/dist/core/types.d.ts +106 -0
- package/dist/domain/bond.d.ts +2051 -0
- package/dist/domain/bond.js +1042 -0
- package/dist/domain/bondSettlementValidation.d.ts +18 -0
- package/dist/domain/bondSettlementValidation.js +122 -0
- package/dist/domain/bondValidation.d.ts +29 -0
- package/dist/domain/bondValidation.js +303 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +41 -1
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BondSettlementDescriptor } from "../core/types";
|
|
2
|
+
export declare function summarizeBondSettlementDescriptor(descriptor: BondSettlementDescriptor): {
|
|
3
|
+
canonicalJson: string;
|
|
4
|
+
hash: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function validateBondSettlementDescriptor(value: unknown): BondSettlementDescriptor;
|
|
7
|
+
export declare function validateBondSettlementMatchesExpected(actual: BondSettlementDescriptor, expected: BondSettlementDescriptor): {
|
|
8
|
+
bondIdMatch: boolean;
|
|
9
|
+
issuanceIdMatch: boolean;
|
|
10
|
+
definitionHashMatch: boolean;
|
|
11
|
+
previousStateHashMatch: boolean;
|
|
12
|
+
nextStateHashMatch: boolean;
|
|
13
|
+
nextContractAddressMatch: boolean;
|
|
14
|
+
redeemAmountMatch: boolean;
|
|
15
|
+
assetIdMatch: boolean;
|
|
16
|
+
nextAmountSatMatch: boolean;
|
|
17
|
+
maxFeeSatMatch: boolean;
|
|
18
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.summarizeBondSettlementDescriptor = summarizeBondSettlementDescriptor;
|
|
4
|
+
exports.validateBondSettlementDescriptor = validateBondSettlementDescriptor;
|
|
5
|
+
exports.validateBondSettlementMatchesExpected = validateBondSettlementMatchesExpected;
|
|
6
|
+
const summary_1 = require("../core/summary");
|
|
7
|
+
const errors_1 = require("../core/errors");
|
|
8
|
+
function assertNonEmptyString(value, code, message) {
|
|
9
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
10
|
+
throw new errors_1.ValidationError(message, { code });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function assertFiniteNumber(value, code, message) {
|
|
14
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
15
|
+
throw new errors_1.ValidationError(message, { code });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function summarizeBondSettlementDescriptor(descriptor) {
|
|
19
|
+
const canonicalJson = (0, summary_1.stableStringify)(descriptor);
|
|
20
|
+
return { canonicalJson, hash: (0, summary_1.sha256HexUtf8)(canonicalJson) };
|
|
21
|
+
}
|
|
22
|
+
function validateBondSettlementDescriptor(value) {
|
|
23
|
+
const descriptor = value;
|
|
24
|
+
assertNonEmptyString(descriptor?.settlementId, "BOND_SETTLEMENT_ID_INVALID", "settlementId must be a non-empty string");
|
|
25
|
+
assertNonEmptyString(descriptor?.bondId, "BOND_SETTLEMENT_BOND_ID_INVALID", "bondId must be a non-empty string");
|
|
26
|
+
assertNonEmptyString(descriptor?.issuanceId, "BOND_SETTLEMENT_ISSUANCE_ID_INVALID", "issuanceId must be a non-empty string");
|
|
27
|
+
assertNonEmptyString(descriptor?.definitionHash, "BOND_SETTLEMENT_DEFINITION_HASH_INVALID", "definitionHash must be a non-empty string");
|
|
28
|
+
assertNonEmptyString(descriptor?.previousStateHash, "BOND_SETTLEMENT_PREVIOUS_STATE_HASH_INVALID", "previousStateHash must be a non-empty string");
|
|
29
|
+
assertNonEmptyString(descriptor?.nextStateHash, "BOND_SETTLEMENT_NEXT_STATE_HASH_INVALID", "nextStateHash must be a non-empty string");
|
|
30
|
+
assertNonEmptyString(descriptor?.transitionAt, "BOND_SETTLEMENT_TRANSITION_AT_INVALID", "transitionAt must be a non-empty string");
|
|
31
|
+
assertNonEmptyString(descriptor?.assetId, "BOND_SETTLEMENT_ASSET_INVALID", "assetId must be a non-empty string");
|
|
32
|
+
assertNonEmptyString(descriptor?.nextContractAddress, "BOND_SETTLEMENT_NEXT_CONTRACT_INVALID", "nextContractAddress must be a non-empty string");
|
|
33
|
+
assertFiniteNumber(descriptor?.redeemAmount, "BOND_SETTLEMENT_REDEEM_AMOUNT_INVALID", "redeemAmount must be a finite number");
|
|
34
|
+
assertFiniteNumber(descriptor?.nextAmountSat, "BOND_SETTLEMENT_AMOUNT_INVALID", "nextAmountSat must be a finite number");
|
|
35
|
+
assertFiniteNumber(descriptor?.maxFeeSat, "BOND_SETTLEMENT_MAX_FEE_INVALID", "maxFeeSat must be a finite number");
|
|
36
|
+
assertFiniteNumber(descriptor?.principal?.issued, "BOND_SETTLEMENT_PRINCIPAL_INVALID", "principal.issued must be a finite number");
|
|
37
|
+
assertFiniteNumber(descriptor?.principal?.previousOutstanding, "BOND_SETTLEMENT_PRINCIPAL_INVALID", "principal.previousOutstanding must be a finite number");
|
|
38
|
+
assertFiniteNumber(descriptor?.principal?.nextOutstanding, "BOND_SETTLEMENT_PRINCIPAL_INVALID", "principal.nextOutstanding must be a finite number");
|
|
39
|
+
assertFiniteNumber(descriptor?.principal?.previousRedeemed, "BOND_SETTLEMENT_PRINCIPAL_INVALID", "principal.previousRedeemed must be a finite number");
|
|
40
|
+
assertFiniteNumber(descriptor?.principal?.nextRedeemed, "BOND_SETTLEMENT_PRINCIPAL_INVALID", "principal.nextRedeemed must be a finite number");
|
|
41
|
+
if (descriptor.transitionKind !== "REDEEM") {
|
|
42
|
+
throw new errors_1.ValidationError("transitionKind must be REDEEM", {
|
|
43
|
+
code: "BOND_SETTLEMENT_TRANSITION_KIND_INVALID",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (descriptor.redeemAmount <= 0) {
|
|
47
|
+
throw new errors_1.ValidationError("redeemAmount must be greater than 0", {
|
|
48
|
+
code: "BOND_SETTLEMENT_REDEEM_AMOUNT_INVALID",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (descriptor.nextAmountSat <= 0) {
|
|
52
|
+
throw new errors_1.ValidationError("nextAmountSat must be greater than 0", {
|
|
53
|
+
code: "BOND_SETTLEMENT_AMOUNT_INVALID",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (descriptor.maxFeeSat < 0) {
|
|
57
|
+
throw new errors_1.ValidationError("maxFeeSat must not be negative", {
|
|
58
|
+
code: "BOND_SETTLEMENT_MAX_FEE_INVALID",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (Number.isNaN(Date.parse(descriptor.transitionAt))) {
|
|
62
|
+
throw new errors_1.ValidationError("transitionAt must be an ISO8601 string", {
|
|
63
|
+
code: "BOND_SETTLEMENT_TRANSITION_AT_INVALID",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return descriptor;
|
|
67
|
+
}
|
|
68
|
+
function validateBondSettlementMatchesExpected(actual, expected) {
|
|
69
|
+
const result = {
|
|
70
|
+
bondIdMatch: actual.bondId === expected.bondId,
|
|
71
|
+
issuanceIdMatch: actual.issuanceId === expected.issuanceId,
|
|
72
|
+
definitionHashMatch: actual.definitionHash === expected.definitionHash,
|
|
73
|
+
previousStateHashMatch: actual.previousStateHash === expected.previousStateHash,
|
|
74
|
+
nextStateHashMatch: actual.nextStateHash === expected.nextStateHash,
|
|
75
|
+
nextContractAddressMatch: actual.nextContractAddress === expected.nextContractAddress,
|
|
76
|
+
redeemAmountMatch: actual.redeemAmount === expected.redeemAmount,
|
|
77
|
+
assetIdMatch: actual.assetId === expected.assetId,
|
|
78
|
+
nextAmountSatMatch: actual.nextAmountSat === expected.nextAmountSat,
|
|
79
|
+
maxFeeSatMatch: actual.maxFeeSat === expected.maxFeeSat,
|
|
80
|
+
};
|
|
81
|
+
if (!result.bondIdMatch) {
|
|
82
|
+
throw new errors_1.ValidationError("Bond settlement bondId does not match expected value", {
|
|
83
|
+
code: "BOND_SETTLEMENT_BOND_ID_MISMATCH",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (!result.issuanceIdMatch) {
|
|
87
|
+
throw new errors_1.ValidationError("Bond settlement issuanceId does not match expected value", {
|
|
88
|
+
code: "BOND_SETTLEMENT_ISSUANCE_ID_MISMATCH",
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (!result.definitionHashMatch) {
|
|
92
|
+
throw new errors_1.ValidationError("Bond settlement definitionHash does not match expected value", {
|
|
93
|
+
code: "BOND_SETTLEMENT_DEFINITION_HASH_MISMATCH",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (!result.previousStateHashMatch || !result.nextStateHashMatch) {
|
|
97
|
+
throw new errors_1.ValidationError("Bond settlement state hashes do not match expected values", {
|
|
98
|
+
code: "BOND_SETTLEMENT_STATE_HASH_MISMATCH",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (!result.nextContractAddressMatch) {
|
|
102
|
+
throw new errors_1.ValidationError("Bond settlement nextContractAddress does not match expected value", {
|
|
103
|
+
code: "BOND_SETTLEMENT_NEXT_CONTRACT_MISMATCH",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (!result.redeemAmountMatch) {
|
|
107
|
+
throw new errors_1.ValidationError("Bond settlement redeemAmount does not match expected value", {
|
|
108
|
+
code: "BOND_SETTLEMENT_REDEEM_AMOUNT_MISMATCH",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (!result.assetIdMatch) {
|
|
112
|
+
throw new errors_1.ValidationError("Bond settlement assetId does not match expected value", {
|
|
113
|
+
code: "BOND_SETTLEMENT_ASSET_MISMATCH",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (!result.nextAmountSatMatch || !result.maxFeeSatMatch) {
|
|
117
|
+
throw new errors_1.ValidationError("Bond settlement amount constraints do not match expected values", {
|
|
118
|
+
code: "BOND_SETTLEMENT_AMOUNT_INVALID",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BondDefinition, BondIssuanceState } from "../core/types";
|
|
2
|
+
export declare function summarizeBondIssuanceState(state: BondIssuanceState): {
|
|
3
|
+
canonicalJson: string;
|
|
4
|
+
hash: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function validateBondDefinition(value: unknown): BondDefinition;
|
|
7
|
+
export declare function validateBondIssuanceState(value: unknown): BondIssuanceState;
|
|
8
|
+
export declare function validateBondCrossChecks(definition: BondDefinition, issuance: BondIssuanceState): {
|
|
9
|
+
bondIdMatch: boolean;
|
|
10
|
+
currencyMatch: boolean;
|
|
11
|
+
controllerMatch: boolean;
|
|
12
|
+
principalInvariantValid: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function validateBondStateTransition(previous: BondIssuanceState, next: BondIssuanceState): {
|
|
15
|
+
issuanceIdMatch: boolean;
|
|
16
|
+
bondIdMatch: boolean;
|
|
17
|
+
currencyMatch: boolean;
|
|
18
|
+
controllerMatch: boolean;
|
|
19
|
+
issuerEntityMatch: boolean;
|
|
20
|
+
issuedPrincipalMatch: boolean;
|
|
21
|
+
previousStateHashMatch: boolean;
|
|
22
|
+
redemptionArithmeticValid: boolean;
|
|
23
|
+
statusProgressionValid: boolean;
|
|
24
|
+
};
|
|
25
|
+
export declare function buildRedeemedBondIssuanceState(input: {
|
|
26
|
+
previous: BondIssuanceState;
|
|
27
|
+
amount: number;
|
|
28
|
+
redeemedAt: string;
|
|
29
|
+
}): BondIssuanceState;
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.summarizeBondIssuanceState = summarizeBondIssuanceState;
|
|
4
|
+
exports.validateBondDefinition = validateBondDefinition;
|
|
5
|
+
exports.validateBondIssuanceState = validateBondIssuanceState;
|
|
6
|
+
exports.validateBondCrossChecks = validateBondCrossChecks;
|
|
7
|
+
exports.validateBondStateTransition = validateBondStateTransition;
|
|
8
|
+
exports.buildRedeemedBondIssuanceState = buildRedeemedBondIssuanceState;
|
|
9
|
+
const summary_1 = require("../core/summary");
|
|
10
|
+
const errors_1 = require("../core/errors");
|
|
11
|
+
function assertNonEmptyString(value, code, message) {
|
|
12
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
13
|
+
throw new errors_1.ValidationError(message, { code });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function assertFiniteNumber(value, code, message) {
|
|
17
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
18
|
+
throw new errors_1.ValidationError(message, { code });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
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", {
|
|
24
|
+
code: "BOND_STATUS_INVALID",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function validateBondTransition(value) {
|
|
29
|
+
const transition = value;
|
|
30
|
+
if (transition.type !== "ISSUE" && transition.type !== "REDEEM") {
|
|
31
|
+
throw new errors_1.ValidationError("lastTransition.type must be ISSUE or REDEEM", {
|
|
32
|
+
code: "BOND_TRANSITION_TYPE_INVALID",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
assertFiniteNumber(transition.amount, "BOND_TRANSITION_AMOUNT_INVALID", "lastTransition.amount must be a finite number");
|
|
36
|
+
if (transition.amount <= 0) {
|
|
37
|
+
throw new errors_1.ValidationError("lastTransition.amount must be greater than 0", {
|
|
38
|
+
code: "BOND_TRANSITION_AMOUNT_INVALID",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
assertNonEmptyString(transition.at, "BOND_TRANSITION_AT_INVALID", "lastTransition.at must be a non-empty string");
|
|
42
|
+
if (Number.isNaN(Date.parse(transition.at))) {
|
|
43
|
+
throw new errors_1.ValidationError("lastTransition.at must be an ISO8601 string", {
|
|
44
|
+
code: "BOND_TRANSITION_AT_INVALID",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return transition;
|
|
48
|
+
}
|
|
49
|
+
function summarizeBondIssuanceState(state) {
|
|
50
|
+
const canonicalJson = (0, summary_1.stableStringify)(state);
|
|
51
|
+
return { canonicalJson, hash: (0, summary_1.sha256HexUtf8)(canonicalJson) };
|
|
52
|
+
}
|
|
53
|
+
function validateBondDefinition(value) {
|
|
54
|
+
const definition = value;
|
|
55
|
+
assertNonEmptyString(definition?.bondId, "BOND_ID_INVALID", "bondId must be a non-empty string");
|
|
56
|
+
assertNonEmptyString(definition?.issuer, "BOND_ISSUER_INVALID", "issuer must be a non-empty string");
|
|
57
|
+
assertFiniteNumber(definition?.faceValue, "BOND_FACE_VALUE_INVALID", "faceValue must be a finite number");
|
|
58
|
+
assertFiniteNumber(definition?.couponBps, "BOND_COUPON_INVALID", "couponBps must be a finite number");
|
|
59
|
+
assertNonEmptyString(definition?.issueDate, "BOND_ISSUE_DATE_INVALID", "issueDate must be a non-empty string");
|
|
60
|
+
assertFiniteNumber(definition?.maturityDate, "BOND_MATURITY_DATE_INVALID", "maturityDate must be a finite number");
|
|
61
|
+
assertNonEmptyString(definition?.currencyAssetId, "BOND_CURRENCY_INVALID", "currencyAssetId must be a non-empty string");
|
|
62
|
+
assertNonEmptyString(definition?.controllerXonly, "BOND_CONTROLLER_INVALID", "controllerXonly must be a non-empty string");
|
|
63
|
+
return definition;
|
|
64
|
+
}
|
|
65
|
+
function validateBondIssuanceState(value) {
|
|
66
|
+
const issuance = value;
|
|
67
|
+
assertNonEmptyString(issuance?.issuanceId, "BOND_ISSUANCE_ID_INVALID", "issuanceId must be a non-empty string");
|
|
68
|
+
assertNonEmptyString(issuance?.bondId, "BOND_ID_INVALID", "bondId must be a non-empty string");
|
|
69
|
+
assertNonEmptyString(issuance?.issuerEntityId, "BOND_ISSUER_ENTITY_INVALID", "issuerEntityId must be a non-empty string");
|
|
70
|
+
assertFiniteNumber(issuance?.issuedPrincipal, "BOND_ISSUED_PRINCIPAL_INVALID", "issuedPrincipal must be a finite number");
|
|
71
|
+
assertFiniteNumber(issuance?.outstandingPrincipal, "BOND_OUTSTANDING_PRINCIPAL_INVALID", "outstandingPrincipal must be a finite number");
|
|
72
|
+
assertFiniteNumber(issuance?.redeemedPrincipal, "BOND_REDEEMED_PRINCIPAL_INVALID", "redeemedPrincipal must be a finite number");
|
|
73
|
+
assertNonEmptyString(issuance?.currencyAssetId, "BOND_CURRENCY_INVALID", "currencyAssetId must be a non-empty string");
|
|
74
|
+
assertNonEmptyString(issuance?.controllerXonly, "BOND_CONTROLLER_INVALID", "controllerXonly must be a non-empty string");
|
|
75
|
+
assertNonEmptyString(issuance?.issuedAt, "BOND_ISSUED_AT_INVALID", "issuedAt must be a non-empty string");
|
|
76
|
+
assertValidStatus(issuance.status);
|
|
77
|
+
if (issuance.issuedPrincipal <= 0) {
|
|
78
|
+
throw new errors_1.ValidationError("issuedPrincipal must be greater than 0", { code: "BOND_PRINCIPAL_INVARIANT_INVALID" });
|
|
79
|
+
}
|
|
80
|
+
if (issuance.outstandingPrincipal < 0 || issuance.redeemedPrincipal < 0) {
|
|
81
|
+
throw new errors_1.ValidationError("principal values must not be negative", { code: "BOND_PRINCIPAL_INVARIANT_INVALID" });
|
|
82
|
+
}
|
|
83
|
+
if (issuance.issuedPrincipal !== issuance.outstandingPrincipal + issuance.redeemedPrincipal) {
|
|
84
|
+
throw new errors_1.ValidationError("issuedPrincipal must equal outstandingPrincipal + redeemedPrincipal", {
|
|
85
|
+
code: "BOND_PRINCIPAL_INVARIANT_INVALID",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (Number.isNaN(Date.parse(issuance.issuedAt))) {
|
|
89
|
+
throw new errors_1.ValidationError("issuedAt must be an ISO8601 string", { code: "BOND_ISSUED_AT_INVALID" });
|
|
90
|
+
}
|
|
91
|
+
if (issuance.previousStateHash !== undefined && issuance.previousStateHash !== null) {
|
|
92
|
+
assertNonEmptyString(issuance.previousStateHash, "BOND_PREVIOUS_STATE_HASH_INVALID", "previousStateHash must be a non-empty string when provided");
|
|
93
|
+
if (!/^[0-9a-f]{64}$/i.test(issuance.previousStateHash)) {
|
|
94
|
+
throw new errors_1.ValidationError("previousStateHash must be a 64-character hex string", {
|
|
95
|
+
code: "BOND_PREVIOUS_STATE_HASH_INVALID",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const transition = issuance.lastTransition ? validateBondTransition(issuance.lastTransition) : undefined;
|
|
100
|
+
if (issuance.status === "ISSUED") {
|
|
101
|
+
if (issuance.redeemedPrincipal !== 0 || issuance.outstandingPrincipal !== issuance.issuedPrincipal) {
|
|
102
|
+
throw new errors_1.ValidationError("ISSUED state must have full outstanding principal and zero redeemed principal", {
|
|
103
|
+
code: "BOND_STATUS_TRANSITION_INVALID",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (issuance.previousStateHash) {
|
|
107
|
+
throw new errors_1.ValidationError("ISSUED state must not set previousStateHash", {
|
|
108
|
+
code: "BOND_PREVIOUS_STATE_HASH_INVALID",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (transition && transition.type !== "ISSUE") {
|
|
112
|
+
throw new errors_1.ValidationError("ISSUED state may only carry an ISSUE transition", {
|
|
113
|
+
code: "BOND_TRANSITION_TYPE_INVALID",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (issuance.status === "PARTIALLY_REDEEMED") {
|
|
118
|
+
if (issuance.redeemedPrincipal <= 0 || issuance.outstandingPrincipal <= 0) {
|
|
119
|
+
throw new errors_1.ValidationError("PARTIALLY_REDEEMED state must have both redeemed and outstanding principal", {
|
|
120
|
+
code: "BOND_STATUS_TRANSITION_INVALID",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (!issuance.previousStateHash) {
|
|
124
|
+
throw new errors_1.ValidationError("PARTIALLY_REDEEMED state must set previousStateHash", {
|
|
125
|
+
code: "BOND_PREVIOUS_STATE_HASH_INVALID",
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (!transition || transition.type !== "REDEEM") {
|
|
129
|
+
throw new errors_1.ValidationError("PARTIALLY_REDEEMED state must carry a REDEEM transition", {
|
|
130
|
+
code: "BOND_TRANSITION_TYPE_INVALID",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (issuance.status === "REDEEMED") {
|
|
135
|
+
if (issuance.outstandingPrincipal !== 0 || issuance.redeemedPrincipal !== issuance.issuedPrincipal) {
|
|
136
|
+
throw new errors_1.ValidationError("REDEEMED state must have zero outstanding principal and full redeemed principal", {
|
|
137
|
+
code: "BOND_STATUS_TRANSITION_INVALID",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (issuance.redeemedPrincipal <= 0) {
|
|
141
|
+
throw new errors_1.ValidationError("REDEEMED state must have positive redeemed principal", {
|
|
142
|
+
code: "BOND_STATUS_TRANSITION_INVALID",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (!issuance.previousStateHash) {
|
|
146
|
+
throw new errors_1.ValidationError("REDEEMED state must set previousStateHash", {
|
|
147
|
+
code: "BOND_PREVIOUS_STATE_HASH_INVALID",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (!transition || transition.type !== "REDEEM") {
|
|
151
|
+
throw new errors_1.ValidationError("REDEEMED state must carry a REDEEM transition", {
|
|
152
|
+
code: "BOND_TRANSITION_TYPE_INVALID",
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return issuance;
|
|
157
|
+
}
|
|
158
|
+
function validateBondCrossChecks(definition, issuance) {
|
|
159
|
+
const result = {
|
|
160
|
+
bondIdMatch: definition.bondId === issuance.bondId,
|
|
161
|
+
currencyMatch: definition.currencyAssetId === issuance.currencyAssetId,
|
|
162
|
+
controllerMatch: definition.controllerXonly === issuance.controllerXonly,
|
|
163
|
+
principalInvariantValid: issuance.issuedPrincipal > 0 &&
|
|
164
|
+
issuance.outstandingPrincipal >= 0 &&
|
|
165
|
+
issuance.redeemedPrincipal >= 0 &&
|
|
166
|
+
issuance.issuedPrincipal === issuance.outstandingPrincipal + issuance.redeemedPrincipal,
|
|
167
|
+
};
|
|
168
|
+
if (!result.bondIdMatch) {
|
|
169
|
+
throw new errors_1.ValidationError("Bond definition and issuance state bondId do not match", {
|
|
170
|
+
code: "BOND_ID_MISMATCH",
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (!result.currencyMatch) {
|
|
174
|
+
throw new errors_1.ValidationError("Bond definition and issuance state currencyAssetId do not match", {
|
|
175
|
+
code: "BOND_CURRENCY_MISMATCH",
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (!result.controllerMatch) {
|
|
179
|
+
throw new errors_1.ValidationError("Bond definition and issuance state controllerXonly do not match", {
|
|
180
|
+
code: "BOND_CONTROLLER_MISMATCH",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (!result.principalInvariantValid) {
|
|
184
|
+
throw new errors_1.ValidationError("Bond issuance principal invariants are invalid", {
|
|
185
|
+
code: "BOND_PRINCIPAL_INVARIANT_INVALID",
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
function validateBondStateTransition(previous, next) {
|
|
191
|
+
const previousHash = summarizeBondIssuanceState(previous).hash;
|
|
192
|
+
const result = {
|
|
193
|
+
issuanceIdMatch: previous.issuanceId === next.issuanceId,
|
|
194
|
+
bondIdMatch: previous.bondId === next.bondId,
|
|
195
|
+
currencyMatch: previous.currencyAssetId === next.currencyAssetId,
|
|
196
|
+
controllerMatch: previous.controllerXonly === next.controllerXonly,
|
|
197
|
+
issuerEntityMatch: previous.issuerEntityId === next.issuerEntityId,
|
|
198
|
+
issuedPrincipalMatch: previous.issuedPrincipal === next.issuedPrincipal,
|
|
199
|
+
previousStateHashMatch: next.previousStateHash === previousHash,
|
|
200
|
+
redemptionArithmeticValid: false,
|
|
201
|
+
statusProgressionValid: false,
|
|
202
|
+
};
|
|
203
|
+
if (!result.issuanceIdMatch) {
|
|
204
|
+
throw new errors_1.ValidationError("Bond issuance transition issuanceId does not match", {
|
|
205
|
+
code: "BOND_ISSUANCE_ID_MISMATCH",
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
if (!result.bondIdMatch) {
|
|
209
|
+
throw new errors_1.ValidationError("Bond issuance transition bondId does not match", {
|
|
210
|
+
code: "BOND_ID_MISMATCH",
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (!result.currencyMatch) {
|
|
214
|
+
throw new errors_1.ValidationError("Bond issuance transition currencyAssetId does not match", {
|
|
215
|
+
code: "BOND_CURRENCY_MISMATCH",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
if (!result.controllerMatch) {
|
|
219
|
+
throw new errors_1.ValidationError("Bond issuance transition controllerXonly does not match", {
|
|
220
|
+
code: "BOND_CONTROLLER_MISMATCH",
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (!result.issuerEntityMatch) {
|
|
224
|
+
throw new errors_1.ValidationError("Bond issuance transition issuerEntityId does not match", {
|
|
225
|
+
code: "BOND_ISSUER_ENTITY_MISMATCH",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (!result.issuedPrincipalMatch) {
|
|
229
|
+
throw new errors_1.ValidationError("Bond issuance transition issuedPrincipal must remain constant", {
|
|
230
|
+
code: "BOND_ISSUED_PRINCIPAL_MISMATCH",
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (!result.previousStateHashMatch) {
|
|
234
|
+
throw new errors_1.ValidationError("Bond issuance transition previousStateHash does not match the prior state hash", {
|
|
235
|
+
code: "BOND_PREVIOUS_STATE_HASH_MISMATCH",
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const transition = next.lastTransition;
|
|
239
|
+
if (!transition || transition.type !== "REDEEM") {
|
|
240
|
+
throw new errors_1.ValidationError("Bond issuance transition must be a REDEEM transition", {
|
|
241
|
+
code: "BOND_TRANSITION_TYPE_INVALID",
|
|
242
|
+
});
|
|
243
|
+
}
|
|
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;
|
|
250
|
+
if (!result.redemptionArithmeticValid) {
|
|
251
|
+
throw new errors_1.ValidationError("Bond issuance redemption arithmetic is invalid", {
|
|
252
|
+
code: "BOND_REDEMPTION_ARITHMETIC_INVALID",
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
result.statusProgressionValid =
|
|
256
|
+
(previous.status === "ISSUED" || previous.status === "PARTIALLY_REDEEMED") &&
|
|
257
|
+
((next.status === "PARTIALLY_REDEEMED" && next.outstandingPrincipal > 0) ||
|
|
258
|
+
(next.status === "REDEEMED" && next.outstandingPrincipal === 0));
|
|
259
|
+
if (!result.statusProgressionValid) {
|
|
260
|
+
throw new errors_1.ValidationError("Bond issuance status progression is invalid", {
|
|
261
|
+
code: "BOND_STATUS_TRANSITION_INVALID",
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
function buildRedeemedBondIssuanceState(input) {
|
|
267
|
+
assertFiniteNumber(input.amount, "BOND_TRANSITION_AMOUNT_INVALID", "amount must be a finite number");
|
|
268
|
+
if (input.amount <= 0) {
|
|
269
|
+
throw new errors_1.ValidationError("amount must be greater than 0", { code: "BOND_TRANSITION_AMOUNT_INVALID" });
|
|
270
|
+
}
|
|
271
|
+
assertNonEmptyString(input.redeemedAt, "BOND_TRANSITION_AT_INVALID", "redeemedAt must be a non-empty string");
|
|
272
|
+
if (Number.isNaN(Date.parse(input.redeemedAt))) {
|
|
273
|
+
throw new errors_1.ValidationError("redeemedAt must be an ISO8601 string", {
|
|
274
|
+
code: "BOND_TRANSITION_AT_INVALID",
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
const previous = validateBondIssuanceState(input.previous);
|
|
278
|
+
if (previous.status === "REDEEMED") {
|
|
279
|
+
throw new errors_1.ValidationError("Cannot redeem an already redeemed bond issuance state", {
|
|
280
|
+
code: "BOND_ALREADY_REDEEMED",
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (input.amount > previous.outstandingPrincipal) {
|
|
284
|
+
throw new errors_1.ValidationError("Redeem amount must not exceed outstandingPrincipal", {
|
|
285
|
+
code: "BOND_REDEEM_AMOUNT_EXCEEDS_OUTSTANDING",
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
const nextOutstanding = previous.outstandingPrincipal - input.amount;
|
|
289
|
+
const nextRedeemed = previous.redeemedPrincipal + input.amount;
|
|
290
|
+
const next = {
|
|
291
|
+
...previous,
|
|
292
|
+
outstandingPrincipal: nextOutstanding,
|
|
293
|
+
redeemedPrincipal: nextRedeemed,
|
|
294
|
+
previousStateHash: summarizeBondIssuanceState(previous).hash,
|
|
295
|
+
status: nextOutstanding === 0 ? "REDEEMED" : "PARTIALLY_REDEEMED",
|
|
296
|
+
lastTransition: {
|
|
297
|
+
type: "REDEEM",
|
|
298
|
+
amount: input.amount,
|
|
299
|
+
at: input.redeemedAt,
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
return validateBondIssuanceState(next);
|
|
303
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,3 +7,7 @@ export * from "./core/errors";
|
|
|
7
7
|
export { listPresets, getPresetOrThrow } from "./core/presets";
|
|
8
8
|
export { loadArtifact, saveArtifact, normalizeArtifact } from "./core/artifact";
|
|
9
9
|
export { loadDefinitionInput, buildArtifactDefinitionMetadata, verifyDefinitionAgainstArtifact, verifyDefinitionDescriptorAgainstArtifact, } from "./core/definition";
|
|
10
|
+
export { loadStateInput, buildArtifactStateMetadata, verifyStateAgainstArtifact, verifyStateDescriptorAgainstArtifact, } from "./core/state";
|
|
11
|
+
export { defineBond, verifyBond, loadBond, redeemBond, verifyBondTransition, buildBondRedemption, buildBondPayload, buildBondSettlementDescriptor, buildBondSettlementPayload, buildBondTransitionPayload, buildBondRolloverPlan, buildBondMachineRolloverPlan, buildBondMachineSettlementPlan, compileBondTransition, compileBondRedemptionMachine, inspectBondMachineRollover, inspectBondMachineSettlement, inspectBondStateRollover, executeBondStateRollover, executeBondMachineRollover, executeBondMachineSettlement, verifyBondRedemptionMachineArtifact, verifyBondSettlementDescriptor, } from "./domain/bond";
|
|
12
|
+
export { validateBondDefinition, validateBondIssuanceState, validateBondCrossChecks, validateBondStateTransition, buildRedeemedBondIssuanceState, summarizeBondIssuanceState, } from "./domain/bondValidation";
|
|
13
|
+
export { summarizeBondSettlementDescriptor, validateBondSettlementDescriptor, validateBondSettlementMatchesExpected, } from "./domain/bondSettlementValidation";
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.verifyDefinitionDescriptorAgainstArtifact = exports.verifyDefinitionAgainstArtifact = exports.buildArtifactDefinitionMetadata = exports.loadDefinitionInput = exports.normalizeArtifact = exports.saveArtifact = exports.loadArtifact = exports.getPresetOrThrow = exports.listPresets = exports.RelayerClient = exports.DeployedContract = exports.CompiledContract = exports.SimplicityClient = exports.createSimplicityClient = void 0;
|
|
17
|
+
exports.validateBondSettlementMatchesExpected = exports.validateBondSettlementDescriptor = exports.summarizeBondSettlementDescriptor = exports.summarizeBondIssuanceState = exports.buildRedeemedBondIssuanceState = exports.validateBondStateTransition = exports.validateBondCrossChecks = exports.validateBondIssuanceState = exports.validateBondDefinition = exports.verifyBondSettlementDescriptor = exports.verifyBondRedemptionMachineArtifact = exports.executeBondMachineSettlement = exports.executeBondMachineRollover = exports.executeBondStateRollover = exports.inspectBondStateRollover = exports.inspectBondMachineSettlement = exports.inspectBondMachineRollover = exports.compileBondRedemptionMachine = exports.compileBondTransition = exports.buildBondMachineSettlementPlan = exports.buildBondMachineRolloverPlan = exports.buildBondRolloverPlan = exports.buildBondTransitionPayload = exports.buildBondSettlementPayload = exports.buildBondSettlementDescriptor = exports.buildBondPayload = exports.buildBondRedemption = exports.verifyBondTransition = exports.redeemBond = exports.loadBond = exports.verifyBond = exports.defineBond = exports.verifyStateDescriptorAgainstArtifact = exports.verifyStateAgainstArtifact = exports.buildArtifactStateMetadata = exports.loadStateInput = exports.verifyDefinitionDescriptorAgainstArtifact = exports.verifyDefinitionAgainstArtifact = exports.buildArtifactDefinitionMetadata = exports.loadDefinitionInput = exports.normalizeArtifact = exports.saveArtifact = exports.loadArtifact = exports.getPresetOrThrow = exports.listPresets = exports.RelayerClient = exports.DeployedContract = exports.CompiledContract = exports.SimplicityClient = exports.createSimplicityClient = void 0;
|
|
18
18
|
var SimplicityClient_1 = require("./client/SimplicityClient");
|
|
19
19
|
Object.defineProperty(exports, "createSimplicityClient", { enumerable: true, get: function () { return SimplicityClient_1.createSimplicityClient; } });
|
|
20
20
|
Object.defineProperty(exports, "SimplicityClient", { enumerable: true, get: function () { return SimplicityClient_1.SimplicityClient; } });
|
|
@@ -38,3 +38,43 @@ Object.defineProperty(exports, "loadDefinitionInput", { enumerable: true, get: f
|
|
|
38
38
|
Object.defineProperty(exports, "buildArtifactDefinitionMetadata", { enumerable: true, get: function () { return definition_1.buildArtifactDefinitionMetadata; } });
|
|
39
39
|
Object.defineProperty(exports, "verifyDefinitionAgainstArtifact", { enumerable: true, get: function () { return definition_1.verifyDefinitionAgainstArtifact; } });
|
|
40
40
|
Object.defineProperty(exports, "verifyDefinitionDescriptorAgainstArtifact", { enumerable: true, get: function () { return definition_1.verifyDefinitionDescriptorAgainstArtifact; } });
|
|
41
|
+
var state_1 = require("./core/state");
|
|
42
|
+
Object.defineProperty(exports, "loadStateInput", { enumerable: true, get: function () { return state_1.loadStateInput; } });
|
|
43
|
+
Object.defineProperty(exports, "buildArtifactStateMetadata", { enumerable: true, get: function () { return state_1.buildArtifactStateMetadata; } });
|
|
44
|
+
Object.defineProperty(exports, "verifyStateAgainstArtifact", { enumerable: true, get: function () { return state_1.verifyStateAgainstArtifact; } });
|
|
45
|
+
Object.defineProperty(exports, "verifyStateDescriptorAgainstArtifact", { enumerable: true, get: function () { return state_1.verifyStateDescriptorAgainstArtifact; } });
|
|
46
|
+
var bond_1 = require("./domain/bond");
|
|
47
|
+
Object.defineProperty(exports, "defineBond", { enumerable: true, get: function () { return bond_1.defineBond; } });
|
|
48
|
+
Object.defineProperty(exports, "verifyBond", { enumerable: true, get: function () { return bond_1.verifyBond; } });
|
|
49
|
+
Object.defineProperty(exports, "loadBond", { enumerable: true, get: function () { return bond_1.loadBond; } });
|
|
50
|
+
Object.defineProperty(exports, "redeemBond", { enumerable: true, get: function () { return bond_1.redeemBond; } });
|
|
51
|
+
Object.defineProperty(exports, "verifyBondTransition", { enumerable: true, get: function () { return bond_1.verifyBondTransition; } });
|
|
52
|
+
Object.defineProperty(exports, "buildBondRedemption", { enumerable: true, get: function () { return bond_1.buildBondRedemption; } });
|
|
53
|
+
Object.defineProperty(exports, "buildBondPayload", { enumerable: true, get: function () { return bond_1.buildBondPayload; } });
|
|
54
|
+
Object.defineProperty(exports, "buildBondSettlementDescriptor", { enumerable: true, get: function () { return bond_1.buildBondSettlementDescriptor; } });
|
|
55
|
+
Object.defineProperty(exports, "buildBondSettlementPayload", { enumerable: true, get: function () { return bond_1.buildBondSettlementPayload; } });
|
|
56
|
+
Object.defineProperty(exports, "buildBondTransitionPayload", { enumerable: true, get: function () { return bond_1.buildBondTransitionPayload; } });
|
|
57
|
+
Object.defineProperty(exports, "buildBondRolloverPlan", { enumerable: true, get: function () { return bond_1.buildBondRolloverPlan; } });
|
|
58
|
+
Object.defineProperty(exports, "buildBondMachineRolloverPlan", { enumerable: true, get: function () { return bond_1.buildBondMachineRolloverPlan; } });
|
|
59
|
+
Object.defineProperty(exports, "buildBondMachineSettlementPlan", { enumerable: true, get: function () { return bond_1.buildBondMachineSettlementPlan; } });
|
|
60
|
+
Object.defineProperty(exports, "compileBondTransition", { enumerable: true, get: function () { return bond_1.compileBondTransition; } });
|
|
61
|
+
Object.defineProperty(exports, "compileBondRedemptionMachine", { enumerable: true, get: function () { return bond_1.compileBondRedemptionMachine; } });
|
|
62
|
+
Object.defineProperty(exports, "inspectBondMachineRollover", { enumerable: true, get: function () { return bond_1.inspectBondMachineRollover; } });
|
|
63
|
+
Object.defineProperty(exports, "inspectBondMachineSettlement", { enumerable: true, get: function () { return bond_1.inspectBondMachineSettlement; } });
|
|
64
|
+
Object.defineProperty(exports, "inspectBondStateRollover", { enumerable: true, get: function () { return bond_1.inspectBondStateRollover; } });
|
|
65
|
+
Object.defineProperty(exports, "executeBondStateRollover", { enumerable: true, get: function () { return bond_1.executeBondStateRollover; } });
|
|
66
|
+
Object.defineProperty(exports, "executeBondMachineRollover", { enumerable: true, get: function () { return bond_1.executeBondMachineRollover; } });
|
|
67
|
+
Object.defineProperty(exports, "executeBondMachineSettlement", { enumerable: true, get: function () { return bond_1.executeBondMachineSettlement; } });
|
|
68
|
+
Object.defineProperty(exports, "verifyBondRedemptionMachineArtifact", { enumerable: true, get: function () { return bond_1.verifyBondRedemptionMachineArtifact; } });
|
|
69
|
+
Object.defineProperty(exports, "verifyBondSettlementDescriptor", { enumerable: true, get: function () { return bond_1.verifyBondSettlementDescriptor; } });
|
|
70
|
+
var bondValidation_1 = require("./domain/bondValidation");
|
|
71
|
+
Object.defineProperty(exports, "validateBondDefinition", { enumerable: true, get: function () { return bondValidation_1.validateBondDefinition; } });
|
|
72
|
+
Object.defineProperty(exports, "validateBondIssuanceState", { enumerable: true, get: function () { return bondValidation_1.validateBondIssuanceState; } });
|
|
73
|
+
Object.defineProperty(exports, "validateBondCrossChecks", { enumerable: true, get: function () { return bondValidation_1.validateBondCrossChecks; } });
|
|
74
|
+
Object.defineProperty(exports, "validateBondStateTransition", { enumerable: true, get: function () { return bondValidation_1.validateBondStateTransition; } });
|
|
75
|
+
Object.defineProperty(exports, "buildRedeemedBondIssuanceState", { enumerable: true, get: function () { return bondValidation_1.buildRedeemedBondIssuanceState; } });
|
|
76
|
+
Object.defineProperty(exports, "summarizeBondIssuanceState", { enumerable: true, get: function () { return bondValidation_1.summarizeBondIssuanceState; } });
|
|
77
|
+
var bondSettlementValidation_1 = require("./domain/bondSettlementValidation");
|
|
78
|
+
Object.defineProperty(exports, "summarizeBondSettlementDescriptor", { enumerable: true, get: function () { return bondSettlementValidation_1.summarizeBondSettlementDescriptor; } });
|
|
79
|
+
Object.defineProperty(exports, "validateBondSettlementDescriptor", { enumerable: true, get: function () { return bondSettlementValidation_1.validateBondSettlementDescriptor; } });
|
|
80
|
+
Object.defineProperty(exports, "validateBondSettlementMatchesExpected", { enumerable: true, get: function () { return bondSettlementValidation_1.validateBondSettlementMatchesExpected; } });
|