@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
@@ -1135,8 +1135,16 @@ export function selectFundingEntry(
1135
1135
  entries,
1136
1136
  requiredSompi,
1137
1137
  skippedOutpoints = [],
1138
+ requiredOutpoint = undefined,
1138
1139
  ) {
1139
- const skipped = new Set(skippedOutpoints);
1140
+ const skipped = new Set(skippedOutpoints.map(normalizeFundingOutpoint));
1141
+ const pinned =
1142
+ requiredOutpoint === undefined || requiredOutpoint === null
1143
+ ? undefined
1144
+ : normalizeFundingOutpoint(requiredOutpoint);
1145
+ if (pinned !== undefined && !pinned) {
1146
+ throw new Error("pinned funding outpoint is invalid");
1147
+ }
1140
1148
  const sorted = [...entries].sort((a, b) => {
1141
1149
  const av = utxoAmountSompi(a);
1142
1150
  const bv = utxoAmountSompi(b);
@@ -1144,17 +1152,50 @@ export function selectFundingEntry(
1144
1152
  return av < bv ? 1 : -1;
1145
1153
  });
1146
1154
  const entry = sorted.find((candidate) => {
1147
- const outpoint = `${candidate.outpoint?.transactionId}:${candidate.outpoint?.index}`;
1155
+ const outpoint = normalizeFundingOutpoint(candidate.outpoint);
1148
1156
  return (
1149
- !skipped.has(outpoint) && utxoAmountSompi(candidate) >= requiredSompi
1157
+ (!pinned || outpoint === pinned) &&
1158
+ !skipped.has(outpoint) &&
1159
+ utxoAmountSompi(candidate) >= requiredSompi
1150
1160
  );
1151
1161
  });
1152
1162
  if (!entry) {
1163
+ if (pinned) {
1164
+ throw new Error(
1165
+ `pinned funding UTXO ${pinned} is unavailable or cannot cover ${requiredSompi} sompi`,
1166
+ );
1167
+ }
1153
1168
  throw new Error(`no single funding UTXO can cover ${requiredSompi} sompi`);
1154
1169
  }
1155
1170
  return entry;
1156
1171
  }
1157
1172
 
1173
+ function normalizeFundingOutpoint(value) {
1174
+ if (typeof value === "string") {
1175
+ const match = value
1176
+ .trim()
1177
+ .toLowerCase()
1178
+ .match(/^([a-f0-9]{64}):(\d+)$/);
1179
+ if (!match) return "";
1180
+ const index = Number(match[2]);
1181
+ return Number.isSafeInteger(index) && index >= 0 && index <= 0xffffffff
1182
+ ? `${match[1]}:${index}`
1183
+ : "";
1184
+ }
1185
+ const transactionId = String(
1186
+ value?.transactionId ?? value?.transactionIdHex ?? value?.txid ?? "",
1187
+ )
1188
+ .trim()
1189
+ .toLowerCase();
1190
+ const index = Number(value?.index ?? value?.vout);
1191
+ return /^[a-f0-9]{64}$/.test(transactionId) &&
1192
+ Number.isSafeInteger(index) &&
1193
+ index >= 0 &&
1194
+ index <= 0xffffffff
1195
+ ? `${transactionId}:${index}`
1196
+ : "";
1197
+ }
1198
+
1158
1199
  export function requiredToccataFeeSompi(kw, network, transaction) {
1159
1200
  if (
1160
1201
  typeof kw.calculateTransactionFee !== "function" ||
@@ -0,0 +1,108 @@
1
+ import { bytesToHex, asciiToBytes32 } from "./encoding.js";
2
+ import { assertKcc20ArtifactScriptHash } from "./artifacts.js";
3
+ import {
4
+ buildControllerGenesisPlan, buildBootstrapGenesisPlan,
5
+ buildSnapshotStartPlan, buildSnapshotTokenMetadata, snapshotPeerPlanCommitment,
6
+ } from "./snapshot-signerless.js";
7
+ import { deriveSnapshotTokenId } from "./snapshot-signerless-transactions.js";
8
+
9
+ export const SNAPSHOT_DEPLOYMENT_PROTOCOL = "signerless-start-v2" as const;
10
+ export interface SnapshotDeploymentShard {
11
+ shardIndex: number;
12
+ globalIndexStart: number;
13
+ holderCount: number;
14
+ depth: number;
15
+ allocationTotal: string;
16
+ initialRoot: string;
17
+ controllerId?: string;
18
+ genesisOutpoint?: { transactionId: string; index: number };
19
+ }
20
+ export interface SnapshotDeploymentDefinition {
21
+ migrationId: string;
22
+ manifestCommitment: string;
23
+ context: string;
24
+ creator: string;
25
+ ticker: string;
26
+ displayScale: string;
27
+ totalSupply: string;
28
+ claimNotBeforeDaaScore: string;
29
+ shards: SnapshotDeploymentShard[];
30
+ bootstrapId?: string;
31
+ bootstrapOutpoint?: { transactionId: string; index: number };
32
+ }
33
+ export interface SnapshotArtifacts {
34
+ controllerArtifact: any;
35
+ bootstrapArtifact: any;
36
+ kcc20Artifact: any;
37
+ }
38
+
39
+ /** Pure deterministic planning. IDs must come from independently confirmed genesis outputs. */
40
+ export function prepareSnapshotDeployment(kw: any, definition: SnapshotDeploymentDefinition, artifacts: SnapshotArtifacts) {
41
+ assertKcc20ArtifactScriptHash("KCC20.placeholder.json", artifacts.kcc20Artifact);
42
+ assertKcc20ArtifactScriptHash("KCC20SnapshotBootstrap.placeholder.json", artifacts.bootstrapArtifact);
43
+ assertKcc20ArtifactScriptHash("KCC20SnapshotController.placeholder.json", artifacts.controllerArtifact);
44
+ const shards = [...definition.shards].sort((a, b) => a.shardIndex - b.shardIndex);
45
+ if (!shards.length || shards.length > 8 || shards.some((s, i) => s.shardIndex !== i)) {
46
+ throw new Error("snapshot requires 1..8 contiguous shards");
47
+ }
48
+ if (shards.reduce((sum, shard) => sum + BigInt(shard.allocationTotal), 0n) !== BigInt(definition.totalSupply)) {
49
+ throw new Error("shard allocations do not equal fixed token supply");
50
+ }
51
+ const extension = {
52
+ kind: 1, creator: definition.creator, ticker: bytesToHex(asciiToBytes32(definition.ticker)),
53
+ name: bytesToHex(asciiToBytes32(definition.ticker)), displayScale: definition.displayScale,
54
+ maxSupply: definition.totalSupply, mintLaneCount: 1, mintPolicy: 0, mintPriceSompi: "0",
55
+ treasury: definition.creator, protocolFeeRecipient: definition.creator, protocolFeeBps: "0",
56
+ };
57
+ const metadata = buildSnapshotTokenMetadata({
58
+ ...extension, migrationId: definition.migrationId, totalAllocation: definition.totalSupply,
59
+ } as any);
60
+ const common = {
61
+ ...artifacts, migrationId: definition.migrationId, migrationCommitment: definition.migrationId,
62
+ manifestCommitment: definition.manifestCommitment, context: definition.context,
63
+ holderExtensionCommitment: metadata.holderExtensionCommitment,
64
+ claimNotBeforeDaaScore: definition.claimNotBeforeDaaScore, shardCount: shards.length,
65
+ };
66
+ const placeholder = "01".repeat(32);
67
+ const controllerPlans = shards.map(shard => buildControllerGenesisPlan({
68
+ ...common, shard, deploymentGuardCommitment: shard.shardIndex === 0 ? placeholder : "00".repeat(32),
69
+ } as any));
70
+ const peerPending = shards.slice(1).find(s => !s.controllerId || !s.genesisOutpoint);
71
+ if (peerPending) return {
72
+ stage: "controller-deploy" as const, shard: peerPending.shardIndex,
73
+ plan: controllerPlans[peerPending.shardIndex], holderExtensionCommitment: metadata.holderExtensionCommitment,
74
+ extension,
75
+ };
76
+ const peerCommitment = snapshotPeerPlanCommitment({
77
+ migrationCommitment: definition.migrationId, manifestCommitment: definition.manifestCommitment,
78
+ shards: shards.map((shard, i) => ({ controllerId: shard.controllerId ?? placeholder, state: controllerPlans[i].controllerState })),
79
+ });
80
+ controllerPlans[0] = buildControllerGenesisPlan({
81
+ ...common, shard: shards[0], deploymentGuardCommitment: peerCommitment,
82
+ } as any);
83
+ if (!shards[0].controllerId || !shards[0].genesisOutpoint) return {
84
+ stage: "controller-deploy" as const, shard: 0, plan: controllerPlans[0],
85
+ holderExtensionCommitment: metadata.holderExtensionCommitment, extension,
86
+ };
87
+ const committedShards = shards.map((shard, i) => ({ controllerId: shard.controllerId!, state: controllerPlans[i].controllerState }));
88
+ const reserves = shards.map(shard => ({
89
+ amount: shard.allocationTotal, owner: shard.controllerId!, ownerScheme: 4,
90
+ borrowScheme: 0, borrowGuard: "00".repeat(32), extensionCommitment: metadata.holderExtensionCommitment,
91
+ }));
92
+ const tokenId = deriveSnapshotTokenId(kw, {
93
+ controllerGenesisOutpoint: shards[0].genesisOutpoint, reserves, kcc20Artifact: artifacts.kcc20Artifact,
94
+ reserveCellKas: "50000000",
95
+ });
96
+ const bootstrapPlan = buildBootstrapGenesisPlan({
97
+ ...common, expectedTokenId: tokenId, totalSupply: definition.totalSupply, shards: committedShards,
98
+ } as any);
99
+ return {
100
+ stage: definition.bootstrapId ? "snapshot-start" as const : "bootstrap-deploy" as const,
101
+ plan: definition.bootstrapId ? buildSnapshotStartPlan({
102
+ ...artifacts, migrationId: definition.migrationId, bootstrapId: definition.bootstrapId,
103
+ bootstrapState: bootstrapPlan.bootstrapState, shards: committedShards,
104
+ }) : bootstrapPlan,
105
+ tokenId, holderExtensionCommitment: metadata.holderExtensionCommitment,
106
+ extension, controllers: committedShards,
107
+ };
108
+ }