@kaspacom/kcc20-tx-builder 0.2.5 → 0.4.0

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 (127) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +54 -8
  3. package/RELEASING.md +93 -0
  4. package/SECURITY.md +16 -0
  5. package/artifacts/KCC20SnapshotBootstrap.placeholder.json +101677 -0
  6. package/artifacts/KCC20SnapshotController.placeholder.json +46547 -0
  7. package/artifacts/kcc20-snapshot-bootstrap/KCC20SnapshotBootstrap.placeholder.json +101677 -0
  8. package/artifacts/kcc20-snapshot-controller/KCC20SnapshotController.placeholder.json +46547 -0
  9. package/artifacts/snapshot-standards.lock.json +56 -0
  10. package/dist/artifacts.d.ts +31 -0
  11. package/dist/artifacts.d.ts.map +1 -1
  12. package/dist/artifacts.js +31 -0
  13. package/dist/artifacts.js.map +1 -1
  14. package/dist/cjs/artifacts.d.ts +31 -0
  15. package/dist/cjs/artifacts.js +32 -1
  16. package/dist/cjs/artifacts.js.map +1 -1
  17. package/dist/cjs/engine.js +58 -44
  18. package/dist/cjs/engine.js.map +1 -1
  19. package/dist/cjs/index.d.ts +7 -0
  20. package/dist/cjs/index.js +8 -1
  21. package/dist/cjs/index.js.map +1 -1
  22. package/dist/cjs/operations.d.ts +1 -1
  23. package/dist/cjs/operations.js +4 -0
  24. package/dist/cjs/operations.js.map +1 -1
  25. package/dist/cjs/sale-common.d.ts +1 -1
  26. package/dist/cjs/sale-common.js +39 -4
  27. package/dist/cjs/sale-common.js.map +1 -1
  28. package/dist/cjs/snapshot-deployment.d.ts +82 -0
  29. package/dist/cjs/snapshot-deployment.js +82 -0
  30. package/dist/cjs/snapshot-deployment.js.map +1 -0
  31. package/dist/cjs/snapshot-operation.d.ts +27 -0
  32. package/dist/cjs/snapshot-operation.js +2433 -0
  33. package/dist/cjs/snapshot-operation.js.map +1 -0
  34. package/dist/cjs/snapshot-signerless-transactions.d.ts +132 -0
  35. package/dist/cjs/snapshot-signerless-transactions.js +746 -0
  36. package/dist/cjs/snapshot-signerless-transactions.js.map +1 -0
  37. package/dist/cjs/snapshot-signerless.d.ts +226 -0
  38. package/dist/cjs/snapshot-signerless.js +1022 -0
  39. package/dist/cjs/snapshot-signerless.js.map +1 -0
  40. package/dist/cjs/snapshot-verification.d.ts +57 -0
  41. package/dist/cjs/snapshot-verification.js +96 -0
  42. package/dist/cjs/snapshot-verification.js.map +1 -0
  43. package/dist/cjs/token-operation.js +3 -3
  44. package/dist/cjs/token-operation.js.map +1 -1
  45. package/dist/engine.d.ts.map +1 -1
  46. package/dist/engine.js +58 -44
  47. package/dist/engine.js.map +1 -1
  48. package/dist/index.d.ts +7 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +7 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/operations.d.ts +1 -1
  53. package/dist/operations.d.ts.map +1 -1
  54. package/dist/operations.js +4 -0
  55. package/dist/operations.js.map +1 -1
  56. package/dist/sale-common.d.ts +1 -1
  57. package/dist/sale-common.d.ts.map +1 -1
  58. package/dist/sale-common.js +39 -4
  59. package/dist/sale-common.js.map +1 -1
  60. package/dist/snapshot-deployment.d.ts +83 -0
  61. package/dist/snapshot-deployment.d.ts.map +1 -0
  62. package/dist/snapshot-deployment.js +78 -0
  63. package/dist/snapshot-deployment.js.map +1 -0
  64. package/dist/snapshot-operation.d.ts +28 -0
  65. package/dist/snapshot-operation.d.ts.map +1 -0
  66. package/dist/snapshot-operation.js +2428 -0
  67. package/dist/snapshot-operation.js.map +1 -0
  68. package/dist/snapshot-signerless-transactions.d.ts +133 -0
  69. package/dist/snapshot-signerless-transactions.d.ts.map +1 -0
  70. package/dist/snapshot-signerless-transactions.js +735 -0
  71. package/dist/snapshot-signerless-transactions.js.map +1 -0
  72. package/dist/snapshot-signerless.d.ts +227 -0
  73. package/dist/snapshot-signerless.d.ts.map +1 -0
  74. package/dist/snapshot-signerless.js +993 -0
  75. package/dist/snapshot-signerless.js.map +1 -0
  76. package/dist/snapshot-verification.d.ts +58 -0
  77. package/dist/snapshot-verification.d.ts.map +1 -0
  78. package/dist/snapshot-verification.js +92 -0
  79. package/dist/snapshot-verification.js.map +1 -0
  80. package/dist/token-operation.js +3 -3
  81. package/dist/token-operation.js.map +1 -1
  82. package/docs/API.md +67 -0
  83. package/docs/COMPATIBILITY.json +84 -0
  84. package/docs/INTEGRATION.md +215 -0
  85. package/docs/LIVE_TRANSFER.md +106 -0
  86. package/docs/RECIPES.md +175 -0
  87. package/docs/TRANSACTION_GUIDE.md +163 -0
  88. package/docs/snapshot-start.md +27 -0
  89. package/examples/README.md +81 -0
  90. package/examples/backend/cli.ts +55 -0
  91. package/examples/backend/recipe.ts +39 -0
  92. package/examples/backend/runtime.ts +13 -0
  93. package/examples/frontend/index.html +48 -0
  94. package/examples/frontend/main.ts +213 -0
  95. package/examples/frontend/runtime.ts +14 -0
  96. package/examples/package-lock.json +1287 -0
  97. package/examples/package.json +23 -0
  98. package/examples/shared/build.ts +179 -0
  99. package/examples/shared/fixture.ts +24 -0
  100. package/examples/shared/live-transfer.ts +155 -0
  101. package/examples/shared/recipe-fixtures.ts +543 -0
  102. package/examples/shared/recipes.ts +55 -0
  103. package/examples/shared/signing.ts +110 -0
  104. package/examples/shared/timeout.ts +8 -0
  105. package/examples/test/browser.ts +116 -0
  106. package/examples/test/build.test.ts +197 -0
  107. package/examples/test/recipes.test.ts +59 -0
  108. package/examples/test/sources.test.ts +73 -0
  109. package/examples/tsconfig.json +26 -0
  110. package/examples/vendor/NOTICE.md +15 -0
  111. package/examples/vendor/kaspa/LICENSE +15 -0
  112. package/examples/vendor/kaspa/kaspa.d.ts +8908 -0
  113. package/examples/vendor/kaspa/kaspa.js +15202 -0
  114. package/examples/vendor/kaspa/kaspa_bg.wasm +0 -0
  115. package/examples/vendor/kaspa/package.json +24 -0
  116. package/package.json +21 -4
  117. package/src/artifacts.ts +35 -0
  118. package/src/engine.ts +91 -97
  119. package/src/index.ts +8 -0
  120. package/src/operations.ts +4 -0
  121. package/src/sale-common.ts +44 -3
  122. package/src/snapshot-deployment.ts +108 -0
  123. package/src/snapshot-operation.ts +3592 -0
  124. package/src/snapshot-signerless-transactions.ts +1272 -0
  125. package/src/snapshot-signerless.ts +1525 -0
  126. package/src/snapshot-verification.ts +86 -0
  127. package/src/token-operation.ts +3 -3
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "kaspa-wasm",
3
+ "type": "module",
4
+ "collaborators": [
5
+ "Kaspa developers"
6
+ ],
7
+ "description": "KASPA WASM bindings",
8
+ "version": "1.1.1-toc.1",
9
+ "license": "ISC",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/kaspanet/rusty-kaspa"
13
+ },
14
+ "files": [
15
+ "kaspa_bg.wasm",
16
+ "kaspa.js",
17
+ "kaspa.d.ts"
18
+ ],
19
+ "main": "kaspa.js",
20
+ "types": "kaspa.d.ts",
21
+ "sideEffects": [
22
+ "./snippets/*"
23
+ ]
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaspacom/kcc20-tx-builder",
3
- "version": "0.2.5",
3
+ "version": "0.4.0",
4
4
  "description": "Runtime-independent KCC20 covenant transaction and PSKT construction primitives.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "KaspaCom",
