@hazbase/simplicity 0.4.8 → 0.4.10

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
@@ -12,7 +12,7 @@ It is intentionally narrower than a full market stack. It does **not** try to be
12
12
 
13
13
  This SDK is designed to help Node developers get productive quickly, but it is still opinionated, early-stage, and best suited today to permissioned settlement pilots on Liquid.
14
14
 
15
- ## Version 0.4.7
15
+ ## Strict policy lifecycle
16
16
 
17
17
  This release extends strict-policy positions from ordinary transfers to a
18
18
  complete controlled lifecycle:
@@ -24,7 +24,7 @@ complete controlled lifecycle:
24
24
  - browser wallets can provide their own audited crypto implementation before
25
25
  any strict-policy helper loads, avoiding extension CSP violations.
26
26
 
27
- For policy-locked redemptions, Liquid x402 now binds a wallet-created holder
27
+ For policy-locked redemptions, Liquid x402 binds a wallet-created holder
28
28
  signature for the locally recomputed transaction hash into the payment payload.
29
29
 
30
30
  ## What You Can Build
@@ -185,6 +185,23 @@ enough for the bounded transaction shape. Do not use it for a secondary market,
185
185
  additional intermediaries, or transactions that need more inputs, outputs, or
186
186
  approved holders.
187
187
 
