@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 +89 -0
- package/dist/cli.js +91 -1
- package/dist/client/ContractFactory.d.ts +2 -1
- package/dist/client/ContractFactory.js +3 -0
- package/dist/client/DeployedContract.d.ts +15 -1
- package/dist/client/DeployedContract.js +22 -0
- package/dist/client/SimplicityClient.d.ts +39 -1
- package/dist/client/SimplicityClient.js +29 -0
- package/dist/core/artifact.js +8 -0
- package/dist/core/compiler.js +60 -5
- package/dist/core/executor.js +24 -0
- package/dist/core/state.d.ts +18 -0
- package/dist/core/state.js +278 -0
- package/dist/core/types.d.ts +73 -0
- package/dist/domain/bond.d.ts +59 -0
- package/dist/domain/bond.js +128 -0
- package/dist/domain/bondValidation.d.ts +9 -0
- package/dist/domain/bondValidation.js +90 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +14 -1
- package/package.json +1 -1
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;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { verifyDefinitionAgainstArtifact } from "../core/definition";
|
|
2
|
-
import {
|
|
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
|
}
|
package/dist/core/artifact.js
CHANGED
|
@@ -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) {
|
package/dist/core/compiler.js
CHANGED
|
@@ -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
|
|
116
|
-
...(
|
|
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
|
|
156
|
-
...(
|
|
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);
|