@kaspacom/kcc20-tx-builder 0.4.0 → 0.6.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 (139) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +2 -2
  3. package/RELEASING.md +19 -14
  4. package/artifacts/ArgentKCC20.reference.json +12428 -0
  5. package/artifacts/ArgentKCC20Wrapper.placeholder.json +12455 -0
  6. package/artifacts/KCC20.placeholder.json +1447 -1973
  7. package/artifacts/KCC20FeeTicket.placeholder.json +816 -1447
  8. package/artifacts/KCC20Orderbook.placeholder.json +1 -2
  9. package/artifacts/KCC20SnapshotBootstrap.placeholder.json +101 -100
  10. package/artifacts/KCC20SnapshotController.placeholder.json +101 -100
  11. package/artifacts/KCC20Vesting.placeholder.json +427 -429
  12. package/artifacts/KCC20Wrapper.placeholder.json +733 -734
  13. package/artifacts/kcc20-snapshot-bootstrap/KCC20SnapshotBootstrap.placeholder.json +101 -100
  14. package/artifacts/kcc20-snapshot-controller/KCC20SnapshotController.placeholder.json +101 -100
  15. package/artifacts/snapshot-standards.lock.json +6 -3
  16. package/artifacts/token-profiles.json +109 -0
  17. package/artifacts/token-profiles.sha256 +1 -0
  18. package/dist/action-source-resolver.d.ts.map +1 -1
  19. package/dist/action-source-resolver.js +11 -2
  20. package/dist/action-source-resolver.js.map +1 -1
  21. package/dist/artifacts.d.ts +10 -10
  22. package/dist/artifacts.d.ts.map +1 -1
  23. package/dist/artifacts.js +10 -10
  24. package/dist/artifacts.js.map +1 -1
  25. package/dist/cjs/action-source-resolver.js +11 -2
  26. package/dist/cjs/action-source-resolver.js.map +1 -1
  27. package/dist/cjs/artifacts.d.ts +10 -10
  28. package/dist/cjs/artifacts.js +10 -10
  29. package/dist/cjs/artifacts.js.map +1 -1
  30. package/dist/cjs/engine.d.ts +10 -0
  31. package/dist/cjs/engine.js +124 -77
  32. package/dist/cjs/engine.js.map +1 -1
  33. package/dist/cjs/index.d.ts +5 -0
  34. package/dist/cjs/index.js +5 -0
  35. package/dist/cjs/index.js.map +1 -1
  36. package/dist/cjs/profile-transfer.d.ts +60 -0
  37. package/dist/cjs/profile-transfer.js +248 -0
  38. package/dist/cjs/profile-transfer.js.map +1 -0
  39. package/dist/cjs/snapshot-operation.d.ts +0 -1
  40. package/dist/cjs/snapshot-operation.js +1 -1126
  41. package/dist/cjs/snapshot-operation.js.map +1 -1
  42. package/dist/cjs/snapshot-signerless-transactions.d.ts +1 -1
  43. package/dist/cjs/snapshot-signerless-transactions.js +3 -6
  44. package/dist/cjs/snapshot-signerless-transactions.js.map +1 -1
  45. package/dist/cjs/snapshot-signerless.js +5 -3
  46. package/dist/cjs/snapshot-signerless.js.map +1 -1
  47. package/dist/cjs/snapshot-verification.d.ts +1 -1
  48. package/dist/cjs/snapshot-verification.js +16 -12
  49. package/dist/cjs/snapshot-verification.js.map +1 -1
  50. package/dist/cjs/token-authorization.d.ts +42 -0
  51. package/dist/cjs/token-authorization.js +93 -0
  52. package/dist/cjs/token-authorization.js.map +1 -0
  53. package/dist/cjs/token-profiles.d.ts +82 -0
  54. package/dist/cjs/token-profiles.generated.d.ts +3 -0
  55. package/dist/cjs/token-profiles.generated.js +114 -0
  56. package/dist/cjs/token-profiles.generated.js.map +1 -0
  57. package/dist/cjs/token-profiles.js +257 -0
  58. package/dist/cjs/token-profiles.js.map +1 -0
  59. package/dist/cjs/transfer-payload.d.ts +9 -0
  60. package/dist/cjs/transfer-payload.js +37 -0
  61. package/dist/cjs/transfer-payload.js.map +1 -0
  62. package/dist/cjs/wrapper-operation.d.ts +1 -0
  63. package/dist/cjs/wrapper-operation.js +1 -0
  64. package/dist/cjs/wrapper-operation.js.map +1 -1
  65. package/dist/engine.d.ts +10 -0
  66. package/dist/engine.d.ts.map +1 -1
  67. package/dist/engine.js +125 -78
  68. package/dist/engine.js.map +1 -1
  69. package/dist/index.d.ts +5 -0
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +5 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/profile-transfer.d.ts +61 -0
  74. package/dist/profile-transfer.d.ts.map +1 -0
  75. package/dist/profile-transfer.js +245 -0
  76. package/dist/profile-transfer.js.map +1 -0
  77. package/dist/snapshot-operation.d.ts +0 -1
  78. package/dist/snapshot-operation.d.ts.map +1 -1
  79. package/dist/snapshot-operation.js +0 -1125
  80. package/dist/snapshot-operation.js.map +1 -1
  81. package/dist/snapshot-signerless-transactions.d.ts +1 -1
  82. package/dist/snapshot-signerless-transactions.d.ts.map +1 -1
  83. package/dist/snapshot-signerless-transactions.js +3 -6
  84. package/dist/snapshot-signerless-transactions.js.map +1 -1
  85. package/dist/snapshot-signerless.d.ts.map +1 -1
  86. package/dist/snapshot-signerless.js +5 -3
  87. package/dist/snapshot-signerless.js.map +1 -1
  88. package/dist/snapshot-verification.d.ts +1 -1
  89. package/dist/snapshot-verification.d.ts.map +1 -1
  90. package/dist/snapshot-verification.js +16 -12
  91. package/dist/snapshot-verification.js.map +1 -1
  92. package/dist/token-authorization.d.ts +43 -0
  93. package/dist/token-authorization.d.ts.map +1 -0
  94. package/dist/token-authorization.js +88 -0
  95. package/dist/token-authorization.js.map +1 -0
  96. package/dist/token-profiles.d.ts +83 -0
  97. package/dist/token-profiles.d.ts.map +1 -0
  98. package/dist/token-profiles.generated.d.ts +4 -0
  99. package/dist/token-profiles.generated.d.ts.map +1 -0
  100. package/dist/token-profiles.generated.js +111 -0
  101. package/dist/token-profiles.generated.js.map +1 -0
  102. package/dist/token-profiles.js +245 -0
  103. package/dist/token-profiles.js.map +1 -0
  104. package/dist/transfer-payload.d.ts +10 -0
  105. package/dist/transfer-payload.d.ts.map +1 -0
  106. package/dist/transfer-payload.js +34 -0
  107. package/dist/transfer-payload.js.map +1 -0
  108. package/dist/wrapper-operation.d.ts +1 -0
  109. package/dist/wrapper-operation.d.ts.map +1 -1
  110. package/dist/wrapper-operation.js +1 -0
  111. package/dist/wrapper-operation.js.map +1 -1
  112. package/docs/API.md +1 -1
  113. package/docs/COMPATIBILITY.json +24 -33
  114. package/docs/INTEGRATION.md +2 -2
  115. package/docs/LAST_CALL.md +50 -0
  116. package/examples/README.md +5 -2
  117. package/examples/backend/cli.ts +2 -1
  118. package/examples/frontend/main.ts +2 -1
  119. package/examples/shared/build.ts +3 -0
  120. package/examples/shared/offline-profile.ts +27 -0
  121. package/examples/shared/recipe-fixtures.ts +3 -2
  122. package/examples/test/browser.ts +2 -2
  123. package/examples/test/build.test.ts +21 -8
  124. package/examples/test/sources.test.ts +3 -2
  125. package/package.json +2 -2
  126. package/src/action-source-resolver.ts +22 -4
  127. package/src/artifacts.ts +12 -10
  128. package/src/engine.ts +208 -105
  129. package/src/index.ts +7 -0
  130. package/src/profile-transfer.ts +425 -0
  131. package/src/snapshot-operation.ts +59 -1731
  132. package/src/snapshot-signerless-transactions.ts +8 -17
  133. package/src/snapshot-signerless.ts +5 -3
  134. package/src/snapshot-verification.ts +17 -13
  135. package/src/token-authorization.ts +135 -0
  136. package/src/token-profiles.generated.ts +112 -0
  137. package/src/token-profiles.ts +456 -0
  138. package/src/transfer-payload.ts +45 -0
  139. package/src/wrapper-operation.ts +2 -0
