@hazbase/simplicity 0.0.4 → 0.1.1

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 (45) hide show
  1. package/README.md +679 -42
  2. package/dist/cli.js +2268 -5
  3. package/dist/client/SimplicityClient.d.ts +64 -25
  4. package/dist/client/SimplicityClient.js +65 -3
  5. package/dist/core/executor.js +67 -92
  6. package/dist/core/outputBinding.d.ts +61 -0
  7. package/dist/core/outputBinding.js +552 -0
  8. package/dist/core/schnorr.d.ts +5 -0
  9. package/dist/core/schnorr.js +34 -0
  10. package/dist/core/types.d.ts +586 -1
  11. package/dist/docs/definitions/bond-anchor.simf +19 -0
  12. package/dist/docs/definitions/bond-definition.json +10 -0
  13. package/dist/docs/definitions/bond-descriptor-bound-settlement-machine.simf +144 -0
  14. package/dist/docs/definitions/bond-issuance-anchor.simf +26 -0
  15. package/dist/docs/definitions/bond-issuance-state-partial-redemption.json +18 -0
  16. package/dist/docs/definitions/bond-issuance-state-redeemed.json +18 -0
  17. package/dist/docs/definitions/bond-issuance-state.json +12 -0
  18. package/dist/docs/definitions/bond-redemption-state-machine.simf +118 -0
  19. package/dist/docs/definitions/bond-redemption-transition.simf +41 -0
  20. package/dist/docs/definitions/bond-script-bound-settlement-machine.simf +142 -0
  21. package/dist/docs/definitions/fund-capital-call-open.simf +82 -0
  22. package/dist/docs/definitions/fund-capital-call-refund-only.simf +33 -0
  23. package/dist/docs/definitions/fund-capital-call-state.json +11 -0
  24. package/dist/docs/definitions/fund-definition.json +8 -0
  25. package/dist/docs/definitions/fund-distribution-claim.simf +57 -0
  26. package/dist/docs/definitions/recursive-delay-direct-next.simf +60 -0
  27. package/dist/docs/definitions/recursive-delay-optional.simf +88 -0
  28. package/dist/docs/definitions/recursive-delay-required.simf +72 -0
  29. package/dist/docs/definitions/recursive-delay.simf +83 -0
  30. package/dist/docs/definitions/recursive-policy-transfer-machine.simf +65 -0
  31. package/dist/domain/bond.d.ts +9855 -1
  32. package/dist/domain/bond.js +2156 -1
  33. package/dist/domain/bondSettlementValidation.d.ts +20 -0
  34. package/dist/domain/bondSettlementValidation.js +150 -0
  35. package/dist/domain/bondValidation.d.ts +26 -0
  36. package/dist/domain/bondValidation.js +278 -3
  37. package/dist/domain/fund.d.ts +2069 -0
  38. package/dist/domain/fund.js +1384 -0
  39. package/dist/domain/fundValidation.d.ts +122 -0
  40. package/dist/domain/fundValidation.js +635 -0
  41. package/dist/domain/policies.d.ts +1051 -0
  42. package/dist/domain/policies.js +1605 -0
  43. package/dist/index.d.ts +7 -2
  44. package/dist/index.js +92 -1
  45. package/package.json +15 -2
package/README.md CHANGED
@@ -11,7 +11,411 @@ This SDK is designed to help Node developers get productive quickly, but it is s
11
11
 
12
12
  Consumer validation note:
13
13
  - The published npm package has been validated from a fresh external Node.js project using `npm install @hazbase/simplicity`.
