@hardkas/sdk 0.8.2-alpha → 0.8.4-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +13 -0
  2. package/dist/index.js +177 -27
  3. package/package.json +13 -13
package/dist/index.d.ts CHANGED
@@ -66,6 +66,9 @@ declare class HardkasTx {
66
66
  amount: string | number | bigint;
67
67
  feeRate?: bigint;
68
68
  workflowId?: string;
69
+ policy?: string;
70
+ networkProfile?: string;
71
+ assumption?: string;
69
72
  }): Promise<TxPlanArtifact>;
70
73
  /**
71
74
  * Signs a transaction plan.
@@ -280,10 +283,18 @@ declare class HardkasArtifactsManager {
280
283
  private workspace;
281
284
  private cache;
282
285
  constructor(workspace: HardkasWorkspace);
286
+ /**
287
+ * Caches an in-memory artifact.
288
+ */
289
+ cacheArtifact(artifact: any): void;
283
290
  /**
284
291
  * Writes a valid artifact to disk (canonical or custom path).
285
292
  */
286
293
  write(artifact: HardkasArtifactBase, options?: WriteArtifactOptions): Promise<WriteArtifactResult>;
294
+ /**
295
+ * Retrieves an artifact from the in-memory cache.
296
+ */
297
+ getCached(id: string): any;
287
298
  /**
288
299
  * Reads an artifact by path or ID/hash from the workspace.
289
300
  */
@@ -306,6 +317,8 @@ declare class HardkasArtifactsManager {
306
317
  */
307
318
  verify(target: any, options?: {
308
319
  throwOnInvalid?: boolean;
320
+ strict?: boolean;
321
+ enforceMetadata?: boolean;
309
322
  }): Promise<any>;
310
323
  }
311
324
 
package/dist/index.js CHANGED
@@ -200,7 +200,7 @@ var HardkasTx = class {
200
200
  feeRateSompiPerMass: options.feeRate ?? 1n
201
201
  });
202
202
  const isSimulated = activeNetwork === "simulated" || this.sdk.config.config.networks?.[activeNetwork]?.kind === "simulated";
