@hazbase/simplicity 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -134,6 +134,82 @@ simplicity-cli definition verify \
134
134
 
135
135
  For a bond-oriented walkthrough, see [docs/definitions/README.md](./docs/definitions/README.md).
136
136
 
137
+ ## Trusted Issuance State JSON
138
+
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:
142
+
143
+ - "these are the bond terms,"
144
+
145
+ but also:
146
+
147
+ - "this bond was issued in this amount, with this outstanding principal, under this controller."
148
+
149
+ The SDK now supports:
150
+
151
+ - loading and hashing a state JSON with `sdk.loadStateDocument(...)`,
152
+ - storing its hash in the artifact,
153
+ - committing `STATE_HASH` into custom `.simf` contract logic,
154
+ - verifying later that the issuance state JSON still matches the compiled contract.
155
+
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
+ }
171
+ ```
172
+
173
+ Minimal TypeScript flow:
174
+
175
+ ```ts
176
+ const compiled = await sdk.bonds.defineBond({
177
+ definitionPath: "./docs/definitions/bond-definition.json",
178
+ issuancePath: "./docs/definitions/bond-issuance-state.json",
179
+ simfPath: "./docs/definitions/bond-issuance-anchor.simf",
180
+ artifactPath: "./bond-issuance.artifact.json",
181
+ });
182
+
183
+ const verification = await sdk.bonds.verifyBond({
184
+ artifactPath: "./bond-issuance.artifact.json",
185
+ definitionPath: "./docs/definitions/bond-definition.json",
186
+ issuancePath: "./docs/definitions/bond-issuance-state.json",
187
+ });
188
+
189
+ console.log(verification.crossChecks.principalInvariantValid);
190
+ console.log(verification.issuance.trust.effectiveMode);
191
+ ```
192
+
193
+ CLI equivalents:
194
+
195
+ ```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
200
+
201
+ simplicity-cli state verify \
202
+ --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
206
+
207
+ simplicity-cli bond verify \
208
+ --artifact ./bond-issuance.artifact.json \
209
+ --definition-json ./docs/definitions/bond-definition.json \
210
+ --issuance-json ./docs/definitions/bond-issuance-state.json
211
+ ```
212
+
137
213
  ## Install
138
214
 
139
215
  You need three things:
@@ -811,6 +887,9 @@ These examples are included to help you jump to the right workflow quickly.
811
887
  - [gasless-transfer.ts](./examples/gasless-transfer.ts): standard relayer-backed gasless L-BTC transfer.
812
888
  - [define-bond.ts](./examples/define-bond.ts): compile a bond example with a trusted definition hash anchor.
813
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.
814
893
 
815
894
  In addition to the in-repo examples, the package has also been validated from a blank external consumer project with:
816
895
  - `npm install @hazbase/simplicity`
@@ -843,6 +922,16 @@ When you compile with `definition: { ... }`, the artifact also carries:
843
922
 
844
923
  That is what allows the SDK and CLI to verify that an off-chain JSON definition still matches the contract you compiled.
845
924
 
925
+ When you also compile with `state: { ... }`, the artifact can additionally carry:
926
+ - `stateType`
927
+ - `stateId`
928
+ - `schemaVersion`
929
+ - `hash`
930
+ - `trustMode`
931
+ - `anchorMode`
932
+
933
+ That is what allows the SDK and CLI to verify that an off-chain issuance state document still matches the contract you compiled.
934
+
846
935
  ### When should I use a preset instead of a custom `.simf` file?
847
936
 
848
937
  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.
package/dist/cli.js CHANGED
@@ -8,6 +8,7 @@ const promises_1 = require("node:fs/promises");
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const artifact_1 = require("./core/artifact");
10
10
  const definition_1 = require("./core/definition");
11
+ const state_1 = require("./core/state");
11
12
  const presets_1 = require("./core/presets");
12
13
  const errors_1 = require("./core/errors");
13
14
  const SimplicityClient_1 = require("./client/SimplicityClient");
@@ -85,6 +86,25 @@ function parseDefinitionInput() {
85
86
  anchorMode,
86
87
  };
87
88
  }
89
+ function parseStateInput() {
90
+ const type = getArg("state-type");
91
+ const id = getArg("state-id");
92
+ const jsonPath = getArg("state-json");
93
+ const valueJson = getArg("state-value");
94
+ const schemaVersion = getArg("state-schema-version");
95
+ const anchorMode = getArg("state-anchor-mode");
96
+ if (!type && !id && !jsonPath && !valueJson && !schemaVersion && !anchorMode) {
97
+ return undefined;
98
+ }
99
+ return {
100
+ type: type ?? "",
101
+ id: id ?? "",
102
+ schemaVersion: schemaVersion ?? undefined,
103
+ jsonPath,
104
+ value: valueJson ? JSON.parse(valueJson) : undefined,
105
+ anchorMode,
106
+ };
107
+ }
88
108
  function resolveConfig() {
89
109
  return {
90
110
  network: getArg("network", "liquidtestnet"),
@@ -389,6 +409,18 @@ function formatArtifactHelp(artifact, preset, utxos) {
389
409
  ` source verified: ${artifact.definition.onChainAnchor?.sourceVerified === true ? "yes" : "no"}`,
390
410
  ].join("\n")
391
411
  : " (none)";
412
+ const state = artifact.state
413
+ ? [
414
+ ` type: ${artifact.state.stateType}`,
415
+ ` id: ${artifact.state.stateId}`,
416
+ ` schema version: ${artifact.state.schemaVersion}`,
417
+ ` hash: ${artifact.state.hash}`,
418
+ ` trust mode: ${artifact.state.trustMode}`,
419
+ ` anchor mode: ${artifact.state.anchorMode}`,
420
+ ` on-chain helper: ${artifact.state.onChainAnchor?.helper ?? "(none)"}`,
421
+ ` source verified: ${artifact.state.onChainAnchor?.sourceVerified === true ? "yes" : "no"}`,
422
+ ].join("\n")
423
+ : " (none)";
392
424
  const compileSource = artifact.source.simfPath ?? artifact.legacy?.simfTemplatePath ?? "(unknown)";
393
425
  const templateVars = artifact.source.templateVars ?? {};
394
426
  const inspectCommand = `simplicity-cli contract inspect --artifact ./artifact.json --wallet simplicity-test --privkey <privkey-hex> --to-address tex1...`;
@@ -410,6 +442,9 @@ function formatArtifactHelp(artifact, preset, utxos) {
410
442
  "Definition Anchor:",
411
443
  definition,
412
444
  "",
445
+ "State Anchor:",
446
+ state,
447
+ "",
413
448
  "Suggested Commands:",
414
449
  ` ${inspectCommand}`,
415
450
  ` ${executeCommand}`,
@@ -451,7 +486,7 @@ async function main() {
451
486
  const subcommand = process.argv[3];
452
487
  const sdk = (0, SimplicityClient_1.createSimplicityClient)(resolveConfig());
453
488
  if (!command) {
454
- throw new Error("Usage: simplicity-cli <compile|presets|preset|contract|artifact|definition|gasless> ...");
489
+ throw new Error("Usage: simplicity-cli <compile|presets|preset|contract|artifact|definition|state|bond|gasless> ...");
455
490
  }
456
491
  if (command === "compile") {
457
492
  const result = await sdk.compileFromFile({
@@ -459,6 +494,7 @@ async function main() {
459
494
  templateVars: parseAssignments(getMultiArgs("template-var")),
460
495
  artifactPath: getArg("artifact"),
461
496
  definition: parseDefinitionInput(),
497
+ state: parseStateInput(),
462
498
  });
463
499
  printJson({ artifact: result.artifact, deployment: result.deployment() });
464
500
  return;
@@ -497,6 +533,40 @@ async function main() {
497
533
  });
498
534
  return;
499
535
  }
536
+ if (command === "state" && subcommand === "show") {
537
+ const state = await (0, state_1.loadStateInput)({
538
+ type: requireArg("type"),
539
+ id: requireArg("id"),
540
+ jsonPath: getArg("json-path"),
541
+ value: getArg("value") ? JSON.parse(getArg("value")) : undefined,
542
+ schemaVersion: getArg("schema-version"),
543
+ });
544
+ printJson({
545
+ ...state,
546
+ anchorRecommendation: "Use --state-anchor-mode on-chain-constant-committed with a blessed custom .simf helper for on-chain enforcement",
547
+ });
548
+ return;
549
+ }
550
+ if (command === "state" && subcommand === "verify") {
551
+ const verification = await sdk.verifyStateAgainstArtifact({
552
+ artifactPath: requireArg("artifact"),
553
+ type: getArg("type"),
554
+ id: getArg("id"),
555
+ expectedType: getArg("expected-type"),
556
+ expectedId: getArg("expected-id"),
557
+ jsonPath: getArg("json-path"),
558
+ value: getArg("value") ? JSON.parse(getArg("value")) : undefined,
559
+ schemaVersion: getArg("schema-version"),
560
+ });
561
+ printJson({
562
+ verified: verification.ok,
563
+ reason: verification.reason,
564
+ state: verification.state,
565
+ artifactState: verification.artifactState ?? null,
566
+ trust: verification.trust,
567
+ });
568
+ return;
569
+ }
500
570
  if (command === "presets" && subcommand === "list") {
501
571
  printJson((0, presets_1.listPresets)().map((preset) => (0, presets_1.describePreset)(preset)));
502
572
  return;
@@ -543,6 +613,7 @@ async function main() {
543
613
  params,
544
614
  artifactPath: getArg("artifact"),
545
615
  definition: parseDefinitionInput(),
616
+ state: parseStateInput(),
546
617
  });
547
618
  printJson({ artifact: result.artifact, deployment: result.deployment() });
548
619
  return;
@@ -572,6 +643,25 @@ async function main() {
572
643
  });
573
644
  return;
574
645
  }
646
+ if (command === "bond" && subcommand === "define") {
647
+ const result = await sdk.bonds.defineBond({
648
+ definitionPath: getArg("definition-json"),
649
+ issuancePath: getArg("issuance-json"),
650
+ simfPath: getArg("simf"),
651
+ artifactPath: getArg("artifact"),
652
+ });
653
+ printJson({ artifact: result.artifact, deployment: result.deployment() });
654
+ return;
655
+ }
656
+ if (command === "bond" && subcommand === "verify") {
657
+ const result = await sdk.bonds.verifyBond({
658
+ artifactPath: requireArg("artifact"),
659
+ definitionPath: getArg("definition-json"),
660
+ issuancePath: getArg("issuance-json"),
661
+ });
662
+ printJson(result);
663
+ return;
664
+ }
575
665
  if (command === "contract" && subcommand === "wait-funding") {
576
666
  const compiled = await sdk.loadArtifact(requireArg("artifact"));
577
667
  const utxos = await compiled.at().waitForFunding({
@@ -1,4 +1,4 @@
1
- import { ArtifactDefinitionMetadata, DeploymentInfo, SimplicityArtifact, SimplicityClientConfig } from "../core/types";
1
+ import { ArtifactDefinitionMetadata, ArtifactStateMetadata, DeploymentInfo, SimplicityArtifact, SimplicityClientConfig } from "../core/types";
2
2
  import { DeployedContract } from "./DeployedContract";
3
3
  export declare class CompiledContract {
4
4
  private readonly config;
@@ -8,6 +8,7 @@ export declare class CompiledContract {
8
8
  get cmr(): string;
9
9
  get program(): string;
10
10
  definition(): ArtifactDefinitionMetadata | null;
11
+ state(): ArtifactStateMetadata | null;
11
12
  deployment(): DeploymentInfo;
12
13
  saveArtifact(path: string): Promise<void>;
13
14
  at(addressOverride?: string): DeployedContract;
@@ -22,6 +22,9 @@ class CompiledContract {
22
22
  definition() {
23
23
  return this.artifact.definition ?? null;
24
24
  }
25
+ state() {
26
+ return this.artifact.state ?? null;
27
+ }
25
28
  deployment() {
26
29
  return {
27
30
  contractAddress: this.artifact.compiled.contractAddress,
@@ -1,5 +1,6 @@
1
1
  import { verifyDefinitionAgainstArtifact } from "../core/definition";
2
- import { ArtifactDefinitionMetadata, ContractUtxo, ExecuteCallInput, ExecuteResult, GaslessExecuteInput, GaslessExecuteResult, InspectCallInput, InspectResult, SimplicityArtifact, SimplicityClientConfig, WaitForFundingInput } from "../core/types";
2
+ import { verifyStateAgainstArtifact } from "../core/state";
3
+ import { ArtifactDefinitionMetadata, ArtifactStateMetadata, ContractUtxo, ExecuteCallInput, ExecuteResult, GaslessExecuteInput, GaslessExecuteResult, InspectCallInput, InspectResult, SimplicityArtifact, SimplicityClientConfig, WaitForFundingInput } from "../core/types";
3
4
  export declare class DeployedContract {
4
5
  private readonly config;
5
6
  readonly artifact: SimplicityArtifact;
@@ -23,4 +24,17 @@ export declare class DeployedContract {
23
24
  reason?: string;
24
25
  trust: Awaited<ReturnType<typeof verifyDefinitionAgainstArtifact>>["trust"];
25
26
  }>;
27
+ getTrustedState(input: {
28
+ jsonPath?: string;
29
+ value?: unknown;
30
+ type?: string;
31
+ id?: string;
32
+ schemaVersion?: string;
33
+ }): Promise<{
34
+ verified: boolean;
35
+ state: Awaited<ReturnType<typeof verifyStateAgainstArtifact>>["state"];
36
+ artifactState: ArtifactStateMetadata | null;
37
+ reason?: string;
38
+ trust: Awaited<ReturnType<typeof verifyStateAgainstArtifact>>["trust"];
39
+ }>;
26
40
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DeployedContract = void 0;
4
4
  const executor_1 = require("../core/executor");
5
5
  const definition_1 = require("../core/definition");
6
+ const state_1 = require("../core/state");
6
7
  const executor_2 = require("../core/executor");
7
8
  class DeployedContract {
8
9
  config;
@@ -61,5 +62,26 @@ class DeployedContract {
61
62
  trust: verification.trust,
62
63
  };
63
64
  }
65
+ async getTrustedState(input) {
66
+ const verification = await (0, state_1.verifyStateAgainstArtifact)({
67
+ artifact: this.artifact,
68
+ state: {
69
+ type: input.type ?? this.artifact.state?.stateType ?? "",
70
+ id: input.id ?? this.artifact.state?.stateId ?? "",
71
+ schemaVersion: input.schemaVersion,
72
+ jsonPath: input.jsonPath,
73
+ value: input.value,
74
+ },
75
+ expectedType: this.artifact.state?.stateType,
76
+ expectedId: this.artifact.state?.stateId,
77
+ });
78
+ return {
79
+ verified: verification.ok,
80
+ state: verification.state,
81
+ artifactState: verification.artifactState ?? null,
82
+ reason: verification.reason,
83
+ trust: verification.trust,
84
+ };
85
+ }
64
86
  }
65
87
  exports.DeployedContract = DeployedContract;
@@ -1,21 +1,48 @@
1
1
  import { ElementsRpcClient } from "../core/rpc";
2
- import { DefinitionInput, DefinitionVerificationResult, CompileFromFileInput, CompileFromPresetInput, DefinitionDescriptor, SimplicityArtifact, SimplicityClientConfig } from "../core/types";
2
+ import { BondDefinition, BondIssuanceState, DefinitionInput, DefinitionVerificationResult, CompileFromFileInput, CompileFromPresetInput, DefinitionDescriptor, StateDocumentDescriptor, StateDocumentInput, StateVerificationResult, SimplicityArtifact, SimplicityClientConfig } from "../core/types";
3
3
  import { RelayerClient } from "../gasless/RelayerClient";
4
4
  import { GaslessTransferInput, GaslessTransferResult, RelayerClientConfig } from "../gasless/types";
5
5
  import { CompiledContract } from "./ContractFactory";
6
6
  import { DeployedContract } from "./DeployedContract";
7
+ import { loadBond, verifyBond } from "../domain/bond";
7
8
  export declare class SimplicityClient {
8
9
  readonly config: SimplicityClientConfig;
9
10
  readonly rpc: ElementsRpcClient;
10
11
  readonly payments: {
11
12
  gaslessTransfer: (input: GaslessTransferInput) => Promise<GaslessTransferResult>;
12
13
  };
14
+ readonly bonds: {
15
+ defineBond: (input: {
16
+ definitionPath?: string;
17
+ definitionValue?: BondDefinition;
18
+ issuancePath?: string;
19
+ issuanceValue?: BondIssuanceState;
20
+ simfPath?: string;
21
+ artifactPath?: string;
22
+ }) => Promise<CompiledContract>;
23
+ verifyBond: (input: {
24
+ artifactPath?: string;
25
+ artifact?: SimplicityArtifact;
26
+ definitionPath?: string;
27
+ definitionValue?: BondDefinition;
28
+ issuancePath?: string;
29
+ issuanceValue?: BondIssuanceState;
30
+ }) => ReturnType<typeof verifyBond>;
31
+ loadBond: (input: {
32
+ artifactPath: string;
33
+ definitionPath?: string;
34
+ definitionValue?: BondDefinition;
35
+ issuancePath?: string;
36
+ issuanceValue?: BondIssuanceState;
37
+ }) => ReturnType<typeof loadBond>;
38
+ };
13
39
  constructor(config: SimplicityClientConfig);
14
40
  compileFromFile(input: CompileFromFileInput): Promise<CompiledContract>;
15
41
  compileFromPreset(input: CompileFromPresetInput): Promise<CompiledContract>;
16
42
  loadArtifact(path: string): Promise<CompiledContract>;
17
43
  define(input: DefinitionInput): Promise<DefinitionDescriptor>;
18
44
  loadDefinition(input: DefinitionInput): Promise<DefinitionDescriptor>;
45
+ loadStateDocument(input: StateDocumentInput): Promise<StateDocumentDescriptor>;
19
46
  verifyDefinitionAgainstArtifact(input: {
20
47
  artifactPath?: string;
21
48
  artifact?: SimplicityArtifact;
@@ -27,6 +54,17 @@ export declare class SimplicityClient {
27
54
  id?: string;
28
55
  schemaVersion?: string;
29
56
  }): Promise<DefinitionVerificationResult>;
57
+ verifyStateAgainstArtifact(input: {
58
+ artifactPath?: string;
59
+ artifact?: SimplicityArtifact;
60
+ jsonPath?: string;
61
+ value?: unknown;
62
+ expectedType?: string;
63
+ expectedId?: string;
64
+ type?: string;
65
+ id?: string;
66
+ schemaVersion?: string;
67
+ }): Promise<StateVerificationResult>;
30
68
  fromArtifact(artifact: SimplicityArtifact): DeployedContract;
31
69
  relayer(config: RelayerClientConfig): RelayerClient;
32
70
  private gaslessTransfer;
@@ -5,21 +5,29 @@ exports.createSimplicityClient = createSimplicityClient;
5
5
  const artifact_1 = require("../core/artifact");
6
6
  const compiler_1 = require("../core/compiler");
7
7
  const definition_1 = require("../core/definition");
8
+ const state_1 = require("../core/state");
8
9
  const errors_1 = require("../core/errors");
9
10
  const rpc_1 = require("../core/rpc");
10
11
  const RelayerClient_1 = require("../gasless/RelayerClient");
11
12
  const ContractFactory_1 = require("./ContractFactory");
12
13
  const DeployedContract_1 = require("./DeployedContract");
14
+ const bond_1 = require("../domain/bond");
13
15
  class SimplicityClient {
14
16
  config;
15
17
  rpc;
16
18
  payments;
19
+ bonds;
17
20
  constructor(config) {
18
21
  this.config = config;
19
22
  this.rpc = new rpc_1.ElementsRpcClient(config.rpc);
20
23
  this.payments = {
21
24
  gaslessTransfer: async (input) => this.gaslessTransfer(input),
22
25
  };
26
+ this.bonds = {
27
+ defineBond: async (input) => (0, bond_1.defineBond)(this, input),
28
+ verifyBond: async (input) => (0, bond_1.verifyBond)(this, input),
29
+ loadBond: async (input) => (0, bond_1.loadBond)(this, input),
30
+ };
23
31
  }
24
32
  async compileFromFile(input) {
25
33
  const artifact = await (0, compiler_1.compileFromFile)(this.config, input);
@@ -39,6 +47,9 @@ class SimplicityClient {
39
47
  async loadDefinition(input) {
40
48
  return (0, definition_1.loadDefinitionInput)(input);
41
49
  }
50
+ async loadStateDocument(input) {
51
+ return (0, state_1.loadStateInput)(input);
52
+ }
42
53
  async verifyDefinitionAgainstArtifact(input) {
43
54
  const artifact = input.artifact ?? (input.artifactPath ? await (0, artifact_1.loadArtifact)(input.artifactPath, this.config.network) : undefined);
44
55
  if (!artifact) {
@@ -57,6 +68,24 @@ class SimplicityClient {
57
68
  expectedId: input.expectedId,
58
69
  });
59
70
  }
71
+ async verifyStateAgainstArtifact(input) {
72
+ const artifact = input.artifact ?? (input.artifactPath ? await (0, artifact_1.loadArtifact)(input.artifactPath, this.config.network) : undefined);
73
+ if (!artifact) {
74
+ throw new errors_1.ValidationError("artifactPath or artifact is required");
75
+ }
76
+ return (0, state_1.verifyStateAgainstArtifact)({
77
+ artifact,
78
+ state: {
79
+ type: input.type ?? input.expectedType ?? artifact.state?.stateType ?? "",
80
+ id: input.id ?? input.expectedId ?? artifact.state?.stateId ?? "",
81
+ schemaVersion: input.schemaVersion,
82
+ jsonPath: input.jsonPath,
83
+ value: input.value,
84
+ },
85
+ expectedType: input.expectedType,
86
+ expectedId: input.expectedId,
87
+ });
88
+ }
60
89
  fromArtifact(artifact) {
61
90
  return new DeployedContract_1.DeployedContract(this.config, artifact);
62
91
  }
@@ -26,6 +26,12 @@ function normalizeArtifact(artifact, networkDefault = "liquidtestnet") {
26
26
  anchorMode: current.definition.anchorMode ?? "artifact-hash-anchor",
27
27
  }
28
28
  : undefined,
29
+ state: current.state
30
+ ? {
31
+ ...current.state,
32
+ anchorMode: current.state.anchorMode ?? "artifact-hash-anchor",
33
+ }
34
+ : undefined,
29
35
  };
30
36
  }
31
37
  if (!isArtifactV5(artifact)) {
@@ -65,6 +71,7 @@ function normalizeArtifact(artifact, networkDefault = "liquidtestnet") {
65
71
  notes: null,
66
72
  },
67
73
  definition: undefined,
74
+ state: undefined,
68
75
  legacy: {
69
76
  simfTemplatePath: artifact.simfTemplatePath,
70
77
  params: artifact.params,
@@ -101,6 +108,7 @@ async function loadArtifact(artifactPath, networkDefault = "liquidtestnet") {
101
108
  }
102
109
  : undefined,
103
110
  definition: normalized.definition,
111
+ state: normalized.state,
104
112
  };
105
113
  }
106
114
  async function saveArtifact(artifactPath, artifact) {
@@ -12,6 +12,7 @@ const artifact_1 = require("./artifact");
12
12
  const definition_1 = require("./definition");
13
13
  const errors_1 = require("./errors");
14
14
  const presets_1 = require("./presets");
15
+ const state_1 = require("./state");
15
16
  const templating_1 = require("./templating");
16
17
  const toolchain_1 = require("./toolchain");
17
18
  function parseSimcProgram(output) {
@@ -74,6 +75,12 @@ async function buildArtifact(input) {
74
75
  onChainAnchor: input.definition.onChainAnchor,
75
76
  })
76
77
  : undefined,
78
+ state: input.state
79
+ ? (0, state_1.buildArtifactStateMetadata)(input.state, {
80
+ anchorMode: input.state.anchorMode,
81
+ onChainAnchor: input.state.onChainAnchor,
82
+ })
83
+ : undefined,
77
84
  legacy: {
78
85
  simfTemplatePath: input.sourceSimfPath,
79
86
  params: {
@@ -85,14 +92,22 @@ async function buildArtifact(input) {
85
92
  }
86
93
  async function compileFromFile(config, input) {
87
94
  const definition = input.definition ? await (0, definition_1.loadDefinitionInput)(input.definition) : undefined;
95
+ const state = input.state ? await (0, state_1.loadStateInput)(input.state) : undefined;
88
96
  if (definition && input.templateVars?.DEFINITION_HASH !== undefined) {
89
97
  throw new errors_1.ValidationError("DEFINITION_HASH must not be provided explicitly when definition metadata is supplied", { code: "DEFINITION_HASH_OVERRIDE_FORBIDDEN" });
90
98
  }
91
99
  if (definition && input.templateVars?.DEFINITION_ID !== undefined) {
92
100
  throw new errors_1.ValidationError("DEFINITION_ID must not be provided explicitly when definition metadata is supplied", { code: "DEFINITION_ID_OVERRIDE_FORBIDDEN" });
93
101
  }
102
+ if (state && input.templateVars?.STATE_HASH !== undefined) {
103
+ throw new errors_1.ValidationError("STATE_HASH must not be provided explicitly when state metadata is supplied", { code: "STATE_HASH_OVERRIDE_FORBIDDEN" });
104
+ }
105
+ if (state && input.templateVars?.STATE_ID !== undefined) {
106
+ throw new errors_1.ValidationError("STATE_ID must not be provided explicitly when state metadata is supplied", { code: "STATE_ID_OVERRIDE_FORBIDDEN" });
107
+ }
94
108
  const rawSource = await (0, promises_1.readFile)(input.simfPath, "utf8");
95
109
  let onChainAnchor;
110
+ let onChainStateAnchor;
96
111
  if (input.definition?.anchorMode === "on-chain-constant-committed") {
97
112
  const detection = (0, definition_1.detectOnChainDefinitionAnchor)(rawSource);
98
113
  if (!rawSource.includes("{{DEFINITION_HASH}}")) {
@@ -110,10 +125,27 @@ async function compileFromFile(config, input) {
110
125
  sourceVerified: true,
111
126
  };
112
127
  }
128
+ if (input.state?.anchorMode === "on-chain-constant-committed") {
129
+ const detection = (0, state_1.detectOnChainStateAnchor)(rawSource);
130
+ if (!rawSource.includes("{{STATE_HASH}}")) {
131
+ throw new errors_1.ValidationError("Requested on-chain constant-committed state anchor, but the .simf source does not contain {{STATE_HASH}}", { code: "STATE_HASH_PLACEHOLDER_MISSING" });
132
+ }
133
+ if (!detection.sourceVerified || !detection.helper) {
134
+ const code = detection.reason?.includes("called")
135
+ ? "STATE_ONCHAIN_HELPER_NOT_CALLED"
136
+ : "STATE_ONCHAIN_HELPER_MISSING";
137
+ throw new errors_1.ValidationError(`Requested on-chain constant-committed state anchor, but the .simf source does not contain the required state helper pattern: ${detection.reason ?? "unknown reason"}`, { code });
138
+ }
139
+ onChainStateAnchor = {
140
+ helper: detection.helper,
141
+ templateVar: "STATE_HASH",
142
+ sourceVerified: true,
143
+ };
144
+ }
113
145
  const templateVars = {
114
146
  ...(input.templateVars ?? {}),
115
- ...(definition && input.templateVars?.DEFINITION_HASH === undefined ? { DEFINITION_HASH: definition.hash } : {}),
116
- ...(definition && input.templateVars?.DEFINITION_ID === undefined ? { DEFINITION_ID: definition.definitionId } : {}),
147
+ ...(definition ? { DEFINITION_HASH: definition.hash, DEFINITION_ID: definition.definitionId } : {}),
148
+ ...(state ? { STATE_HASH: state.hash, STATE_ID: state.stateId } : {}),
117
149
  };
118
150
  const rendered = (0, templating_1.renderTemplate)(rawSource, templateVars);
119
151
  const workDir = await (0, promises_1.mkdtemp)(node_path_1.default.join((0, node_os_1.tmpdir)(), "simplicity-sdk-compile-"));
@@ -123,7 +155,7 @@ async function compileFromFile(config, input) {
123
155
  config,
124
156
  renderedSimfPath: renderedPath,
125
157
  sourceMode: "file",
126
- sourceSimfPath: input.simfPath,
158
+ sourceSimfPath: node_path_1.default.resolve(input.simfPath),
127
159
  templateVars,
128
160
  definition: definition
129
161
  ? {
@@ -132,6 +164,13 @@ async function compileFromFile(config, input) {
132
164
  onChainAnchor,
133
165
  }
134
166
  : undefined,
167
+ state: state
168
+ ? {
169
+ ...state,
170
+ anchorMode: input.state?.anchorMode ?? "artifact-hash-anchor",
171
+ onChainAnchor: onChainStateAnchor,
172
+ }
173
+ : undefined,
135
174
  });
136
175
  if (input.artifactPath) {
137
176
  await (0, artifact_1.saveArtifact)(input.artifactPath, artifact);
@@ -143,17 +182,27 @@ async function compileFromPreset(config, input) {
143
182
  if (input.definition?.anchorMode === "on-chain-constant-committed") {
144
183
  throw new errors_1.UnsupportedFeatureError(`Preset '${preset.id}' does not yet support on-chain constant-committed definition anchors`, { code: "DEFINITION_ANCHOR_MODE_UNSUPPORTED_FOR_PRESET", preset: preset.id });
145
184
  }
185
+ if (input.state?.anchorMode === "on-chain-constant-committed") {
186
+ throw new errors_1.UnsupportedFeatureError(`Preset '${preset.id}' does not yet support on-chain constant-committed state anchors`, { code: "STATE_ANCHOR_MODE_UNSUPPORTED_FOR_PRESET", preset: preset.id });
187
+ }
146
188
  const definition = input.definition ? await (0, definition_1.loadDefinitionInput)(input.definition) : undefined;
189
+ const state = input.state ? await (0, state_1.loadStateInput)(input.state) : undefined;
147
190
  if (definition && input.params.DEFINITION_HASH !== undefined) {
148
191
  throw new errors_1.ValidationError("DEFINITION_HASH must not be provided explicitly when definition metadata is supplied", { code: "DEFINITION_HASH_OVERRIDE_FORBIDDEN" });
149
192
  }
150
193
  if (definition && input.params.DEFINITION_ID !== undefined) {
151
194
  throw new errors_1.ValidationError("DEFINITION_ID must not be provided explicitly when definition metadata is supplied", { code: "DEFINITION_ID_OVERRIDE_FORBIDDEN" });
152
195
  }
196
+ if (state && input.params.STATE_HASH !== undefined) {
197
+ throw new errors_1.ValidationError("STATE_HASH must not be provided explicitly when state metadata is supplied", { code: "STATE_HASH_OVERRIDE_FORBIDDEN" });
198
+ }
199
+ if (state && input.params.STATE_ID !== undefined) {
200
+ throw new errors_1.ValidationError("STATE_ID must not be provided explicitly when state metadata is supplied", { code: "STATE_ID_OVERRIDE_FORBIDDEN" });
201
+ }
153
202
  const params = {
154
203
  ...(0, presets_1.validatePresetParams)(preset, input.params),
155
- ...(definition && input.params.DEFINITION_HASH === undefined ? { DEFINITION_HASH: definition.hash } : {}),
156
- ...(definition && input.params.DEFINITION_ID === undefined ? { DEFINITION_ID: definition.definitionId } : {}),
204
+ ...(definition ? { DEFINITION_HASH: definition.hash, DEFINITION_ID: definition.definitionId } : {}),
205
+ ...(state ? { STATE_HASH: state.hash, STATE_ID: state.stateId } : {}),
157
206
  };
158
207
  const rawSource = await (0, promises_1.readFile)(preset.simfTemplatePath, "utf8");
159
208
  const rendered = (0, templating_1.renderTemplate)(rawSource, params);
@@ -173,6 +222,12 @@ async function compileFromPreset(config, input) {
173
222
  anchorMode: input.definition?.anchorMode ?? "artifact-hash-anchor",
174
223
  }
175
224
  : undefined,
225
+ state: state
226
+ ? {
227
+ ...state,
228
+ anchorMode: input.state?.anchorMode ?? "artifact-hash-anchor",
229
+ }
230
+ : undefined,
176
231
  });
177
232
  if (input.artifactPath) {
178
233
  await (0, artifact_1.saveArtifact)(input.artifactPath, artifact);