14
- - Verified flows include preset-based contract execution, custom `.simf` execution, and relayer-backed gasless execution on `liquidtestnet`.
14
+ - Verified flows include preset-based contract execution, custom `.simf` execution, relayer-backed gasless execution, and LP-fund business-flow validation on `liquidtestnet`.
15
+
16
+ ## Recursive Policy SDK
17
+
18
+ The SDK now also exposes a generic `sdk.policies` domain for **parametric recursive covenants**.
19
+
20
+ That means you can model a UTXO as:
21
+ - a policy template definition,
22
+ - a concrete policy state for the current recipient,
23
+ - and a next constrained output that can re-apply the same `.simf` template to the next hop.
24
+
25
+ Initial reference implementation:
26
+ - `recursive-delay-required.simf`
27
+ - `recursive-delay-optional.simf`
28
+
29
+ Main entrypoints:
30
+ - `sdk.outputBinding.describeSupport()`
31
+ - `sdk.outputBinding.evaluateSupport(...)`
32
+ - `sdk.policies.describeTemplate(...)`
33
+ - `sdk.policies.validateTemplateParams(...)`
34
+ - `sdk.policies.issue(...)`
35
+ - `sdk.policies.prepareTransfer(...)`
36
+ - `sdk.policies.inspectTransfer(...)`
37
+ - `sdk.policies.executeTransfer(...)`
38
+ - `sdk.policies.verifyState(...)`
39
+ - `sdk.policies.verifyTransfer(...)`
40
+ - `sdk.policies.exportEvidence(...)`
41
+
42
+ Reference examples:
43
+ - [describe-policy-template.ts](./examples/describe-policy-template.ts)
44
+ - [custom-recursive-delay-required.manifest.json](./examples/custom-recursive-delay-required.manifest.json)
45
+ - [show-required-policy-transfer.ts](./examples/show-required-policy-transfer.ts)
46
+ - [show-optional-policy-transfer.ts](./examples/show-optional-policy-transfer.ts)
47
+ - [execute-required-policy-transfer.ts](./examples/execute-required-policy-transfer.ts)
48
+ - [execute-optional-policy-transfer.ts](./examples/execute-optional-policy-transfer.ts)
49
+
50
+ ### Policy Quickstart
51
+
52
+ The shortest happy path is:
53
+ 1. Describe the template and validate params
54
+ 2. Issue the first policy-aware UTXO
55
+ 3. Prepare, inspect, execute, and verify the next transfer
56
+
57
+ Required 1tx recursive hop:
58
+
59
+ ```ts
60
+ const templates = sdk.policies.listTemplates();
61
+ const bindingSupport = sdk.outputBinding.describeSupport();
62
+ const bindingEvaluation = sdk.outputBinding.evaluateSupport({
63
+ assetId: "bitcoin",
64
+ requestedBindingMode: "descriptor-bound",
65
+ outputForm: { amountForm: "confidential" },
66
+ });
67
+
68
+ const manifest = sdk.policies.describeTemplate({
69
+ templateId: "recursive-delay",
70
+ propagationMode: "required",
71
+ });
72
+
73
+ const params = sdk.policies.validateTemplateParams({
74
+ templateId: manifest.templateId,
75
+ propagationMode: "required",
76
+ params: { lockDistanceBlocks: 2 },
77
+ });
78
+
79
+ const externalManifest = await sdk.policies.loadTemplateManifest({
80
+ manifestPath: "./examples/custom-recursive-delay-required.manifest.json",
81
+ });
82
+
83
+ const issued = await sdk.policies.issue({
84
+ recipient: { mode: "policy", recipientXonly: currentRecipientXonly },
85
+ template: { templateId: "recursive-delay", value: { policyTemplateId: "recursive-delay" } },
86
+ params,
87
+ amountSat: 6000,
88
+ assetId: "bitcoin",
89
+ propagationMode: "required",
90
+ });
91
+
92
+ const prepared = await sdk.policies.prepareTransfer({
93
+ currentArtifact: issued.compiled.artifact,
94
+ template: { templateId: "recursive-delay", value: { policyTemplateId: "recursive-delay" } },
95
+ currentStateValue: issued.state,
96
+ nextReceiver: { mode: "policy", recipientXonly: nextRecipientXonly },
97
+ nextAmountSat: 6000,
98
+ nextParams: { lockDistanceBlocks: 2 },
99
+ outputBindingMode: "descriptor-bound",
100
+ });
101
+ ```
102
+
103
+ `sdk.outputBinding.describeSupport()` is the public support matrix for generalized binding. It tells you:
104
+ - which output forms can be auto-derived today,
105
+ - when manual `nextOutputHash` is still allowed,
106
+ - when `descriptor-bound` falls back to `script-bound`,
107
+ - and which runtime paths are validated locally vs on testnet.
108
+
109
+ `sdk.outputBinding.evaluateSupport(...)` is the quick deterministic answer for a specific scenario. It tells you:
110
+ - the requested binding mode,
111
+ - the resolved binding mode,
112
+ - the reason code,
113
+ - which unsupported features are blocking auto-derive,
114
+ - and whether the manual-hash path would keep `descriptor-bound` available.
115
+
116
+ If you want the `optional` branch instead, swap:
117
+ - `propagationMode: "optional"`
118
+ - `nextReceiver: { mode: "plain", address: ... }` for plain exit, or keep `mode: "policy"` for the recursive branch
119
+
120
+ The matching CLI flow is:
121
+ - `policy list-templates`
122
+ - `binding describe-support`
123
+ - `binding evaluate-support`
124
+ - `policy describe-template`
125
+ - `policy validate-template-params`
126
+ - `policy issue`
127
+ - `policy build-output-descriptor`
128
+ - `policy prepare-transfer`
129
+ - `policy inspect-transfer`
130
+ - `policy execute-transfer`
131
+ - `policy verify-state`
132
+ - `policy verify-transfer`
133
+ - `policy export-evidence`
134
+
135
+ For a testnet runtime example that spends an already-funded current policy UTXO, see:
136
+ - [execute-required-policy-transfer.ts](./examples/execute-required-policy-transfer.ts)
137
+ - [execute-optional-policy-transfer.ts](./examples/execute-optional-policy-transfer.ts)
138
+
139
+ The intended model is:
140
+ - `propagationMode: "required"`: next hop must remain policy-aware
141
+ - `propagationMode: "optional"`: next hop may remain policy-aware or exit to a plain address
142
+ - `propagationMode: "none"`: the policy ends at this hop
143
+
144
+ In `required` mode, the current state uses a **1tx direct hop**. The contract checks the current recipient, the relative timelock, the fee output shape, and the next constrained output script hash in the same spend.
145
+ In `optional` mode, the same current state can either:
146
+ - exit to a plain address, or
147
+ - bind the next constrained output in the same transaction
148
+ and the verification report marks the recursive branch as `conditional-hop`.
149
+
150
+ Current policy enforcement labels:
151
+ - `direct-hop`: `required` 1tx current -> next constrained output
152
+ - `conditional-hop`: `optional` mode when the recursive branch is chosen
153
+ - `sdk-path`: plain exit / no recursive enforcement
154
+
155
+ If a caller can supply `nextOutputHash`, both recursive policy templates can also take the stronger `descriptor-bound` path and compare `output_hash(0)` at runtime. In that case the amount becomes runtime-bound together with the rest of the output descriptor.
156
+ To make that path easier to use from the public policy API, the SDK now also exposes `sdk.policies.buildOutputDescriptor(...)` and the CLI command `policy build-output-descriptor`. These help derive the next constrained output address, script hash, and canonical descriptor summary before the transfer call.
157
+ The public binding paths are now:
158
+ - `explicit-v1`: `buildOutputDescriptor(...)` auto-derives `nextOutputHash` from high-level inputs when the asset is supplied as `bitcoin` or as a 64-character asset id
159
+ - `raw-output-v1`: advanced callers pass `assetBytesHex`, `amountBytesHex`, `nonceBytesHex`, plus either `scriptPubKeyHex` or `scriptPubKeyHashHex`, and either `rangeProofHex` or `rangeProofHashHex`, and the SDK derives `output_hash(0)` deterministically
160
+ - `manual-hash`: callers supply `nextOutputHash` directly and keep `descriptor-bound` even when auto-derive is unavailable or intentionally bypassed
161
+ - unsupported: the SDK falls back to `script-bound` and records the reason explicitly
162
+ Advanced callers can also pass output-form hints (`assetForm`, `amountForm`, `nonceForm`, `rangeProofForm`) to make confidential/generalized shapes explicit in the descriptor metadata.
163
+ Elements excludes surjection proofs from `output_hash(0)`, so the current generalized binding surface does not require `surjectionProofHex`.
164
+ If that auto-derivation is not available, the SDK now falls back to `script-bound` and records the fallback reason in the verification report instead of silently pretending the stronger mode succeeded.
165
+ The descriptor build result, transfer verification report, and evidence bundle now all carry the same derivation metadata:
166
+ - `supportedForm`
167
+ - `reasonCode`
168
+ - `autoDerived`
169
+ - `fallbackReason`
170
+ - `bindingInputs`
171
+
172
+ The policy verification and evidence JSON shapes are now versioned:
173
+ - `PolicyVerificationReport.schemaVersion = "policy-verification-report/v1"`
174
+ - `PolicyEvidenceBundle.schemaVersion = "policy-evidence-bundle/v1"`
175
+ and built-in/external template manifests use:
176
+ - `PolicyTemplateManifest.manifestVersion = "policy-template-manifest/v1"`
177
+
178
+ For relative timelocks, the public policy flow now also derives the contract input sequence from `lockDistanceBlocks`, so `check_lock_distance(...)` can be exercised on testnet without dropping back to the default spend sequence.
179
+
180
+ The public verification report now uses the same trust vocabulary across policy flows:
181
+ - `committed`
182
+ - `runtimeBound`
183
+ - `sdkVerified`
184
+
185
+ When an output binding is present, the report can also include:
186
+ - `supportedForm`
187
+ - `reasonCode`
188
+ - `nextOutputHash`
189
+ - `autoDerived`
190
+ - `fallbackReason`
191
+ - `bindingInputs`
192
+
193
+ This is intentionally explicit about current scope:
194
+ - `explicit-v1`:
195
+ - explicit asset
196
+ - explicit amount
197
+ - null nonce
198
+ - empty range proof
199
+ - asset input as `bitcoin` or a 64-character asset id
200
+ - `raw-output-v1`:
201
+ - caller provides raw output field bytes
202
+ - SDK derives `output_hash(0)` from those bytes deterministically
203
+ - `manual-hash`:
204
+ - caller provides `nextOutputHash` directly
205
+ - not yet treated as a default path:
206
+ - wallet/RPC-driven confidential output reconstruction
207
+
208
+ For the current runtime validation scope and timelock test-environment caveats, see [docs/design/recursive-policy-runtime-validation.md](./docs/design/recursive-policy-runtime-validation.md).
209
+ Reproducible policy confidence commands:
210
+ - `npm run e2e:policy-local`
211
+ - `POLICY_OUTPUT_BINDING_MODE=script-bound npm run e2e:policy-testnet`
212
+ - `POLICY_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:policy-testnet`
213
+ - `npm run e2e:policy-consumer`
214
+
215
+ ## LP Fund Settlement Layer
216
+
217
+ The SDK now also exposes `sdk.funds` as a dedicated business layer for **LP fund settlement on Liquid**.
218
+
219
+ Security-first vNext model:
220
+ - capital calls are explicit `open -> rollover -> refund-only`
221
+ - manager claim exists only on the `open` artifact
222
+ - LP refund exists only on the `refund-only` artifact
223
+ - `LPPositionReceipt` is an off-chain canonical document wrapped in a manager-attested envelope
224
+ - closing accepts only the latest attested receipt envelope
225
+
226
+ This layer is intentionally narrower than a full fund-admin system. It focuses on:
227
+ - capital call funding / manager claim / rollover / LP refund
228
+ - signed `LPPositionReceiptEnvelope` generation and reconciliation
229
+ - later one-shot distribution claim contracts
230
+ - finality / close-out evidence
231
+
232
+ It does **not** introduce `sdk.rwas`. The public architecture remains:
233
+ - `sdk.outputBinding`: shared binding support / fallback contract
234
+ - `sdk.policies`: generic recursive covenant engine
235
+ - `sdk.bonds`: credit / bond business layer
236
+ - `sdk.funds`: LP fund settlement business layer
237
+
238
+ Main entrypoints:
239
+ - `sdk.funds.define(...)`
240
+ - `sdk.funds.verify(...)`
241
+ - `sdk.funds.load(...)`
242
+ - `sdk.funds.prepareCapitalCall(...)`
243
+ - `sdk.funds.inspectCapitalCallClaim(...)`
244
+ - `sdk.funds.executeCapitalCallClaim(...)`
245
+ - `sdk.funds.inspectCapitalCallRollover(...)`
246
+ - `sdk.funds.executeCapitalCallRollover(...)`
247
+ - `sdk.funds.inspectCapitalCallRefund(...)`
248
+ - `sdk.funds.executeCapitalCallRefund(...)`
249
+ - `sdk.funds.verifyCapitalCall(...)`
250
+ - `sdk.funds.signPositionReceipt(...)`
251
+ - `sdk.funds.verifyPositionReceipt(...)`
252
+ - `sdk.funds.prepareDistribution(...)`
253
+ - `sdk.funds.inspectDistributionClaim(...)`
254
+ - `sdk.funds.executeDistributionClaim(...)`
255
+ - `sdk.funds.verifyDistribution(...)`
256
+ - `sdk.funds.reconcilePosition(...)`
257
+ - `sdk.funds.prepareClosing(...)`
258
+ - `sdk.funds.verifyClosing(...)`
259
+ - `sdk.funds.exportEvidence(...)`
260
+ - `sdk.funds.exportFinalityPayload(...)`
261
+
262
+ Reference examples:
263
+ - [show-fund-claim-close-flow.ts](./examples/show-fund-claim-close-flow.ts)
264
+ - [show-fund-refund-flow.ts](./examples/show-fund-refund-flow.ts)
265
+ - [fund-definition.json](./docs/definitions/fund-definition.json)
266
+ - [fund-capital-call-state.json](./docs/definitions/fund-capital-call-state.json)
267
+
268
+ ### Fund Quickstart
269
+
270
+ The intended split for LP fund workflows is:
271
+
272
+ - off-chain:
273
+ - KYC/AML
274
+ - subscription docs
275
+ - capital account / waterfall / NAV
276
+ - allocation calculation
277
+ - on-chain SDK:
278
+ - capital call funding / claim / rollover / refund
279
+ - distribution payout
280
+ - finality / close-out evidence
281
+
282
+ Minimal flow:
283
+
284
+ ```ts
285
+ const capitalCall = await sdk.funds.prepareCapitalCall({
286
+ definitionPath: "./docs/definitions/fund-definition.json",
287
+ capitalCallPath: "./docs/definitions/fund-capital-call-state.json",
288
+ });
289
+
290
+ const initialReceipt = buildLPPositionReceipt({
291
+ positionId: "POS-001",
292
+ capitalCall: capitalCall.capitalCallValue,
293
+ effectiveAt: "2026-03-18T00:00:00Z",
294
+ });
295
+
296
+ const signedInitialReceipt = await sdk.funds.signPositionReceipt({
297
+ definitionPath: "./docs/definitions/fund-definition.json",
298
+ positionReceiptValue: initialReceipt,
299
+ signer: { type: "schnorrPrivkeyHex", privkeyHex: managerPrivkeyHex },
300
+ signedAt: "2026-03-18T00:00:00Z",
301
+ });
302
+
303
+ const firstDistribution = await sdk.funds.prepareDistribution({
304
+ definitionPath: "./docs/definitions/fund-definition.json",
305
+ positionReceiptValue: signedInitialReceipt.positionReceiptEnvelope,
306
+ distributionId: "DIST-001",
307
+ assetId: capitalCall.capitalCallValue.currencyAssetId,
308
+ amountSat: 2000,
309
+ approvedAt: "2027-03-18T00:00:00Z",
310
+ });
311
+
312
+ const afterFirst = await sdk.funds.reconcilePosition({
313
+ definitionPath: "./docs/definitions/fund-definition.json",
314
+ positionReceiptValue: signedInitialReceipt.positionReceiptEnvelope,
315
+ distributionValue: firstDistribution.distributionValue,
316
+ signer: { type: "schnorrPrivkeyHex", privkeyHex: managerPrivkeyHex },
317
+ signedAt: "2027-03-18T00:00:00Z",
318
+ });
319
+
320
+ const secondDistribution = await sdk.funds.prepareDistribution({
321
+ definitionPath: "./docs/definitions/fund-definition.json",
322
+ positionReceiptValue: afterFirst.reconciledReceiptEnvelope,
323
+ distributionId: "DIST-002",
324
+ assetId: capitalCall.capitalCallValue.currencyAssetId,
325
+ amountSat: initialReceipt.fundedAmount - 2000,
326
+ approvedAt: "2028-03-18T00:00:00Z",
327
+ });
328
+
329
+ const afterSecond = await sdk.funds.reconcilePosition({
330
+ definitionPath: "./docs/definitions/fund-definition.json",
331
+ positionReceiptValue: afterFirst.reconciledReceiptEnvelope,
332
+ distributionValue: secondDistribution.distributionValue,
333
+ signer: { type: "schnorrPrivkeyHex", privkeyHex: managerPrivkeyHex },
334
+ signedAt: "2028-03-18T00:00:00Z",
335
+ });
336
+
337
+ const closing = await sdk.funds.prepareClosing({
338
+ definitionPath: "./docs/definitions/fund-definition.json",
339
+ positionReceiptValue: afterSecond.reconciledReceiptEnvelope,
340
+ closingId: "CLOSE-001",
341
+ finalDistributionHashes: [
342
+ firstDistribution.distributionSummary.hash,
343
+ secondDistribution.distributionSummary.hash,
344
+ ],
345
+ closedAt: "2029-03-18T00:00:00Z",
346
+ });
347
+ ```
348
+
349
+ The split examples are:
350
+ - `show-fund-claim-close-flow.ts`: receipt envelope -> two distributions -> manager-attested reconciliation -> closing/finality
351
+ - `show-fund-refund-flow.ts`: open capital call -> rollover -> refund-only evidence/finality
352
+
353
+ The matching CLI flow is:
354
+ - `fund define`
355
+ - `fund verify`
356
+ - `fund prepare-capital-call`
357
+ - `fund inspect-capital-call-claim`
358
+ - `fund execute-capital-call-claim`
359
+ - `fund inspect-capital-call-rollover`
360
+ - `fund execute-capital-call-rollover`
361
+ - `fund inspect-capital-call-refund`
362
+ - `fund execute-capital-call-refund`
363
+ - `fund verify-capital-call`
364
+ - `fund sign-position-receipt`
365
+ - `fund verify-position-receipt`
366
+ - `fund prepare-distribution`
367
+ - `fund inspect-distribution-claim`
368
+ - `fund execute-distribution-claim`
369
+ - `fund verify-distribution`
370
+ - `fund reconcile-position`
371
+ - `fund prepare-closing`
372
+ - `fund verify-closing`
373
+ - `fund export-evidence`
374
+ - `fund export-finality-payload`
375
+
376
+ Reproducible fund confidence commands:
377
+ - `npm run e2e:fund-local`
378
+ - `npm run e2e:fund-consumer`
379
+ - `FUND_OUTPUT_BINDING_MODE=script-bound npm run e2e:fund-testnet`
380
+ - `FUND_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:fund-testnet`
381
+ - `FUND_FLOW_MODE=refund FUND_OUTPUT_BINDING_MODE=script-bound npm run e2e:fund-testnet`
382
+ - `FUND_OUTPUT_BINDING_MODE=script-bound FUND_DISTRIBUTION_AMOUNTS_SAT=2000,4000 FUND_DISTRIBUTION_IDS=DIST-...-1,DIST-...-2 FUND_APPROVED_ATS=2027-03-18T00:00:00Z,2028-03-18T00:00:00Z npm run e2e:fund-testnet`
383
+ - `FUND_OUTPUT_BINDING_MODE=descriptor-bound FUND_DISTRIBUTION_AMOUNTS_SAT=2000,4000 FUND_DISTRIBUTION_IDS=DIST-...-1,DIST-...-2 FUND_APPROVED_ATS=2027-03-18T00:00:00Z,2028-03-18T00:00:00Z npm run e2e:fund-testnet`
384
+
385
+ Security notes for `sdk.funds`:
386
+ - on-chain enforced:
387
+ - `open` capital call allows manager claim only
388
+ - `refund-only` capital call allows LP refund only
389
+ - cutoff height is committed into the `open` artifact
390
+ - output binding uses the shared `script-bound` / `descriptor-bound` / `raw-output-v1` engine
391
+ - off-chain attested:
392
+ - `LPPositionReceiptEnvelope`
393
+ - manager attestation over receipt hash and sequence
394
+ - operationally enforced:
395
+ - watcher/keeper monitors `open` capital calls through `claimCutoffHeight`
396
+ - watcher/keeper submits rollover after cutoff
397
+ - LP refund becomes operationally available only after rollover confirmation
398
+
399
+ Latest fund testnet reruns:
400
+
401
+ | Flow | Funding txid | Main execution txid(s) | Closing / receipt | Rerun command |
402
+ | --- | --- | --- | --- | --- |
403
+ | `script-bound` claim-close | `a488a5c6e7c56ecca8d5860bf495590d38cfe8087f678f5664241b8eb90396de` | claim `43974a18c048f67f3399b614ea476ffb87c4622d8083636d1aeacbb51747d94d`; distributions `90fb3abdb000d9f66a18b1e2061fa06b56a51fe64513d358c47cc992c374e209`, `0644820b7fee4c11a9a66dbd1a14733ed2420435449717f4895d589f55b05b30` | receipt `f1f294c0f33405f31abbdd8a3a258e57c614bd381b61a7fba2ae420a35e77c70`; closing `19dfd60dfe04cbe1f145a2ab112ef5cf30a087e084837fa7bcce3ed80df72a2a` | `FUND_OUTPUT_BINDING_MODE=script-bound npm run e2e:fund-testnet` |
404
+ | `descriptor-bound` claim-close | `201b66c3843b9999703b08d242c547ca1d1f35619ca99e916d93e4ed70338bc6` | claim `c27621a27cb3b99b4d960cfa4be98de925beed7c3a7bd799fd3f88a3cb680762`; distributions `d4066986e90a9faa000032198307955583e318c0afd09d98691db7b07dc1b022`, `bbfe830790ef5a86a5595d0dccdd2a344efc7d3eebe7a597be1f2a5c2330dabb` | receipt `05b3e4b2c33518b22bd28939cef36972e7fe0c7273b24a6867fba40a0e374fce`; closing `b1ff75e989628130ec56e753f7bc48cc0648b82272c90dfee03f7582879835dd` | `FUND_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:fund-testnet` |
405
+ | `script-bound` refund | `3adf9025c4656d099752f2e5f43738495fbad712177b75f9330c05273eff12ff` | rollover `8b1224e808ad9824d7bf85924a07d62c75c038987a4163b2e450c455b962ed9d`; refund `6be225448ff062e1f2e8992cf3319601098b7b19edb0ea27823487e9a881f415` | refund-only settlement | `FUND_FLOW_MODE=refund FUND_OUTPUT_BINDING_MODE=script-bound npm run e2e:fund-testnet` |
406
+
407
+ Current runtime truth for claim-close / refund commands, latest txids, and rerun guidance lives in:
408
+ - [docs/design/fund-runtime-validation.md](./docs/design/fund-runtime-validation.md)
409
+
410
+ Latest Bond testnet reruns:
411
+
412
+ | Mode | Funding txid | Execution txid | Rerun command |
413
+ | --- | --- | --- | --- |
414
+ | `script-bound` | `1c982864ef6c83da4eb7f8018edc4cbdff439db7c6366984b3f85ad4937e2c4f` | `d659c4bdce6b32650ff58ac37ccaa55209a9f04d5dc4595f956fad034089f580` | `BOND_OUTPUT_BINDING_MODE=script-bound npm run e2e:bond-testnet` |
415
+ | `descriptor-bound` | `72d0015b51a74c3cc81f7abb74a4f6f894c7f7bbd1e83647939459d7b40e504f` | `85e0830a7b2ba33ca37d5f11bd981938418fc472e98657095680ada71387974c` | `BOND_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:bond-testnet` |
416
+
417
+ For current fund runtime scope and caveats, see:
418
+ - [docs/design/fund-runtime-validation.md](./docs/design/fund-runtime-validation.md)
15
419
 
