@hazbase/simplicity 0.0.1 → 0.0.3

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
@@ -1,4 +1,6 @@
1
1
  # @hazbase/simplicity
2
+ [![npm version](https://badge.fury.io/js/@hazbase%2Fsimplicity.svg)](https://badge.fury.io/js/@hazbase%2Fsimplicity)
3
+ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
2
4
 
3
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.
4
6
 
@@ -7,6 +9,22 @@ This SDK is designed to help Node developers get productive quickly, but it is s
7
9
  - This SDK currently optimizes for explicit / unblinded success paths first.
8
10
  - Gasless support exists, but it comes in multiple modes with different tradeoffs.
9
11
 
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, and relayer-backed gasless execution on `liquidtestnet`.
15
+
16
+ ## Validated Scenarios
17
+
18
+ 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).
19
+
20
+ | Scenario | Status | Notes |
21
+ | --- | --- | --- |
22
+ | Fresh install + JS import | Success | `npm install @hazbase/simplicity` and `import { createSimplicityClient } from "@hazbase/simplicity"` both worked |
23
+ | CLI smoke | Success | `npx simplicity-cli presets list` worked from the external project |
24
+ | Preset flow (`p2pkLockHeight`) | Success | compile -> fund -> inspect -> execute(`broadcast=true`) |
25
+ | Custom `.simf` flow | Success | `compileFromFile(...)` -> fund -> inspect -> execute(`broadcast=true`) |
26
+ | Relayer-backed gasless flow | Success | `executeGasless(...)` succeeded from the external project |
27
+
10
28
  ## Who This Is For
11
29
 
12
30
  This README is for you if:
@@ -45,6 +63,77 @@ With the current SDK you can build and test flows such as:
45
63
 
46
64
  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.
47
65
 
66
+ ## Trusted Definition JSON
67
+
68
+ 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.
69
+
70
+ What that means:
71
+ - the SDK canonicalizes the JSON using stable key ordering,
72
+ - computes `sha256(canonicalJson)`,
73
+ - stores that hash in the artifact as a definition anchor,
74
+ - injects `DEFINITION_HASH` and `DEFINITION_ID` into compile-time template vars when a definition is provided,
75
+ - lets you verify later that the JSON you are reading still matches the contract/artifact it was compiled against.
76
+
77
+ There are now two anchor modes:
78
+ - `artifact-hash-anchor`: the JSON hash is anchored in the artifact and verified later against that artifact.
79
+ - `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.
80
+
81
+ 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.
82
+ 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`.
83
+
84
+ Minimal TypeScript flow:
85
+
86
+ ```ts
87
+ const definition = await sdk.loadDefinition({
88
+ type: "bond",
89
+ id: "BOND-2026-001",
90
+ jsonPath: "./docs/definitions/bond-definition.json",
91
+ });
92
+
93
+ const compiled = await sdk.compileFromFile({
94
+ simfPath: "./docs/definitions/bond-anchor.simf",
95
+ templateVars: {
96
+ MIN_HEIGHT: 2344430,
97
+ SIGNER_XONLY: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
98
+ },
99
+ definition: {
100
+ type: definition.definitionType,
101
+ id: definition.definitionId,
102
+ schemaVersion: definition.schemaVersion,
103
+ jsonPath: definition.sourcePath,
104
+ anchorMode: "on-chain-constant-committed",
105
+ },
106
+ artifactPath: "./bond.artifact.json",
107
+ });
108
+
109
+ const verification = await sdk.verifyDefinitionAgainstArtifact({
110
+ artifactPath: "./bond.artifact.json",
111
+ jsonPath: "./docs/definitions/bond-definition.json",
112
+ type: "bond",
113
+ id: "BOND-2026-001",
114
+ });
115
+
116
+ console.log(verification.ok);
117
+ console.log(verification.trust.effectiveMode);
118
+ ```
119
+
120
+ CLI equivalents:
121
+
122
+ ```bash
123
+ simplicity-cli definition show \
124
+ --type bond \
125
+ --id BOND-2026-001 \
126
+ --json-path ./docs/definitions/bond-definition.json
127
+
128
+ simplicity-cli definition verify \
129
+ --artifact ./bond.artifact.json \
130
+ --type bond \
131
+ --id BOND-2026-001 \
132
+ --json-path ./docs/definitions/bond-definition.json
133
+ ```
134
+
135
+ For a bond-oriented walkthrough, see [docs/definitions/README.md](./docs/definitions/README.md).
136
+
48
137
  ## Install
49
138
 
50
139
  You need three things:
@@ -467,6 +556,12 @@ Use custom `.simf` when:
467
556
  - you need your own parameterization,
468
557
  - you want to build app-specific wrappers on top of the generic SDK.
469
558
 
559
+ A real external-consumer validation of this path has been completed with:
560
+ - a fresh project created outside this repo,
561
+ - a local `contract.simf` file owned by that project,
562
+ - `compileFromFile(...)`,
563
+ - funding + inspect + `broadcast: true` execution.
564
+
470
565
  Recommended path:
471
566
  - learn the lifecycle with a preset first,
472
567
  - then move to `compileFromFile(...)` once the model is clear.
@@ -714,6 +809,15 @@ These examples are included to help you jump to the right workflow quickly.
714
809
  - [execute-htlc.ts](./examples/execute-htlc.ts): HTLC preset with custom witness values.
715
810
  - [execute-transfer-with-timeout-cooperative.ts](./examples/execute-transfer-with-timeout-cooperative.ts): cooperative multi-witness timeout flow.
716
811
  - [gasless-transfer.ts](./examples/gasless-transfer.ts): standard relayer-backed gasless L-BTC transfer.
812
+ - [define-bond.ts](./examples/define-bond.ts): compile a bond example with a trusted definition hash anchor.
813
+ - [show-bond-definition.ts](./examples/show-bond-definition.ts): verify and retrieve a trusted bond definition from JSON + artifact.
814
+
815
+ In addition to the in-repo examples, the package has also been validated from a blank external consumer project with:
816
+ - `npm install @hazbase/simplicity`
817
+ - JS/TS import of `createSimplicityClient`
818
+ - preset compile -> fund -> inspect -> execute
819
+ - custom `.simf` compile -> fund -> inspect -> execute
820
+ - relayer-backed gasless execution
717
821
 
718
822
  ## FAQ / Practical Notes
719
823
 
@@ -729,6 +833,16 @@ It means: compile the contract, derive its address, then fund that address with
729
833
 
730
834
  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.
731
835
 
836
+ When you compile with `definition: { ... }`, the artifact also carries:
837
+ - `definitionType`
838
+ - `definitionId`
839
+ - `schemaVersion`
840
+ - `hash`
841
+ - `trustMode`
842
+ - `anchorMode`
843
+
844
+ That is what allows the SDK and CLI to verify that an off-chain JSON definition still matches the contract you compiled.
845
+
732
846
  ### When should I use a preset instead of a custom `.simf` file?
733
847
 
734
848
  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
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
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
+ const definition_1 = require("./core/definition");
10
11
  const presets_1 = require("./core/presets");
11
12
  const errors_1 = require("./core/errors");
12
13
  const SimplicityClient_1 = require("./client/SimplicityClient");
@@ -65,6 +66,25 @@ function parseWitnessSigners(values) {
65
66
  return [name, { type: "schnorrPrivkeyHex", privkeyHex }];
66
67
  }));
