@hazbase/simplicity 0.1.1 → 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 (37) hide show
  1. package/README.md +139 -1534
  2. package/dist/cli.js +1107 -1
  3. package/dist/client/SimplicityClient.d.ts +30 -3
  4. package/dist/client/SimplicityClient.js +28 -0
  5. package/dist/core/lineage.d.ts +18 -0
  6. package/dist/core/lineage.js +30 -0
  7. package/dist/core/reporting.d.ts +22 -0
  8. package/dist/core/reporting.js +40 -0
  9. package/dist/core/schnorr.d.ts +3 -3
  10. package/dist/core/schnorr.js +19 -7
  11. package/dist/core/types.d.ts +251 -5
  12. package/dist/docs/definitions/fund-capital-call-refund-only.simf +36 -2
  13. package/dist/docs/definitions/receivable-definition.json +9 -0
  14. package/dist/docs/definitions/receivable-funding-claim.json +13 -0
  15. package/dist/docs/definitions/receivable-funding-claim.simf +58 -0
  16. package/dist/docs/definitions/receivable-repayment-claim.json +13 -0
  17. package/dist/docs/definitions/receivable-repayment-claim.simf +59 -0
  18. package/dist/docs/definitions/receivable-state-funded.json +20 -0
  19. package/dist/docs/definitions/receivable-state-originated.json +19 -0
  20. package/dist/docs/definitions/receivable-state-repaid.json +20 -0
  21. package/dist/domain/bond.d.ts +82 -15
  22. package/dist/domain/bond.js +159 -10
  23. package/dist/domain/bondValidation.d.ts +31 -1
  24. package/dist/domain/bondValidation.js +73 -9
  25. package/dist/domain/fund.d.ts +451 -68
  26. package/dist/domain/fund.js +460 -88
  27. package/dist/domain/fundValidation.d.ts +33 -3
  28. package/dist/domain/fundValidation.js +137 -5
  29. package/dist/domain/policies.d.ts +13 -0
  30. package/dist/domain/policies.js +50 -30
  31. package/dist/domain/receivable.d.ts +824 -0
  32. package/dist/domain/receivable.js +1375 -0
  33. package/dist/domain/receivableValidation.d.ts +147 -0
  34. package/dist/domain/receivableValidation.js +831 -0
  35. package/dist/index.d.ts +5 -3
  36. package/dist/index.js +55 -3
  37. package/package.json +6 -1