@@ -60,8 +60,22 @@
60
60
  "index.d.ts",
61
61
  "src",
62
62
  "artifacts",
63
+ "docs",
63
64
  "README.md",
64
- "LICENSE"
65
+ "LICENSE",
66
+ "RELEASING.md",
67
+ "SECURITY.md",
68
+ "CHANGELOG.md",
69
+ "examples/shared",
70
+ "examples/backend",
71
+ "examples/frontend/*.ts",
72
+ "examples/frontend/index.html",
73
+ "examples/vendor",
74
+ "examples/package.json",
75
+ "examples/package-lock.json",
76
+ "examples/tsconfig.json",
77
+ "examples/README.md",
78
+ "examples/test"
65
79
  ],
66
80
  "engines": {
67
81
  "node": ">=20"
@@ -72,8 +86,11 @@
72
86
  "scripts": {
73
87
  "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node scripts/write-cjs-package.mjs",
74
88
  "typecheck": "tsc -p tsconfig.json --noEmit",
75
- "test": "npm run typecheck && npm run build && node test/smoke.mjs && node test/cjs-smoke.cjs",
76
- "prepack": "npm test"
89
+ "test": "npm run typecheck && npm run build && node test/smoke.mjs && node test/cjs-smoke.cjs && node test/snapshot-plan.mjs",
90
+ "prepack": "npm test",
91
+ "test:examples": "npm run verify:compatibility && node scripts/test-examples.mjs --browser",
92
+ "verify:compatibility": "node scripts/verify-compatibility.mjs",
93
+ "test:snapshot:transactions": "npm run build && node test/snapshot-transactions.mjs && node test/snapshot-claims.mjs"
77
94
  },
78
95
  "devDependencies": {
79
96
  "typescript": "^5.9.3"
package/src/artifacts.ts CHANGED
@@ -7,6 +7,8 @@ export const KCC20_CURRENT_NATIVE_ARTIFACT_KEY =
7
7
  "KCC20.placeholder.json" as const;
8
8
 
9
9
  export const KCC20_ARTIFACT_SCRIPT_SHA256 = Object.freeze({
10
+ "KCC20SnapshotBootstrap.placeholder.json": "85edea7401eb415c447e6a0d0a45e1667802a269dde9fbe33d5881c4a4de51e2",
11
+ "KCC20SnapshotController.placeholder.json": "8f90525afcc1c23240f1d18eed30b3b377a72a323e8b48925dfac53bd5aab0ae",
10
12
  [KCC20_CURRENT_NATIVE_ARTIFACT_KEY]:
11
13
  "4633b082f26adbf14600b767d6e2146e36ce67d63d8202c0281857922c05ac41",
12
14
  "KCC20FeeTicket.placeholder.json":
@@ -19,6 +21,39 @@ export const KCC20_ARTIFACT_SCRIPT_SHA256 = Object.freeze({
19
21
  "1b84d74aa01b6a50edbff5bf1f9e988a5a6a00ec645458dfd3912ac532b77c33",
20
22
  });
21
23
 
24
+ /**
25
+ * Canonical raw artifact pins and structural metadata for signerless snapshot
26
+ * migrations. Consumers must use these values instead of maintaining their
27
+ * own copies; the raw SHA-256 pins cover the exact packaged JSON bytes.
28
+ */
29
+ export const KCC20_SNAPSHOT_ARTIFACT_PINS = Object.freeze({
30
+ bootstrap: Object.freeze({
31
+ key: "KCC20SnapshotBootstrap.placeholder.json",
32
+ contractName: "KCC20SnapshotBootstrap",
33
+ artifactSha256:
34
+ "bc566a964b5feb1c2055d5130af25951142d695472287b888c5537e9a56ee3d2",
35
+ scriptLength: 33718,
36
+ stateStart: 1,
37
+ stateLength: 405,
38
+ }),
39
+ controller: Object.freeze({
40
+ key: "KCC20SnapshotController.placeholder.json",
41
+ contractName: "KCC20SnapshotController",
42
+ artifactSha256:
43
+ "f0d7b082e0680a90736948cd6658ccbc8600e097d9b4d5568013acdaafb39bf5",
44
+ scriptLength: 15297,
45
+ stateStart: 1,
46
+ stateLength: 432,
47
+ }),
48
+ });
49
+
50
+ /** Canonical KCC20 template committed by both snapshot contracts. */
51
+ export const KCC20_SNAPSHOT_TOKEN_TEMPLATE_PIN = Object.freeze({
52
+ hash: "911f0638ccb7368bf36d117f1725073ae7ee487ce8b58ca3e8375051c2d40f6c",
53
+ prefixLength: 1,
54
+ suffixLength: 25439,
55
+ });
56
+
22
57
  export type Kcc20ArtifactKey = keyof typeof KCC20_ARTIFACT_SCRIPT_SHA256;
23
58
 
24
59
  export function isKcc20ArtifactKey(value: string): value is Kcc20ArtifactKey {
package/src/engine.ts CHANGED
@@ -62,6 +62,7 @@ import {
62
62
  ownerAuthorizationWitness,
63
63
  } from "./abi.js";
64
64
  import { KCC20_BUILDER_KEYS } from "./operations.js";
65
+ import { createKcc20SnapshotPsktBuilder } from "./snapshot-operation.js";
65
66
  import { splitKcc20MintSupply } from "./protocol.js";
66
67
  import { buildKcc20DeployIntentOutputs } from "./deploy-receipt.js";
67
68
  import {
@@ -143,12 +144,9 @@ export function createKcc20PsktBuilderEngine(
143
144
  );
144
145
  }
145
146
  }
146
- const kaspaWasm = new Proxy(options.wasm, {
147
- get(target, property, receiver) {
148
- if (property === "RpcClient") return InjectedRpcClient;
149
- return Reflect.get(target, property, receiver);
150
- },
151
- });
147
+ // Bundlers can freeze module namespace exports. A Proxy cannot replace a
148
+ // non-configurable RpcClient property; copy the host exports before adapting it.
149
+ const kaspaWasm = { ...options.wasm, RpcClient: InjectedRpcClient };
152
150
 
153
151
  const INPUT_SCHEMA = "kcc20-in-process-pskt-builder-input/v1";
154
152
  const OUTPUT_SCHEMA = "kcc20-in-process-pskt-builder-output/v1";
@@ -156,7 +154,7 @@ export function createKcc20PsktBuilderEngine(
156
154
  const DEFAULT_PRIORITY_FEE = 10_000n;
157
155
  const DEFAULT_MATCHER_PRIORITY_FEE = 30_000_000n;
158
156
  const DEFAULT_WRAPPER_DEPLOY_PRIORITY_FEE = 10_000_000n;
159
- const DEFAULT_WRPC_URL = "ws://65.108.107.30:17210";
157
+ const DEFAULT_WRPC_URL = "wss://tn10-node.kaspa.com/testnet-10";
160
158
  const MIN_FILL_GROSS_SOMPI = 50_000_000n;
161
159
  const MIN_RELAY_FEE_PER_MASS_SOMPI = 100n;
162
160
  const TOCCATA_COMPUTE_BUDGET_MASS_PER_UNIT = 100n;
@@ -221,7 +219,11 @@ export function createKcc20PsktBuilderEngine(
221
219
  .then((artifact) => {
222
220
  if (!artifact)
223
221
  throw new Error(`Missing supplied KCC20 artifact: ${path}`);
224
- const normalized = normalizeSilverScriptArtifact(artifact);
222
+ const artifactValue =
223
+ artifact && typeof artifact === "object" && !Array.isArray(artifact)
224
+ ? (artifact.artifact ?? artifact)
225
+ : artifact;
226
+ const normalized = normalizeSilverScriptArtifact(artifactValue);
225
227
  assertKcc20ArtifactScriptHash(path, normalized);
226
228
  return normalized;
227
229
  })
@@ -487,6 +489,15 @@ export function createKcc20PsktBuilderEngine(
487
489
  return new kw.ScriptBuilder({ flags: { covenantsEnabled: true } });
488
490
  }
489
491
 
492
+ const buildSnapshotMigrationPskt = createKcc20SnapshotPsktBuilder({
493
+ wasm: options.wasm,
494
+ artifacts: options.artifacts,
495
+ artifactProvider: options.artifactProvider,
496
+ config: options.config,
497
+ sourceProvider: options.sourceProvider,
498
+ rpcClientFactory: options.rpcClientFactory,
499
+ transactionWithCalculatedFeeChange,
500
+ });
490
501
  const psktBuilderByKey = Object.freeze({
491
502
  "kcc20.deploy-token": buildKcc20DeployPskt,
492
503
  "kcc20.mint": buildKcc20MintPskt,
@@ -512,6 +523,10 @@ export function createKcc20PsktBuilderEngine(
512
523
  "kcc20orderbook.cancel-ask": buildWrappedCancelPskt,
513
524
  "kcc20orderbook.cancel-bid": buildWrappedCancelPskt,
514
525
  "kcc20orderbook.matcher-settle-crossed": buildWrappedCrossMatchPskt,
526
+ "kcc20-snapshot.deploy-bootstrap": buildSnapshotMigrationPskt,
527
+ "kcc20-snapshot.deploy-controller": buildSnapshotMigrationPskt,
528
+ "kcc20-snapshot.start": buildSnapshotMigrationPskt,
529
+ "kcc20-snapshot.claim": buildSnapshotMigrationPskt,
515
530
  });
516
531
  const missingBuilderKeys = KCC20_BUILDER_KEYS.filter(
517
532
  (builderKey) => !psktBuilderByKey[builderKey],
@@ -570,8 +585,7 @@ export function createKcc20PsktBuilderEngine(
570
585
  params.utilityTokenAmount,
571
586
  "utilityTokenAmount",
572
587
  );
573
- const network =
574
- request.network || env.KASPA_NETWORK || "testnet-10";
588
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
575
589
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
576
590
  const rootAddress = requireKaspaAddress(rootUtxo.address);
577
591
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
@@ -792,8 +806,7 @@ export function createKcc20PsktBuilderEngine(
792
806
  "FeeTicket batch utility token amount must fit in an unsigned 64-bit integer",
793
807
  );
794
808
  }
795
- const network =
796
- request.network || env.KASPA_NETWORK || "testnet-10";
809
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
797
810
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
798
811
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
799
812
  const tokenOutputSompi =
@@ -1148,8 +1161,7 @@ export function createKcc20PsktBuilderEngine(
1148
1161
  throw new Error("FeeTicket burn source outpoints must be unique");
1149
1162
  }
1150
1163
 
1151
- const network =
1152
- request.network || env.KASPA_NETWORK || "testnet-10";
1164
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
1153
1165
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
1154
1166
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
1155
1167
  const priorityFee = parseU64(
@@ -1376,8 +1388,7 @@ export function createKcc20PsktBuilderEngine(
1376
1388
  throw new Error("FeeTicket transfer source outpoints must be unique");
1377
1389
  }
1378
1390
 
1379
- const network =
1380
- request.network || env.KASPA_NETWORK || "testnet-10";
1391
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
1381
1392
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
1382
1393
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
1383
1394
  const priorityFee = parseU64(
@@ -2602,8 +2613,7 @@ export function createKcc20PsktBuilderEngine(
2602
2613
  "priceScale",
2603
2614
  );
2604
2615
  assertWrapperArtifactPriceScale(priceScale);
2605
- const network =
2606
- request.network || env.KASPA_NETWORK || "testnet-10";
2616
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
2607
2617
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
2608
2618
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
2609
2619
  const amountTkas = parseAmountTkas(
@@ -2915,8 +2925,7 @@ export function createKcc20PsktBuilderEngine(
2915
2925
  params.feeTicketId || ZERO_HASH,
2916
2926
  "fee ticket id",
2917
2927
  );
2918
- const network =
2919
- request.network || env.KASPA_NETWORK || "testnet-10";
2928
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
2920
2929
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
2921
2930
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
2922
2931
  const tokenOutputSompi = parseU64(
@@ -2925,8 +2934,7 @@ export function createKcc20PsktBuilderEngine(
2925
2934
  "tokenOutputSompi",
2926
2935
  );
2927
2936
  const priorityFee = parseU64(
2928
- env.KCC20_WRAP_PRIORITY_FEE_SOMPI ||
2929
- DEFAULT_PRIORITY_FEE.toString(),
2937
+ env.KCC20_WRAP_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
2930
2938
  "priorityFee",
2931
2939
  );
2932
2940
  const computeBudget = parsePositiveNumber(
@@ -3333,8 +3341,7 @@ export function createKcc20PsktBuilderEngine(
3333
3341
  "KCC20 recipient owner",
3334
3342
  );
3335
3343
  const tokenAmount = parsePositiveU64(params.tokenAmount, "tokenAmount");
3336
- const network =
3337
- request.network || env.KASPA_NETWORK || "testnet-10";
3344
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
3338
3345
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
3339
3346
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
3340
3347
  const tokenOutputSompi = parseU64(
@@ -3343,8 +3350,7 @@ export function createKcc20PsktBuilderEngine(
3343
3350
  "tokenOutputSompi",
3344
3351
  );
3345
3352
  const priorityFee = parseU64(
3346
- env.KCC20_UNWRAP_PRIORITY_FEE_SOMPI ||
3347
- DEFAULT_PRIORITY_FEE.toString(),
3353
+ env.KCC20_UNWRAP_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
3348
3354
  "priorityFee",
3349
3355
  );
3350
3356
  const computeBudget = parsePositiveNumber(
@@ -3824,8 +3830,7 @@ export function createKcc20PsktBuilderEngine(
3824
3830
  const expectedFeeTicketId = params.feeTicketId
3825
3831
  ? requireHex32(params.feeTicketId, "fee ticket id")
3826
3832
  : null;
3827
- const network =
3828
- request.network || env.KASPA_NETWORK || "testnet-10";
3833
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
3829
3834
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
3830
3835
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
3831
3836
  const tokenOutputSompi = parseU64(
@@ -3834,8 +3839,7 @@ export function createKcc20PsktBuilderEngine(
3834
3839
  "tokenOutputSompi",
3835
3840
  );
3836
3841
  const priorityFee = parseU64(
3837
- env.KCC20_ORDER_PRIORITY_FEE_SOMPI ||
3838
- DEFAULT_PRIORITY_FEE.toString(),
3842
+ env.KCC20_ORDER_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
3839
3843
  "priorityFee",
3840
3844
  );
3841
3845
  const computeBudget = parsePositiveNumber(
@@ -4182,8 +4186,7 @@ export function createKcc20PsktBuilderEngine(
4182
4186
  params.unitPriceSompi,
4183
4187
  "unitPriceSompi",
4184
4188
  );
4185
- const network =
4186
- request.network || env.KASPA_NETWORK || "testnet-10";
4189
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
4187
4190
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
4188
4191
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
4189
4192
  const tokenOutputSompi = parseU64(
@@ -4192,8 +4195,7 @@ export function createKcc20PsktBuilderEngine(
4192
4195
  "tokenOutputSompi",
4193
4196
  );
4194
4197
  const priorityFee = parseU64(
4195
- env.KCC20_FILL_PRIORITY_FEE_SOMPI ||
4196
- DEFAULT_PRIORITY_FEE.toString(),
4198
+ env.KCC20_FILL_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
4197
4199
  "priorityFee",
4198
4200
  );
4199
4201
  const computeBudget = parsePositiveNumber(
@@ -4605,8 +4607,7 @@ export function createKcc20PsktBuilderEngine(
4605
4607
  params.unitPriceSompi,
4606
4608
  "unitPriceSompi",
4607
4609
  );
4608
- const network =
4609
- request.network || env.KASPA_NETWORK || "testnet-10";
4610
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
4610
4611
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
4611
4612
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
4612
4613
  const tokenOutputSompi = parseU64(
@@ -4615,8 +4616,7 @@ export function createKcc20PsktBuilderEngine(
4615
4616
  "tokenOutputSompi",
4616
4617
  );
4617
4618
  const priorityFee = parseU64(
4618
- env.KCC20_FILL_PRIORITY_FEE_SOMPI ||
4619
- DEFAULT_PRIORITY_FEE.toString(),
4619
+ env.KCC20_FILL_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
4620
4620
  "priorityFee",
4621
4621
  );
4622
4622
  const computeBudget = parsePositiveNumber(
@@ -5197,8 +5197,7 @@ export function createKcc20PsktBuilderEngine(
5197
5197
  "wrapped market id",
5198
5198
  );
5199
5199
  const buyerOwner = requireHex32(request.owner?.kcc20Owner, "buyer owner");
5200
- const network =
5201
- request.network || env.KASPA_NETWORK || "testnet-10";
5200
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
5202
5201
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
5203
5202
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
5204
5203
  const tokenOutputSompi = parseU64(
@@ -5207,8 +5206,7 @@ export function createKcc20PsktBuilderEngine(
5207
5206
  "tokenOutputSompi",
5208
5207
  );
5209
5208
  const priorityFee = parseU64(
5210
- env.KCC20_FILL_PRIORITY_FEE_SOMPI ||
5211
- DEFAULT_PRIORITY_FEE.toString(),
5209
+ env.KCC20_FILL_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
5212
5210
  "priorityFee",
5213
5211
  );
5214
5212
  const wrappedArtifacts = await readWrappedArtifacts();
@@ -5746,8 +5744,7 @@ export function createKcc20PsktBuilderEngine(
5746
5744
  "wrapped market id",
5747
5745
  );
5748
5746
  const sellerOwner = requireHex32(request.owner?.kcc20Owner, "seller owner");
5749
- const network =
5750
- request.network || env.KASPA_NETWORK || "testnet-10";
5747
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
5751
5748
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
5752
5749
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
5753
5750
  const tokenOutputSompi = parseU64(
@@ -5756,8 +5753,7 @@ export function createKcc20PsktBuilderEngine(
5756
5753
  "tokenOutputSompi",
5757
5754
  );
5758
5755
  const priorityFee = parseU64(
5759
- env.KCC20_FILL_PRIORITY_FEE_SOMPI ||
5760
- DEFAULT_PRIORITY_FEE.toString(),
5756
+ env.KCC20_FILL_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
5761
5757
  "priorityFee",
5762
5758
  );
5763
5759
  const wrappedArtifacts = await readWrappedArtifacts();
@@ -6503,8 +6499,7 @@ export function createKcc20PsktBuilderEngine(
6503
6499
  "wrapped market id",
6504
6500
  );
6505
6501
  const sellerOwner = requireHex32(request.owner?.kcc20Owner, "seller owner");
6506
- const network =
6507
- request.network || env.KASPA_NETWORK || "testnet-10";
6502
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
6508
6503
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
6509
6504
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
6510
6505
  const configuredTokenOutputSompi = parseU64(
@@ -6513,8 +6508,7 @@ export function createKcc20PsktBuilderEngine(
6513
6508
  "tokenOutputSompi",
6514
6509
  );
6515
6510
  const priorityFee = parseU64(
6516
- env.KCC20_FILL_PRIORITY_FEE_SOMPI ||
6517
- DEFAULT_PRIORITY_FEE.toString(),
6511
+ env.KCC20_FILL_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
6518
6512
  "priorityFee",
6519
6513
  );
6520
6514
  const wrappedArtifacts = await readWrappedArtifacts();
@@ -7282,8 +7276,7 @@ export function createKcc20PsktBuilderEngine(
7282
7276
  "requested wrapped market id",
7283
7277
  );
7284
7278
  const owner = requireHex32(request.owner?.kcc20Owner, "token owner");
7285
- const network =
7286
- request.network || env.KASPA_NETWORK || "testnet-10";
7279
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
7287
7280
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
7288
7281
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
7289
7282
  const priorityFee = parseU64(
@@ -7582,8 +7575,7 @@ export function createKcc20PsktBuilderEngine(
7582
7575
  ? requireHex32(params.feeTicketId, "fee ticket id")
7583
7576
  : null;
7584
7577
  const maxFeeSompi = parseU64(params.maxFeeSompi || "0", "maxFeeSompi");
7585
- const network =
7586
- request.network || env.KASPA_NETWORK || "testnet-10";
7578
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
7587
7579
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
7588
7580
  const expectedOwner = request.owner?.kcc20Owner
7589
7581
  ? requireHex32(request.owner.kcc20Owner, "matcher owner")
@@ -7607,14 +7599,10 @@ export function createKcc20PsktBuilderEngine(
7607
7599
  );
7608
7600
  const transactionFeeReserve = priorityFee + networkFeeReserve;
7609
7601
  const computeBudget = parsePositiveNumber(
7610
- env.KCC20_MATCHER_COMPUTE_BUDGET ||
7611
- env.KCC20_FILL_COMPUTE_BUDGET ||
7612
- 800,
7602
+ env.KCC20_MATCHER_COMPUTE_BUDGET || env.KCC20_FILL_COMPUTE_BUDGET || 800,
7613
7603
  "computeBudget",
7614
7604
  );
7615
- const privateKeyHex = String(
7616
- env.KCC20_WRAPPER_OPERATOR_PRIVATE_KEY || "",
7617
- )
7605
+ const privateKeyHex = String(env.KCC20_WRAPPER_OPERATOR_PRIVATE_KEY || "")
7618
7606
  .trim()
7619
7607
  .replace(/^0x/i, "");
7620
7608
  if (!privateKeyHex) {
@@ -8278,13 +8266,11 @@ export function createKcc20PsktBuilderEngine(
8278
8266
  params.targetOrderId,
8279
8267
  "target order id",
8280
8268
  );
8281
- const network =
8282
- request.network || env.KASPA_NETWORK || "testnet-10";
8269
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
8283
8270
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
8284
8271
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
8285
8272
  const priorityFee = parseU64(
8286
- env.KCC20_CANCEL_PRIORITY_FEE_SOMPI ||
8287
- DEFAULT_PRIORITY_FEE.toString(),
8273
+ env.KCC20_CANCEL_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
8288
8274
  "priorityFee",
8289
8275
  );
8290
8276
  const computeBudget = parsePositiveNumber(
@@ -8602,8 +8588,7 @@ export function createKcc20PsktBuilderEngine(
8602
8588
  const minterTxid = requireHex32(activeMinterUtxo.txidHex, "minter txid");
8603
8589
  const minterVout = parseVout(activeMinterUtxo.vout, "minter vout");
8604
8590
  const minterAddress = requireKaspaAddress(activeMinterUtxo.address);
8605
- const network =
8606
- request.network || env.KASPA_NETWORK || "testnet-10";
8591
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
8607
8592
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
8608
8593
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
8609
8594
  const tokenOutputSompi = assertKcc20TokenOutputSompi(
@@ -8615,8 +8600,7 @@ export function createKcc20PsktBuilderEngine(
8615
8600
  "mint",
8616
8601
  );
8617
8602
  const priorityFee = parseU64(
8618
- env.KCC20_MINT_PRIORITY_FEE_SOMPI ||
8619
- DEFAULT_PRIORITY_FEE.toString(),
8603
+ env.KCC20_MINT_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
8620
8604
  "priorityFee",
8621
8605
  );
8622
8606
  const computeBudget = parsePositiveNumber(
@@ -8988,8 +8972,7 @@ export function createKcc20PsktBuilderEngine(
8988
8972
  }
8989
8973
 
8990
8974
  const covenantId = requireHex32(params.covenantId, "canonical covenant id");
8991
- const network =
8992
- request.network || env.KASPA_NETWORK || "testnet-10";
8975
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
8993
8976
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
8994
8977
  const walletOwner = requireHex32(
8995
8978
  request.owner?.kcc20Owner,
@@ -9114,8 +9097,7 @@ export function createKcc20PsktBuilderEngine(
9114
9097
  "priorityFee",
9115
9098
  );
9116
9099
  const computeBudget = parsePositiveNumber(
9117
- env.KCC20_MINT_AVAILABILITY_COMPUTE_BUDGET ||
9118
- DEFAULT_COMPUTE_BUDGET,
9100
+ env.KCC20_MINT_AVAILABILITY_COMPUTE_BUDGET || DEFAULT_COMPUTE_BUDGET,
9119
9101
  "computeBudget",
9120
9102
  );
9121
9103
  const rpc = new kaspaWasm.RpcClient({
@@ -9360,8 +9342,7 @@ export function createKcc20PsktBuilderEngine(
9360
9342
  "app canonical token id",
9361
9343
  );
9362
9344
  const owner = requireHex32(request.owner?.kcc20Owner, "token owner");
9363
- const network =
9364
- request.network || env.KASPA_NETWORK || "testnet-10";
9345
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
9365
9346
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
9366
9347
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
9367
9348
  const priorityFee = parseU64(
@@ -9627,13 +9608,11 @@ export function createKcc20PsktBuilderEngine(
9627
9608
  const params = request.params || {};
9628
9609
  const covenantId = requireHex32(params.covenantId, "canonical covenant id");
9629
9610
  const owner = requireHex32(request.owner?.kcc20Owner, "token owner");
9630
- const network =
9631
- request.network || env.KASPA_NETWORK || "testnet-10";
9611
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
9632
9612
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
9633
9613
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
9634
9614
  const priorityFee = parseU64(
9635
- env.KCC20_REVEAL_PRIORITY_FEE_SOMPI ||
9636
- DEFAULT_PRIORITY_FEE.toString(),
9615
+ env.KCC20_REVEAL_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
9637
9616
  "priorityFee",
9638
9617
  );
9639
9618
  const computeBudget = parsePositiveNumber(
@@ -9832,8 +9811,7 @@ export function createKcc20PsktBuilderEngine(
9832
9811
  "recipientOwnerScheme",
9833
9812
  );
9834
9813
  const transferAmount = parsePositiveU64(params.tokenAmount, "tokenAmount");
9835
- const network =
9836
- request.network || env.KASPA_NETWORK || "testnet-10";
9814
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
9837
9815
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
9838
9816
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
9839
9817
  const tokenOutputSompi = assertKcc20TokenOutputSompi(
@@ -9845,8 +9823,7 @@ export function createKcc20PsktBuilderEngine(
9845
9823
  "transfer",
9846
9824
  );
9847
9825
  const priorityFee = parseU64(
9848
- env.KCC20_TRANSFER_PRIORITY_FEE_SOMPI ||
9849
- DEFAULT_PRIORITY_FEE.toString(),
9826
+ env.KCC20_TRANSFER_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
9850
9827
  "priorityFee",
9851
9828
  );
9852
9829
  const computeBudget = parsePositiveNumber(
@@ -9924,7 +9901,18 @@ export function createKcc20PsktBuilderEngine(
9924
9901
  `active holder UTXO ${tokenTxid}:${tokenVout} not found`,
9925
9902
  );
9926
9903
  }
9927
- const spendCovenantId = covenantIdFromUtxoEntry(tokenEntry) ?? covenantId;
9904
+ const rpcScript = tokenEntry.scriptPublicKey ??
9905
+ tokenEntry.entry?.scriptPublicKey ?? tokenEntry.utxoEntry?.scriptPublicKey;
9906
+ if (!rpcScript || kw.addressFromScriptPublicKey(rpcScript, network)?.toString() !== tokenAddress) {
9907
+ throw new Error("active holder RPC script does not match rebuilt covenant state");
9908
+ }
9909
+ if (activeHolderUtxo.amountSompi != null &&
9910
+ BigInt(activeHolderUtxo.amountSompi) !== utxoAmountSompi(tokenEntry)) {
9911
+ throw new Error("active holder KAS amount does not match RPC");
9912
+ }
9913
+ const spendCovenantId = params.activeHolderNativeCovenantId
9914
+ ? requireMatchingCovenantId(tokenEntry, covenantId, "active holder")
9915
+ : covenantIdFromUtxoEntry(tokenEntry) ?? covenantId;
9928
9916
 
9929
9917
  let outputSompi = 0n;
9930
9918
  const recipientOutputIndex = 0;
@@ -10115,16 +10103,22 @@ export function createKcc20PsktBuilderEngine(
10115
10103
  );
10116
10104
  const decimals = parseU64(params.decimals ?? "0", "decimals");
10117
10105
  const priceScale = 10n ** decimals;
10118
- const network =
10119
- request.network || env.KASPA_NETWORK || "testnet-10";
10106
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
10120
10107
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
10121
10108
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
10122
- const amountTkas = parseAmountTkas(
10123
- env.KCC20_DEPLOY_OUTPUT_TKAS || DEFAULT_DEPLOY_TKAS,
10124
- );
10109
+ const requestedOutputSompi =
10110
+ params.outputSompi === undefined || params.outputSompi === null
10111
+ ? undefined
10112
+ : assertKcc20TokenOutputSompi(
10113
+ parseU64(params.outputSompi, "outputSompi"),
10114
+ "deploy",
10115
+ );
10116
+ const amountTkas =
10117
+ requestedOutputSompi === undefined
10118
+ ? parseAmountTkas(env.KCC20_DEPLOY_OUTPUT_TKAS || DEFAULT_DEPLOY_TKAS)
10119
+ : undefined;
10125
10120
  const priorityFee = parseU64(
10126
- env.KCC20_DEPLOY_PRIORITY_FEE_SOMPI ||
10127
- DEFAULT_PRIORITY_FEE.toString(),
10121
+ env.KCC20_DEPLOY_PRIORITY_FEE_SOMPI || DEFAULT_PRIORITY_FEE.toString(),
10128
10122
  "priorityFee",
10129
10123
  );
10130
10124
  const metadata = {
@@ -10249,7 +10243,9 @@ export function createKcc20PsktBuilderEngine(
10249
10243
  kw,
10250
10244
  covenantOutputs[0].script,
10251
10245
  );
10252
- const deploySompi = BigInt(Math.round(amountTkas * Number(SOMPI_PER_TKAS)));
10246
+ const deploySompi =
10247
+ requestedOutputSompi ??
10248
+ BigInt(Math.round(amountTkas! * Number(SOMPI_PER_TKAS)));
10253
10249
  const premintOutputIndex = premintState ? covenantOutputs.length - 1 : null;
10254
10250
 
10255
10251
  const rpc = new RpcClient({
@@ -10264,6 +10260,8 @@ export function createKcc20PsktBuilderEngine(
10264
10260
  const fundingEntry = selectFundingEntry(
10265
10261
  utxos.entries,
10266
10262
  outputSompi + priorityFee + 10_000_000n,
10263
+ [],
10264
+ params.fundingOutpoint,
10267
10265
  );
10268
10266
  const fundingChangeOutputIndex = covenantOutputs.length;
10269
10267
  const change = utxoAmountSompi(fundingEntry) - outputSompi - priorityFee;
@@ -10455,8 +10453,7 @@ export function createKcc20PsktBuilderEngine(
10455
10453
  params.utilityTokenAmount,
10456
10454
  "utilityTokenAmount",
10457
10455
  );
10458
- const network =
10459
- request.network || env.KASPA_NETWORK || "testnet-10";
10456
+ const network = request.network || env.KASPA_NETWORK || "testnet-10";
10460
10457
  const walletAddress = requireKaspaAddress(request.owner?.walletAddress);
10461
10458
  const rpcUrl = env.KASPA_WRPC_URL || DEFAULT_WRPC_URL;
10462
10459
  const amountTkas = parseAmountTkas(
@@ -11699,10 +11696,7 @@ export function createKcc20PsktBuilderEngine(
11699
11696
  }
11700
11697
 
11701
11698
  async function retryUtxosByAddresses(rpc, addresses, isReady, initial) {
11702
- const attempts = parseRetryInteger(
11703
- env.KCC20_SOURCE_UTXO_RETRY_ATTEMPTS,
11704
- 3,
11705
- );
11699
+ const attempts = parseRetryInteger(env.KCC20_SOURCE_UTXO_RETRY_ATTEMPTS, 3);
11706
11700
  const delayMs = parseRetryInteger(
11707
11701
  env.KCC20_SOURCE_UTXO_RETRY_DELAY_MS,
11708
11702
  500,
package/src/index.ts CHANGED
@@ -22,7 +22,15 @@ export * from "./runtime.js";
22
22
  export * as saleCommon from "./sale-common.js";
23
23
  export * from "./source-provider.js";
24
24
  export * from "./source-validation.js";
25
+ export * from "./snapshot-operation.js";
26
+ export * from "./snapshot-signerless.js";
27
+ export * from "./snapshot-signerless-transactions.js";
25
28
  export * from "./wasm-provider.js";
26
29
  export * from "./wallet-operation.js";
27
30
  export * from "./wrapper-operation.js";
28
31
  export * from "./verify-operation.js";
32
+
33
+ export * as snapshot from "./snapshot-signerless.js";
34
+ export * as snapshotTransactions from "./snapshot-signerless-transactions.js";
35
+ export * from "./snapshot-deployment.js";
36
+ export * from "./snapshot-verification.js";
package/src/operations.ts CHANGED
@@ -22,6 +22,10 @@ export const KCC20_BUILDER_KEYS = [
22
22
  "fee-ticket.create-from-utility-burn",
23
23
  "fee-ticket.burn",
24
24
  "fee-ticket.transfer",
25
+ "kcc20-snapshot.deploy-bootstrap",
26
+ "kcc20-snapshot.deploy-controller",
27
+ "kcc20-snapshot.start",
28
+ "kcc20-snapshot.claim",
25
29
  ] as const;
26
30
 
27
31
  export type Kcc20BuilderKey = (typeof KCC20_BUILDER_KEYS)[number];