67
68
  }
69
+ function parseDefinitionInput() {
70
+ const type = getArg("definition-type");
71
+ const id = getArg("definition-id");
72
+ const jsonPath = getArg("definition-json");
73
+ const valueJson = getArg("definition-value");
74
+ const schemaVersion = getArg("definition-schema-version");
75
+ const anchorMode = getArg("definition-anchor-mode");
76
+ if (!type && !id && !jsonPath && !valueJson && !schemaVersion && !anchorMode) {
77
+ return undefined;
78
+ }
79
+ return {
80
+ type: type ?? "",
81
+ id: id ?? "",
82
+ schemaVersion: schemaVersion ?? undefined,
83
+ jsonPath,
84
+ value: valueJson ? JSON.parse(valueJson) : undefined,
85
+ anchorMode,
86
+ };
87
+ }
68
88
  function resolveConfig() {
69
89
  return {
70
90
  network: getArg("network", "liquidtestnet"),
@@ -357,6 +377,18 @@ function formatArtifactHelp(artifact, preset, utxos) {
357
377
  ` status: ${status}`,
358
378
  ` ready: ${ready ? "yes" : "no"}`,
359
379
  ].join("\n");
380
+ const definition = artifact.definition
381
+ ? [
382
+ ` type: ${artifact.definition.definitionType}`,
383
+ ` id: ${artifact.definition.definitionId}`,
384
+ ` schema version: ${artifact.definition.schemaVersion}`,
385
+ ` hash: ${artifact.definition.hash}`,
386
+ ` trust mode: ${artifact.definition.trustMode}`,
387
+ ` anchor mode: ${artifact.definition.anchorMode}`,
388
+ ` on-chain helper: ${artifact.definition.onChainAnchor?.helper ?? "(none)"}`,
389
+ ` source verified: ${artifact.definition.onChainAnchor?.sourceVerified === true ? "yes" : "no"}`,
390
+ ].join("\n")
391
+ : " (none)";
360
392
  const compileSource = artifact.source.simfPath ?? artifact.legacy?.simfTemplatePath ?? "(unknown)";
361
393
  const templateVars = artifact.source.templateVars ?? {};
362
394
  const inspectCommand = `simplicity-cli contract inspect --artifact ./artifact.json --wallet simplicity-test --privkey <privkey-hex> --to-address tex1...`;
@@ -375,6 +407,9 @@ function formatArtifactHelp(artifact, preset, utxos) {
375
407
  "Template Vars:",
376
408
  indent(JSON.stringify(templateVars, null, 2)),
377
409
  "",
410
+ "Definition Anchor:",
411
+ definition,
412
+ "",
378
413
  "Suggested Commands:",
379
414
  ` ${inspectCommand}`,
380
415
  ` ${executeCommand}`,
@@ -416,17 +451,52 @@ async function main() {
416
451
  const subcommand = process.argv[3];
417
452
  const sdk = (0, SimplicityClient_1.createSimplicityClient)(resolveConfig());
418
453
  if (!command) {
419
- throw new Error("Usage: simplicity-cli <compile|presets|preset|contract|artifact|gasless> ...");
454
+ throw new Error("Usage: simplicity-cli <compile|presets|preset|contract|artifact|definition|gasless> ...");
420
455
  }
421
456
  if (command === "compile") {
422
457
  const result = await sdk.compileFromFile({
423
458
  simfPath: requireArg("simf"),
424
459
  templateVars: parseAssignments(getMultiArgs("template-var")),
425
460
  artifactPath: getArg("artifact"),
461
+ definition: parseDefinitionInput(),
426
462
  });
427
463
  printJson({ artifact: result.artifact, deployment: result.deployment() });
428
464
  return;
429
465
  }
466
+ if (command === "definition" && subcommand === "show") {
467
+ const definition = await (0, definition_1.loadDefinitionInput)({
468
+ type: requireArg("type"),
469
+ id: requireArg("id"),
470
+ jsonPath: getArg("json-path"),
471
+ value: getArg("value") ? JSON.parse(getArg("value")) : undefined,
472
+ schemaVersion: getArg("schema-version"),
473
+ });
474
+ printJson({
475
+ ...definition,
476
+ anchorRecommendation: "Use --definition-anchor-mode on-chain-constant-committed with a blessed custom .simf helper for on-chain enforcement",
477
+ });
478
+ return;
479
+ }
480
+ if (command === "definition" && subcommand === "verify") {
481
+ const verification = await sdk.verifyDefinitionAgainstArtifact({
482
+ artifactPath: requireArg("artifact"),
483
+ type: getArg("type"),
484
+ id: getArg("id"),
485
+ expectedType: getArg("expected-type"),
486
+ expectedId: getArg("expected-id"),
487
+ jsonPath: getArg("json-path"),
488
+ value: getArg("value") ? JSON.parse(getArg("value")) : undefined,
489
+ schemaVersion: getArg("schema-version"),
490
+ });
491
+ printJson({
492
+ verified: verification.ok,
493
+ reason: verification.reason,
494
+ definition: verification.definition,
495
+ artifactDefinition: verification.artifactDefinition ?? null,
496
+ trust: verification.trust,
497
+ });
498
+ return;
499
+ }
430
500
  if (command === "presets" && subcommand === "list") {
431
501
  printJson((0, presets_1.listPresets)().map((preset) => (0, presets_1.describePreset)(preset)));
432
502
  return;
@@ -472,6 +542,7 @@ async function main() {
472
542
  preset: requireArg("preset"),
473
543
  params,
474
544
  artifactPath: getArg("artifact"),
545
+ definition: parseDefinitionInput(),
475
546
  });
476
547
  printJson({ artifact: result.artifact, deployment: result.deployment() });
477
548
  return;
@@ -1,4 +1,4 @@
1
- import { DeploymentInfo, SimplicityArtifact, SimplicityClientConfig } from "../core/types";
1
+ import { ArtifactDefinitionMetadata, DeploymentInfo, SimplicityArtifact, SimplicityClientConfig } from "../core/types";
2
2
  import { DeployedContract } from "./DeployedContract";
3
3
  export declare class CompiledContract {
4
4
  private readonly config;
@@ -7,6 +7,7 @@ export declare class CompiledContract {
7
7
  get contractAddress(): string;
8
8
  get cmr(): string;
9
9
  get program(): string;
10
+ definition(): ArtifactDefinitionMetadata | null;
10
11
  deployment(): DeploymentInfo;
11
12
  saveArtifact(path: string): Promise<void>;
12
13
  at(addressOverride?: string): DeployedContract;
@@ -19,6 +19,9 @@ class CompiledContract {
19
19
  get program() {
20
20
  return this.artifact.compiled.program;
21
21
  }
22
+ definition() {
23
+ return this.artifact.definition ?? null;
24
+ }
22
25
  deployment() {
23
26
  return {
24
27
  contractAddress: this.artifact.compiled.contractAddress,
@@ -1,4 +1,5 @@
1
- import { ContractUtxo, ExecuteCallInput, ExecuteResult, GaslessExecuteInput, GaslessExecuteResult, InspectCallInput, InspectResult, SimplicityArtifact, SimplicityClientConfig, WaitForFundingInput } from "../core/types";
1
+ import { verifyDefinitionAgainstArtifact } from "../core/definition";
2
+ import { ArtifactDefinitionMetadata, ContractUtxo, ExecuteCallInput, ExecuteResult, GaslessExecuteInput, GaslessExecuteResult, InspectCallInput, InspectResult, SimplicityArtifact, SimplicityClientConfig, WaitForFundingInput } from "../core/types";
2
3
  export declare class DeployedContract {
3
4
  private readonly config;
4
5
  readonly artifact: SimplicityArtifact;
@@ -9,4 +10,17 @@ export declare class DeployedContract {
9
10
  inspectCall(input: InspectCallInput): Promise<InspectResult>;
10
11
  execute(input: ExecuteCallInput): Promise<ExecuteResult>;
11
12
  executeGasless(input: GaslessExecuteInput): Promise<GaslessExecuteResult>;
13
+ getTrustedDefinition(input: {
14
+ jsonPath?: string;
15
+ value?: unknown;
16
+ type?: string;
17
+ id?: string;
18
+ schemaVersion?: string;
19
+ }): Promise<{
20
+ verified: boolean;
21
+ definition: Awaited<ReturnType<typeof verifyDefinitionAgainstArtifact>>["definition"];
22
+ artifactDefinition: ArtifactDefinitionMetadata | null;
23
+ reason?: string;
24
+ trust: Awaited<ReturnType<typeof verifyDefinitionAgainstArtifact>>["trust"];
25
+ }>;
12
26
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DeployedContract = void 0;
4
4
  const executor_1 = require("../core/executor");
5
+ const definition_1 = require("../core/definition");
5
6
  const executor_2 = require("../core/executor");
6
7
  class DeployedContract {
7
8
  config;
@@ -39,5 +40,26 @@ class DeployedContract {
39
40
  async executeGasless(input) {
40
41
  return (0, executor_2.executeGaslessContractCall)(this.config, this.artifact, input);
41
42
  }
43
+ async getTrustedDefinition(input) {
44
+ const verification = await (0, definition_1.verifyDefinitionAgainstArtifact)({
45
+ artifact: this.artifact,
46
+ definition: {
47
+ type: input.type ?? this.artifact.definition?.definitionType ?? "",
48
+ id: input.id ?? this.artifact.definition?.definitionId ?? "",
49
+ schemaVersion: input.schemaVersion,
50
+ jsonPath: input.jsonPath,
51
+ value: input.value,
52
+ },
53
+ expectedType: this.artifact.definition?.definitionType,
54
+ expectedId: this.artifact.definition?.definitionId,
55
+ });
56
+ return {
57
+ verified: verification.ok,
58
+ definition: verification.definition,
59
+ artifactDefinition: verification.artifactDefinition ?? null,
60
+ reason: verification.reason,
61
+ trust: verification.trust,
62
+ };
63
+ }
42
64
  }
43
65
  exports.DeployedContract = DeployedContract;
@@ -1,5 +1,5 @@
1
1
  import { ElementsRpcClient } from "../core/rpc";
2
- import { CompileFromFileInput, CompileFromPresetInput, SimplicityArtifact, SimplicityClientConfig } from "../core/types";
2
+ import { DefinitionInput, DefinitionVerificationResult, CompileFromFileInput, CompileFromPresetInput, DefinitionDescriptor, 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";
@@ -14,6 +14,19 @@ export declare class SimplicityClient {
14
14
  compileFromFile(input: CompileFromFileInput): Promise<CompiledContract>;
15
15
  compileFromPreset(input: CompileFromPresetInput): Promise<CompiledContract>;
16
16
  loadArtifact(path: string): Promise<CompiledContract>;
17
+ define(input: DefinitionInput): Promise<DefinitionDescriptor>;
18
+ loadDefinition(input: DefinitionInput): Promise<DefinitionDescriptor>;
19
+ verifyDefinitionAgainstArtifact(input: {
20
+ artifactPath?: string;
21
+ artifact?: SimplicityArtifact;
22
+ jsonPath?: string;
23
+ value?: unknown;
24
+ expectedType?: string;
25
+ expectedId?: string;
26
+ type?: string;
27
+ id?: string;
28
+ schemaVersion?: string;
29
+ }): Promise<DefinitionVerificationResult>;
17
30
  fromArtifact(artifact: SimplicityArtifact): DeployedContract;
18
31
  relayer(config: RelayerClientConfig): RelayerClient;
19
32
  private gaslessTransfer;
@@ -4,6 +4,8 @@ exports.SimplicityClient = void 0;
4
4
  exports.createSimplicityClient = createSimplicityClient;
5
5
  const artifact_1 = require("../core/artifact");
6
6
  const compiler_1 = require("../core/compiler");
7
+ const definition_1 = require("../core/definition");
8
+ const errors_1 = require("../core/errors");
7
9
  const rpc_1 = require("../core/rpc");
8
10
  const RelayerClient_1 = require("../gasless/RelayerClient");
9
11
  const ContractFactory_1 = require("./ContractFactory");
@@ -31,6 +33,30 @@ class SimplicityClient {
31
33
  const artifact = await (0, artifact_1.loadArtifact)(path, this.config.network);
32
34
  return new ContractFactory_1.CompiledContract(this.config, artifact);
33
35
  }
36
+ async define(input) {
37
+ return (0, definition_1.loadDefinitionInput)(input);
38
+ }
39
+ async loadDefinition(input) {
40
+ return (0, definition_1.loadDefinitionInput)(input);
41
+ }
42
+ async verifyDefinitionAgainstArtifact(input) {
43
+ const artifact = input.artifact ?? (input.artifactPath ? await (0, artifact_1.loadArtifact)(input.artifactPath, this.config.network) : undefined);
44
+ if (!artifact) {
45
+ throw new errors_1.ValidationError("artifactPath or artifact is required");
46
+ }
47
+ return (0, definition_1.verifyDefinitionAgainstArtifact)({
48
+ artifact,
49
+ definition: {
50
+ type: input.type ?? input.expectedType ?? artifact.definition?.definitionType ?? "",
51
+ id: input.id ?? input.expectedId ?? artifact.definition?.definitionId ?? "",
52
+ schemaVersion: input.schemaVersion,
53
+ jsonPath: input.jsonPath,
54
+ value: input.value,
55
+ },
56
+ expectedType: input.expectedType,
57
+ expectedId: input.expectedId,
58
+ });
59
+ }
34
60
  fromArtifact(artifact) {
35
61
  return new DeployedContract_1.DeployedContract(this.config, artifact);
36
62
  }
@@ -17,7 +17,16 @@ function isArtifactV5(value) {
17
17
  }
18
18
  function normalizeArtifact(artifact, networkDefault = "liquidtestnet") {
19
19
  if (artifact.version === exports.SDK_ARTIFACT_VERSION) {
20
- return artifact;
20
+ const current = artifact;
21
+ return {
22
+ ...current,
23
+ definition: current.definition
24
+ ? {
25
+ ...current.definition,
26
+ anchorMode: current.definition.anchorMode ?? "artifact-hash-anchor",
27
+ }
28
+ : undefined,
29
+ };
21
30
  }
22
31
  if (!isArtifactV5(artifact)) {
23
32
  throw new errors_1.ArtifactError("Unsupported artifact version", artifact);
@@ -55,6 +64,7 @@ function normalizeArtifact(artifact, networkDefault = "liquidtestnet") {
55
64
  sdkVersion: exports.SDK_PACKAGE_VERSION,
56
65
  notes: null,
57
66
  },
67
+ definition: undefined,
58
68
  legacy: {
59
69
  simfTemplatePath: artifact.simfTemplatePath,
60
70
  params: artifact.params,
@@ -90,6 +100,7 @@ async function loadArtifact(artifactPath, networkDefault = "liquidtestnet") {
90
100
  simfTemplatePath: legacySimfTemplatePath,
91
101
  }
92
102
  : undefined,
103
+ definition: normalized.definition,
93
104
  };
94
105
  }
95
106
  async function saveArtifact(artifactPath, artifact) {
@@ -9,6 +9,7 @@ const promises_1 = require("node:fs/promises");
9
9
  const node_os_1 = require("node:os");
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
11
  const artifact_1 = require("./artifact");
12
+ const definition_1 = require("./definition");
12
13
  const errors_1 = require("./errors");
13
14
  const presets_1 = require("./presets");
14
15
  const templating_1 = require("./templating");
@@ -67,6 +68,12 @@ async function buildArtifact(input) {
67
68
  sdkVersion: artifact_1.SDK_PACKAGE_VERSION,
68
69
  notes: null,
69
70
  },
71
+ definition: input.definition
72
+ ? (0, definition_1.buildArtifactDefinitionMetadata)(input.definition, {
73
+ anchorMode: input.definition.anchorMode,
74
+ onChainAnchor: input.definition.onChainAnchor,
75
+ })
76
+ : undefined,
70
77
  legacy: {
71
78
  simfTemplatePath: input.sourceSimfPath,
72
79
  params: {
@@ -77,8 +84,38 @@ async function buildArtifact(input) {
77
84
  }, input.config.network);
78
85
  }
79
86
  async function compileFromFile(config, input) {
87
+ const definition = input.definition ? await (0, definition_1.loadDefinitionInput)(input.definition) : undefined;
88
+ if (definition && input.templateVars?.DEFINITION_HASH !== undefined) {
89
+ throw new errors_1.ValidationError("DEFINITION_HASH must not be provided explicitly when definition metadata is supplied", { code: "DEFINITION_HASH_OVERRIDE_FORBIDDEN" });
90
+ }
91
+ if (definition && input.templateVars?.DEFINITION_ID !== undefined) {
92
+ throw new errors_1.ValidationError("DEFINITION_ID must not be provided explicitly when definition metadata is supplied", { code: "DEFINITION_ID_OVERRIDE_FORBIDDEN" });
93
+ }
80
94
  const rawSource = await (0, promises_1.readFile)(input.simfPath, "utf8");
81
- const rendered = (0, templating_1.renderTemplate)(rawSource, input.templateVars ?? {});
95
+ let onChainAnchor;
96
+ if (input.definition?.anchorMode === "on-chain-constant-committed") {
97
+ const detection = (0, definition_1.detectOnChainDefinitionAnchor)(rawSource);
98
+ if (!rawSource.includes("{{DEFINITION_HASH}}")) {
99
+ throw new errors_1.ValidationError("Requested on-chain constant-committed definition anchor, but the .simf source does not contain {{DEFINITION_HASH}}", { code: "DEFINITION_HASH_PLACEHOLDER_MISSING" });
100
+ }
101
+ if (!detection.sourceVerified || !detection.helper) {
102
+ const code = detection.reason?.includes("called")
103
+ ? "DEFINITION_ONCHAIN_HELPER_NOT_CALLED"
104
+ : "DEFINITION_ONCHAIN_HELPER_MISSING";
105
+ throw new errors_1.ValidationError(`Requested on-chain constant-committed definition anchor, but the .simf source does not contain the required anchor helper pattern: ${detection.reason ?? "unknown reason"}`, { code });
106
+ }
107
+ onChainAnchor = {
108
+ helper: detection.helper,
109
+ templateVar: "DEFINITION_HASH",
110
+ sourceVerified: true,
111
+ };
112
+ }
113
+ const templateVars = {
114
+ ...(input.templateVars ?? {}),
115
+ ...(definition && input.templateVars?.DEFINITION_HASH === undefined ? { DEFINITION_HASH: definition.hash } : {}),
116
+ ...(definition && input.templateVars?.DEFINITION_ID === undefined ? { DEFINITION_ID: definition.definitionId } : {}),
117
+ };
118
+ const rendered = (0, templating_1.renderTemplate)(rawSource, templateVars);
82
119
  const workDir = await (0, promises_1.mkdtemp)(node_path_1.default.join((0, node_os_1.tmpdir)(), "simplicity-sdk-compile-"));
83
120
  const renderedPath = node_path_1.default.join(workDir, node_path_1.default.basename(input.simfPath));
84
121
  await (0, promises_1.writeFile)(renderedPath, rendered, "utf8");
@@ -87,7 +124,14 @@ async function compileFromFile(config, input) {
87
124
  renderedSimfPath: renderedPath,
88
125
  sourceMode: "file",
89
126
  sourceSimfPath: input.simfPath,
90
- templateVars: input.templateVars,
127
+ templateVars,
128
+ definition: definition
129
+ ? {
130
+ ...definition,
131
+ anchorMode: input.definition?.anchorMode ?? "artifact-hash-anchor",
132
+ onChainAnchor,
133
+ }
134
+ : undefined,
91
135
  });
92
136
  if (input.artifactPath) {
93
137
  await (0, artifact_1.saveArtifact)(input.artifactPath, artifact);
@@ -96,7 +140,21 @@ async function compileFromFile(config, input) {
96
140
  }
97
141
  async function compileFromPreset(config, input) {
98
142
  const preset = (0, presets_1.getPresetOrThrow)(input.preset);
99
- const params = (0, presets_1.validatePresetParams)(preset, input.params);
143
+ if (input.definition?.anchorMode === "on-chain-constant-committed") {
144
+ 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
+ }
146
+ const definition = input.definition ? await (0, definition_1.loadDefinitionInput)(input.definition) : undefined;
147
+ if (definition && input.params.DEFINITION_HASH !== undefined) {
148
+ throw new errors_1.ValidationError("DEFINITION_HASH must not be provided explicitly when definition metadata is supplied", { code: "DEFINITION_HASH_OVERRIDE_FORBIDDEN" });
149
+ }
150
+ if (definition && input.params.DEFINITION_ID !== undefined) {
151
+ throw new errors_1.ValidationError("DEFINITION_ID must not be provided explicitly when definition metadata is supplied", { code: "DEFINITION_ID_OVERRIDE_FORBIDDEN" });
152
+ }
153
+ const params = {
154
+ ...(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 } : {}),
157
+ };
100
158
  const rawSource = await (0, promises_1.readFile)(preset.simfTemplatePath, "utf8");
101
159
  const rendered = (0, templating_1.renderTemplate)(rawSource, params);
102
160
  const workDir = await (0, promises_1.mkdtemp)(node_path_1.default.join((0, node_os_1.tmpdir)(), "simplicity-sdk-preset-"));
@@ -109,6 +167,12 @@ async function compileFromPreset(config, input) {
109
167
  sourceSimfPath: preset.simfTemplatePath,
110
168
  preset: preset.id,
111
169
  templateVars: params,
170
+ definition: definition
171
+ ? {
172
+ ...definition,
173
+ anchorMode: input.definition?.anchorMode ?? "artifact-hash-anchor",
174
+ }
175
+ : undefined,
112
176
  });
113
177
  if (input.artifactPath) {
114
178
  await (0, artifact_1.saveArtifact)(input.artifactPath, artifact);
@@ -0,0 +1,18 @@
1
+ import { ArtifactDefinitionMetadata, DefinitionAnchorMode, DefinitionDescriptor, DefinitionInput, DefinitionVerificationResult, SimplicityArtifact } from "./types";
2
+ export declare function loadDefinitionInput(input: DefinitionInput): Promise<DefinitionDescriptor>;
3
+ export declare function detectOnChainDefinitionAnchor(simfSource: string): {
4
+ sourceVerified: boolean;
5
+ helper?: "nonzero-eq_256";
6
+ reason?: string;
7
+ };
8
+ export declare function buildArtifactDefinitionMetadata(definition: DefinitionDescriptor, options?: {
9
+ anchorMode?: DefinitionAnchorMode;
10
+ onChainAnchor?: ArtifactDefinitionMetadata["onChainAnchor"];
11
+ }): ArtifactDefinitionMetadata;
12
+ export declare function verifyDefinitionDescriptorAgainstArtifact(definition: DefinitionDescriptor, artifactDefinition?: ArtifactDefinitionMetadata, expectedType?: string, expectedId?: string): DefinitionVerificationResult;
13
+ export declare function verifyDefinitionAgainstArtifact(input: {
14
+ artifact: SimplicityArtifact;
15
+ definition: DefinitionInput;
16
+ expectedType?: string;
17
+ expectedId?: string;
18
+ }): Promise<DefinitionVerificationResult>;
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadDefinitionInput = loadDefinitionInput;
7
+ exports.detectOnChainDefinitionAnchor = detectOnChainDefinitionAnchor;
8
+ exports.buildArtifactDefinitionMetadata = buildArtifactDefinitionMetadata;
9
+ exports.verifyDefinitionDescriptorAgainstArtifact = verifyDefinitionDescriptorAgainstArtifact;
10
+ exports.verifyDefinitionAgainstArtifact = verifyDefinitionAgainstArtifact;
11
+ const promises_1 = require("node:fs/promises");
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const errors_1 = require("./errors");
14
+ const summary_1 = require("./summary");
15
+ const DEFAULT_SCHEMA_VERSION = "1";
16
+ const DEFAULT_ANCHOR_MODE = "artifact-hash-anchor";
17
+ const ZERO_HASH_256 = "0x0000000000000000000000000000000000000000000000000000000000000000";
18
+ function stripComments(source) {
19
+ return source
20
+ .replace(/\/\*[\s\S]*?\*\//g, "")
21
+ .replace(/(^|[^:])\/\/.*$/gm, "$1");
22
+ }
23
+ function extractFunctionBody(source, functionName) {
24
+ const marker = `fn ${functionName}()`;
25
+ const start = source.indexOf(marker);
26
+ if (start === -1)
27
+ return null;
28
+ const braceStart = source.indexOf("{", start);
29
+ if (braceStart === -1)
30
+ return null;
31
+ let depth = 0;
32
+ for (let i = braceStart; i < source.length; i += 1) {
33
+ const char = source[i];
34
+ if (char === "{")
35
+ depth += 1;
36
+ if (char === "}") {
37
+ depth -= 1;
38
+ if (depth === 0) {
39
+ return source.slice(braceStart + 1, i);
40
+ }
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ function assertNonEmpty(value, fieldName) {
46
+ if (!value || value.trim().length === 0) {
47
+ throw new errors_1.DefinitionError(`${fieldName} must not be empty`);
48
+ }
49
+ return value;
50
+ }
51
+ function ensureSerializable(value, seen = new WeakSet()) {
52
+ if (value === undefined) {
53
+ throw new errors_1.DefinitionError("Definition JSON must not contain undefined values");
54
+ }
55
+ if (value === null)
56
+ return;
57
+ if (typeof value === "bigint") {
58
+ throw new errors_1.DefinitionError("Definition JSON must not contain bigint values");
59
+ }
60
+ if (value instanceof Date) {
61
+ throw new errors_1.DefinitionError("Definition JSON must not contain Date objects; normalize them first");
62
+ }
63
+ if (Array.isArray(value)) {
64
+ for (const entry of value)
65
+ ensureSerializable(entry, seen);
66
+ return;
67
+ }
68
+ if (typeof value === "object") {
69
+ const objectValue = value;
70
+ if (seen.has(objectValue)) {
71
+ throw new errors_1.DefinitionError("Definition JSON must not contain circular references");
72
+ }
73
+ seen.add(objectValue);
74
+ for (const entry of Object.values(objectValue))
75
+ ensureSerializable(entry, seen);
76
+ }
77
+ }
78
+ async function resolveDefinitionValue(input) {
79
+ if ((input.jsonPath ? 1 : 0) + (input.value !== undefined ? 1 : 0) !== 1) {
80
+ throw new errors_1.DefinitionError("Exactly one of jsonPath or value must be provided");
81
+ }
82
+ if (input.jsonPath) {
83
+ const sourcePath = node_path_1.default.resolve(input.jsonPath);
84
+ const raw = await (0, promises_1.readFile)(sourcePath, "utf8");
85
+ try {
86
+ return { value: JSON.parse(raw), sourcePath };
87
+ }
88
+ catch (error) {
89
+ throw new errors_1.DefinitionError(`Failed to parse definition JSON at ${sourcePath}`, error);
90
+ }
91
+ }
92
+ return { value: input.value };
93
+ }
94
+ async function loadDefinitionInput(input) {
95
+ const definitionType = assertNonEmpty(input.type, "definition.type");
96
+ const definitionId = assertNonEmpty(input.id, "definition.id");
97
+ const schemaVersion = assertNonEmpty(input.schemaVersion ?? DEFAULT_SCHEMA_VERSION, "definition.schemaVersion");
98
+ const { value, sourcePath } = await resolveDefinitionValue(input);
99
+ ensureSerializable(value);
100
+ const canonicalJson = (0, summary_1.stableStringify)(value);
101
+ return {
102
+ definitionType,
103
+ definitionId,
104
+ schemaVersion,
105
+ canonicalJson,
106
+ hash: (0, summary_1.sha256HexUtf8)(canonicalJson),
107
+ sourcePath,
108
+ };
109
+ }
110
+ function detectOnChainDefinitionAnchor(simfSource) {
111
+ const source = stripComments(simfSource.replace(/\r\n/g, "\n"));
112
+ if (!source.includes("{{DEFINITION_HASH}}")) {
113
+ return { sourceVerified: false, reason: "DEFINITION_HASH placeholder is missing" };
114
+ }
115
+ const helperBody = extractFunctionBody(source, "require_definition_anchor");
116
+ if (!helperBody) {
117
+ return { sourceVerified: false, reason: "Required anchor helper function is missing" };
118
+ }
119
+ if (!helperBody.includes("let anchored_definition_hash: u256 = 0x{{DEFINITION_HASH}};")) {
120
+ return { sourceVerified: false, reason: "Required anchored_definition_hash assignment is missing" };
121
+ }
122
+ if (!helperBody.includes(`let zero_hash: u256 = ${ZERO_HASH_256};`)) {
123
+ return { sourceVerified: false, reason: "Required zero_hash assignment is missing" };
124
+ }
125
+ if (!helperBody.includes("assert!(not(jet::eq_256(anchored_definition_hash, zero_hash)));")) {
126
+ return { sourceVerified: false, reason: "Required eq_256 assertion is missing" };
127
+ }
128
+ const mainBody = extractFunctionBody(source, "main");
129
+ if (!mainBody) {
130
+ return { sourceVerified: false, reason: "main function is missing" };
131
+ }
132
+ if (!mainBody.includes("require_definition_anchor();")) {
133
+ return { sourceVerified: false, reason: "require_definition_anchor() is not called from main" };
134
+ }
135
+ return { sourceVerified: true, helper: "nonzero-eq_256" };
136
+ }
137
+ function buildArtifactDefinitionMetadata(definition, options) {
138
+ return {
139
+ definitionType: definition.definitionType,
140
+ definitionId: definition.definitionId,
141
+ schemaVersion: definition.schemaVersion,
142
+ hash: definition.hash,
143
+ trustMode: "hash-anchor",
144
+ anchorMode: options?.anchorMode ?? DEFAULT_ANCHOR_MODE,
145
+ onChainAnchor: options?.onChainAnchor,
146
+ };
147
+ }
148
+ function verifyDefinitionDescriptorAgainstArtifact(definition, artifactDefinition, expectedType, expectedId) {
149
+ const noDefinitionTrust = {
150
+ artifactMatch: false,
151
+ onChainAnchorPresent: false,
152
+ onChainAnchorVerified: false,
153
+ effectiveMode: "none",
154
+ };
155
+ if (expectedType && expectedType !== definition.definitionType) {
156
+ return {
157
+ ok: false,
158
+ reason: `Definition type mismatch: expected=${expectedType} actual=${definition.definitionType}`,
159
+ definition,
160
+ artifactDefinition,
161
+ trust: noDefinitionTrust,
162
+ };
163
+ }
164
+ if (expectedId && expectedId !== definition.definitionId) {
165
+ return {
166
+ ok: false,
167
+ reason: `Definition id mismatch: expected=${expectedId} actual=${definition.definitionId}`,
168
+ definition,
169
+ artifactDefinition,
170
+ trust: noDefinitionTrust,
171
+ };
172
+ }
173
+ if (!artifactDefinition) {
174
+ return {
175
+ ok: false,
176
+ reason: "Artifact does not contain definition metadata",
177
+ definition,
178
+ trust: noDefinitionTrust,
179
+ };
180
+ }
181
+ const trust = {
182
+ artifactMatch: false,
183
+ onChainAnchorPresent: artifactDefinition.anchorMode === "on-chain-constant-committed",
184
+ onChainAnchorVerified: false,
185
+ effectiveMode: artifactDefinition.anchorMode,
186
+ };
187
+ if (artifactDefinition.definitionType !== definition.definitionType) {
188
+ return {
189
+ ok: false,
190
+ reason: `Definition type mismatch: artifact=${artifactDefinition.definitionType} actual=${definition.definitionType}`,
191
+ definition,
192
+ artifactDefinition,
193
+ trust,
194
+ };
195
+ }
196
+ if (artifactDefinition.definitionId !== definition.definitionId) {
197
+ return {
198
+ ok: false,
199
+ reason: `Definition id mismatch: artifact=${artifactDefinition.definitionId} actual=${definition.definitionId}`,
200
+ definition,
201
+ artifactDefinition,
202
+ trust,
203
+ };
204
+ }
205
+ if (artifactDefinition.schemaVersion !== definition.schemaVersion) {
206
+ return {
207
+ ok: false,
208
+ reason: `Definition schemaVersion mismatch: artifact=${artifactDefinition.schemaVersion} actual=${definition.schemaVersion}`,
209
+ definition,
210
+ artifactDefinition,
211
+ trust,
212
+ };
213
+ }
214
+ if (artifactDefinition.hash !== definition.hash) {
215
+ return {
216
+ ok: false,
217
+ reason: `Definition hash mismatch: artifact=${artifactDefinition.hash} actual=${definition.hash}`,
218
+ definition,
219
+ artifactDefinition,
220
+ trust,
221
+ };
222
+ }
223
+ return {
224
+ ok: true,
225
+ definition,
226
+ artifactDefinition,
227
+ trust: {
228
+ ...trust,
229
+ artifactMatch: true,
230
+ },
231
+ };
232
+ }
233
+ async function resolveDefinitionTrust(artifact, baseTrust) {
234
+ if (!artifact.definition) {
235
+ return {
236
+ artifactMatch: false,
237
+ onChainAnchorPresent: false,
238
+ onChainAnchorVerified: false,
239
+ effectiveMode: "none",
240
+ };
241
+ }
242
+ if (artifact.definition.anchorMode !== "on-chain-constant-committed") {
243
+ return baseTrust;
244
+ }
245
+ if (artifact.source.mode !== "file" || !artifact.source.simfPath) {
246
+ return {
247
+ ...baseTrust,
248
+ onChainAnchorPresent: true,
249
+ onChainAnchorVerified: false,
250
+ effectiveMode: "on-chain-constant-committed",
251
+ };
252
+ }
253
+ try {
254
+ const source = await (0, promises_1.readFile)(artifact.source.simfPath, "utf8");
255
+ const detection = detectOnChainDefinitionAnchor(source);
256
+ return {
257
+ ...baseTrust,
258
+ onChainAnchorPresent: true,
259
+ onChainAnchorVerified: detection.sourceVerified === true,
260
+ effectiveMode: "on-chain-constant-committed",
261
+ };
262
+ }
263
+ catch {
264
+ return {
265
+ ...baseTrust,
266
+ onChainAnchorPresent: true,
267
+ onChainAnchorVerified: false,
268
+ effectiveMode: "on-chain-constant-committed",
269
+ };
270
+ }
271
+ }
272
+ async function verifyDefinitionAgainstArtifact(input) {
273
+ const definition = await loadDefinitionInput(input.definition);
274
+ const result = verifyDefinitionDescriptorAgainstArtifact(definition, input.artifact.definition, input.expectedType, input.expectedId);
275
+ return {
276
+ ...result,
277
+ trust: await resolveDefinitionTrust(input.artifact, result.trust),
278
+ };
279
+ }
@@ -31,3 +31,9 @@ export declare class ValidationError extends SimplicitySdkError {
31
31
  export declare class PresetExecutionError extends SimplicitySdkError {
32
32
  constructor(message: string, details?: unknown);
33
33
  }
34
+ export declare class DefinitionError extends SimplicitySdkError {
35
+ constructor(message: string, details?: unknown);
36
+ }
37
+ export declare class DefinitionVerificationError extends SimplicitySdkError {
38
+ constructor(message: string, details?: unknown);
39
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PresetExecutionError = exports.ValidationError = exports.UnsupportedFeatureError = exports.RelayerError = exports.UtxoNotFoundError = exports.ExecutionError = exports.ArtifactError = exports.CompilerError = exports.ToolchainError = exports.SimplicitySdkError = void 0;
3
+ exports.DefinitionVerificationError = exports.DefinitionError = exports.PresetExecutionError = exports.ValidationError = exports.UnsupportedFeatureError = exports.RelayerError = exports.UtxoNotFoundError = exports.ExecutionError = exports.ArtifactError = exports.CompilerError = exports.ToolchainError = exports.SimplicitySdkError = void 0;
4
4
  class SimplicitySdkError extends Error {
5
5
  code;
6
6
  details;
@@ -68,3 +68,15 @@ class PresetExecutionError extends SimplicitySdkError {
68
68
  }
69
69
  }
70
70
  exports.PresetExecutionError = PresetExecutionError;
71
+ class DefinitionError extends SimplicitySdkError {
72
+ constructor(message, details) {
73
+ super("DEFINITION_ERROR", message, details);
74
+ }
75
+ }
76
+ exports.DefinitionError = DefinitionError;
77
+ class DefinitionVerificationError extends SimplicitySdkError {
78
+ constructor(message, details) {
79
+ super("DEFINITION_VERIFICATION_ERROR", message, details);
80
+ }
81
+ }
82
+ exports.DefinitionVerificationError = DefinitionVerificationError;
@@ -202,8 +202,17 @@ function buildPsetSummary(decoded, meta) {
202
202
  return {
203
203
  network: meta.network,
204
204
  purpose: meta.purpose,
205
- bondDefinitionId: meta.bondDefinitionId ?? null,
205
+ bondDefinitionId: meta.definitionType === "bond"
206
+ ? (meta.definitionId ?? meta.bondDefinitionId ?? null)
207
+ : (meta.bondDefinitionId ?? null),
206
208
  periodId: meta.periodId ?? null,
209
+ definition: {
210
+ type: meta.definitionType ?? null,
211
+ id: meta.definitionId ?? null,
212
+ hash: meta.definitionHash ?? null,
213
+ trustMode: meta.definitionTrustMode ?? null,
214
+ anchorMode: meta.definitionAnchorMode ?? null,
215
+ },
207
216
  contract: {
208
217
  address: meta.contractAddress,
209
218
  cmr: meta.cmr,
@@ -290,6 +299,11 @@ async function buildExecutionState(config, artifact, input) {
290
299
  purpose: input.purpose ?? "sdk_execute",
291
300
  bondDefinitionId: input.bondDefinitionId,
292
301
  periodId: input.periodId,
302
+ definitionType: artifact.definition?.definitionType,
303
+ definitionId: artifact.definition?.definitionId,
304
+ definitionHash: artifact.definition?.hash,
305
+ definitionTrustMode: artifact.definition?.trustMode,
306
+ definitionAnchorMode: artifact.definition?.anchorMode,
293
307
  expectedLiquidReceiver: input.expectedLiquidReceiver ?? recipientAddress,
294
308
  contractAddress: artifact.compiled.contractAddress,
295
309
  cmr: artifact.compiled.cmr,
@@ -440,6 +454,12 @@ async function executeGaslessContractCall(config, artifact, input) {
440
454
  const summary = buildPsetSummary(decoded, {
441
455
  network: artifact.network,
442
456
  purpose: "sdk_gasless_execute",
457
+ bondDefinitionId: null,
458
+ definitionType: artifact.definition?.definitionType,
459
+ definitionId: artifact.definition?.definitionId,
460
+ definitionHash: artifact.definition?.hash,
461
+ definitionTrustMode: artifact.definition?.trustMode,
462
+ definitionAnchorMode: artifact.definition?.anchorMode,
443
463
  contractAddress: artifact.compiled.contractAddress,
444
464
  cmr: artifact.compiled.cmr,
445
465
  internalKey: artifact.compiled.internalKey,
@@ -624,8 +644,15 @@ async function executeRelayedGaslessContractCall(config, artifact, input, relaye
624
644
  summary: {
625
645
  network: artifact.network,
626
646
  purpose: "sdk_gasless_execute_relayer",
627
- bondDefinitionId: null,
647
+ bondDefinitionId: artifact.definition?.definitionType === "bond" ? artifact.definition.definitionId : null,
628
648
  periodId: null,
649
+ definition: {
650
+ type: artifact.definition?.definitionType ?? null,
651
+ id: artifact.definition?.definitionId ?? null,
652
+ hash: artifact.definition?.hash ?? null,
653
+ trustMode: artifact.definition?.trustMode ?? null,
654
+ anchorMode: artifact.definition?.anchorMode ?? null,
655
+ },
629
656
  contract: {
630
657
  address: request.detailedSummary.contract.contractAddress,
631
658
  cmr: request.detailedSummary.contract.cmr,
@@ -1,5 +1,7 @@
1
1
  export type NetworkName = "liquidtestnet" | "liquidv1" | "regtest";
2
2
  export type UtxoPolicy = "smallest_over" | "largest" | "newest";
3
+ export type DefinitionTrustMode = "hash-anchor";
4
+ export type DefinitionAnchorMode = "artifact-hash-anchor" | "on-chain-constant-committed";
3
5
  export interface RpcConfig {
4
6
  url: string;
5
7
  username: string;
@@ -68,6 +70,7 @@ export interface SimplicityArtifact {
68
70
  sdkVersion: string;
69
71
  notes: string | null;
70
72
  };
73
+ definition?: ArtifactDefinitionMetadata;
71
74
  legacy?: {
72
75
  simfTemplatePath?: string;
73
76
  params?: {
@@ -81,11 +84,42 @@ export interface CompileFromFileInput {
81
84
  simfPath: string;
82
85
  templateVars?: Record<string, string | number>;
83
86
  artifactPath?: string;
87
+ definition?: DefinitionInput;
84
88
  }
85
89
  export interface CompileFromPresetInput {
86
90
  preset: string;
87
91
  params: Record<string, string | number>;
88
92
  artifactPath?: string;
93
+ definition?: DefinitionInput;
94
+ }
95
+ export interface DefinitionInput {
96
+ type: string;
97
+ id: string;
98
+ schemaVersion?: string;
99
+ jsonPath?: string;
100
+ value?: unknown;
101
+ anchorMode?: DefinitionAnchorMode;
102
+ }
103
+ export interface DefinitionDescriptor {
104
+ definitionType: string;
105
+ definitionId: string;
106
+ schemaVersion: string;
107
+ canonicalJson: string;
108
+ hash: string;
109
+ sourcePath?: string;
110
+ }
111
+ export interface ArtifactDefinitionMetadata {
112
+ definitionType: string;
113
+ definitionId: string;
114
+ schemaVersion: string;
115
+ hash: string;
116
+ trustMode: DefinitionTrustMode;
117
+ anchorMode: DefinitionAnchorMode;
118
+ onChainAnchor?: {
119
+ helper: "nonzero-eq_256";
120
+ templateVar: "DEFINITION_HASH";
121
+ sourceVerified: boolean;
122
+ };
89
123
  }
90
124
  export interface DeploymentInfo {
91
125
  contractAddress: string;
@@ -151,6 +185,13 @@ export interface PsetSummary {
151
185
  purpose?: string;
152
186
  bondDefinitionId?: string | null;
153
187
  periodId?: string | null;
188
+ definition?: {
189
+ type: string | null;
190
+ id: string | null;
191
+ hash: string | null;
192
+ trustMode: DefinitionTrustMode | null;
193
+ anchorMode: DefinitionAnchorMode | null;
194
+ };
154
195
  contract: {
155
196
  address: string;
156
197
  cmr: string;
@@ -218,6 +259,18 @@ export interface GaslessExecuteResult {
218
259
  amountSat: number;
219
260
  };
220
261
  }
262
+ export interface DefinitionVerificationResult {
263
+ ok: boolean;
264
+ reason?: string;
265
+ definition: DefinitionDescriptor;
266
+ artifactDefinition?: ArtifactDefinitionMetadata;
267
+ trust: {
268
+ artifactMatch: boolean;
269
+ onChainAnchorPresent: boolean;
270
+ onChainAnchorVerified: boolean;
271
+ effectiveMode: "none" | DefinitionAnchorMode;
272
+ };
273
+ }
221
274
  export interface WaitForFundingInput {
222
275
  minAmountSat?: number;
223
276
  pollIntervalMs?: number;
package/dist/index.d.ts CHANGED
@@ -6,3 +6,4 @@ export * from "./core/types";
6
6
  export * from "./core/errors";
7
7
  export { listPresets, getPresetOrThrow } from "./core/presets";
8
8
  export { loadArtifact, saveArtifact, normalizeArtifact } from "./core/artifact";
9
+ export { loadDefinitionInput, buildArtifactDefinitionMetadata, verifyDefinitionAgainstArtifact, verifyDefinitionDescriptorAgainstArtifact, } from "./core/definition";
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.normalizeArtifact = exports.saveArtifact = exports.loadArtifact = exports.getPresetOrThrow = exports.listPresets = exports.RelayerClient = exports.DeployedContract = exports.CompiledContract = exports.SimplicityClient = exports.createSimplicityClient = void 0;
17
+ exports.verifyDefinitionDescriptorAgainstArtifact = exports.verifyDefinitionAgainstArtifact = exports.buildArtifactDefinitionMetadata = exports.loadDefinitionInput = exports.normalizeArtifact = exports.saveArtifact = exports.loadArtifact = exports.getPresetOrThrow = exports.listPresets = exports.RelayerClient = exports.DeployedContract = exports.CompiledContract = exports.SimplicityClient = exports.createSimplicityClient = void 0;
18
18
  var SimplicityClient_1 = require("./client/SimplicityClient");
19
19
  Object.defineProperty(exports, "createSimplicityClient", { enumerable: true, get: function () { return SimplicityClient_1.createSimplicityClient; } });
20
20
  Object.defineProperty(exports, "SimplicityClient", { enumerable: true, get: function () { return SimplicityClient_1.SimplicityClient; } });
@@ -33,3 +33,8 @@ var artifact_1 = require("./core/artifact");
33
33
  Object.defineProperty(exports, "loadArtifact", { enumerable: true, get: function () { return artifact_1.loadArtifact; } });
34
34
  Object.defineProperty(exports, "saveArtifact", { enumerable: true, get: function () { return artifact_1.saveArtifact; } });
35
35
  Object.defineProperty(exports, "normalizeArtifact", { enumerable: true, get: function () { return artifact_1.normalizeArtifact; } });
36
+ var definition_1 = require("./core/definition");
37
+ Object.defineProperty(exports, "loadDefinitionInput", { enumerable: true, get: function () { return definition_1.loadDefinitionInput; } });
38
+ Object.defineProperty(exports, "buildArtifactDefinitionMetadata", { enumerable: true, get: function () { return definition_1.buildArtifactDefinitionMetadata; } });
39
+ Object.defineProperty(exports, "verifyDefinitionAgainstArtifact", { enumerable: true, get: function () { return definition_1.verifyDefinitionAgainstArtifact; } });
40
+ Object.defineProperty(exports, "verifyDefinitionDescriptorAgainstArtifact", { enumerable: true, get: function () { return definition_1.verifyDefinitionDescriptorAgainstArtifact; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazbase/simplicity",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "An SDK for Simplicity on Liquid",
5
5
  "author": "IndieSquare Inc <info@hazbase.com>",
6
6
  "keywords": [
@@ -36,7 +36,8 @@
36
36
  "build": "tsc && node scripts/copy-presets.mjs",
37
37
  "start": "node dist/cli.js",
38
38
  "test": "npm run build && node --test dist/test",
39
- "e2e:simplicity-relayer": "npm run build && node scripts/e2e-simplicity-relayer.mjs"
39
+ "e2e:simplicity-relayer": "npm run build && node scripts/e2e-simplicity-relayer.mjs",
40
+ "prepublishOnly": "npm run build"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@types/node": "^22.0.0",