package/README.md CHANGED
@@ -2,869 +2,48 @@
2
2
  [![npm version](https://badge.fury.io/js/@hazbase%2Fsimplicity.svg)](https://badge.fury.io/js/@hazbase%2Fsimplicity)
3
3
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
4
4
 
5
- `@hazbase/simplicity` is a Node.js / TypeScript SDK for working with Simplicity contracts on Liquid with an EVM-like developer workflow. It lets you compile SimplicityHL (`.simf`) contracts, derive the contract address, fund that address, inspect the spend you are about to make, execute the contract, and optionally run fee-sponsored flows through a sponsor wallet or relayer. It also ships with built-in presets so you can start from known-good contract templates before moving to custom `.simf` code.
5
+ ## What This SDK Is
6
6
 
7
- This SDK is designed to help Node developers get productive quickly, but it is still opinionated and early-stage:
8
- - SimplicityHL is still upstream work-in-progress and is not production-ready.
9
- - This SDK currently optimizes for explicit / unblinded success paths first.
10
- - Gasless support exists, but it comes in multiple modes with different tradeoffs.
7
+ `@hazbase/simplicity` is a Node.js / TypeScript SDK for building and validating Simplicity-based flows on Liquid.
11
8
 
12
- Consumer validation note:
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, relayer-backed gasless execution, and LP-fund business-flow validation on `liquidtestnet`.
9
+ Today, the SDK is best understood as a **permissioned settlement / lineage / finality toolkit**. It helps developers compile and fund Simplicity contracts, execute constrained payouts, verify off-chain definitions and state documents, and export evidence or finality payloads for higher-level review.
15
10
 
16
- ## Recursive Policy SDK
11
+ It is intentionally narrower than a full market stack. It does **not** try to be a full investor registry, KYC/AML system, fund-admin platform, market-ops layer, or open retail trading protocol.
17
12
 
18
- The SDK now also exposes a generic `sdk.policies` domain for **parametric recursive covenants**.
13
+ This SDK is designed to help Node developers get productive quickly, but it is still opinionated, early-stage, and best suited today to permissioned settlement pilots on Liquid.
19
14
 
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.
15
+ ## What You Can Build
24
16
 
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)
419
-
420
- ## Validated Scenarios
421
-
422
- The published package has been exercised from a blank external consumer project. For the full reproducible fixture, see [docs/consumer-validation/README.md](./docs/consumer-validation/README.md).
423
-
424
- | Scenario | Status | Notes |
425
- | --- | --- | --- |
426
- | Fresh install + JS import | Success | `npm install @hazbase/simplicity` and `import { createSimplicityClient } from "@hazbase/simplicity"` both worked |
427
- | CLI smoke | Success | `npx simplicity-cli presets list` worked from the external project |
428
- | Preset flow (`p2pkLockHeight`) | Success | compile -> fund -> inspect -> execute(`broadcast=true`) |
429
- | Custom `.simf` flow | Success | `compileFromFile(...)` -> fund -> inspect -> execute(`broadcast=true`) |
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 |
432
-
433
- ## Who This Is For
434
-
435
- This README is for you if:
436
- - you are comfortable with Node.js / TypeScript,
437
- - you want to experiment with Simplicity on Liquid without building everything from scratch,
438
- - you want a clear path from `compile` to `fund` to `inspect` to `execute`,
439
- - you want to understand how presets, custom contracts, witnesses, and gasless execution fit together.
440
-
441
- This README is not assuming you already know Simplicity well. It will explain the model first, then show the happy path, then move into advanced topics.
442
-
443
- ## Mental Model First
444
-
445
- Before touching the API, it helps to anchor on how Simplicity on Liquid differs from an EVM contract.
446
-
447
- - A **Simplicity contract** is a spend condition that can be tied to a contract address.
448
- - **Deploying** a contract is not a separate bytecode deployment transaction. In practice, you compile the contract, get its derived address, and fund that address with a UTXO.
449
- - An **artifact** is the compile output plus the metadata you need later to inspect and execute that contract again.
450
- - **Executing** a contract means consuming the contract UTXO and building a new transaction that satisfies the contract's witness rules.
451
- - **Inspecting** a contract call means building the spend first and reviewing what will happen before broadcasting.
452
- - **Gasless** means the fee is paid by a sponsor wallet or relayer instead of by the contract caller directly.
453
-
454
- If you come from Ethereum, a helpful translation is:
455
- - EVM `deploy contract` -> Simplicity `compile contract and fund its address`
456
- - EVM `call contract` -> Simplicity `spend a contract UTXO with the correct witness`
457
- - EVM `transaction preview / wallet confirmation` -> Simplicity `inspectCall()` / summary hash review
458
-
459
- ## What You Can Build With This SDK
460
-
461
- With the current SDK you can build and test flows such as:
462
- - single-sig contract spends,
463
- - single-sig spends gated by block height,
464
- - HTLC-style contracts,
465
- - cooperative transfer with unilateral timeout recovery,
466
- - relayer-backed fee-sponsored contract execution,
467
- - custom `.simf` contract workflows driven from TypeScript or CLI.
468
-
469
- You can also design more advanced systems such as ERC20-like token behavior, but the model is different from Ethereum. On Liquid/Simplicity, you usually represent state transitions as UTXO transitions instead of account storage updates. So the SDK can support that kind of application, but it does not mean you port Solidity account logic 1:1.
470
-
471
- ## Trusted Definition JSON
472
-
473
- When your contract depends on off-chain business metadata such as a bond definition, coupon schedule, note terms, or asset terms, you usually do not want to trust a plain JSON file by itself. This SDK now supports a **hash-anchor** model for definition JSON.
474
-
475
- What that means:
476
- - the SDK canonicalizes the JSON using stable key ordering,
477
- - computes `sha256(canonicalJson)`,
478
- - stores that hash in the artifact as a definition anchor,
479
- - injects `DEFINITION_HASH` and `DEFINITION_ID` into compile-time template vars when a definition is provided,
480
- - lets you verify later that the JSON you are reading still matches the contract/artifact it was compiled against.
481
-
482
- There are now two anchor modes:
483
- - `artifact-hash-anchor`: the JSON hash is anchored in the artifact and verified later against that artifact.
484
- - `on-chain-constant-committed`: the JSON hash is anchored in the artifact and also committed into executed contract logic, so it materially affects the compiled program, CMR, and contract address.
485
-
486
- Today, `on-chain-constant-committed` is guaranteed for custom `.simf` contracts that include the blessed `require_definition_anchor()` helper pattern. Built-in presets still default to artifact-only anchors for now.
487
- The SDK does **not** trust artifact JSON alone for this verdict. `trust.onChainAnchorVerified` only becomes `true` when the SDK can read the source file again and re-detect the blessed helper pattern. If the source file is unavailable, the claimed mode may still be `on-chain-constant-committed`, but `onChainAnchorVerified` will remain `false`.
488
-
489
- Minimal TypeScript flow:
490
-
491
- ```ts
492
- const definition = await sdk.loadDefinition({
493
- type: "bond",
494
- id: "BOND-2026-001",
495
- jsonPath: "./docs/definitions/bond-definition.json",
496
- });
497
-
498
- const compiled = await sdk.compileFromFile({
499
- simfPath: "./docs/definitions/bond-anchor.simf",
500
- templateVars: {
501
- MIN_HEIGHT: 2344430,
502
- SIGNER_XONLY: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
503
- },
504
- definition: {
505
- type: definition.definitionType,
506
- id: definition.definitionId,
507
- schemaVersion: definition.schemaVersion,
508
- jsonPath: definition.sourcePath,
509
- anchorMode: "on-chain-constant-committed",
510
- },
511
- artifactPath: "./bond.artifact.json",
512
- });
513
-
514
- const verification = await sdk.verifyDefinitionAgainstArtifact({
515
- artifactPath: "./bond.artifact.json",
516
- jsonPath: "./docs/definitions/bond-definition.json",
517
- type: "bond",
518
- id: "BOND-2026-001",
519
- });
520
-
521
- console.log(verification.ok);
522
- console.log(verification.trust.effectiveMode);
523
- ```
524
-
525
- CLI equivalents:
526
-
527
- ```bash
528
- simplicity-cli definition show \
529
- --type bond \
530
- --id BOND-2026-001 \
531
- --json-path ./docs/definitions/bond-definition.json
532
-
533
- simplicity-cli definition verify \
534
- --artifact ./bond.artifact.json \
535
- --type bond \
536
- --id BOND-2026-001 \
537
- --json-path ./docs/definitions/bond-definition.json
538
- ```
539
-
540
- For a bond-oriented walkthrough, see [docs/definitions/README.md](./docs/definitions/README.md).
541
-
542
- ## Trusted Issuance State JSON
543
-
544
- The same hash-anchor model also applies to issuance state documents such as a bond issuance record.
545
-
546
- This lets us say not only:
547
- - "these are the bond terms,"
548
-
549
- but also:
550
- - "this bond was issued in this amount, with this outstanding principal, under this controller."
551
-
552
- The SDK supports:
553
- - loading and hashing a state JSON with `sdk.loadStateDocument(...)`,
554
- - storing its hash in the artifact,
555
- - committing `STATE_HASH` into custom `.simf` contract logic,
556
- - verifying later that the issuance state JSON still matches the compiled contract.
557
-
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`
17
+ With the current public SDK you can build and test:
18
+ - constrained transfers with explicit next-hop rules
19
+ - output binding with public support/fallback reporting
20
+ - bond redemption / settlement / close-out flows
21
+ - LP fund capital call / distribution / close-out flows
22
+ - receivable repayment-first funding / repayment / closing flows
23
+ - evidence, trust summary, lineage, and finality exports
568
24
 
569
25
  ## Public Architecture
570
26
 
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
- });
664
- ```
665
-
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
678
-
679
- ```ts
680
- const compiled = await sdk.bonds.define({
681
- definitionPath: "./docs/definitions/bond-definition.json",
682
- issuancePath: "./docs/definitions/bond-issuance-state.json",
683
- simfPath: "./docs/definitions/bond-issuance-anchor.simf",
684
- artifactPath: "./bond-issuance.artifact.json",
685
- });
686
-
687
- const verified = await sdk.bonds.verify({
688
- artifactPath: "./bond-issuance.artifact.json",
689
- definitionPath: "./docs/definitions/bond-definition.json",
690
- issuancePath: "./docs/definitions/bond-issuance-state.json",
691
- });
692
-
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
- });
725
- ```
726
-
727
- ### Bond CLI Flow
728
-
729
- ```bash
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
735
-
736
- simplicity-cli bond verify \
737
- --artifact ./bond-issuance.artifact.json \
738
- --definition-json ./docs/definitions/bond-definition.json \
739
- --issuance-json ./docs/definitions/bond-issuance-state.json
740
-
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 \
766
- --artifact ./bond-issuance.artifact.json \
767
- --definition-json ./docs/definitions/bond-definition.json \
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
814
- ```
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).
27
+ The public SDK is organized into five layers:
28
+ - `sdk.outputBinding`: shared output-binding support, evaluation, and fallback behavior
29
+ - `sdk.policies`: generic constrained transfer and recursive policy engine
30
+ - `sdk.bonds`: private bond / credit settlement business layer
31
+ - `sdk.funds`: LP fund settlement business layer
32
+ - `sdk.receivables`: repayment-first receivable business layer
826
33
 