@@ -1,6 +1,6 @@
1
1
  # Integrating the TX Builder
2
2
 
3
- This guide describes the API shipped in `@kaspacom/kcc20-tx-builder@0.2.5`.
3
+ This guide describes the API shipped in `@kaspacom/kcc20-tx-builder@0.5.0`.
4
4
  The host application supplies wallet identity, network configuration, contract
5
5
  artifacts, chain data, and an initialized Kaspa WASM runtime. The package
6
6
  constructs operations and transaction JSON. The host signs, validates,
@@ -9,7 +9,7 @@ broadcasts, and tracks the result.
9
9
  ## Install from the public registry
10
10
 
11
11
  ```sh
12
- npm install --save-exact @kaspacom/kcc20-tx-builder@0.2.5 \
12
+ npm install --save-exact @kaspacom/kcc20-tx-builder@0.5.0 \
13
13
  --registry=https://registry.npmjs.org/ \
14
14
  --@kaspacom:registry=https://registry.npmjs.org/
15
15
  ```
@@ -0,0 +1,50 @@
1
+ # KCC20 Last Call builders
2
+
3
+ Target revision: `3fbec524abfbc20e87652eb938db218f8c17db17`.
4
+ Local package: `0.5.0-lastcall.1`. Funded TN10 qualification remains a release task.
5
+
6
+ ## Profile boundary
7
+
8
+ Authenticate the registry digest, then match a bundled profile to a revealed
9
+ program and current RPC output. `authenticateTokenArtifact` checks the entire
10
+ artifact, ABI, state layout and executable template commitment. Names and
11
+ uploaded JSON are not authority.
12
+
13
+ `buildProfileTransferTransaction` constructs explicit owner or borrowed-receive
14
+ transactions. It supports Schnorr P2PK, Schnorr/ECDSA P2PKH, supplied P2SH
15
+ authorities and covenant-ID authorities. Callers must obtain fresh authenticated
16
+ RPC entries before building and recheck acceptance before signing. Authority
17
+ inputs require a reviewed witness adapter. Outputs preserve authority value,
18
+ script and identity. Pass the trusted registry manifest, its expected digest and
19
+ the selected profile ID; a standalone profile object is not authorization. This
20
+ API does not broadcast.
21
+
22
+ `planTokenOwnerWitness` checks wallet capabilities before signing. Borrowing is
23
+ never selected automatically. The normal wallet engine supports its reviewed
24
+ public-key paths; advanced authority/borrow callers use the explicit API.
25
+
26
+ Wrapper construction resolves the bundled token template and its committed
27
+ wrapper artifact. External custom mint/burn operations are rejected.
28
+ Profiles with `displayDecimals` pin the wrapper price scale to that precision.
29
+
30
+ ## Artifact synchronization
31
+
32
+ From the builder repository:
33
+
34
+ ```sh
35
+ node scripts/sync-reviewed-artifacts.mjs /path/to/kaspa-covenants
36
+ npm test
37
+ npm run verify:compatibility
38
+ SNAPSHOT_TEST_WASM=/path/to/kaspa-covenants/wallet-lab/vendor/kaspa-wasm-toc \
39
+ npm run test:snapshot:transactions
40
+ npm pack
41
+ ```
42
+
43
+ Both backend and frontend must consume the same archive hash. Snapshot pins
44
+ include the corrected token, bootstrap and controller artifacts. Earlier source
45
+ snapshots are reusable, but previous contract approvals cannot be rebound.
46
+
47
+ Local tests cover independent bytecode/state offset, profile bounds, owner and
48
+ borrow witness construction, malformed commitments, and one/two/eight-shard
49
+ snapshot construction. Synthetic funding in these tests is not a funded TN10
50
+ proof. Funded deploy and trading checks belong to release qualification.
@@ -17,15 +17,18 @@ npm --prefix examples run frontend
17
17
  ```
18
18
 
19
19
  `test:examples` packs the current candidate and installs that tarball into the
20
- examples extracted from that tarball before testing; it also installs the same candidate into the checkout. This matters before 0.4.0 is published: the lockfile's
20
+ examples extracted from that tarball before testing; it also installs the same candidate into the checkout. This matters before 0.5.0 is published: the lockfile's
21
21
  registry bootstrap dependency is 0.2.5, which predates the browser compatibility
22
22
  fix. Do not skip the candidate installation when testing a checkout.
23
23
 
24
+ Synthetic offline examples use the same digest-authenticated bundled profile as
25
+ live builders. They do not broadcast transactions.
26
+
24
27
  After publication, users can copy `examples` from the package or repository,
25
28
  run `npm ci` inside it, then install the matching release explicitly:
26
29
 
27
30
  ```sh