203
- return createTxPlanArtifact({
203
+ const basePlan = createTxPlanArtifact({
204
204
  networkId: activeNetwork,
205
205
  mode: isSimulated ? "simulated" : "real",
206
206
  from: {
@@ -216,13 +216,63 @@ var HardkasTx = class {
216
216
  plan: builderPlan,
217
217
  ctx: options.workflowId ? { ...systemRuntimeContext, workflowId: options.workflowId } : systemRuntimeContext
218
218
  });
219
+ if (options.policy || options.policies) {
220
+ const inputPolicies = options.policies || (options.policy ? [options.policy] : []);
221
+ const resolvedRefs = [];
222
+ for (const p of inputPolicies) {
223
+ try {
224
+ const pol = await this.sdk.artifacts.read(p);
225
+ resolvedRefs.push(pol.contentHash || pol.artifactId || p);
226
+ } catch (e) {
227
+ resolvedRefs.push(p);
228
+ }
229
+ }
230
+ if (resolvedRefs.length > 0) {
231
+ basePlan.policyRefs = resolvedRefs;
232
+ basePlan.policyRef = resolvedRefs[0];
233
+ }
234
+ }
235
+ if (options.networkProfile) {
236
+ try {
237
+ const net = await this.sdk.artifacts.read(options.networkProfile);
238
+ basePlan.networkProfileRef = net.contentHash || net.artifactId || options.networkProfile;
239
+ } catch (e) {
240
+ basePlan.networkProfileRef = options.networkProfile;
241
+ }
242
+ }
243
+ if (options.assumption) {
244
+ try {
245
+ const asm = await this.sdk.artifacts.read(options.assumption);
246
+ basePlan.assumptionRef = asm.contentHash || asm.artifactId || options.assumption;
247
+ } catch (e) {
248
+ basePlan.assumptionRef = options.assumption;
249
+ }
250
+ }
251
+ const { CURRENT_HASH_VERSION: CURRENT_HASH_VERSION2, calculateContentHash: calculateContentHash2 } = await import("@hardkas/artifacts");
252
+ const newHash = calculateContentHash2(basePlan, CURRENT_HASH_VERSION2);
253
+ basePlan.contentHash = newHash;
254
+ if (basePlan.lineage) {
255
+ basePlan.lineage.artifactId = newHash;
256
+ basePlan.lineage.parentArtifactId = newHash;
257
+ basePlan.lineage.rootArtifactId = newHash;
258
+ const finalHash = calculateContentHash2(basePlan, CURRENT_HASH_VERSION2);
259
+ basePlan.contentHash = finalHash;
260
+ basePlan.lineage.artifactId = finalHash;
261
+ basePlan.lineage.parentArtifactId = finalHash;
262
+ basePlan.lineage.rootArtifactId = finalHash;
263
+ }
264
+ this.sdk.artifacts.cacheArtifact(basePlan);
265
+ if (basePlan.policyRefs && basePlan.policyRefs.length > 0) {
266
+ await this.sdk.artifacts.verify(basePlan, { throwOnInvalid: true, strict: true, enforceMetadata: false });
267
+ }
268
+ return basePlan;
219
269
  }
220
270
  /**
221
271
  * Signs a transaction plan.
222
272
  */
223
273
  async sign(plan, account, options) {
224
274
  if (typeof plan === "object" && plan !== null && plan.contentHash) {
225
- await this.sdk.artifacts.verify(plan, { throwOnInvalid: true });
275
+ await this.sdk.artifacts.verify(plan, { throwOnInvalid: true, strict: true, enforceMetadata: false });
226
276
  }
227
277
  let resolvedAccount;
228
278
  if (typeof account === "string") {
@@ -301,7 +351,7 @@ var HardkasTx = class {
301
351
  lineage: {
302
352
  artifactId: "",
303
353
  // To be computed from contentHash
304
- lineageId: partialTx.lineage?.lineageId || `lineage-${Math.random().toString(36).slice(2, 10)}`,
354
+ lineageId: partialTx.lineage?.lineageId || partialTx.contentHash || "0".repeat(64),
305
355
  parentArtifactId: partialTx.contentHash || partialTx.signedId,
306
356
  rootArtifactId: partialTx.lineage?.rootArtifactId || partialTx.sourcePlanId
307
357
  }
@@ -320,7 +370,7 @@ var HardkasTx = class {
320
370
  const hash = calculateContentHash(draft, CURRENT_HASH_VERSION2);
321
371
  draft.signedId = `signed-${hash.slice(0, 16)}`;
322
372
  draft.contentHash = hash;
323
- if (draft.lineage) draft.lineage.artifactId = draft.signedId;
373
+ if (draft.lineage) draft.lineage.artifactId = hash;
324
374
  signedArtifact = draft;
325
375
  } else if (plan.schema === "hardkas.txPlan") {
326
376
  if (options?.append) {
@@ -385,7 +435,7 @@ var HardkasTx = class {
385
435
  lineage: {
386
436
  artifactId: "",
387
437
  // To be computed
388
- lineageId: plan.lineage?.lineageId || `lineage-${Math.random().toString(36).slice(2, 10)}`,
438
+ lineageId: plan.lineage?.lineageId || plan.contentHash || "0".repeat(64),
389
439
  parentArtifactId: plan.contentHash || plan.planId,
390
440
  rootArtifactId: plan.contentHash || plan.planId
391
441
  },
@@ -401,7 +451,7 @@ var HardkasTx = class {
401
451
  const hash = calculateContentHash(draft, CURRENT_HASH_VERSION2);
402
452
  draft.signedId = `signed-${hash.slice(0, 16)}`;
403
453
  draft.contentHash = hash;
404
- if (draft.lineage) draft.lineage.artifactId = draft.signedId;
454
+ if (draft.lineage) draft.lineage.artifactId = hash;
405
455
  signedArtifact = draft;
406
456
  } else {
407
457
  if (resolvedAccount.address !== plan.from.address) {
@@ -444,9 +494,22 @@ var HardkasTx = class {
444
494
  */
445
495
  async simulate(target, options = {}) {
446
496
  if (typeof target === "object" && target !== null && target.contentHash) {
447
- await this.sdk.artifacts.verify(target, { throwOnInvalid: true });
497
+ await this.sdk.artifacts.verify(target, { throwOnInvalid: true, strict: true, enforceMetadata: false });
448
498
  }
449
499
  const persist = options.persist ?? true;
500
+ if (typeof target === "object" && target !== null) {
501
+ const checkTxId = target.txId || (target.schema === ARTIFACT_SCHEMAS.SIGNED_TX ? `simulated-${target.sourcePlanId || "unknown"}-tx` : `simulated-${target.planId || target.id || "unknown"}-tx`);
502
+ if (checkTxId) {
503
+ try {
504
+ const existingReceipt = await this.sdk.artifacts.read(checkTxId, { expectedSchema: ARTIFACT_SCHEMAS.TX_RECEIPT });
505
+ if (existingReceipt && existingReceipt.schema === ARTIFACT_SCHEMAS.TX_RECEIPT) {
506
+ const receiptPath2 = getDefaultReceiptPath(checkTxId, this.sdk.config.cwd);
507
+ return { receipt: existingReceipt, receiptPath: receiptPath2 };
508
+ }
509
+ } catch (e) {
510
+ }
511
+ }
512
+ }
450
513
  const {
451
514
  loadOrCreateLocalnetState,
452
515
  saveLocalnetState,
@@ -477,10 +540,13 @@ var HardkasTx = class {
477
540
  signedId = targetObj.signedId || targetObj.id || "unknown";
478
541
  sourcePlanId = targetObj.sourcePlanId || "unknown";
479
542
  txId = targetObj.txId || `simulated-${sourcePlanId}-tx`;
480
- try {
481
- planArtifact = await this.sdk.artifacts.read(sourcePlanId, { expectedSchema: ARTIFACT_SCHEMAS.TX_PLAN });
482
- } catch (e) {
483
- planArtifact = targetObj;
543
+ planArtifact = this.sdk.artifacts.getCached(sourcePlanId);
544
+ if (!planArtifact) {
545
+ try {
546
+ planArtifact = await this.sdk.artifacts.read(sourcePlanId, { expectedSchema: ARTIFACT_SCHEMAS.TX_PLAN });
547
+ } catch (e) {
548
+ throw new Error("parent_plan_unresolved");
549
+ }
484
550
  }
485
551
  } else {
486
552
  planArtifact = targetObj;
@@ -546,9 +612,22 @@ var HardkasTx = class {
546
612
  submittedAt: simResult.receipt.createdAt,
547
613
  confirmedAt: simResult.receipt.createdAt,
548
614
  rpcUrl: "simulated://local",
549
- tracePath
615
+ tracePath,
616
+ lineage: {
617
+ artifactId: "",
618
+ // To be computed
619
+ lineageId: targetObj.lineage?.lineageId || targetObj.contentHash || "0".repeat(64),
620
+ parentArtifactId: targetObj.contentHash || "0".repeat(64),
621
+ rootArtifactId: targetObj.lineage?.rootArtifactId || "0".repeat(64),
622
+ sequence: (targetObj.lineage?.sequence || 1) + 1
623
+ }
550
624
  };
551
625
  receiptBase.contentHash = calculateContentHash(receiptBase, CURRENT_HASH_VERSION);
626
+ if (receiptBase.lineage) {
627
+ receiptBase.lineage.artifactId = receiptBase.contentHash;
628
+ receiptBase.contentHash = calculateContentHash(receiptBase, CURRENT_HASH_VERSION);
629
+ receiptBase.lineage.artifactId = receiptBase.contentHash;
630
+ }
552
631
  const receipt = Object.freeze(receiptBase);
553
632
  const traceSteps = events.map((ev) => ({
554
633
  phase: ev.phase || ev.message || "unknown",
@@ -606,7 +685,7 @@ var HardkasTx = class {
606
685
  */
607
686
  async send(signedArtifact, urlOrOptions) {
608
687
  if (typeof signedArtifact === "object" && signedArtifact !== null && signedArtifact.contentHash) {
609
- await this.sdk.artifacts.verify(signedArtifact, { throwOnInvalid: true });
688
+ await this.sdk.artifacts.verify(signedArtifact, { throwOnInvalid: true, strict: true, enforceMetadata: false });
610
689
  }
611
690
  const verification = verifySignedTxSemantics(signedArtifact);
612
691
  if (!verification.ok) {
@@ -687,12 +766,26 @@ var HardkasTx = class {
687
766
  feeSompi: signedArtifact.metadata?.estimatedFeeSompi || "0",
688
767
  submittedAt: (/* @__PURE__ */ new Date()).toISOString(),
689
768
  ...url ? { rpcUrl: url } : {},
690
- ...signedArtifact.workflowId ? { workflowId: signedArtifact.workflowId } : {}
769
+ ...signedArtifact.workflowId ? { workflowId: signedArtifact.workflowId } : {},
770
+ tracePath: void 0,
771
+ lineage: {
772
+ artifactId: "",
773
+ // To be computed
774
+ lineageId: signedArtifact.lineage?.lineageId || signedArtifact.contentHash || "0".repeat(64),
775
+ parentArtifactId: signedArtifact.contentHash || "0".repeat(64),
776
+ rootArtifactId: signedArtifact.lineage?.rootArtifactId || "0".repeat(64),
777
+ sequence: (signedArtifact.lineage?.sequence || 1) + 1
778
+ }
691
779
  };
692
780
  realReceiptBase.contentHash = calculateContentHash(
693
781
  realReceiptBase,
694
782
  CURRENT_HASH_VERSION
695
783
  );
784
+ if (realReceiptBase.lineage) {
785
+ realReceiptBase.lineage.artifactId = realReceiptBase.contentHash;
786
+ realReceiptBase.contentHash = calculateContentHash(realReceiptBase, CURRENT_HASH_VERSION);
787
+ realReceiptBase.lineage.artifactId = realReceiptBase.contentHash;
788
+ }
696
789
  const receipt = realReceiptBase;
697
790
  const receiptPath = getDefaultReceiptPath(receipt.txId, this.sdk.config.cwd);
698
791
  await writeArtifact(receiptPath, receipt);
@@ -997,8 +1090,20 @@ var HardkasReplay = class {
997
1090
  * against the mathematically reconstructed localnet state.
998
1091
  */
999
1092
  async verify(targetOrOptions, options) {
1093
+ const throwOnInvalid = options?.throwOnInvalid !== false;
1000
1094
  if (typeof targetOrOptions === "object" && targetOrOptions !== null && targetOrOptions.contentHash) {
1001
- await this.sdk.artifacts.verify(targetOrOptions, { throwOnInvalid: true });
1095
+ const verifyRes = await this.sdk.artifacts.verify(targetOrOptions, { throwOnInvalid, strict: true });
1096
+ if (!verifyRes.valid && !throwOnInvalid) {
1097
+ return {
1098
+ passed: false,
1099
+ artifactsScanned: 1,
1100
+ lineage: "invalid",
1101
+ determinism: "failed",
1102
+ contamination: "clean",
1103
+ report: null,
1104
+ error: `Artifact verification failed: ${verifyRes.reason}`
1105
+ };
1106
+ }
1002
1107
  }
1003
1108
  let opts = options || {};
1004
1109
  if (typeof targetOrOptions === "string") {
@@ -1247,6 +1352,17 @@ var HardkasArtifactsManager = class {
1247
1352
  }
1248
1353
  workspace;
1249
1354
  cache = /* @__PURE__ */ new Map();
1355
+ /**
1356
+ * Caches an in-memory artifact.
1357
+ */
1358
+ cacheArtifact(artifact) {
1359
+ const record = artifact;
1360
+ const hash = record.contentHash || "unknown";
1361
+ if (record.planId) this.cache.set(record.planId, artifact);
1362
+ if (record.signedId) this.cache.set(record.signedId, artifact);
1363
+ if (record.txId) this.cache.set(record.txId, artifact);
1364
+ this.cache.set(hash, artifact);
1365
+ }
1250
1366
  /**
1251
1367
  * Writes a valid artifact to disk (canonical or custom path).
1252
1368
  */
@@ -1306,17 +1422,16 @@ var HardkasArtifactsManager = class {
1306
1422
  contentHash: hash
1307
1423
  };
1308
1424
  }
1425
+ /**
1426
+ * Retrieves an artifact from the in-memory cache.
1427
+ */
1428
+ getCached(id) {
1429
+ return this.cache.get(id);
1430
+ }
1309
1431
  /**
1310
1432
  * Reads an artifact by path or ID/hash from the workspace.
1311
1433
  */
1312
1434
  async read(id, options) {
1313
- if (this.cache.has(id)) {
1314
- const cached = this.cache.get(id);
1315
- if (options?.expectedSchema && cached.schema !== options.expectedSchema) {
1316
- throw new Error(`Artifact ${id} has schema '${cached.schema}' but expected '${options.expectedSchema}'`);
1317
- }
1318
- return cached;
1319
- }
1320
1435
  const { readArtifact } = await import("@hardkas/artifacts");
1321
1436
  let filePath = id;
1322
1437
  let resolvedPath = path3.resolve(this.workspace.root, filePath);
@@ -1333,7 +1448,27 @@ var HardkasArtifactsManager = class {
1333
1448
  if (!fs3.existsSync(filePath)) {
1334
1449
  if (fs3.existsSync(this.workspace.artifactsDir)) {
1335
1450
  const files = fs3.readdirSync(this.workspace.artifactsDir);
1336
- const found = files.find((f) => f === `${id}.json` || f.startsWith(`${id}-`) || f.startsWith(`${id}.`));
1451
+ let found = files.find(
1452
+ (f) => f === `${id}.json` || f.startsWith(`${id}-`) || f.startsWith(`${id}.`) || f.endsWith(`-${id}.json`) || f.endsWith(`-${id}.plan.json`) || f.endsWith(`-${id}.signed.json`) || f.endsWith(`-${id}.receipt.json`)
1453
+ );
1454
+ if (!found) {
1455
+ const shortId = id.startsWith("plan-") || id.startsWith("signed-") ? id : id.slice(0, 16);
1456
+ for (const file of files) {
1457
+ if (!file.endsWith(".json")) continue;
1458
+ if (file.includes(id) || file.includes(shortId) || file.includes(id.slice(0, 8))) {
1459
+ const fp = path3.join(this.workspace.artifactsDir, file);
1460
+ try {
1461
+ const content = fs3.readFileSync(fp, "utf-8");
1462
+ const obj = JSON.parse(content);
1463
+ if (obj.contentHash === id || obj.artifactId === id || obj.planId === id || obj.signedId === id || obj.txId === id) {
1464
+ found = file;
1465
+ break;
1466
+ }
1467
+ } catch {
1468
+ }
1469
+ }
1470
+ }
1471
+ }
1337
1472
  if (found) {
1338
1473
  filePath = path3.join(this.workspace.artifactsDir, found);
1339
1474
  } else {
@@ -1383,6 +1518,8 @@ var HardkasArtifactsManager = class {
1383
1518
  */
1384
1519
  async verify(target, options = {}) {
1385
1520
  const throwOnInvalid = options.throwOnInvalid ?? true;
1521
+ const strict = options.strict ?? false;
1522
+ const enforceMetadata = options.enforceMetadata ?? true;
1386
1523
  let artifact;
1387
1524
  let id;
1388
1525
  if (typeof target === "string") {
@@ -1401,10 +1538,23 @@ var HardkasArtifactsManager = class {
1401
1538
  artifact = target;
1402
1539
  id = artifact.artifactId || artifact.contentHash || "";
1403
1540
  }
1404
- const { verifyArtifactIntegrity: verifyArtifactIntegrity2 } = await import("@hardkas/artifacts");
1541
+ const { verifyArtifactIntegrity: verifyArtifactIntegrity2, verifyArtifactSemantics } = await import("@hardkas/artifacts");
1405
1542
  const result = await verifyArtifactIntegrity2(artifact);
1543
+ if (result.ok && strict) {
1544
+ const semResult = verifyArtifactSemantics(artifact, {
1545
+ strict: true,
1546
+ artifactsDir: this.workspace.artifactsDir,
1547
+ enforceMetadata,
1548
+ resolveArtifact: (id2) => this.cache.get(id2)
1549
+ });
1550
+ if (!semResult.ok) {
1551
+ result.ok = false;
1552
+ result.errors.push(...semResult.errors);
1553
+ result.issues.push(...semResult.issues);
1554
+ }
1555
+ }
1406
1556
  if (!result.ok) {
1407
- const mappedReason = result.issues[0]?.code === "HASH_MISMATCH" ? "content_hash_mismatch" : result.issues[0]?.code === "MISSING_CONTENT_HASH" ? "missing_content_hash" : result.issues[0]?.code === "MISSING_SIGNATURE" ? "missing_signature" : "schema_invalid";
1557
+ const mappedReason = result.issues[0]?.code === "HASH_MISMATCH" ? "content_hash_mismatch" : result.issues[0]?.code === "MISSING_CONTENT_HASH" ? "missing_content_hash" : result.issues[0]?.code === "MISSING_SIGNATURE" ? "missing_signature" : result.issues[0]?.code === "REFERENCE_MISSING" ? "reference_missing" : result.issues[0]?.code === "REFERENCE_HASH_MISMATCH" ? "reference_hash_mismatch" : result.issues[0]?.code === "POLICY_VIOLATION" ? "policy_violation" : result.issues[0]?.code === "PARENT_MISSING" ? "parent_missing" : "schema_invalid";
1408
1558
  if (throwOnInvalid) {
1409
1559
  throw new Error(`Artifact ${id} corrupted or invalid: ` + JSON.stringify(result.issues, null, 2));
1410
1560
  }
@@ -1757,8 +1907,8 @@ var Hardkas = class _Hardkas {
1757
1907
  resolveRpcUrl() {
1758
1908
  const networkId = this.config.config.defaultNetwork || "simnet";
1759
1909
  const target = this.config.config.networks?.[networkId];
1760
- if (target && (target.kind === "kaspa-rpc" || target.kind === "igra" || target.kind === "kaspa-node")) {
1761
- return target.rpcUrl || "ws://127.0.0.1:18210";
1910
+ if (target && "rpcUrl" in target && typeof target.rpcUrl === "string") {
1911
+ return target.rpcUrl;
1762
1912
  }
1763
1913
  return "ws://127.0.0.1:18210";
1764
1914
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/sdk",
3
- "version": "0.8.2-alpha",
3
+ "version": "0.8.4-alpha",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -23,18 +23,18 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@hardkas/artifacts": "0.8.2-alpha",
27
- "@hardkas/config": "0.8.2-alpha",
28
- "@hardkas/kaspa-rpc": "0.8.2-alpha",
29
- "@hardkas/core": "0.8.2-alpha",
30
- "@hardkas/l2": "0.8.2-alpha",
31
- "@hardkas/accounts": "0.8.2-alpha",
32
- "@hardkas/localnet": "0.8.2-alpha",
33
- "@hardkas/query": "0.8.2-alpha",
34
- "@hardkas/simulator": "0.8.2-alpha",
35
- "@hardkas/tx-builder": "0.8.2-alpha",
36
- "@hardkas/wallet-adapter": "0.8.2-alpha",
37
- "@hardkas/query-store": "0.8.2-alpha"
26
+ "@hardkas/accounts": "0.8.4-alpha",
27
+ "@hardkas/config": "0.8.4-alpha",
28
+ "@hardkas/artifacts": "0.8.4-alpha",
29
+ "@hardkas/core": "0.8.4-alpha",
30
+ "@hardkas/kaspa-rpc": "0.8.4-alpha",
31
+ "@hardkas/l2": "0.8.4-alpha",
32
+ "@hardkas/localnet": "0.8.4-alpha",
33
+ "@hardkas/query": "0.8.4-alpha",
34
+ "@hardkas/wallet-adapter": "0.8.4-alpha",
35
+ "@hardkas/query-store": "0.8.4-alpha",
36
+ "@hardkas/simulator": "0.8.4-alpha",
37
+ "@hardkas/tx-builder": "0.8.4-alpha"
38
38
  },
39
39
  "devDependencies": {
40
40
  "tsup": "^8.3.5",