827
- ## Install
34
+ A useful mental model is:
35
+ - `sdk.outputBinding` + `sdk.policies` provide the shared settlement kernel
36
+ - `sdk.bonds`, `sdk.funds`, and `sdk.receivables` build domain flows on top of that kernel
828
37
 
829
- You need three things:
830
- 1. the npm package,
831
- 2. a local Simplicity toolchain,
832
- 3. a reachable Elements / Liquid RPC endpoint.
38
+ ## Quickstart
833
39
 
834
- ### Package
40
+ Install the package:
835
41
 
836
42
  ```bash
837
43
  npm install @hazbase/simplicity
838
44
  ```
839
45
 
840
- ### Runtime assumptions
841
-
842
- The current SDK assumes you have access to:
843
- - `simc`
844
- - `hal-simplicity`
845
- - an Elements-compatible RPC endpoint
846
- - a wallet-enabled RPC when you want to inspect or execute spends
847
-
848
- ### Node version
849
-
850
- - Node.js `>= 20`
851
-
852
- ## Quickstart: First Working Contract
853
-
854
- This section is the shortest path to a real success case. We will:
855
- 1. create a client,
856
- 2. compile the built-in `p2pkLockHeight` preset,
857
- 3. get the contract address,
858
- 4. fund it,
859
- 5. confirm the contract UTXO exists,
860
- 6. inspect the call,
861
- 7. execute it.
862
-
863
- We use `p2pkLockHeight` first because it has a simple witness model and is the easiest way to understand how the SDK works end to end.
864
-
865
- ### Step 1: Create a client
866
-
867
- This is the main entrypoint for the SDK.
46
+ Create a client:
868
47
 
869
48
  ```ts
870
49
  import { createSimplicityClient } from "@hazbase/simplicity";
@@ -872,747 +51,173 @@ import { createSimplicityClient } from "@hazbase/simplicity";
872
51
  const sdk = createSimplicityClient({
873
52
  network: "liquidtestnet",
874
53
  rpc: {
875
- url: process.env.ELEMENTS_RPC_URL || "http://127.0.0.1:18884",
876
- username: process.env.ELEMENTS_RPC_USER || "<rpc-user>",
877
- password: process.env.ELEMENTS_RPC_PASSWORD || "<rpc-password>",
878
- wallet: process.env.ELEMENTS_RPC_WALLET || "simplicity-test",
54
+ url: process.env.ELEMENTS_RPC_URL ?? "http://127.0.0.1:18884",
55
+ username: process.env.ELEMENTS_RPC_USER ?? "<rpc-user>",
56
+ password: process.env.ELEMENTS_RPC_PASSWORD ?? "<rpc-password>",
57
+ wallet: process.env.ELEMENTS_RPC_WALLET ?? "simplicity-test",
879
58
  },
880
59
  toolchain: {
881
- simcPath: process.env.SIMC_PATH || "simc",
882
- halSimplicityPath: process.env.HAL_SIMPLICITY_PATH || "hal-simplicity",
883
- elementsCliPath: process.env.ELEMENTS_CLI_PATH || "eltc",
884
- },
885
- });
886
- ```
887
-
888
- If you are wondering whether a public RPC endpoint is enough: usually not for full execution flows. Public RPC endpoints often do not expose wallet methods such as `walletprocesspsbt`, so for real `inspect` / `execute` flows you should assume a trusted, authenticated RPC.
889
-
890
- CLI equivalent for discovery starts here:
891
-
892
- ```bash
893
- simplicity-cli presets list
894
- simplicity-cli presets show --preset p2pkLockHeight
895
- ```
896
-
897
- ### Step 2: Compile a preset
898
-
899
- Now compile the built-in preset and save an artifact.
900
-
901
- ```ts
902
- const compiled = await sdk.compileFromPreset({
903
- preset: "p2pkLockHeight",
904
- params: {
905
- MIN_HEIGHT: 2344430,
906
- SIGNER_XONLY: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
60
+ simcPath: process.env.SIMC_PATH ?? "simc",
61
+ halSimplicityPath: process.env.HAL_SIMPLICITY_PATH ?? "hal-simplicity",
62
+ elementsCliPath: process.env.ELEMENTS_CLI_PATH ?? "eltc",
907
63
  },
908
- artifactPath: "./artifact.json",
909
64
  });
910
65
 
911
- console.log(compiled.deployment());
66
+ console.log(sdk.policies.listTemplates());
67
+ console.log(sdk.outputBinding.describeSupport());
912
68
  ```
913
69
 