16
420
  ## Validated Scenarios
17
421
 
@@ -24,6 +428,7 @@ The published package has been exercised from a blank external consumer project.
24
428
  | Preset flow (`p2pkLockHeight`) | Success | compile -> fund -> inspect -> execute(`broadcast=true`) |
25
429
  | Custom `.simf` flow | Success | `compileFromFile(...)` -> fund -> inspect -> execute(`broadcast=true`) |
26
430
  | Relayer-backed gasless flow | Success | `executeGasless(...)` succeeded from the external project |
431
+ | LP fund business flow | Success | packaged `sdk.funds` consumer smoke covers capital call, distribution, and finality |
27
432
 
28
433
  ## Who This Is For
29
434
 
@@ -136,80 +541,289 @@ For a bond-oriented walkthrough, see [docs/definitions/README.md](./docs/definit
136
541
 
137
542
  ## Trusted Issuance State JSON
138
543
 
139
- The same hash-anchor model now also applies to issuance state documents such as a bond issuance record.
140
-
141
- This is useful when you want to say not only:
544
+ The same hash-anchor model also applies to issuance state documents such as a bond issuance record.
142
545
 
546
+ This lets us say not only:
143
547
  - "these are the bond terms,"
144
548
 
145
549
  but also:
146
-
147
550
  - "this bond was issued in this amount, with this outstanding principal, under this controller."
148
551
 
149
- The SDK now supports:
150
-
552
+ The SDK supports:
151
553
  - loading and hashing a state JSON with `sdk.loadStateDocument(...)`,
152
554
  - storing its hash in the artifact,
153
555
  - committing `STATE_HASH` into custom `.simf` contract logic,
154
556
  - verifying later that the issuance state JSON still matches the compiled contract.
155
557
 
156
- For Bond issuance, the recommended shape is:
157
-
158
- ```json
159
- {
160
- "issuanceId": "BOND-2026-001-ISSUE-1",
161
- "bondId": "BOND-2026-001",
162
- "issuerEntityId": "hazbase-treasury",
163
- "issuedPrincipal": 1000000,
164
- "outstandingPrincipal": 1000000,
165
- "redeemedPrincipal": 0,
166
- "currencyAssetId": "bitcoin",
167
- "controllerXonly": "<xonly>",
168
- "issuedAt": "2026-03-10T00:00:00Z",
169
- "status": "ISSUED"
170
- }
558
+ The recommended Bond issuance shape still captures:
559
+ - `issuanceId`
560
+ - `bondId`
561
+ - `issuedPrincipal`
562
+ - `outstandingPrincipal`
563
+ - `redeemedPrincipal`
564
+ - `currencyAssetId`
565
+ - `controllerXonly`
566
+ - `issuedAt`
567
+ - `status`
568
+
569
+ ## Public Architecture
570
+
571
+ The SDK is now organized around four public layers:
572
+ - `sdk.outputBinding`: cross-domain output-binding support matrix and fallback behavior
573
+ - `sdk.policies`: generic recursive covenant / transfer engine
574
+ - `sdk.bonds`: Bond business layer built on Policy Core primitives and shared output binding
575
+ - `sdk.funds`: LP fund settlement business layer for capital calls, distributions, closing, and finality
576
+
577
+ This means:
578
+ - use `sdk.policies` when you want a generic constrained-transfer engine,
579
+ - use `sdk.bonds` when you want bond definition / issuance / redemption / settlement / closing / evidence semantics,
580
+ - use `sdk.funds` when you want LP capital call / distribution / closing semantics,
581
+ - use `sdk.outputBinding.describeSupport()` when you want the canonical explanation of supported forms, manual hash paths, and fallback behavior,
582
+ - use `sdk.outputBinding.evaluateSupport(...)` when you want the deterministic answer for one concrete output-form scenario.
583
+
584
+ ## Bond Domain Layer
585
+
586
+ `sdk.bonds` is now intentionally a **thin business facade**.
587
+
588
+ Its public responsibility is:
589
+ - bond definition / issuance / settlement / closing schema and invariant handling,
590
+ - business event orchestration,
591
+ - audit / evidence / finality payload export.
592
+
593
+ Public Bond API:
594
+ - `sdk.bonds.define(...)`
595
+ - `sdk.bonds.verify(...)`
596
+ - `sdk.bonds.load(...)`
597
+ - `sdk.bonds.issue(...)`
598
+ - `sdk.bonds.prepareRedemption(...)`
599
+ - `sdk.bonds.inspectRedemption(...)`
600
+ - `sdk.bonds.executeRedemption(...)`
601
+ - `sdk.bonds.verifyRedemption(...)`
602
+ - `sdk.bonds.buildSettlement(...)`
603
+ - `sdk.bonds.verifySettlement(...)`
604
+ - `sdk.bonds.prepareClosing(...)`
605
+ - `sdk.bonds.inspectClosing(...)`
606
+ - `sdk.bonds.executeClosing(...)`
607
+ - `sdk.bonds.verifyClosing(...)`
608
+ - `sdk.bonds.exportEvidence(...)`
609
+ - `sdk.bonds.exportFinalityPayload(...)`
610
+
611
+ What is no longer part of the public Bond surface:
612
+ - machine / rollover / state-machine helpers,
613
+ - script-bound / descriptor-bound machine compile helpers,
614
+ - expected-output descriptor helpers as a standalone public API.
615
+
616
+ Those lower-level primitives are kept only for internal regression and protocol research under:
617
+ - `src/internal/experimental/bond.ts`
618
+ - `examples/internal/experimental/bonds/`
619
+
620
+ ### Shared Output Binding
621
+
622
+ Bond settlement/build/verify now uses the same binding engine as Policy Core.
623
+ That means Bond and Policy return the same binding metadata vocabulary:
624
+ - `supportedForm`
625
+ - `reasonCode`
626
+ - `autoDerived`
627
+ - `fallbackReason`
628
+ - `bindingInputs`
629
+
630
+ Current practical behavior:
631
+ - `script-bound`: runtime binds next output script hash, output count, and fee output position
632
+ - `descriptor-bound`: runtime binds `output_hash(0)` when the output form is supported or when the caller supplies a manual `nextOutputHash`
633
+ - unsupported `descriptor-bound` requests fall back to `script-bound` with an explicit reason code
634
+ - supported advanced paths:
635
+ - `explicit-v1`
636
+ - `raw-output-v1`
637
+ - `manual-hash`
638
+ - current generalized/confidential story is explicit on purpose:
639
+ - unsupported high-level confidential forms still report why they fall back
640
+ - `raw-output-v1` exists for callers that already know the output bytes or already know the SHA-256 hashes of the scriptPubKey / range proof
641
+ - surjection proofs are intentionally outside this contract because Elements excludes them from `output_hash(0)`
642
+ - wallet/RPC-backed confidential auto-reconstruction is still a non-goal in this phase
643
+
644
+ You can evaluate a concrete binding scenario before building a transfer:
645
+
646
+ ```ts
647
+ const evaluation = sdk.outputBinding.evaluateSupport({
648
+ assetId: "bitcoin",
649
+ requestedBindingMode: "descriptor-bound",
650
+ outputForm: { amountForm: "confidential" },
651
+ });
652
+
653
+ const rawEvaluation = sdk.outputBinding.evaluateSupport({
654
+ assetId: "unsupported-asset-alias",
655
+ requestedBindingMode: "descriptor-bound",
656
+ rawOutput: {
657
+ assetBytesHex: "01" + "22".repeat(32),
658
+ amountBytesHex: "01000000000000076c",
659
+ nonceBytesHex: "00",
660
+ scriptPubKeyHex: "5120" + "11".repeat(32),
661
+ rangeProofHex: "",
662
+ },
663
+ });
171
664
  ```
172
665
 
173
- Minimal TypeScript flow:
666
+ ### Bond Runtime Confidence
667
+
668
+ Latest fresh Bond testnet reruns:
669
+
670
+ | Binding | Funding txid | Execution txid | Rerun command |
671
+ | --- | --- | --- | --- |
672
+ | `script-bound` | `1c982864ef6c83da4eb7f8018edc4cbdff439db7c6366984b3f85ad4937e2c4f` | `d659c4bdce6b32650ff58ac37ccaa55209a9f04d5dc4595f956fad034089f580` | `BOND_OUTPUT_BINDING_MODE=script-bound npm run e2e:bond-testnet` |
673
+ | `descriptor-bound` | `72d0015b51a74c3cc81f7abb74a4f6f894c7f7bbd1e83647939459d7b40e504f` | `85e0830a7b2ba33ca37d5f11bd981938418fc472e98657095680ada71387974c` | `BOND_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:bond-testnet` |
674
+
675
+ For the current truth source and caveats, see [docs/design/bond-runtime-validation.md](./docs/design/bond-runtime-validation.md).
676
+
677
+ ### Minimal Bond Flow
174
678
 
175
679
  ```ts
176
- const compiled = await sdk.bonds.defineBond({
680
+ const compiled = await sdk.bonds.define({
177
681
  definitionPath: "./docs/definitions/bond-definition.json",
178
682
  issuancePath: "./docs/definitions/bond-issuance-state.json",
179
683
  simfPath: "./docs/definitions/bond-issuance-anchor.simf",
180
684
  artifactPath: "./bond-issuance.artifact.json",
181
685
  });
182
686
 
183
- const verification = await sdk.bonds.verifyBond({
687
+ const verified = await sdk.bonds.verify({
184
688
  artifactPath: "./bond-issuance.artifact.json",
185
689
  definitionPath: "./docs/definitions/bond-definition.json",
186
690
  issuancePath: "./docs/definitions/bond-issuance-state.json",
187
691
  });
188
692
 
189
- console.log(verification.crossChecks.principalInvariantValid);
190
- console.log(verification.issuance.trust.effectiveMode);
693
+ const redemption = await sdk.bonds.prepareRedemption({
694
+ definitionPath: "./docs/definitions/bond-definition.json",
695
+ previousIssuancePath: "./docs/definitions/bond-issuance-state.json",
696
+ amount: 250000,
697
+ redeemedAt: "2027-03-10T00:00:00Z",
698
+ nextStateSimfPath: "./docs/definitions/bond-issuance-anchor.simf",
699
+ nextAmountSat: 1900,
700
+ outputBindingMode: "script-bound",
701
+ });
702
+
703
+ const settlement = await sdk.bonds.buildSettlement({
704
+ definitionPath: "./docs/definitions/bond-definition.json",
705
+ previousIssuancePath: "./docs/definitions/bond-issuance-state.json",
706
+ nextIssuanceValue: redemption.preview.next,
707
+ nextStateSimfPath: "./docs/definitions/bond-issuance-anchor.simf",
708
+ nextAmountSat: 1900,
709
+ outputBindingMode: "script-bound",
710
+ });
711
+
712
+ const closing = await sdk.bonds.prepareClosing({
713
+ definitionPath: "./docs/definitions/bond-definition.json",
714
+ redeemedIssuancePath: "./docs/definitions/bond-issuance-state-redeemed.json",
715
+ settlementDescriptorValue: settlement.descriptor,
716
+ closedAt: "2027-03-10T00:00:00Z",
717
+ });
718
+
719
+ const evidence = await sdk.bonds.exportEvidence({
720
+ artifactPath: "./bond-issuance.artifact.json",
721
+ definitionPath: "./docs/definitions/bond-definition.json",
722
+ issuancePath: "./docs/definitions/bond-issuance-state.json",
723
+ settlementDescriptorValue: settlement.descriptor,
724
+ });
191
725
  ```
192
726
 
193
- CLI equivalents:
727
+ ### Bond CLI Flow
194
728
 
195
729
  ```bash
196
- simplicity-cli state show \
197
- --type bond-issuance \
198
- --id BOND-2026-001-ISSUE-1 \
199
- --json-path ./docs/definitions/bond-issuance-state.json
730
+ simplicity-cli bond define \
731
+ --definition-json ./docs/definitions/bond-definition.json \
732
+ --issuance-json ./docs/definitions/bond-issuance-state.json \
733
+ --simf ./docs/definitions/bond-issuance-anchor.simf \
734
+ --artifact ./bond-issuance.artifact.json
200
735
 
201
- simplicity-cli state verify \
736
+ simplicity-cli bond verify \
202
737
  --artifact ./bond-issuance.artifact.json \
203
- --type bond-issuance \
204
- --id BOND-2026-001-ISSUE-1 \
205
- --json-path ./docs/definitions/bond-issuance-state.json
738
+ --definition-json ./docs/definitions/bond-definition.json \
739
+ --issuance-json ./docs/definitions/bond-issuance-state.json
206
740
 
207
- simplicity-cli bond verify \
741
+ simplicity-cli bond prepare-redemption \
742
+ --definition-json ./docs/definitions/bond-definition.json \
743
+ --previous-issuance-json ./docs/definitions/bond-issuance-state.json \
744
+ --amount 250000 \
745
+ --redeemed-at 2027-03-10T00:00:00Z \
746
+ --next-state-simf ./docs/definitions/bond-issuance-anchor.simf \
747
+ --next-amount-sat 1900 \
748
+ --output-binding-mode script-bound \
749
+ --next-issuance-out ./next-bond-issuance-state.json
750
+
751
+ simplicity-cli bond build-settlement \
752
+ --definition-json ./docs/definitions/bond-definition.json \
753
+ --previous-issuance-json ./docs/definitions/bond-issuance-state.json \
754
+ --next-issuance-json ./next-bond-issuance-state.json \
755
+ --next-state-simf ./docs/definitions/bond-issuance-anchor.simf \
756
+ --next-amount-sat 1900 \
757
+ --output-binding-mode script-bound
758
+
759
+ simplicity-cli bond prepare-closing \
760
+ --definition-json ./docs/definitions/bond-definition.json \
761
+ --redeemed-issuance-json ./docs/definitions/bond-issuance-state-redeemed.json \
762
+ --settlement-descriptor-json ./bond-settlement.json \
763
+ --closed-at 2027-03-10T00:00:00Z
764
+
765
+ simplicity-cli bond export-evidence \
208
766
  --artifact ./bond-issuance.artifact.json \
209
767
  --definition-json ./docs/definitions/bond-definition.json \
210
768
  --issuance-json ./docs/definitions/bond-issuance-state.json
769
+
770
+ simplicity-cli binding evaluate-support \
771
+ --asset-id bitcoin \
772
+ --output-binding-mode descriptor-bound \
773
+ --amount-form confidential
774
+ ```
775
+
776
+ Example `bond build-settlement` summary:
777
+
778
+ ```text
779
+ descriptorHash=4d8f...
780
+ bindingMode=descriptor-bound
781
+ previousStateHash=8c3b...
782
+ nextStateHash=56ae...
783
+ nextContractAddress=tex1p...
784
+ nextAmountSat=1900
785
+ maxFeeSat=100
786
+ supportedForm=explicit-v1
787
+ reasonCode=OK_EXPLICIT
788
+ autoDerived=true
789
+ nextOutputHash=0b9a...
790
+ bindingInputs(asset=bitcoin, amountSat=1900, nextOutputIndex=0, feeIndex=1, maxFeeSat=100)
791
+ bindingInputForms(assetForm=explicit, amountForm=explicit, nonceForm=null, rangeProofForm=empty)
792
+ ```
793
+
794
+ Example `bond verify-redemption` summary:
795
+
796
+ ```text
797
+ phase=verify
798
+ mode=descriptor-bound
799
+ descriptorHash=4d8f...
800
+ nextStateHash=56ae...
801
+ nextAmountSat=1900
802
+ verified=true
803
+ bindingMode=descriptor-bound
804
+ supportedForm=explicit-v1
805
+ reasonCode=OK_EXPLICIT
806
+ autoDerived=true
807
+ nextOutputHash=0b9a...
808
+ outputBinding.mode=descriptor-bound
809
+ outputBinding.nextContractAddressCommitted=true
810
+ outputBinding.outputCountRuntimeBound=true
811
+ outputBinding.feeIndexRuntimeBound=true
812
+ outputBinding.nextOutputHashRuntimeBound=true
813
+ outputBinding.nextOutputScriptRuntimeBound=false
211
814
  ```
212
815
 
816
+ Important limitation:
817
+ - Bond finality is stronger than before, but still intentionally partial.
818
+ - `script-bound` currently runtime-binds output count, fee position, and next output script hash.
819
+ - `descriptor-bound` adds runtime `output_hash(0)` binding for supported explicit/manual-hash paths.
820
+ - exact next output amount is still not a fully generalized covenant across all output forms.
821
+ - unsupported confidential/generalized output forms fall back deterministically and report why.
822
+
823
+ For a Bond-oriented walkthrough that matches the current public surface, see [docs/definitions/README.md](./docs/definitions/README.md).
824
+ For a packaged external-consumer smoke of the public business flow, run `npm run e2e:bond-consumer`.
825
+ For the resumable Bond runtime/testnet validation flow, see [docs/design/bond-runtime-validation.md](./docs/design/bond-runtime-validation.md).
826
+
213
827
  ## Install
214
828
 
215
829
  You need three things:
@@ -876,6 +1490,7 @@ simplicity-cli gasless request \
876
1490
 
877
1491
  These examples are included to help you jump to the right workflow quickly.
878
1492
 
1493
+ Core contract workflows:
879
1494
  - [compile-custom.ts](./examples/compile-custom.ts): compile a custom `.simf` file.
880
1495
  - [compile-preset.ts](./examples/compile-preset.ts): compile a built-in preset.
881
1496
  - [inspect-contract.ts](./examples/inspect-contract.ts): inspect a contract spend before broadcast.
@@ -885,11 +1500,28 @@ These examples are included to help you jump to the right workflow quickly.
885
1500
  - [execute-htlc.ts](./examples/execute-htlc.ts): HTLC preset with custom witness values.
886
1501
  - [execute-transfer-with-timeout-cooperative.ts](./examples/execute-transfer-with-timeout-cooperative.ts): cooperative multi-witness timeout flow.
887
1502
  - [gasless-transfer.ts](./examples/gasless-transfer.ts): standard relayer-backed gasless L-BTC transfer.
888
- - [define-bond.ts](./examples/define-bond.ts): compile a bond example with a trusted definition hash anchor.
889
- - [show-bond-definition.ts](./examples/show-bond-definition.ts): verify and retrieve a trusted bond definition from JSON + artifact.
890
- - [define-bond-issuance.ts](./examples/define-bond-issuance.ts): compile a bond example with both trusted definition and issuance state anchors.
891
- - [show-bond-issuance.ts](./examples/show-bond-issuance.ts): load a bond artifact together with its verified issuance state.
892
- - [verify-bond-issuance.ts](./examples/verify-bond-issuance.ts): run combined Bond definition/state verification and invariant checks.
1503
+
1504
+ Policy Core examples:
1505
+ - [describe-policy-template.ts](./examples/describe-policy-template.ts): inspect a public policy manifest and validate params.
1506
+ - [show-required-policy-transfer.ts](./examples/show-required-policy-transfer.ts): preview a required 1tx recursive transfer.
1507
+ - [show-optional-policy-transfer.ts](./examples/show-optional-policy-transfer.ts): preview an optional plain-or-recursive transfer.
1508
+ - [execute-required-policy-transfer.ts](./examples/execute-required-policy-transfer.ts): execute a funded required policy UTXO.
1509
+ - [execute-optional-policy-transfer.ts](./examples/execute-optional-policy-transfer.ts): execute an optional plain or recursive branch.
1510
+ - [custom-recursive-delay-required.manifest.json](./examples/custom-recursive-delay-required.manifest.json): example external manifest for custom template loading.
1511
+
1512
+ Bond business-layer examples:
1513
+ - [define-bond.ts](./examples/define-bond.ts): compile a bond definition anchor with the generic contract API.
1514
+ - [show-bond-definition.ts](./examples/show-bond-definition.ts): verify and retrieve a trusted bond definition.
1515
+ - [define-bond-issuance.ts](./examples/define-bond-issuance.ts): define a bond issuance artifact.
1516
+ - [show-bond-issuance.ts](./examples/show-bond-issuance.ts): load a bond artifact with verified issuance state.
1517
+ - [verify-bond-issuance.ts](./examples/verify-bond-issuance.ts): run bond invariant checks against artifact + JSON inputs.
1518
+ - [show-bond-business-flow.ts](./examples/show-bond-business-flow.ts): walk the business-layer flow from definition through finality payload export.
1519
+ - [redeem-bond-issuance.ts](./examples/redeem-bond-issuance.ts): prepare a bond redemption preview and next issuance state.
1520
+ - [show-bond-settlement-payload.ts](./examples/show-bond-settlement-payload.ts): build a public settlement descriptor.
1521
+ - [verify-bond-settlement.ts](./examples/verify-bond-settlement.ts): verify a settlement descriptor against bond inputs.
1522
+
1523
+ Internal / experimental Bond regressions:
1524
+ - [examples/internal/experimental/bonds/README.md](./examples/internal/experimental/bonds/README.md): low-level machine / rollover / transition helpers retained for research and regression only.
893
1525
 
894
1526
  In addition to the in-repo examples, the package has also been validated from a blank external consumer project with:
895
1527
  - `npm install @hazbase/simplicity`
@@ -897,6 +1529,11 @@ In addition to the in-repo examples, the package has also been validated from a
897
1529
  - preset compile -> fund -> inspect -> execute
898
1530
  - custom `.simf` compile -> fund -> inspect -> execute
899
1531
  - relayer-backed gasless execution
1532
+ - policy quickstart smoke via `npm run e2e:policy-consumer`
1533
+
1534
+ ## Covenant Roadmap
1535
+
1536
+ If you want to see how the current bond lifecycle work can evolve toward stronger output binding, see [docs/design/full-covenant-output-binding.md](./docs/design/full-covenant-output-binding.md). That note captures what the local SimplicityHL examples appear to expose today and what the next realistic covenant milestones are.
900
1537
 
901
1538
  ## FAQ / Practical Notes
902
1539