@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.
- package/CHANGELOG.md +12 -1
- package/README.md +2 -2
- package/RELEASING.md +19 -14
- package/artifacts/ArgentKCC20.reference.json +12428 -0
- package/artifacts/ArgentKCC20Wrapper.placeholder.json +12455 -0
- package/artifacts/KCC20.placeholder.json +1447 -1973
- package/artifacts/KCC20FeeTicket.placeholder.json +816 -1447
- package/artifacts/KCC20Orderbook.placeholder.json +1 -2
- package/artifacts/KCC20SnapshotBootstrap.placeholder.json +101 -100
- package/artifacts/KCC20SnapshotController.placeholder.json +101 -100
- package/artifacts/KCC20Vesting.placeholder.json +427 -429
- package/artifacts/KCC20Wrapper.placeholder.json +733 -734
- package/artifacts/kcc20-snapshot-bootstrap/KCC20SnapshotBootstrap.placeholder.json +101 -100
- package/artifacts/kcc20-snapshot-controller/KCC20SnapshotController.placeholder.json +101 -100
- package/artifacts/snapshot-standards.lock.json +6 -3
- package/artifacts/token-profiles.json +109 -0
- package/artifacts/token-profiles.sha256 +1 -0
- package/dist/action-source-resolver.d.ts.map +1 -1
- package/dist/action-source-resolver.js +11 -2
- package/dist/action-source-resolver.js.map +1 -1
- package/dist/artifacts.d.ts +10 -10
- package/dist/artifacts.d.ts.map +1 -1
- package/dist/artifacts.js +10 -10
- package/dist/artifacts.js.map +1 -1
- package/dist/cjs/action-source-resolver.js +11 -2
- package/dist/cjs/action-source-resolver.js.map +1 -1
- package/dist/cjs/artifacts.d.ts +10 -10
- package/dist/cjs/artifacts.js +10 -10
- package/dist/cjs/artifacts.js.map +1 -1
- package/dist/cjs/engine.d.ts +10 -0
- package/dist/cjs/engine.js +124 -77
- package/dist/cjs/engine.js.map +1 -1
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/profile-transfer.d.ts +60 -0
- package/dist/cjs/profile-transfer.js +248 -0
- package/dist/cjs/profile-transfer.js.map +1 -0
- package/dist/cjs/snapshot-operation.d.ts +0 -1
- package/dist/cjs/snapshot-operation.js +1 -1126
- package/dist/cjs/snapshot-operation.js.map +1 -1
- package/dist/cjs/snapshot-signerless-transactions.d.ts +1 -1
- package/dist/cjs/snapshot-signerless-transactions.js +3 -6
- package/dist/cjs/snapshot-signerless-transactions.js.map +1 -1
- package/dist/cjs/snapshot-signerless.js +5 -3
- package/dist/cjs/snapshot-signerless.js.map +1 -1
- package/dist/cjs/snapshot-verification.d.ts +1 -1
- package/dist/cjs/snapshot-verification.js +16 -12
- package/dist/cjs/snapshot-verification.js.map +1 -1
- package/dist/cjs/token-authorization.d.ts +42 -0
- package/dist/cjs/token-authorization.js +93 -0
- package/dist/cjs/token-authorization.js.map +1 -0
- package/dist/cjs/token-profiles.d.ts +82 -0
- package/dist/cjs/token-profiles.generated.d.ts +3 -0
- package/dist/cjs/token-profiles.generated.js +114 -0
- package/dist/cjs/token-profiles.generated.js.map +1 -0
- package/dist/cjs/token-profiles.js +257 -0
- package/dist/cjs/token-profiles.js.map +1 -0
- package/dist/cjs/transfer-payload.d.ts +9 -0
- package/dist/cjs/transfer-payload.js +37 -0
- package/dist/cjs/transfer-payload.js.map +1 -0
- package/dist/cjs/wrapper-operation.d.ts +1 -0
- package/dist/cjs/wrapper-operation.js +1 -0
- package/dist/cjs/wrapper-operation.js.map +1 -1
- package/dist/engine.d.ts +10 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +125 -78
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/profile-transfer.d.ts +61 -0
- package/dist/profile-transfer.d.ts.map +1 -0
- package/dist/profile-transfer.js +245 -0
- package/dist/profile-transfer.js.map +1 -0
- package/dist/snapshot-operation.d.ts +0 -1
- package/dist/snapshot-operation.d.ts.map +1 -1
- package/dist/snapshot-operation.js +0 -1125
- package/dist/snapshot-operation.js.map +1 -1
- package/dist/snapshot-signerless-transactions.d.ts +1 -1
- package/dist/snapshot-signerless-transactions.d.ts.map +1 -1
- package/dist/snapshot-signerless-transactions.js +3 -6
- package/dist/snapshot-signerless-transactions.js.map +1 -1
- package/dist/snapshot-signerless.d.ts.map +1 -1
- package/dist/snapshot-signerless.js +5 -3
- package/dist/snapshot-signerless.js.map +1 -1
- package/dist/snapshot-verification.d.ts +1 -1
- package/dist/snapshot-verification.d.ts.map +1 -1
- package/dist/snapshot-verification.js +16 -12
- package/dist/snapshot-verification.js.map +1 -1
- package/dist/token-authorization.d.ts +43 -0
- package/dist/token-authorization.d.ts.map +1 -0
- package/dist/token-authorization.js +88 -0
- package/dist/token-authorization.js.map +1 -0
- package/dist/token-profiles.d.ts +83 -0
- package/dist/token-profiles.d.ts.map +1 -0
- package/dist/token-profiles.generated.d.ts +4 -0
- package/dist/token-profiles.generated.d.ts.map +1 -0
- package/dist/token-profiles.generated.js +111 -0
- package/dist/token-profiles.generated.js.map +1 -0
- package/dist/token-profiles.js +245 -0
- package/dist/token-profiles.js.map +1 -0
- package/dist/transfer-payload.d.ts +10 -0
- package/dist/transfer-payload.d.ts.map +1 -0
- package/dist/transfer-payload.js +34 -0
- package/dist/transfer-payload.js.map +1 -0
- package/dist/wrapper-operation.d.ts +1 -0
- package/dist/wrapper-operation.d.ts.map +1 -1
- package/dist/wrapper-operation.js +1 -0
- package/dist/wrapper-operation.js.map +1 -1
- package/docs/API.md +1 -1
- package/docs/COMPATIBILITY.json +24 -33
- package/docs/INTEGRATION.md +2 -2
- package/docs/LAST_CALL.md +50 -0
- package/examples/README.md +5 -2
- package/examples/backend/cli.ts +2 -1
- package/examples/frontend/main.ts +2 -1
- package/examples/shared/build.ts +3 -0
- package/examples/shared/offline-profile.ts +27 -0
- package/examples/shared/recipe-fixtures.ts +3 -2
- package/examples/test/browser.ts +2 -2
- package/examples/test/build.test.ts +21 -8
- package/examples/test/sources.test.ts +3 -2
- package/package.json +2 -2
- package/src/action-source-resolver.ts +22 -4
- package/src/artifacts.ts +12 -10
- package/src/engine.ts +208 -105
- package/src/index.ts +7 -0
- package/src/profile-transfer.ts +425 -0
- package/src/snapshot-operation.ts +59 -1731
- package/src/snapshot-signerless-transactions.ts +8 -17
- package/src/snapshot-signerless.ts +5 -3
- package/src/snapshot-verification.ts +17 -13
- package/src/token-authorization.ts +135 -0
- package/src/token-profiles.generated.ts +112 -0
- package/src/token-profiles.ts +456 -0
- package/src/transfer-payload.ts +45 -0
- package/src/wrapper-operation.ts +2 -0
package/docs/INTEGRATION.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
package/examples/README.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
package/examples/backend/cli.ts
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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),
|
package/examples/shared/build.ts
CHANGED
|
@@ -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 =
|
|
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
|
},
|
package/examples/test/browser.ts
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
() =>
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 {
|
|
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 =
|
|
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.
|
|
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"]) ===
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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":
|
|
11
|
-
|
|
10
|
+
"KCC20SnapshotBootstrap.placeholder.json":
|
|
11
|
+
"3d64211a2b3c5ac24009b9ce59d4e8ecb8f2114dbf0c43b5a060b2ed91704e02",
|
|
12
|
+
"KCC20SnapshotController.placeholder.json":
|
|
13
|
+
"fdc2e396a66a1f708522d68f879ec67d71bd0070c7417b1e7f5dd3a6c8cfda88",
|
|
12
14
|
[KCC20_CURRENT_NATIVE_ARTIFACT_KEY]:
|
|
13
|
-
"
|
|
15
|
+
"dd9cb334cd9e9220b15644782f271ef3ecdba34fd0762010e3d6bc18176837ae",
|
|
14
16
|
"KCC20FeeTicket.placeholder.json":
|
|
15
|
-
"
|
|
17
|
+
"c0e95cb8e5226462674647f86d5760626b5c38b7bd97a6e2ec9ba61fdef8941f",
|
|
16
18
|
"KCC20Orderbook.placeholder.json":
|
|
17
19
|
"ef9fe81ff7aa2dc2df118d8d7cc126aca6f2f9c181b9c2d15727a538b23c6f71",
|
|
18
20
|
"KCC20Wrapper.placeholder.json":
|
|
19
|
-
"
|
|
21
|
+
"23cbd5473860cab4e63890d24ae1bd6732fce5beaa34f594dad01e845e3e9b9f",
|
|
20
22
|
"KCC20Vesting.placeholder.json":
|
|
21
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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: "
|
|
54
|
+
hash: "5d6cacdf57c734536fcb8a9c833f0318671226727ec77c503a98fc5be59f9e29",
|
|
53
55
|
prefixLength: 1,
|
|
54
|
-
suffixLength:
|
|
56
|
+
suffixLength: 25264,
|
|
55
57
|
});
|
|
56
58
|
|
|
57
59
|
export type Kcc20ArtifactKey = keyof typeof KCC20_ARTIFACT_SCRIPT_SHA256;
|