914
- What you get back:
915
- - a `CompiledContract`,
916
- - a deployable contract address,
917
- - the CMR,
918
- - the internal key,
919
- - an artifact you can reload later.
920
-
921
- CLI equivalent:
922
-
923
- ```bash
924
- simplicity-cli preset compile \
925
- --preset p2pkLockHeight \
926
- --param MIN_HEIGHT=2344430 \
927
- --param SIGNER_XONLY=79be... \
928
- --artifact ./artifact.json
929
- ```
930
-
931
- ### Step 3: Understand the deployment output
932
-
933
- `compiled.deployment()` tells you where to send funds and what contract you just created.
934
-
935
- The most important fields are:
936
- - `contractAddress`: where you send L-BTC to make the contract live,
937
- - `cmr`: the commitment merkle root for the compiled contract,
938
- - `internalKey`: the internal taproot key used in the address derivation.
939
-
940
- This is the point where Simplicity differs from EVM most clearly: **you are not broadcasting a separate deployment transaction here**. You are preparing a spend condition and then making it live by funding the resulting address.
941
-
942
- ### Step 4: Fund the contract
943
-
944
- Send L-BTC to the `contractAddress` from your Liquid wallet.
945
-
946
- Example with `eltc`:
947
-
948
- ```bash
949
- eltc -rpcwallet=simplicity-test sendtoaddress "<contract-address>" 0.00002
950
- ```
951
-
952
- You can then wait for the UTXO from TypeScript:
953
-
954
- ```ts
955
- const contract = compiled.at();
956
-
957
- await contract.waitForFunding({
958
- minAmountSat: 1000,
959
- pollIntervalMs: 5000,
960
- timeoutMs: 120000,
961
- });
962
- ```
963
-
964
- Or inspect the artifact status from the CLI:
965
-
966
- ```bash
967
- simplicity-cli artifact show --artifact ./artifact.json
968
- ```
969
-
970
- What `artifact show` tells you:
971
- - whether the contract is unfunded,
972
- - whether only unconfirmed UTXOs are visible,
973
- - whether it is executable,
974
- - which contract UTXOs are currently visible.
975
-
976
- The state you usually want before calling `execute` is:
977
- - `status: executable`
978
- - `ready: yes`
979
-
980
- ### Step 5: Inspect before broadcast
981
-
982
- Before broadcasting a contract spend, build it and inspect it.
983
-
984
- ```ts
985
- const inspectResult = await contract.inspectCall({
986
- wallet: "simplicity-test",
987
- toAddress: "tex1...",
988
- signer: {
989
- type: "schnorrPrivkeyHex",
990
- privkeyHex: process.env.SIMPLICITY_PRIMARY_PRIVKEY || "<primary-privkey-hex>",
991
- },
992
- });
993
-
994
- console.log(inspectResult.summaryHash);
995
- console.log(inspectResult.summary);
996
- ```
997
-
998
- Why this matters:
999
- - you see the candidate transaction before broadcasting,
1000
- - you can inspect inputs, outputs, and fee behavior,
1001
- - you can log or verify the `summaryHash` in higher-level applications.
1002
-
1003
- CLI equivalent:
1004
-
1005
- ```bash
1006
- simplicity-cli contract inspect \
1007
- --artifact ./artifact.json \
1008
- --wallet simplicity-test \
1009
- --privkey <primary-privkey-hex> \
1010
- --to-address tex1...
1011
- ```
1012
-
1013
- A practical rule: use `inspect` first, especially when you are still learning the contract or changing witness logic.
1014
-
1015
- ### Step 6: Execute
1016
-
1017
- Once you are satisfied with the preview, execute the spend.
1018
-
1019
- A safe first step is to build the final raw transaction without broadcasting:
1020
-
1021
- ```ts
1022
- const executeResult = await contract.execute({
1023
- wallet: "simplicity-test",
1024
- toAddress: "tex1...",
1025
- signer: {
1026
- type: "schnorrPrivkeyHex",
1027
- privkeyHex: process.env.SIMPLICITY_PRIMARY_PRIVKEY || "<primary-privkey-hex>",
1028
- },
1029
- broadcast: false,
1030
- });
1031
-
1032
- console.log(executeResult.rawTxHex);
1033
- ```
1034
-
1035
- Then switch to broadcast mode when you are ready:
1036
-
1037
- ```ts
1038
- const broadcastResult = await contract.execute({
1039
- wallet: "simplicity-test",
1040
- toAddress: "tex1...",
1041
- signer: {
1042
- type: "schnorrPrivkeyHex",
1043
- privkeyHex: process.env.SIMPLICITY_PRIMARY_PRIVKEY || "<primary-privkey-hex>",
1044
- },
1045
- broadcast: true,
1046
- });
1047
-
1048
- console.log(broadcastResult.txId);
1049
- ```
1050
-
1051
- CLI equivalent:
1052
-
1053
- ```bash
1054
- simplicity-cli contract execute \
1055
- --artifact ./artifact.json \
1056
- --wallet simplicity-test \
1057
- --privkey <primary-privkey-hex> \
1058
- --to-address tex1... \
1059
- --broadcast
1060
- ```
1061
-
1062
- Recommended habit:
1063
- 1. compile
1064
- 2. fund
1065
- 3. artifact check
1066
- 4. inspect
1067
- 5. execute with `broadcast: false`
1068
- 6. execute with `broadcast: true`
1069
-
1070
- ## Step-by-Step Walkthrough
1071
-
1072
- This section explains the same flow in terms of the SDK types and responsibilities.
1073
-
1074
- ### Create a Client
1075
-
1076
- Use `createSimplicityClient(config)` to define three pieces of infrastructure:
1077
- - which Liquid network you are targeting,
1078
- - which RPC endpoint and wallet you will use,
1079
- - where the local toolchain binaries live.
1080
-
1081
- This client is the root object for both JS/TS flows and relayer integrations.
1082
-
1083
- ### Compile a Built-in Preset
1084
-
1085
- Use `sdk.compileFromPreset(...)` when you want the fastest route to a working contract.
1086
-
1087
- Use it when:
1088
- - you are learning Simplicity with the SDK,
1089
- - your contract matches a built-in pattern,
1090
- - you want a known witness schema and a stable example path.
1091
-
1092
- The return value is a `CompiledContract`, which gives you:
1093
- - `deployment()`
1094
- - `saveArtifact(path)`
1095
- - `at()` to turn it into a deployed contract handle.
1096
-
1097
- ### Understand Deployment
1098
-
1099
- `deployment()` gives you the metadata you need to make the contract live:
1100
- - `contractAddress`
1101
- - `cmr`
1102
- - `internalKey`
1103
- - `instructions`
1104
-
1105
- This is the point where the README should change your mental model: **compiling does not put anything on chain yet**. Funding the derived address is what makes the contract usable.
1106
-
1107
- ### Fund the Contract
1108
-
1109
- A compiled contract becomes executable only after a UTXO exists at its address.
1110
-
1111
- Helpful SDK / CLI tools here:
1112
- - `contract.waitForFunding(...)`
1113
- - `contract.findUtxos()`
1114
- - `simplicity-cli artifact show --artifact ...`
1115
-
1116
- Use `artifact show` when you want a human-readable view of:
1117
- - address,
1118
- - compile source,
1119
- - linked preset,
1120
- - live UTXO status.
1121
-
1122
- ### Inspect Before Broadcast
1123
-
1124
- `inspectCall()` is the safe preview path.
1125
-
1126
- It answers:
1127
- - which UTXO is being spent,
1128
- - where the outputs go,
1129
- - what fee output exists,
1130
- - what summary hash represents the proposed spend.
1131
-
1132
- This is especially important if you intend to build signing UX or higher-level approval logic later.
1133
-
1134
- ### Execute
1135
-
1136
- `execute()` is the direct contract spend path.
1137
-
1138
- Use:
1139
- - `broadcast: false` when you want to generate and inspect the final raw transaction,
1140
- - `broadcast: true` when you actually want to submit the spend.
1141
-
1142
- For beginners, the safest practice is:
1143
- - inspect first,
1144
- - dry-run execute second,
1145
- - broadcast last.
1146
-
1147
- ## Common Workflow Patterns
1148
-
1149
- This section helps you choose the right path for real work.
1150
-
1151
- ### Pattern A: Start from a preset
1152
-
1153
- Best for:
1154
- - first experiments,
1155
- - demos,
1156
- - validating a toolchain setup,
1157
- - learning witness behavior.
1158
-
1159
- Use:
1160
- - `p2pkLockHeight` first,
1161
- - then `p2pk` if you do not need a timelock.
1162
-
1163
- ### Pattern B: Move to custom `.simf`
1164
-
1165
- Best for:
1166
- - your own contract logic,
1167
- - app-specific spend rules,
1168
- - moving from prototype to product-specific behavior.
1169
-
1170
- Use:
1171
- - `sdk.compileFromFile(...)`
1172
- - `templateVars`
1173
- - `artifactPath`
1174
-
1175
- A common progression is:
1176
- 1. start from a preset,
1177
- 2. inspect the preset's witness model,
1178
- 3. write your own `.simf`,
1179
- 4. keep the same artifact / inspect / execute lifecycle.
1180
-
1181
- ### Pattern C: Multi-witness contracts
1182
-
1183
- Best for:
1184
- - HTLC-style logic,
1185
- - cooperative spends,
1186
- - timeout recovery,
1187
- - flows where multiple branches of witness data are possible.
1188
-
1189
- Relevant SDK features:
1190
- - `witness.values`
1191
- - `witness.signers`
1192
- - witness schema validation
1193
-
1194
- Relevant presets:
1195
- - `htlc`
1196
- - `transferWithTimeout`
1197
-
1198
- ### Pattern D: Gasless execution
1199
-
1200
- Best for:
1201
- - developer experience where the caller should not manage fees directly,
1202
- - fee-sponsored app flows,
1203
- - relayer-backed applications.
1204
-
1205
- There are three different gasless-style paths in this SDK, and they are not interchangeable:
1206
- - standard L-BTC transfer through a relayer,
1207
- - local sponsor wallet mode for Simplicity contract execution,
1208
- - relayer-backed Simplicity execution.
1209
-
1210
- ## Presets Overview
1211
-
1212
- These presets are built into the SDK and are the best place to start.
1213
-
1214
- | Preset | What it does | When to use it | Custom witness? | Relayer execute? | Best first use |
1215
- | --- | --- | --- | --- | --- | --- |
1216
- | `p2pkLockHeight` | Single signer spend gated by block height | First end-to-end tutorial, timelocked tests | No | Yes | Yes |
1217
- | `p2pk` | Basic single key spend | Minimal happy path | No | Yes | Yes |
1218
- | `htlc` | Hash/time based branch contract | Preimage or timeout experiments | Yes | Yes | After presets without custom witness |
1219
- | `transferWithTimeout` | Cooperative transfer with unilateral timeout fallback | Multi-witness and branch logic | Yes | Yes | After HTLC basics |
1220
-
1221
- Use the CLI to inspect presets interactively:
70
+ Quick CLI checks:
1222
71
 
