@kaspacom/kcc20-tx-builder 0.2.0 → 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,86 @@
1
+ import { prepareSnapshotDeployment, type SnapshotArtifacts, type SnapshotDeploymentDefinition } from './snapshot-deployment.js';
2
+ import { bytesToHex } from './encoding.js';
3
+ import * as tx from './snapshot-signerless-transactions.js';
4
+
5
+ export function snapshotStartPayload(definition: SnapshotDeploymentDefinition, prepared: ReturnType<typeof prepareSnapshotDeployment>): string {
6
+ const plan = prepared.plan;
7
+ if (prepared.stage !== 'snapshot-start') throw new Error('snapshot start plan required');
8
+ const opening = {
9
+ ...prepared.extension, ticker: definition.ticker, name: definition.ticker,
10
+ premintSupply: definition.totalSupply, premintRecipient: plan.reserves[0].owner, premintOwnerScheme: 4,
11
+ holderExtensionCommitment: prepared.holderExtensionCommitment, snapshotMigrationId: definition.migrationId,
12
+ snapshotManifestCommitment: definition.manifestCommitment,
13
+ snapshotReserves: plan.reserves.map((reserve: any, shardIndex: number) => ({ ...reserve, shardIndex })),
14
+ };
15
+ return bytesToHex(new TextEncoder().encode(JSON.stringify({ tn10: { v: 1, tmpl: 'KCC20',
16
+ args: Object.entries(opening).map(([name, value]) => ({ name, type: 'value', value })),
17
+ } })));
18
+ }
19
+
20
+ /** Reconstruct the whole transaction from approved definition and pinned artifacts. */
21
+ export function verifySnapshotDeploymentTransaction(kw: any, args: {
22
+ definition: SnapshotDeploymentDefinition; artifacts: SnapshotArtifacts; transactionJson: string;
23
+ walletAddress: string; maximumFeeSompi: string; stage: string; signingInputIndexes: number[];
24
+ }) {
25
+ const prepared = prepareSnapshotDeployment(kw, args.definition, args.artifacts);
26
+ if (prepared.stage !== args.stage) throw new Error('snapshot stage differs from approved custody');
27
+ const transaction = JSON.parse(args.transactionJson);
28
+ const fundingIndex = prepared.stage === 'snapshot-start' ? args.definition.shards.length + 1 : 0;
29
+ if (JSON.stringify(args.signingInputIndexes) !== JSON.stringify([fundingIndex])) {
30
+ throw new Error('only the snapshot funding input may receive the wallet signature');
31
+ }
32
+ if (transaction.inputs.length !== fundingIndex + 1) throw new Error('unexpected snapshot input count');
33
+ const input = (index: number, script?: Uint8Array) => {
34
+ const value = transaction.inputs[index];
35
+ const previousOutpoint = value.previousOutpoint ?? { transactionId: value.transactionId, index: value.index };
36
+ const serializedSpk = value.utxo.scriptPublicKey;
37
+ const scriptPublicKey = typeof serializedSpk === 'string'
38
+ ? new kw.ScriptPublicKey(parseInt(serializedSpk.slice(0, 4), 16), serializedSpk.slice(4)) : serializedSpk;
39
+ const utxo: any = Object.fromEntries(Object.entries(value.utxo).filter(([, value]) => value !== null));
40
+ return { previousOutpoint, utxo: { ...utxo, amount: BigInt(utxo.amount), blockDaaScore: BigInt(utxo.blockDaaScore), outpoint: previousOutpoint, scriptPublicKey }, script };
41
+ };
42
+ const fundingInput = input(fundingIndex);
43
+ if (fundingInput.utxo.covenantId || fundingInput.utxo.covenant ||
44
+ String(fundingInput.utxo.scriptPublicKey.script) !== String(kw.payToAddressScript(args.walletAddress).script)) {
45
+ throw new Error('snapshot funding must belong to the connected wallet');
46
+ }
47
+ const fee = transaction.inputs.reduce((sum: bigint, value: any) => sum + BigInt(value.utxo.amount), 0n) -
48
+ transaction.outputs.reduce((sum: bigint, value: any) => sum + BigInt(value.value), 0n);
49
+ if (fee < 0n || fee > BigInt(args.maximumFeeSompi)) throw new Error('snapshot fee exceeds reviewed amount');
50
+ const common = { ...args.artifacts, plan: prepared.plan, fundingInput, changeAddress: args.walletAddress, feeSompi: fee };
51
+ let expected;
52
+ if (prepared.stage === 'controller-deploy') expected = tx.buildControllerGenesisUnsignedTransaction(kw, common);
53
+ else if (prepared.stage === 'bootstrap-deploy') expected = tx.buildBootstrapGenesisUnsignedTransaction(kw, common);
54
+ else {
55
+ const plan = prepared.plan;
56
+ const bootstrapInput = input(0, tx.buildSnapshotBootstrapScriptForState(args.artifacts.bootstrapArtifact, plan.bootstrapState));
57
+ const approvedBootstrap = args.definition.bootstrapOutpoint;
58
+ if (!approvedBootstrap || bootstrapInput.previousOutpoint.transactionId !== approvedBootstrap.transactionId || bootstrapInput.previousOutpoint.index !== approvedBootstrap.index) {
59
+ throw new Error('snapshot bootstrap input differs from approved genesis');
60
+ }
61
+ expected = tx.buildSnapshotStartUnsignedTransaction(kw, {
62
+ ...common, payload: snapshotStartPayload(args.definition, prepared),
63
+ bootstrapInput,
64
+ controllerInputs: plan.controllers.map((controller: any, index: number) => {
65
+ const actual = input(index + 1, tx.buildSnapshotControllerScriptForState(args.artifacts.controllerArtifact, controller.stateBefore));
66
+ const approved = args.definition.shards.find(shard => shard.shardIndex === index)?.genesisOutpoint;
67
+ if (!approved || actual.previousOutpoint.transactionId !== approved.transactionId || actual.previousOutpoint.index !== approved.index) {
68
+ throw new Error('snapshot controller input differs from approved genesis');
69
+ }
70
+ return actual;
71
+ }),
72
+ });
73
+ }
74
+ const canonical = (value: any): string => {
75
+ const copy = JSON.parse(JSON.stringify(value));
76
+ delete copy.id; delete copy.mass; delete copy.storageMass;
77
+ delete copy.inputs[fundingIndex].signatureScript;
78
+ // UTXO SDK metadata is host supplied; transaction fields and scripts are compared below.
79
+ for (const entry of copy.inputs) delete entry.utxo;
80
+ const sort = (item: any): any => Array.isArray(item) ? item.map(sort) : item && typeof item === 'object'
81
+ ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a.localeCompare(b)).map(([key, val]) => [key, sort(val)])) : item;
82
+ return JSON.stringify(sort(copy));
83
+ };
84
+ if (canonical(transaction) !== canonical(expected.unsignedTransaction)) throw new Error('snapshot transaction differs from the approved plan');
85
+ return prepared;
86
+ }
@@ -233,7 +233,7 @@ export function buildKcc20TransferTokenOperation(
233
233
  : "No single KCC20 holder UTXO is available for this wallet and amount",
234
234
  );
235
235
  }
236
- const nativeCovenantId = holder.covenantId ?? covenantId;
236
+ const nativeCovenantId = holder.covenantId;
237
237
  return buildKcc20WalletOperationFromPlan(
238
238
  wallet,
239
239
  {
@@ -245,7 +245,7 @@ export function buildKcc20TransferTokenOperation(
245
245
  params: {
246
246
  covenantId,
247
247
  appCanonicalCovenantId: covenantId,
248
- activeHolderNativeCovenantId: nativeCovenantId,
248
+ ...(nativeCovenantId ? { activeHolderNativeCovenantId: nativeCovenantId } : {}),
249
249
  tokenAmount,
250
250
  recipientOwner,
251
251
  recipientOwnerScheme: 0,
@@ -257,7 +257,7 @@ export function buildKcc20TransferTokenOperation(
257
257
  {
258
258
  type: "kcc20-holder-utxo",
259
259
  covenantId,
260
- activeHolderNativeCovenantId: nativeCovenantId,
260
+ ...(nativeCovenantId ? { activeHolderNativeCovenantId: nativeCovenantId } : {}),
261
261
  owner,
262
262
  minimumTokenAmount: tokenAmount,
263
263
  ...summary,