28
- npm install --no-save --package-lock=false @kaspacom/kcc20-tx-builder@0.4.0 \
31
+ npm install --no-save --package-lock=false @kaspacom/kcc20-tx-builder@0.5.0 \
29
32
  --registry=https://registry.npmjs.org/ --@kaspacom:registry=https://registry.npmjs.org/
30
33
  npm run backend -- offline
31
34
  npm run frontend
@@ -10,11 +10,12 @@ import {
10
10
  network,
11
11
  } from "../shared/build.ts";
12
12
  import { fixture, recipient } from "../shared/fixture.ts";
13
+ import { createOfflineBuilder } from "../shared/offline-profile.ts";
13
14
  const wasm = await runtime();
14
15
  const mode = process.argv[2] ?? "offline";
15
16
  if (mode === "offline") {
16
17
  const data = fixture(wasm);
17
- const build = createBuilder(wasm, data.sources, loadArtifact);
18
+ const build = createOfflineBuilder(wasm, data.sources, loadArtifact);
18
19
  const deploy = await build(deployOperation(data.wallet));
19
20
  const transfer = await build(
20
21
  transferOperation(data.wallet, data.addDeploy(deploy), recipient),
@@ -12,6 +12,7 @@ import {
12
12
  type Built,
13
13
  } from "../shared/build.ts";
14
14
  import { fixture, recipient } from "../shared/fixture.ts";
15
+ import { createOfflineBuilder } from "../shared/offline-profile.ts";
15
16
  import { attachScripts, signingInputs } from "../shared/signing.ts";
16
17
  const status = document.querySelector("#status")!;
17
18
  const output = document.querySelector("#result")!;
@@ -60,7 +61,7 @@ async function clear() {
60
61
  export async function offline() {
61
62
  const wasm = await runtime();
62
63
  const data = fixture(wasm);
63
- const build = createBuilder(wasm, data.sources, loadArtifact);
64
+ const build = createOfflineBuilder(wasm, data.sources, loadArtifact);
64
65
  const deploy = await build(deployOperation(data.wallet));
65
66
  const transfer = await build(
66
67
  transferOperation(data.wallet, data.addDeploy(deploy), recipient),
@@ -7,6 +7,7 @@ import {
7
7
  isKcc20ArtifactKey,
8
8
  } from "@kaspacom/kcc20-tx-builder/artifacts";
9
9
  import type { Kcc20IndexedCovenantUtxo } from "@kaspacom/kcc20-tx-builder/action-source-resolver";
10
+ import type { AuthenticatedTokenProfileRegistry } from "@kaspacom/kcc20-tx-builder/token-profiles";
10
11
  import type * as Wasm from "../vendor/kaspa/kaspa.js";
11
12
 
12
13
  export const network = "testnet-10";
@@ -74,6 +75,7 @@ export function createBuilder(
74
75
  wasm: Runtime,
75
76
  sources: Sources,
76
77
  loadArtifact: (key: string) => Promise<unknown>,
78
+ options: { tokenProfileRegistry?: AuthenticatedTokenProfileRegistry } = {},
77
79
  ) {
78
80
  for (const key of [
79
81
  "Transaction",
@@ -99,6 +101,7 @@ export function createBuilder(
99
101
  assertKcc20ArtifactScriptHash(key, artifact);
100
102
  return artifact;
101
103
  },
104
+ tokenProfileRegistry: options.tokenProfileRegistry,
102
105
  });
103
106
  return async (operation: Operation): Promise<Built> => {
104
107
  const result = await engine.build(engineInput(operation));
@@ -0,0 +1,27 @@
1
+ import {
2
+ BUNDLED_TOKEN_PROFILES,
3
+ BUNDLED_TOKEN_PROFILE_DIGEST,
4
+ } from "@kaspacom/kcc20-tx-builder/token-profiles.generated";
5
+ import type { AuthenticatedTokenProfileRegistry } from "@kaspacom/kcc20-tx-builder/token-profiles";
6
+ import {
7
+ createBuilder,
8
+ type Runtime,
9
+ type Sources,
10
+ } from "./build.ts";
11
+
12
+ /** Offline fixtures use the same authenticated registry as live construction. */
13
+ export const OFFLINE_EXAMPLE_TOKEN_PROFILE_REGISTRY: AuthenticatedTokenProfileRegistry =
14
+ Object.freeze({
15
+ manifest: BUNDLED_TOKEN_PROFILES,
16
+ sha256: BUNDLED_TOKEN_PROFILE_DIGEST,
17
+ });
18
+
19
+ export function createOfflineBuilder(
20
+ wasm: Runtime,
21
+ sources: Sources,
22
+ loadArtifact: (key: string) => Promise<unknown>,
23
+ ) {
24
+ return createBuilder(wasm, sources, loadArtifact, {
25
+ tokenProfileRegistry: OFFLINE_EXAMPLE_TOKEN_PROFILE_REGISTRY,
26
+ });
27
+ }
@@ -10,7 +10,6 @@ import {
10
10
  } from "@kaspacom/kcc20-tx-builder/sale-common";
11
11
  import type { Kcc20IndexedCovenantUtxo } from "@kaspacom/kcc20-tx-builder/action-source-resolver";
12
12
  import {
13
- createBuilder,
14
13
  deployOperation,
15
14
  network,
16
15
  type Runtime,
@@ -18,6 +17,7 @@ import {
18
17
  } from "./build.ts";
19
18
  import { fixture, owner, recipient } from "./fixture.ts";
20
19
  import { recipes } from "./recipes.ts";
20
+ import { createOfflineBuilder } from "./offline-profile.ts";
21
21
 
22
22
  // These are independent synthetic source snapshots, not a simulated chain ledger.
23
23
  // Never broadcast their transactions or fund their public test-vector addresses.
@@ -26,7 +26,7 @@ export async function recipeFixtures(
26
26
  loadArtifact: (key: string) => Promise<unknown>,
27
27
  ) {
28
28
  const data = fixture(wasm);
29
- const build = createBuilder(wasm, data.sources, loadArtifact);
29
+ const build = createOfflineBuilder(wasm, data.sources, loadArtifact);
30
30
  const options = {
31
31
  network,
32
32
  createdAt: "2026-01-01T00:00:00.000Z",
@@ -416,6 +416,7 @@ export async function recipeFixtures(
416
416
  wallet,
417
417
  {
418
418
  canonicalCovenantId: tokenId,
419
+ tokenUtxo: baseHolder,
419
420
  tokenDecimals: 2,
420
421
  wrappedRootAmount: "0",
421
422
  },
@@ -3,13 +3,13 @@ import { preview } from "vite";
3
3
  import { chromium } from "playwright";
4
4
  import { runtime, loadArtifact } from "../backend/runtime.ts";
5
5
  import {
6
- createBuilder,
7
6
  deployOperation,
8
7
  transferOperation,
9
8
  } from "../shared/build.ts";
10
9
  import { recipeFixtures } from "../shared/recipe-fixtures.ts";
11
10
  import { recipes } from "../shared/recipes.ts";
12
11
  import { fixture, recipient, owner } from "../shared/fixture.ts";
12
+ import { createOfflineBuilder } from "../shared/offline-profile.ts";
13
13
  const server = await preview({
14
14
  root: "frontend",
15
15
  preview: { host: "127.0.0.1", port: 4173, strictPort: true },
@@ -38,7 +38,7 @@ try {
38
38
  );
39
39
  const wasm = await runtime();
40
40
  const data = fixture(wasm);
41
- const build = createBuilder(wasm, data.sources, loadArtifact);
41
+ const build = createOfflineBuilder(wasm, data.sources, loadArtifact);
42
42
  const deploy = await build(deployOperation(data.wallet));
43
43
  const transfer = await build(
44
44
  transferOperation(data.wallet, data.addDeploy(deploy), recipient),
@@ -11,6 +11,7 @@ import {
11
11
  type Runtime,
12
12
  } from "../shared/build.ts";
13
13
  import { fixture, recipient } from "../shared/fixture.ts";
14
+ import { createOfflineBuilder } from "../shared/offline-profile.ts";
14
15
  import { attachScripts, signingInputs } from "../shared/signing.ts";
15
16
  const wasm = await runtime();
16
17
  // Offline construction must not silently fall back to any backend or RPC.
@@ -18,9 +19,21 @@ globalThis.fetch = async () => {
18
19
  throw new Error("Network access forbidden in offline examples");
19
20
  };
20
21
 
22
+ test("production and offline builders use the same bundled profile", async () => {
23
+ const data = fixture(wasm);
24
+ await assert.doesNotReject(
25
+ createBuilder(wasm, data.sources, loadArtifact)(deployOperation(data.wallet)),
26
+ );
27
+ await assert.doesNotReject(
28
+ createOfflineBuilder(wasm, data.sources, loadArtifact)(
29
+ deployOperation(data.wallet),
30
+ ),
31
+ );
32
+ });
33
+
21
34
  test("build deploy and transfer with actual WASM, covenant bindings, amounts and change", async () => {
22
35
  const data = fixture(wasm);
23
- const build = createBuilder(
36
+ const build = createOfflineBuilder(
24
37
  Object.freeze({ ...wasm }),
25
38
  data.sources,
26
39
  loadArtifact,
@@ -61,7 +74,7 @@ test("build deploy and transfer with actual WASM, covenant bindings, amounts and
61
74
  });
62
75
  test("reject missing funding, stale source, wrong owner, network and incompatible artifact/runtime", async () => {
63
76
  const data = fixture(wasm);
64
- const build = createBuilder(wasm, data.sources, loadArtifact);
77
+ const build = createOfflineBuilder(wasm, data.sources, loadArtifact);
65
78
  const operation = deployOperation(data.wallet);
66
79
  const deploy = await build(operation);
67
80
  const holder = data.addDeploy(deploy);
@@ -88,20 +101,20 @@ test("reject missing funding, stale source, wrong owner, network and incompatibl
88
101
  /holder UTXO/,
89
102
  );
90
103
  await assert.rejects(
91
- createBuilder(wasm, fixture(wasm).sources, async () => ({
104
+ createOfflineBuilder(wasm, fixture(wasm).sources, async () => ({
92
105
  script: [1, 2],
93
106
  }))(operation),
94
107
  /hash mismatch/,
95
108
  );
96
109
  assert.throws(
97
- () => createBuilder({} as Runtime, data.sources, loadArtifact),
110
+ () => createOfflineBuilder({} as Runtime, data.sources, loadArtifact),
98
111
  /Incompatible WASM/,
99
112
  );
100
113
  });
101
114
 
102
115
  test("reject conflicting explicit native identity, but retain legacy alias resolution", async () => {
103
116
  const data = fixture(wasm);
104
- const build = createBuilder(wasm, data.sources, loadArtifact);
117
+ const build = createOfflineBuilder(wasm, data.sources, loadArtifact);
105
118
  const holder = data.addDeploy(await build(deployOperation(data.wallet)));
106
119
  const operation = transferOperation(data.wallet, holder, recipient);
107
120
  const entry = data.rows.get(holder.address)![0];
@@ -130,7 +143,7 @@ test("reject conflicting explicit native identity, but retain legacy alias resol
130
143
 
131
144
  test("preserve witnesses outside requested signing inputs and reject malformed signatures", async () => {
132
145
  const data = fixture(wasm);
133
- const build = createBuilder(wasm, data.sources, loadArtifact);
146
+ const build = createOfflineBuilder(wasm, data.sources, loadArtifact);
134
147
  const deploy = await build(deployOperation(data.wallet));
135
148
  const holder = data.addDeploy(deploy);
136
149
  const transfer = await build(
@@ -171,7 +184,7 @@ test("preserve witnesses outside requested signing inputs and reject malformed s
171
184
 
172
185
  test("generic transfer rejects conflicting RPC scripts and KAS amounts", async () => {
173
186
  const data = fixture(wasm),
174
- build = createBuilder(wasm, data.sources, loadArtifact);
187
+ build = createOfflineBuilder(wasm, data.sources, loadArtifact);
175
188
  const holder = data.addDeploy(await build(deployOperation(data.wallet)));
176
189
  const operation = transferOperation(data.wallet, holder, recipient);
177
190
  const row = data.rows.get(holder.address)![0];
@@ -187,7 +200,7 @@ test("reject a present funding input that cannot cover the required KAS", async
187
200
  const data = fixture(wasm);
188
201
  data.rows.get(data.wallet.walletAddress)![0].amount = 1n;
189
202
  await assert.rejects(
190
- createBuilder(
203
+ createOfflineBuilder(
191
204
  wasm,
192
205
  data.sources,
193
206
  loadArtifact,
@@ -1,13 +1,14 @@
1
1
  import { test } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { runtime, loadArtifact } from "../backend/runtime.ts";
4
- import { createBuilder, deployOperation, network } from "../shared/build.ts";
4
+ import { deployOperation, network } from "../shared/build.ts";
5
5
  import { fixture, recipient } from "../shared/fixture.ts";
6
+ import { createOfflineBuilder } from "../shared/offline-profile.ts";
6
7
  import { liveTransferOperation } from "../shared/live-transfer.ts";
7
8
  const wasm = await runtime();
8
9
  async function setup() {
9
10
  const data = fixture(wasm);
10
- const build = createBuilder(wasm, data.sources, loadArtifact);
11
+ const build = createOfflineBuilder(wasm, data.sources, loadArtifact);
11
12
  const holder = data.addDeploy(await build(deployOperation(data.wallet)));
12
13
  const config = {
13
14
  network,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaspacom/kcc20-tx-builder",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Runtime-independent KCC20 covenant transaction and PSKT construction primitives.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "KaspaCom",
@@ -86,7 +86,7 @@
86
86
  "scripts": {
87
87
  "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node scripts/write-cjs-package.mjs",
88
88
  "typecheck": "tsc -p tsconfig.json --noEmit",
89
- "test": "npm run typecheck && npm run build && node test/smoke.mjs && node test/cjs-smoke.cjs && node test/snapshot-plan.mjs",
89
+ "test": "npm run typecheck && npm run build && node test/smoke.mjs && node test/cjs-smoke.cjs && node test/snapshot-plan.mjs && node test/token-profiles.mjs && node test/profile-transfer.mjs",
90
90
  "prepack": "npm test",
91
91
  "test:examples": "npm run verify:compatibility && node scripts/test-examples.mjs --browser",
92
92
  "verify:compatibility": "node scripts/verify-compatibility.mjs",
@@ -1,3 +1,4 @@
1
+ import { tokenPublicKeyHash } from "./token-authorization.js";
1
2
  export interface Kcc20IndexedCovenantUtxo {
2
3
  [field: string]: unknown;
3
4
  txidHex: string;
@@ -125,6 +126,16 @@ export function ownerNativeHolderUtxos(
125
126
  owner: string,
126
127
  ): Kcc20IndexedCovenantUtxo[] {
127
128
  const normalizedOwner = owner.toLowerCase();
129
+ const hashedOwner = /^[a-f0-9]{64}$/.test(normalizedOwner)
130
+ ? Array.from(
131
+ tokenPublicKeyHash(
132
+ Uint8Array.from(normalizedOwner.match(/../g)!, (b) =>
133
+ parseInt(b, 16),
134
+ ),
135
+ ),
136
+ (b) => b.toString(16).padStart(2, "0"),
137
+ ).join("")
138
+ : null;
128
139
  return utxos.filter((utxo) => {
129
140
  const state = utxo.state;
130
141
  const value = amount(state?.["amount"]);
@@ -132,10 +143,17 @@ export function ownerNativeHolderUtxos(
132
143
  value !== null &&
133
144
  value > 0n &&
134
145
  state?.["isMintAuthority"] !== true &&
135
- numberValue(state?.["ownerScheme"] ?? state?.["identifierType"]) === 0 &&
136
- stringValue(
137
- state?.["ownerIdentifier"] ?? state?.["owner"],
138
- )?.toLowerCase() === normalizedOwner
146
+ ((numberValue(state?.["ownerScheme"] ?? state?.["identifierType"]) ===
147
+ 0 &&
148
+ stringValue(
149
+ state?.["ownerIdentifier"] ?? state?.["owner"],
150
+ )?.toLowerCase() === normalizedOwner) ||
151
+ (numberValue(state?.["ownerScheme"] ?? state?.["identifierType"]) ===
152
+ 1 &&
153
+ hashedOwner !== null &&
154
+ stringValue(
155
+ state?.["ownerIdentifier"] ?? state?.["owner"],
156
+ )?.toLowerCase() === hashedOwner))
139
157
  );
140
158
  });
141
159
  }
package/src/artifacts.ts CHANGED
@@ -7,18 +7,20 @@ 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
+ "KCC20SnapshotBootstrap.placeholder.json":
11
+ "3d64211a2b3c5ac24009b9ce59d4e8ecb8f2114dbf0c43b5a060b2ed91704e02",
12
+ "KCC20SnapshotController.placeholder.json":
13
+ "fdc2e396a66a1f708522d68f879ec67d71bd0070c7417b1e7f5dd3a6c8cfda88",
12
14
  [KCC20_CURRENT_NATIVE_ARTIFACT_KEY]:
13
- "4633b082f26adbf14600b767d6e2146e36ce67d63d8202c0281857922c05ac41",
15
+ "dd9cb334cd9e9220b15644782f271ef3ecdba34fd0762010e3d6bc18176837ae",
14
16
  "KCC20FeeTicket.placeholder.json":
15
- "ed81365466ee4c44db1210b3360783af8145f74e49e847848195e09c71ad6930",
17
+ "c0e95cb8e5226462674647f86d5760626b5c38b7bd97a6e2ec9ba61fdef8941f",
16
18
  "KCC20Orderbook.placeholder.json":
17
19
  "ef9fe81ff7aa2dc2df118d8d7cc126aca6f2f9c181b9c2d15727a538b23c6f71",
18
20
  "KCC20Wrapper.placeholder.json":
19
- "f3bdf09be9aa258708a306283a4278a93cfb64f35fc50ed7d316b9b189ef9030",
21
+ "23cbd5473860cab4e63890d24ae1bd6732fce5beaa34f594dad01e845e3e9b9f",
20
22
  "KCC20Vesting.placeholder.json":
21
- "1b84d74aa01b6a50edbff5bf1f9e988a5a6a00ec645458dfd3912ac532b77c33",
23
+ "e32d9f5bde124526475a61075ba46eef41955d4662b1422713860cfbd7a59661",
22
24
  });
23
25
 
24
26
  /**
@@ -31,7 +33,7 @@ export const KCC20_SNAPSHOT_ARTIFACT_PINS = Object.freeze({
31
33
  key: "KCC20SnapshotBootstrap.placeholder.json",
32
34
  contractName: "KCC20SnapshotBootstrap",
33
35
  artifactSha256:
34
- "bc566a964b5feb1c2055d5130af25951142d695472287b888c5537e9a56ee3d2",
36
+ "5f0caea9818f5bd6110023a7f089a0d26f87d3fd0901cb52e24a32bd1853b302",
35
37
  scriptLength: 33718,
36
38
  stateStart: 1,
37
39
  stateLength: 405,
@@ -40,7 +42,7 @@ export const KCC20_SNAPSHOT_ARTIFACT_PINS = Object.freeze({
40
42
  key: "KCC20SnapshotController.placeholder.json",
41
43
  contractName: "KCC20SnapshotController",
42
44
  artifactSha256:
43
- "f0d7b082e0680a90736948cd6658ccbc8600e097d9b4d5568013acdaafb39bf5",
45
+ "8c5d2056bb0a445ffc1a6967238fac46378372b5f357524c6795201699bf2b2f",
44
46
  scriptLength: 15297,
45
47
  stateStart: 1,
46
48
  stateLength: 432,
@@ -49,9 +51,9 @@ export const KCC20_SNAPSHOT_ARTIFACT_PINS = Object.freeze({
49
51
 
50
52
  /** Canonical KCC20 template committed by both snapshot contracts. */
51
53
  export const KCC20_SNAPSHOT_TOKEN_TEMPLATE_PIN = Object.freeze({
52
- hash: "911f0638ccb7368bf36d117f1725073ae7ee487ce8b58ca3e8375051c2d40f6c",
54
+ hash: "5d6cacdf57c734536fcb8a9c833f0318671226727ec77c503a98fc5be59f9e29",
53
55
  prefixLength: 1,
54
- suffixLength: 25439,
56
+ suffixLength: 25264,
55
57
  });
56
58
 
57
59
  export type Kcc20ArtifactKey = keyof typeof KCC20_ARTIFACT_SCRIPT_SHA256;