1223
72
  ```bash
1224
- simplicity-cli presets list
1225
- simplicity-cli presets show --preset transferWithTimeout
1226
- simplicity-cli presets scaffold --preset transferWithTimeout --write-dir ./transfer-timeout-scaffold
1227
- ```
1228
-
1229
- ## Custom `.simf` Contracts
1230
-
1231
- When built-in presets are no longer enough, move to your own `.simf` file.
1232
-
1233
- ```ts
1234
- const compiled = await sdk.compileFromFile({
1235
- simfPath: "./contracts/my-contract.simf",
1236
- templateVars: {
1237
- ADMIN_XONLY: "79be...",
1238
- MIN_HEIGHT: 2344430,
1239
- },
1240
- artifactPath: "./artifacts/my-contract.artifact.json",
1241
- });
1242
- ```
1243
-
1244
- Use custom `.simf` when:
1245
- - your business logic is not represented by a preset,
1246
- - you need your own parameterization,
1247
- - you want to build app-specific wrappers on top of the generic SDK.
1248
-
1249
- A real external-consumer validation of this path has been completed with:
1250
- - a fresh project created outside this repo,
1251
- - a local `contract.simf` file owned by that project,
1252
- - `compileFromFile(...)`,
1253
- - funding + inspect + `broadcast: true` execution.
1254
-
1255
- Recommended path:
1256
- - learn the lifecycle with a preset first,
1257
- - then move to `compileFromFile(...)` once the model is clear.
1258
-
1259
- ## Witnesses Explained
1260
-
1261
- A **witness** is the runtime data needed to satisfy a Simplicity contract spend.
1262
-
1263
- ### Auto-generated witness
1264
-
1265
- For the simplest presets, you usually do not need to build witness data manually.
1266
-
1267
- Examples:
1268
- - `p2pkLockHeight`
1269
- - `p2pk`
1270
-
1271
- These rely on the default signature path and use the primary signer you pass to `inspectCall()` or `execute()`.
1272
-
1273
- ### `witness.values`
1274
-
1275
- Use `witness.values` when the contract needs structured runtime data in addition to the primary signer.
1276
-
1277
- Example:
1278
-
1279
- ```ts
1280
- witness: {
1281
- values: {
1282
- COMPLETE_OR_CANCEL: {
1283
- type: "Either<(u256, Signature), Signature>",
1284
- value: "Left((0x0000000000000000000000000000000000000000000000000000000000000000, ${SIGNATURE}))",
1285
- },
1286
- },
1287
- }
1288
- ```
1289
-
1290
- ### `${SIGNATURE}`
1291
-
1292
- `${SIGNATURE}` is replaced by the SDK with the actual Simplicity signature for the current contract input.
1293
-
1294
- This means you can express witness templates declaratively while letting the SDK calculate the actual signature material.
1295
-
1296
- ### `${SIGNATURE:NAME}` and `witness.signers`
1297
-
1298
- Use named signer placeholders when a contract requires more than one signature source.
1299
-
1300
- Example:
1301
-
1302
- ```ts
1303
- witness: {
1304
- signers: {
1305
- RECIPIENT: {
1306
- type: "schnorrPrivkeyHex",
1307
- privkeyHex: "<recipient-privkey-hex>",
1308
- },
1309
- },
1310
- values: {
1311
- SENDER_SIG: {
1312
- type: "Signature",
1313
- value: "${SIGNATURE}",
1314
- },
1315
- TRANSFER_OR_TIMEOUT: {
1316
- type: "Option<Signature>",
1317
- value: "Some(${SIGNATURE:RECIPIENT})",
1318
- },
1319
- },
1320
- }
73
+ npx simplicity-cli presets list
74
+ npx simplicity-cli binding describe-support
75
+ npx simplicity-cli --help
1321
76
  ```