188
+ `strict_v2_confidential` is a separate verifier profile for Atomic DvP
189
+ purchases. It keeps the protected RWA inputs and outputs explicit, while the
190
+ single treasury-payment output at index 2 uses standard Liquid confidentiality.
191
+ The verifier still checks strict-asset conservation and rejects confidential
192
+ strict outputs. A wallet must inspect the complete explicit proposal first,
193
+ blind only that fixed payment output locally, and sign the resulting PSET. The
194
+ treasury then independently unblinds and checks the payment before service
195
+ finalization. Existing `strict_v2` and `strict_v2_compact` positions are not
196
+ changed or reinterpreted by this profile.
197
+
198
+ `strict_v2_compact_confidential` applies the same fixed payment-output
199
+ confidentiality to the bounded pilot envelope: at most 3 inputs, 4 outputs,
200
+ and 2 approved holders. It is intended for one-position issuance and purchase
201
+ flows where the general profile would exceed the network execution budget. It
202
+ does not weaken the holder signature, whitelist, asset-conservation, or
203
+ fixed-index payment checks.
204
+
188
205
  ```ts
189
206
  const verifier = normalizeStrictPolicyVerifierCovenantParams({
190
207
  assetId,
@@ -0,0 +1,265 @@
1
+ /*
2
+ * Strict v2 confidential-value verifier covenant.
3
+ *
4
+ * This is a distinct profile from strict-verifier-ordinary-v1. It permits a
5
+ * single, designated confidential payment output at index 2. Every strict
6
+ * RWA input and output remains explicit, and the covenant requires their
7
+ * aggregate amounts to match. This prevents a strict RWA from being moved
8
+ * through the confidential payment output while allowing standard PSET
9
+ * value-and-asset blinding for the payment itself.
10
+ */
11
+ fn not(bit: bool) -> bool {
12
+ <u1>::into(jet::complement_1(<bool>::into(bit)))
13
+ }
14
+
15
+ fn explicit_input_asset_amount(index: u32) -> (u256, u64) {
16
+ let pair: (Asset1, Amount1) = unwrap(jet::input_amount(index));
17
+ let (asset, amount): (Asset1, Amount1) = pair;
18
+ let asset_bits: u256 = unwrap_right::<(u1, u256)>(asset);
19
+ let amount_bits: u64 = unwrap_right::<(u1, u256)>(amount);
20
+ (asset_bits, amount_bits)
21
+ }
22
+
23
+ fn explicit_output_asset_amount(index: u32) -> (u256, u64) {
24
+ let pair: (Asset1, Amount1) = unwrap(jet::output_amount(index));
25
+ let (asset, amount): (Asset1, Amount1) = pair;
26
+ let asset_bits: u256 = unwrap_right::<(u1, u256)>(asset);
27
+ let amount_bits: u64 = unwrap_right::<(u1, u256)>(amount);
28
+ (asset_bits, amount_bits)
29
+ }
30
+
31
+ fn add_amount(left: u64, right: u64) -> u64 {
32
+ let (carry, sum): (bool, u64) = jet::add_64(left, right);
33
+ assert!(not(carry));
34
+ sum
35
+ }
36
+
37
+ fn whitelist_owner_leaf(owner: Pubkey) -> u256 {
38
+ let tag_hash: u256 = 0x{{WHITELIST_OWNER_TAG_HASH}};
39
+ let ctx: Ctx8 = jet::sha_256_ctx_8_init();
40
+ let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash);
41
+ let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash);
42
+ let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, owner);
43
+ jet::sha_256_ctx_8_finalize(ctx)
44
+ }
45
+
46
+ fn whitelist_step(step: (bool, u256), node: u256) -> u256 {
47
+ let (sibling_on_left, sibling): (bool, u256) = step;
48
+ let tag_hash: u256 = 0x{{WHITELIST_NODE_TAG_HASH}};
49
+ let ctx: Ctx8 = jet::sha_256_ctx_8_init();
50
+ let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash);
51
+ let ctx: Ctx8 = jet::sha_256_ctx_8_add_32(ctx, tag_hash);
52
+ let ctx: Ctx8 = match sibling_on_left {
53
+ true => jet::sha_256_ctx_8_add_32(ctx, sibling),
54
+ false => jet::sha_256_ctx_8_add_32(ctx, node),
55
+ };
56
+ let ctx: Ctx8 = match sibling_on_left {
57
+ true => jet::sha_256_ctx_8_add_32(ctx, node),
58
+ false => jet::sha_256_ctx_8_add_32(ctx, sibling),
59
+ };
60
+ jet::sha_256_ctx_8_finalize(ctx)
61
+ }
62
+
63
+ fn require_whitelisted(owner: Pubkey, proof: [(bool, u256); {{WHITELIST_DEPTH}}]) {
64
+ let leaf: u256 = whitelist_owner_leaf(owner);
65
+ let root: u256 = array_fold::<whitelist_step, {{WHITELIST_DEPTH}}>(proof, leaf);
66
+ let expected_root: u256 = 0x{{WHITELIST_ROOT}};
67
+ assert!(jet::eq_256(root, expected_root));
68
+ }
69
+
70
+ fn canonical_holder_script_hash(owner: Pubkey) -> u256 {
71
+ let holder_cmr: u256 = 0x{{HOLDER_PROGRAM_CMR}};
72
+ let holder_leaf: u256 = jet::build_tapleaf_simplicity(holder_cmr);
73
+ let tapdata_ctx: Ctx8 = jet::tapdata_init();
74
+ let tapdata_ctx: Ctx8 = jet::sha_256_ctx_8_add_32(tapdata_ctx, owner);
75
+ let owner_tapdata: u256 = jet::sha_256_ctx_8_finalize(tapdata_ctx);
76
+ let holder_root: u256 = jet::build_tapbranch(holder_leaf, owner_tapdata);
77
+ let holder_internal_key: Pubkey = 0x{{HOLDER_NUMS_INTERNAL_KEY}};
78
+ let output_key: u256 = jet::build_taptweak(holder_internal_key, holder_root);
79
+
80
+ let script_ctx: Ctx8 = jet::sha_256_ctx_8_init();
81
+ let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_1(script_ctx, 0x51);
82
+ let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_1(script_ctx, 0x20);
83
+ let script_ctx: Ctx8 = jet::sha_256_ctx_8_add_32(script_ctx, output_key);
84
+ jet::sha_256_ctx_8_finalize(script_ctx)
85
+ }
86
+
87
+ fn next_index(index: u32) -> u32 {
88
+ let (carry, next): (bool, u32) = jet::add_32(index, 1);
89
+ assert!(not(carry));
90
+ next
91
+ }
92
+
93
+ fn scan_input(unused: (), state: (u32, u64)) -> (u32, u64) {
94
+ let (index, strict_total): (u32, u64) = state;
95
+ let past_end: bool = jet::le_32(jet::num_inputs(), index);
96
+ let strict_total: u64 = match past_end {
97
+ true => strict_total,
98
+ false => {
99
+ let (asset, amount): (u256, u64) = explicit_input_asset_amount(index);
100
+ let strict_asset: u256 = 0x{{STRICT_ASSET_ID_JET_HEX}};
101
+ let is_strict: bool = jet::eq_256(asset, strict_asset);
102
+ match is_strict {
103
+ true => add_amount(strict_total, amount),
104
+ false => strict_total,
105
+ }
106
+ },
107
+ };
108
+ (next_index(index), strict_total)
109
+ }
110
+
111
+ fn scan_output_ordinary(
112
+ owner_proof: (Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]),
113
+ state: (u32, u64)
114
+ ) -> (u32, u64) {
115
+ let (index, strict_total): (u32, u64) = state;
116
+ let past_end: bool = jet::le_32(jet::num_outputs(), index);
117
+ let strict_total: u64 = match past_end {
118
+ true => strict_total,
119
+ false => {
120
+ let pair: (Asset1, Amount1) = unwrap(jet::output_amount(index));
121
+ let (asset, amount): (Asset1, Amount1) = pair;
122
+ match asset {
123
+ // PSET supports standard confidential outputs only when both
124
+ // asset and value are blinded. This profile permits exactly
125
+ // the atomic purchase payment at output 2 to use that form.
126
+ Left(unused_confidential_asset: (u1, u256)) => {
127
+ assert!(jet::eq_32(index, 2));
128
+ strict_total
129
+ },
130
+ Right(asset_bits: u256) => {
131
+ let strict_asset: u256 = 0x{{STRICT_ASSET_ID_JET_HEX}};
132
+ match jet::eq_256(asset_bits, strict_asset) {
133
+ true => {
134
+ let amount_bits: u64 = unwrap_right::<(u1, u256)>(amount);
135
+ let (owner, proof): (Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]) = owner_proof;
136
+ require_whitelisted(owner, proof);
137
+ let actual_script_hash: u256 = unwrap(jet::output_script_hash(index));
138
+ let expected_script_hash: u256 = canonical_holder_script_hash(owner);
139
+ assert!(jet::eq_256(actual_script_hash, expected_script_hash));
140
+ add_amount(strict_total, amount_bits)
141
+ },
142
+ false => strict_total,
143
+ }
144
+ },
145
+ }
146
+ },
147
+ };
148
+ (next_index(index), strict_total)
149
+ }
150
+
151
+ fn require_verifier_successor(expected_script_hash: u256) {
152
+ let (verifier_asset, verifier_amount): (u256, u64) = explicit_input_asset_amount(0);
153
+ let expected_verifier_asset: u256 = 0x{{VERIFIER_ASSET_ID_JET_HEX}};
154
+ assert!(jet::eq_256(verifier_asset, expected_verifier_asset));
155
+ assert!(jet::eq_64(verifier_amount, {{VERIFIER_AMOUNT_ATOMIC}}));
156
+
157
+ let (successor_asset, successor_amount): (u256, u64) = explicit_output_asset_amount(0);
158
+ assert!(jet::eq_256(successor_asset, expected_verifier_asset));
159
+ assert!(jet::eq_64(successor_amount, {{VERIFIER_AMOUNT_ATOMIC}}));
160
+ let successor_script_hash: u256 = unwrap(jet::output_script_hash(0));
161
+ assert!(jet::eq_256(successor_script_hash, expected_script_hash))
162
+ }
163
+
164
+ fn scan_output_redemption(unused: (), state: (u32, u64)) -> (u32, u64) {
165
+ let (index, strict_total): (u32, u64) = state;
166
+ let past_end: bool = jet::le_32(jet::num_outputs(), index);
167
+ let strict_total: u64 = match past_end {
168
+ true => strict_total,
169
+ false => {
170
+ let pair: (Asset1, Amount1) = unwrap(jet::output_amount(index));
171
+ let (asset, amount): (Asset1, Amount1) = pair;
172
+ match asset {
173
+ Left(unused_confidential_asset: (u1, u256)) => {
174
+ assert!(false);
175
+ strict_total
176
+ },
177
+ Right(asset_bits: u256) => {
178
+ let strict_asset: u256 = 0x{{STRICT_ASSET_ID_JET_HEX}};
179
+ match jet::eq_256(asset_bits, strict_asset) {
180
+ true => {
181
+ let amount_bits: u64 = unwrap_right::<(u1, u256)>(amount);
182
+ let actual_script_hash: u256 = unwrap(jet::output_script_hash(index));
183
+ let redemption_script_hash: u256 = 0x{{REDEMPTION_SCRIPT_HASH}};
184
+ assert!(jet::eq_256(actual_script_hash, redemption_script_hash));
185
+ add_amount(strict_total, amount_bits)
186
+ },
187
+ false => strict_total,
188
+ }
189
+ },
190
+ }
191
+ },
192
+ };
193
+ (next_index(index), strict_total)
194
+ }
195
+
196
+ fn strict_input_total() -> u64 {
197
+ let input_slots: [(); {{MAX_INPUTS}}] = [{{INPUT_UNIT_SLOTS}}];
198
+ let input_state: (u32, u64) =
199
+ array_fold::<scan_input, {{MAX_INPUTS}}>(input_slots, (0, 0));
200
+ let (unused_input_index, strict_total): (u32, u64) = input_state;
201
+ strict_total
202
+ }
203
+
204
+ fn ordinary_transfer(
205
+ output_owner_proofs: [(Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]); {{MAX_OUTPUTS}}]
206
+ ) {
207
+ require_verifier_successor(jet::current_script_hash());
208
+ let strict_inputs: u64 = strict_input_total();
209
+ assert!(not(jet::eq_64(strict_inputs, 0)));
210
+
211
+ let output_state: (u32, u64) =
212
+ array_fold::<scan_output_ordinary, {{MAX_OUTPUTS}}>(output_owner_proofs, (0, 0));
213
+ let (unused_output_index, strict_outputs): (u32, u64) = output_state;
214
+ assert!(jet::eq_64(strict_inputs, strict_outputs))
215
+ }
216
+
217
+ fn redemption(authority_signature: Signature) {
218
+ require_verifier_successor(jet::current_script_hash());
219
+ let strict_inputs: u64 = strict_input_total();
220
+ assert!(not(jet::eq_64(strict_inputs, 0)));
221
+
222
+ let output_slots: [(); {{MAX_OUTPUTS}}] = [{{OUTPUT_UNIT_SLOTS}}];
223
+ let output_state: (u32, u64) =
224
+ array_fold::<scan_output_redemption, {{MAX_OUTPUTS}}>(output_slots, (0, 0));
225
+ let (unused_output_index, strict_outputs): (u32, u64) = output_state;
226
+ assert!(jet::eq_64(strict_inputs, strict_outputs));
227
+
228
+ let authority: Pubkey = 0x{{AUTHORITY_XONLY}};
229
+ jet::bip_0340_verify((authority, jet::sig_all_hash()), authority_signature)
230
+ }
231
+
232
+ fn governance(next: (u256, Signature)) {
233
+ let (next_verifier_script_hash, authority_signature): (u256, Signature) = next;
234
+ require_verifier_successor(next_verifier_script_hash);
235
+ assert!(jet::eq_64(strict_input_total(), 0));
236
+
237
+ let output_slots: [(); {{MAX_OUTPUTS}}] = [{{OUTPUT_UNIT_SLOTS}}];
238
+ let output_state: (u32, u64) =
239
+ array_fold::<scan_output_redemption, {{MAX_OUTPUTS}}>(output_slots, (0, 0));
240
+ let (unused_output_index, strict_outputs): (u32, u64) = output_state;
241
+ assert!(jet::eq_64(strict_outputs, 0));
242
+
243
+ let authority: Pubkey = 0x{{AUTHORITY_XONLY}};
244
+ jet::bip_0340_verify((authority, jet::sig_all_hash()), authority_signature)
245
+ }
246
+
247
+ fn main() {
248
+ assert!(jet::eq_32(jet::current_index(), 0));
249
+ assert!(jet::le_32(jet::num_inputs(), {{MAX_INPUTS}}));
250
+ assert!(jet::le_32(jet::num_outputs(), {{MAX_OUTPUTS}}));
251
+
252
+ let action:
253
+ Either<
254
+ [(Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]); {{MAX_OUTPUTS}}],
255
+ Either<Signature, (u256, Signature)>
256
+ > = witness::ACTION;
257
+ match action {
258
+ Left(output_owner_proofs : [(Pubkey, [(bool, u256); {{WHITELIST_DEPTH}}]); {{MAX_OUTPUTS}}]) =>
259
+ ordinary_transfer(output_owner_proofs),
260
+ Right(protected_action : Either<Signature, (u256, Signature)>) => match protected_action {
261
+ Left(authority_signature : Signature) => redemption(authority_signature),
262
+ Right(next : (u256, Signature)) => governance(next),
263
+ },
264
+ }
265
+ }
@@ -83,7 +83,7 @@ export declare function normalizeStrictPolicyVerifierCovenantParams(value: Stric
83
83
  export declare function strictPolicyHolderCovenantTemplatePath(): string;
84
84
  export declare function renderStrictPolicyHolderCovenantSource(value: StrictPolicyHolderCovenantParams | unknown): Promise<string>;
85
85
  export declare function compileStrictPolicyHolderCovenant(config: SimplicityClientConfig, value: StrictPolicyHolderCovenantParams | unknown): Promise<SimplicityArtifact>;
86
- export declare function strictPolicyVerifierCovenantTemplatePath(): string;
86
+ export declare function strictPolicyVerifierCovenantTemplatePath(resourceProfile?: StrictPolicyVerifierResourceProfileId): string;
87
87
  export declare function renderStrictPolicyVerifierCovenantSource(value: StrictPolicyVerifierCovenantParams | unknown): Promise<string>;
88
88
  export declare function compileStrictPolicyVerifierCovenant(config: SimplicityClientConfig, value: StrictPolicyVerifierCovenantParams | unknown): Promise<SimplicityArtifact>;
89
89
  export declare function taggedHashHexUtf8(tag: string, canonicalPayload: string): string;
@@ -242,7 +242,11 @@ async function compileStrictPolicyHolderCovenant(config, value) {
242
242
  },
243
243
  });
244
244
  }
245
- function strictPolicyVerifierCovenantTemplatePath() {
245
+ function strictPolicyVerifierCovenantTemplatePath(resourceProfile = "strict_v2") {
246
+ if (resourceProfile === "strict_v2_confidential"
247
+ || resourceProfile === "strict_v2_compact_confidential") {
248
+ return `${__dirname}/../docs/definitions/strict-verifier-confidential-v1.simf`;
249
+ }
246
250
  return `${__dirname}/../docs/definitions/strict-verifier-ordinary-v1.simf`;
247
251
  }
248
252
  function strictPolicyVerifierTemplateVars(value) {
@@ -268,14 +272,16 @@ function strictPolicyVerifierTemplateVars(value) {
268
272
  };
269
273
  }
270
274
  async function renderStrictPolicyVerifierCovenantSource(value) {
271
- const source = await readTemplateFile(strictPolicyVerifierCovenantTemplatePath());
272
- return (0, templating_1.renderTemplate)(source, strictPolicyVerifierTemplateVars(value));
275
+ const params = normalizeStrictPolicyVerifierCovenantParams(value);
276
+ const source = await readTemplateFile(strictPolicyVerifierCovenantTemplatePath(params.resourceProfile));
277
+ return (0, templating_1.renderTemplate)(source, strictPolicyVerifierTemplateVars(params));
273
278
  }
274
279
  async function compileStrictPolicyVerifierCovenant(config, value) {
275
280
  const { compileFromFile } = await loadCompiler();
281
+ const params = normalizeStrictPolicyVerifierCovenantParams(value);
276
282
  return compileFromFile(config, {
277
- simfPath: strictPolicyVerifierCovenantTemplatePath(),
278
- templateVars: strictPolicyVerifierTemplateVars(value),
283
+ simfPath: strictPolicyVerifierCovenantTemplatePath(params.resourceProfile),
284
+ templateVars: strictPolicyVerifierTemplateVars(params),
279
285
  });
280
286
  }
281
287
  function taggedHashHexUtf8(tag, canonicalPayload) {
@@ -22,6 +22,20 @@ export declare const STRICT_POLICY_VERIFIER_RESOURCE_PROFILES: {
22
22
  readonly whitelistDepth: 1;
23
23
  readonly maxWhitelistOwners: number;
24
24
  };
25
+ readonly strict_v2_confidential: {
26
+ readonly id: "strict_v2_confidential";
27
+ readonly maxInputs: 16;
28
+ readonly maxOutputs: 8;
29
+ readonly whitelistDepth: 8;
30
+ readonly maxWhitelistOwners: number;
31
+ };
32
+ readonly strict_v2_compact_confidential: {
33
+ readonly id: "strict_v2_compact_confidential";
34
+ readonly maxInputs: 3;
35
+ readonly maxOutputs: 4;
36
+ readonly whitelistDepth: 1;
37
+ readonly maxWhitelistOwners: number;
38
+ };
25
39
  };
26
40
  export type StrictPolicyVerifierResourceProfileId = keyof typeof STRICT_POLICY_VERIFIER_RESOURCE_PROFILES;
27
41
  export type StrictPolicyVerifierResourceProfile = (typeof STRICT_POLICY_VERIFIER_RESOURCE_PROFILES)[StrictPolicyVerifierResourceProfileId];
@@ -40,6 +40,24 @@ exports.STRICT_POLICY_VERIFIER_RESOURCE_PROFILES = {
40
40
  whitelistDepth: exports.STRICT_POLICY_COMPACT_WHITELIST_DEPTH,
41
41
  maxWhitelistOwners: exports.STRICT_POLICY_COMPACT_MAX_WHITELIST_OWNERS,
42
42
  },
43
+ // This profile permits one full confidential treasury payment output while
44
+ // strict RWA inputs and outputs remain explicit for deterministic checks.
45
+ strict_v2_confidential: {
46
+ id: "strict_v2_confidential",
47
+ maxInputs: 16,
48
+ maxOutputs: 8,
49
+ whitelistDepth: exports.STRICT_POLICY_WHITELIST_DEPTH,
50
+ maxWhitelistOwners: exports.STRICT_POLICY_MAX_WHITELIST_OWNERS,
51
+ },
52
+ // Keeps the confidential treasury-payment path within the same bounded
53
+ // execution envelope as the initial one-position pilot.
54
+ strict_v2_compact_confidential: {
55
+ id: "strict_v2_compact_confidential",
56
+ maxInputs: 3,
57
+ maxOutputs: 4,
58
+ whitelistDepth: exports.STRICT_POLICY_COMPACT_WHITELIST_DEPTH,
59
+ maxWhitelistOwners: exports.STRICT_POLICY_COMPACT_MAX_WHITELIST_OWNERS,
60
+ },
43
61
  };
44
62
  exports.STRICT_POLICY_WHITELIST_OWNER_DOMAIN = "HAZBASE-RWA-STRICT-WHITELIST-OWNER-V1";
45
63
  exports.STRICT_POLICY_WHITELIST_EMPTY_DOMAIN = "HAZBASE-RWA-STRICT-WHITELIST-EMPTY-V1";
@@ -52,7 +70,7 @@ function validationError(field, message) {
52
70
  }
53
71
  function resolveStrictPolicyVerifierResourceProfile(value = "strict_v2") {
54
72
  if (typeof value !== "string" || !(value in exports.STRICT_POLICY_VERIFIER_RESOURCE_PROFILES)) {
55
- validationError("resourceProfile", "must be strict_v2 or strict_v2_compact");
73
+ validationError("resourceProfile", "must be strict_v2, strict_v2_compact, strict_v2_confidential, or strict_v2_compact_confidential");
56
74
  }
57
75
  return exports.STRICT_POLICY_VERIFIER_RESOURCE_PROFILES[value];
58
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazbase/simplicity",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "description": "An SDK for Simplicity on Liquid",
5
5
  "author": "IndieSquare Inc <info@hazbase.com>",
6
6
  "keywords": [