1322
77
 
1323
- ### Validation rules
1324
-
1325
- The SDK validates preset witness usage before calling `simc`.
1326
-
1327
- That means it can reject mistakes such as:
1328
- - missing required witness fields,
1329
- - mismatched witness type strings,
1330
- - a named signature placeholder without a matching signer entry.
1331
-
1332
- ### Which presets need custom witness?
78
+ For richer walkthroughs and sample JSON / `.simf` files, use [`docs/definitions/README.md`](./docs/definitions/README.md).
1333
79
 
1334
- - `p2pkLockHeight`: no
1335
- - `p2pk`: no
1336
- - `htlc`: yes
1337
- - `transferWithTimeout`: yes
80
+ ## Domain Overview
1338
81
 
1339
- ## Gasless Modes Explained
82
+ ### Policy
1340
83
 
1341
- Gasless support exists in three forms, and it is important to understand the difference.
84
+ Use `sdk.policies` when you want constrained transfers, recursive next-hop rules, or restricted OTC-style settlement kernels.
1342
85
 
1343
- ### 1. Standard gasless transfer
1344
-
1345
- Use this when you want a relayer to sponsor a normal L-BTC payment flow.
1346
-
1347
- ```ts
1348
- const result = await sdk.payments.gaslessTransfer({
1349
- relayer: sdk.relayer({
1350
- baseUrl: process.env.SIMPLICITY_RELAYER_URL || "http://127.0.0.1:3000",
1351
- apiKey: process.env.SIMPLICITY_RELAYER_API_KEY || "<relayer-api-key>",
1352
- }),
1353
- amount: 0.0001,
1354
- toAddress: "tex1...",
1355
- fromLabel: "user-1",
1356
- userWallet: "userwallet",
1357
- });
1358
- ```
1359
-
1360
- Use it when:
1361
- - you want fee sponsorship,
1362
- - you are sending L-BTC,
1363
- - you are not executing a Simplicity contract input.
86
+ Main entrypoints:
87
+ - `sdk.policies.listTemplates(...)`
88
+ - `sdk.policies.describeTemplate(...)`
89
+ - `sdk.policies.issue(...)`
90
+ - `sdk.policies.prepareTransfer(...)`
91
+ - `sdk.policies.inspectTransfer(...)`
92
+ - `sdk.policies.executeTransfer(...)`
93
+ - `sdk.policies.verifyTransfer(...)`
94
+ - `sdk.policies.exportEvidence(...)`
1364
95
 
1365
- ### 2. Local sponsor wallet mode
96
+ Representative example:
97
+ - [show-policy-restricted-otc-transfer.ts](./examples/show-policy-restricted-otc-transfer.ts)
1366
98
 
1367
- Use this when the contract spend is local, but another wallet on the same system should pay the fee.
99
+ ### Bonds
1368
100
 
1369
- ```ts
1370
- const result = await compiled.at().executeGasless({
1371
- wallet: "simplicity-test",
1372
- sponsorWallet: "sponsorwallet",
1373
- toAddress: "tex1...",
1374
- signer: {
1375
- type: "schnorrPrivkeyHex",
1376
- privkeyHex: process.env.SIMPLICITY_PRIMARY_PRIVKEY || "<primary-privkey-hex>",
1377
- },
1378
- broadcast: true,
1379
- });
1380
- ```
101
+ Use `sdk.bonds` for permissioned private bond or private credit flows where definition, issuance, settlement, closing, and lineage all matter.
1381
102
 
1382
- Use it when:
1383
- - you control both wallets,
1384
- - you do not need a separate external relayer service,
1385
- - you want a local fee-sponsored contract execution path.
103
+ Main entrypoints:
104
+ - `sdk.bonds.define(...)`
105
+ - `sdk.bonds.verify(...)`
106
+ - `sdk.bonds.verifyIssuanceHistory(...)`
107
+ - `sdk.bonds.prepareRedemption(...)`
108
+ - `sdk.bonds.buildSettlement(...)`
109
+ - `sdk.bonds.prepareClosing(...)`
110
+ - `sdk.bonds.verifyClosing(...)`
111
+ - `sdk.bonds.exportFinalityPayload(...)`
1386
112
 
1387
- ### 3. Relayer-backed Simplicity execution
113
+ Representative example:
114
+ - [show-bond-business-flow.ts](./examples/show-bond-business-flow.ts)
1388
115
 
1389
- Use this when a separate relayer service should sponsor and submit the Simplicity execution.
116
+ ### Funds
1390
117
 
1391
- ```ts
1392
- const relayer = sdk.relayer({
1393
- baseUrl: process.env.SIMPLICITY_RELAYER_URL || "http://127.0.0.1:3000",
1394
- apiKey: process.env.SIMPLICITY_RELAYER_API_KEY || "<relayer-api-key>",
1395
- });
118
+ Use `sdk.funds` for LP fund settlement flows such as capital calls, rollover into refund-only, distributions, receipt reconciliation, and close-out.
1396
119
 
1397
- const result = await compiled.at().executeGasless({
1398
- relayer,
1399
- fromLabel: "demo-user",
1400
- wallet: "simplicity-test",
1401
- toAddress: "tex1...",
1402
- signer: {
1403
- type: "schnorrPrivkeyHex",
1404
- privkeyHex: process.env.SIMPLICITY_PRIMARY_PRIVKEY || "<primary-privkey-hex>",
1405
- },
1406
- });
1407
- ```
120
+ Main entrypoints:
121
+ - `sdk.funds.define(...)`
122
+ - `sdk.funds.prepareCapitalCall(...)`
123
+ - `sdk.funds.executeCapitalCallClaim(...)`
124
+ - `sdk.funds.executeCapitalCallRollover(...)`
125
+ - `sdk.funds.executeCapitalCallRefund(...)`
126
+ - `sdk.funds.prepareDistribution(...)`
127
+ - `sdk.funds.verifyPositionReceiptChain(...)`
128
+ - `sdk.funds.exportFinalityPayload(...)`
1408
129
 
1409
- Use it when:
1410
- - your app has a relayer backend,
1411
- - users should not manage fees directly,
1412
- - you want contract execution plus fee sponsorship.
130
+ Representative example:
131
+ - [show-fund-claim-close-flow.ts](./examples/show-fund-claim-close-flow.ts)
1413
132
 
1414
- ## CLI Guide
133
+ ### Receivables
1415
134
 
1416
- If you prefer the CLI, the same lifecycle is available there.
135
+ Use `sdk.receivables` for repayment-first receivable or invoice-style pilots where you want canonical state transitions, runtime claim descriptors, lineage verification, and terminal closing.
1417
136
 
1418
- ### Discover
137
+ Main entrypoints:
138
+ - `sdk.receivables.define(...)`
139
+ - `sdk.receivables.prepareFunding(...)`
140
+ - `sdk.receivables.prepareFundingClaim(...)`
141
+ - `sdk.receivables.prepareRepayment(...)`
142
+ - `sdk.receivables.prepareRepaymentClaim(...)`
143
+ - `sdk.receivables.prepareClosing(...)`
144
+ - `sdk.receivables.verifyStateHistory(...)`
145
+ - `sdk.receivables.exportFinalityPayload(...)`
1419
146
 
1420
- ```bash
1421
- simplicity-cli presets list
1422
- simplicity-cli presets show --preset p2pkLockHeight
1423
- simplicity-cli presets show --preset htlc
1424
- ```
147
+ Representative example:
148
+ - [show-receivable-business-flow.ts](./examples/show-receivable-business-flow.ts)
1425
149
 
1426
- ### Scaffold
150
+ ## CLI and Confidence Commands
1427
151
 
1428
- ```bash
1429
- simplicity-cli presets scaffold --preset transferWithTimeout
1430
- simplicity-cli presets scaffold --preset transferWithTimeout --write-dir ./transfer-timeout-scaffold
1431
- ```
152
+ ### Validation Surface
1432
153
 
1433
- Use scaffold when you want a starting bundle with:
1434
- - params JSON,
1435
- - witness JSON,
1436
- - compile / execute command examples,
1437
- - `.env.example`,
1438
- - a small TypeScript example.
154
+ The public package is exercised through:
155
+ - unit / integration tests
156
+ - packaged consumer smoke runs
157
+ - public local / testnet e2e commands
1439
158
 
1440
- ### Compile and deploy
159
+ Core confidence commands:
1441
160
 
1442
161
  ```bash
1443
- simplicity-cli preset compile \
1444
- --preset p2pkLockHeight \
1445
- --param MIN_HEIGHT=2344430 \
1446
- --param SIGNER_XONLY=79be... \
1447
- --artifact ./artifact.json
1448
-
1449
- simplicity-cli artifact show --artifact ./artifact.json
162
+ npm test
163
+ npm run e2e:policy-consumer
164
+ npm run e2e:bond-consumer
165
+ npm run e2e:fund-consumer
166
+ npm run e2e:receivable-consumer
1450
167
  ```
1451
168
 
1452
- ### Execute
169
+ Public runtime confidence commands:
1453
170
 
1454
171
  ```bash
1455
- simplicity-cli contract inspect \
1456
- --artifact ./artifact.json \
1457
- --wallet simplicity-test \
1458
- --privkey <primary-privkey-hex> \
1459
- --to-address tex1...
1460
-
1461
- simplicity-cli contract execute \
1462
- --artifact ./artifact.json \
1463
- --wallet simplicity-test \
1464
- --privkey <primary-privkey-hex> \
1465
- --to-address tex1... \
1466
- --broadcast
172
+ npm run e2e:policy-local
173
+ npm run e2e:policy-restricted-otc-local
174
+ npm run e2e:policy-restricted-otc-testnet
175
+ POLICY_OUTPUT_BINDING_MODE=script-bound npm run e2e:policy-testnet
176
+ POLICY_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:policy-testnet
1467
177
 
1468
- simplicity-cli contract execute-gasless \
1469
- --artifact ./artifact.json \
1470
- --wallet simplicity-test \
1471
- --relayer http://127.0.0.1:3000 \
1472
- --api-key <relayer-api-key> \
1473
- --from-label demo-user \
1474
- --privkey <primary-privkey-hex> \
1475
- --to-address tex1...
1476
- ```
178
+ BOND_OUTPUT_BINDING_MODE=script-bound npm run e2e:bond-testnet
179
+ BOND_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:bond-testnet
1477
180
 
1478
- ### Gasless transfer
181
+ npm run e2e:fund-local
182
+ FUND_OUTPUT_BINDING_MODE=script-bound npm run e2e:fund-testnet
183
+ FUND_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:fund-testnet
184
+ FUND_FLOW_MODE=refund FUND_OUTPUT_BINDING_MODE=script-bound npm run e2e:fund-testnet
1479
185
 
1480
- ```bash
1481
- simplicity-cli gasless request \
1482
- --relayer http://127.0.0.1:3000 \
1483
- --api-key <relayer-api-key> \
1484
- --from-label user-1 \
1485
- --to-address tex1... \
1486
- --amount 0.0001
186
+ npm run e2e:receivable-local
187
+ RECEIVABLE_OUTPUT_BINDING_MODE=script-bound npm run e2e:receivable-testnet
188
+ RECEIVABLE_OUTPUT_BINDING_MODE=descriptor-bound npm run e2e:receivable-testnet
1487
189
  ```
1488
190
 
1489
- ## Examples Map
1490
-
1491
- These examples are included to help you jump to the right workflow quickly.
1492
-
1493
- Core contract workflows:
1494
- - [compile-custom.ts](./examples/compile-custom.ts): compile a custom `.simf` file.
1495
- - [compile-preset.ts](./examples/compile-preset.ts): compile a built-in preset.
1496
- - [inspect-contract.ts](./examples/inspect-contract.ts): inspect a contract spend before broadcast.
1497
- - [execute-contract.ts](./examples/execute-contract.ts): execute a contract directly.
1498
- - [execute-contract-gasless.ts](./examples/execute-contract-gasless.ts): execute with a local sponsor wallet paying fees.
1499
- - [execute-contract-gasless-relayer.ts](./examples/execute-contract-gasless-relayer.ts): execute through a relayer-backed gasless flow.
1500
- - [execute-htlc.ts](./examples/execute-htlc.ts): HTLC preset with custom witness values.
1501
- - [execute-transfer-with-timeout-cooperative.ts](./examples/execute-transfer-with-timeout-cooperative.ts): cooperative multi-witness timeout flow.
1502
- - [gasless-transfer.ts](./examples/gasless-transfer.ts): standard relayer-backed gasless L-BTC transfer.
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.
191
+ These commands are useful both as reproducible checks and as examples of the SDK's current validated surface.
1525
192
 
1526
- In addition to the in-repo examples, the package has also been validated from a blank external consumer project with:
1527
- - `npm install @hazbase/simplicity`
1528
- - JS/TS import of `createSimplicityClient`
1529
- - preset compile -> fund -> inspect -> execute
1530
- - custom `.simf` compile -> fund -> inspect -> execute
1531
- - relayer-backed gasless execution
1532
- - policy quickstart smoke via `npm run e2e:policy-consumer`
193
+ ## Where To Go Next
1533
194
 
1534
- ## Covenant Roadmap
195
+ If you want to go deeper, use these entrypoints:
196
+ - [`docs/definitions/README.md`](./docs/definitions/README.md) for the public domain deep-dive and sample JSON / `.simf` assets
197
+ - [`examples/`](./examples) for runnable code samples across policy, bond, fund, and receivable flows
198
+ - `npx simplicity-cli --help` for the full CLI surface
1535
199
 
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.
200
+ A good next path is:
201
+ 1. read the architecture and flow notes in [`docs/definitions/README.md`](./docs/definitions/README.md)
202
+ 2. run one representative example from [`examples/`](./examples)
203
+ 3. run one consumer smoke or local/testnet e2e command that matches your target domain
1537
204
 
1538
- ## FAQ / Practical Notes
1539
-
1540
- ### Can I use a public RPC endpoint?
1541
-
1542
- Sometimes for read-only or light inspection flows, but usually not for full execution. Many public endpoints do not expose wallet RPC methods, and this SDK relies on wallet-aware flows for inspect / execute in practical setups.
205
+ ## Practical Notes / FAQ
1543
206
 
1544
207
  ### What does “deploy” mean here?
1545
208
 
1546
- It means: compile the contract, derive its address, then fund that address with a UTXO. There is no separate EVM-style bytecode deployment transaction.
209
+ In the Simplicity-on-Liquid model, “deploy” means compile a contract, derive its contract address, and fund that address with a UTXO. There is no separate EVM-style bytecode deployment transaction.
1547
210
 
1548
211
  ### What is an artifact?
1549
212
 
1550
- An artifact is the contract's compile output plus the metadata needed to reload, inspect, and execute it later. Think of it as the bridge between compilation time and on-chain execution time.
1551
-
1552
- When you compile with `definition: { ... }`, the artifact also carries:
1553
- - `definitionType`
1554
- - `definitionId`
1555
- - `schemaVersion`
1556
- - `hash`
1557
- - `trustMode`
1558
- - `anchorMode`
1559
-
1560
- That is what allows the SDK and CLI to verify that an off-chain JSON definition still matches the contract you compiled.
1561
-
1562
- When you also compile with `state: { ... }`, the artifact can additionally carry:
1563
- - `stateType`
1564
- - `stateId`
1565
- - `schemaVersion`
1566
- - `hash`
1567
- - `trustMode`
1568
- - `anchorMode`
1569
-
1570
- That is what allows the SDK and CLI to verify that an off-chain issuance state document still matches the contract you compiled.
1571
-
1572
- ### When should I use a preset instead of a custom `.simf` file?
1573
-
1574
- Use a preset first when you are learning the lifecycle or your use case already matches a built-in contract. Move to custom `.simf` when your business rules are app-specific.
1575
-
1576
- ### Does gasless mean the contract itself is free?
1577
-
1578
- No. It means someone else pays the transaction fee. The transaction still has a fee; the caller just does not provide it directly.
1579
-
1580
- ### Can I build an ERC20-like token with this SDK?
1581
-
1582
- Yes, but not by copying the EVM account model directly. On Liquid/Simplicity, you usually model token logic as UTXO state transitions rather than storage mappings. The SDK can support that workflow, but the contract design is different from Solidity.
1583
-
1584
- ## Practical Limitations
1585
-
1586
- Be aware of these current constraints:
1587
- - SimplicityHL is still upstream work-in-progress.
1588
- - The SDK currently prioritizes explicit / unblinded paths.
1589
- - Public RPC endpoints are usually not enough for full wallet-based execution.
1590
- - Gasless support exists in multiple modes and should be chosen deliberately.
1591
- - This is not a browser SDK.
1592
- - Full confidential / blinded support is not the current success path.
1593
-
1594
- ## E2E Note
1595
-
1596
- The repository also includes an E2E script for relayer-backed Simplicity execution:
1597
-
1598
- ```bash
1599
- PATH="/tmp:$PATH" npm run e2e:simplicity-relayer
1600
- ```
1601
-
1602
- Helpful env vars include:
1603
- - `SIMPLICITY_ARTIFACT`
1604
- - `SIMPLICITY_RELAYER_PORT`
1605
- - `SIMPLICITY_RELAYER_API_KEY`
1606
- - `SIMPLICITY_RELAYER_DIR`
1607
- - `SIMPLICITY_FROM_LABEL`
1608
- - `SIMPLICITY_PRIVKEY`
1609
- - `ELEMENTS_RPC_URL`
1610
- - `ELEMENTS_RPC_USER`
1611
- - `ELEMENTS_RPC_PASSWORD`
1612
- - `ELEMENTS_RPC_WALLET`
213
+ An artifact is the SDK's durable compile output plus the metadata needed to reload, inspect, verify, and execute a contract later. It is the bridge between contract definition time and spend time.
1613
214
 
1614
- You do not need this script to understand the SDK, but it is useful once you want to validate relayer-backed flows end to end.
215
+ ### What do I need locally?
1615
216
 
1616
- ## License
217
+ For practical usage you typically need:
218
+ - Node.js 20+
219
+ - the npm package itself
220
+ - a local Simplicity toolchain (`simc`, `hal-simplicity`, and usually `eltc`)
221
+ - a reachable Elements / Liquid RPC endpoint
1617
222
 
1618
- Apache-2.0
223
+ Some commands are SDK-only and work without live spends. Local and testnet e2e flows need the toolchain and, for broadcasted flows, an RPC environment.