@hardkas/accounts 0.12.0-rc.2 → 0.12.0-rc.21
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/dist/{authorizers-OVWWAL5J.js → authorizers-CP222OTW.js} +1 -1
- package/dist/{chunk-TRVIQTIO.js → chunk-7KKJXU73.js} +84 -29
- package/dist/{chunk-6DPO5V3N.js → chunk-BNF566PS.js} +11 -20
- package/dist/{chunk-6Q4K4A24.js → chunk-CELVD4YG.js} +136 -135
- package/dist/{chunk-YJL7P33W.js → chunk-E73SSWAX.js} +4 -5
- package/dist/{chunk-R5P3WIWR.js → chunk-GM2BY4VS.js} +37 -15
- package/dist/{dev-accounts-B6ZVTPCW.js → dev-accounts-BSWPKAAE.js} +4 -4
- package/dist/index.d.ts +358 -10
- package/dist/index.js +575 -36
- package/dist/internal/wasm-rpc-serialization.d.ts +5 -1
- package/dist/internal/wasm-rpc-serialization.js +1 -1
- package/dist/{resolve-GHZEED5L.js → resolve-KMMTZWC3.js} +3 -1
- package/dist/{signer-backend-W3LNCQA3.js → signer-backend-LHWY7ULI.js} +5 -3
- package/package.json +6 -7
|
@@ -219,7 +219,7 @@ function resolveHardkasAccount(options) {
|
|
|
219
219
|
let alias = nameOrAddress;
|
|
220
220
|
if (alias === "0") alias = "alice";
|
|
221
221
|
if (alias === "1") alias = "bob";
|
|
222
|
-
const accounts = listHardkasAccounts(config);
|
|
222
|
+
const accounts = listHardkasAccounts(config, options.executionTarget);
|
|
223
223
|
const found = accounts.find((a) => a.name === alias);
|
|
224
224
|
if (found) {
|
|
225
225
|
return found;
|
|
@@ -229,17 +229,63 @@ function resolveHardkasAccount(options) {
|
|
|
229
229
|
`Unknown HardKAS account '${nameOrAddress}'. Available accounts: ${available}`
|
|
230
230
|
);
|
|
231
231
|
}
|
|
232
|
-
function
|
|
232
|
+
function assertAccountCompatible(account, target) {
|
|
233
|
+
if (account.kind === "synthetic" && target.mode !== "simulator") {
|
|
234
|
+
throw new AccountNetworkMismatchError({
|
|
235
|
+
expected: `Execution mode 'simulator'`,
|
|
236
|
+
actual: `Execution mode '${target.mode}'`,
|
|
237
|
+
detail: `Account '${account.name}' is a synthetic simulator account and cannot be used with mode '${target.mode}'.`
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
if ("network" in account && account.network && target.network && account.network !== target.network) {
|
|
241
|
+
throw new AccountNetworkMismatchError({
|
|
242
|
+
expected: target.network,
|
|
243
|
+
actual: account.network,
|
|
244
|
+
detail: `Account '${account.name}' is bound to network '${account.network}' but execution target is '${target.network}'.`
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function listHardkasAccounts(config, executionTarget) {
|
|
233
249
|
const accounts = /* @__PURE__ */ new Map();
|
|
250
|
+
let targetMode = "localnet";
|
|
251
|
+
if (executionTarget) {
|
|
252
|
+
targetMode = executionTarget.mode;
|
|
253
|
+
} else {
|
|
254
|
+
const execConfig = config?.execution;
|
|
255
|
+
if (execConfig) {
|
|
256
|
+
if (execConfig.mode) {
|
|
257
|
+
targetMode = execConfig.mode;
|
|
258
|
+
} else if (execConfig.default) {
|
|
259
|
+
const targetName = execConfig.default;
|
|
260
|
+
const target = execConfig.targets?.[targetName];
|
|
261
|
+
if (target?.mode) {
|
|
262
|
+
targetMode = target.mode;
|
|
263
|
+
} else if (targetName === "simulator") {
|
|
264
|
+
targetMode = "simulator";
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} else if (config?.defaultNetwork === "simulated") {
|
|
268
|
+
targetMode = "simulator";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
234
271
|
const detAccounts = createDeterministicAccounts();
|
|
235
272
|
for (const det of detAccounts) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
273
|
+
if (targetMode === "simulator") {
|
|
274
|
+
accounts.set(det.name, {
|
|
275
|
+
name: det.name,
|
|
276
|
+
kind: "synthetic",
|
|
277
|
+
executionMode: "simulator",
|
|
278
|
+
address: `kaspa:sim_${det.name}`,
|
|
279
|
+
evmAddress: det.evmAddress
|
|
280
|
+
});
|
|
281
|
+
} else {
|
|
282
|
+
accounts.set(det.name, {
|
|
283
|
+
name: det.name,
|
|
284
|
+
kind: "kaspa",
|
|
285
|
+
network: "simnet",
|
|
286
|
+
address: det.address
|
|
287
|
+
});
|
|
288
|
+
}
|
|
243
289
|
}
|
|
244
290
|
const workspaceRoot = config?.cwd || process.cwd();
|
|
245
291
|
const devAccountsDir = path2.join(workspaceRoot, ".hardkas", "dev-accounts");
|
|
@@ -255,13 +301,23 @@ function listHardkasAccounts(config) {
|
|
|
255
301
|
if (!keystore.metadata?.network) {
|
|
256
302
|
throw new AccountNetworkMismatchError({ expected: "known network", actual: "undefined", detail: `at ${path2.join(devAccountsDir, file)}` });
|
|
257
303
|
}
|
|
258
|
-
|
|
259
|
-
name,
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
304
|
+
if (targetMode === "simulator") {
|
|
305
|
+
accounts.set(name, {
|
|
306
|
+
name,
|
|
307
|
+
kind: "synthetic",
|
|
308
|
+
executionMode: "simulator",
|
|
309
|
+
address: `kaspa:sim_${name}`,
|
|
310
|
+
keystorePath: path2.join(devAccountsDir, file)
|
|
311
|
+
});
|
|
312
|
+
} else {
|
|
313
|
+
accounts.set(name, {
|
|
314
|
+
name,
|
|
315
|
+
kind: "kaspa",
|
|
316
|
+
network: keystore.metadata.network,
|
|
317
|
+
address: keystore.payload?.address || keystore.metadata?.address,
|
|
318
|
+
keystorePath: path2.join(devAccountsDir, file)
|
|
319
|
+
});
|
|
320
|
+
}
|
|
265
321
|
}
|
|
266
322
|
} catch (e) {
|
|
267
323
|
if (e instanceof AccountNetworkMismatchError) throw e;
|
|
@@ -275,19 +331,15 @@ function listHardkasAccounts(config) {
|
|
|
275
331
|
const data = fs2.readFileSync(keystoreJsonPath, "utf-8");
|
|
276
332
|
const ks = JSON.parse(data);
|
|
277
333
|
for (const [name, acc] of Object.entries(ks)) {
|
|
278
|
-
const existing = accounts.get(name);
|
|
279
|
-
const configKind = acc.type === "simulated" ? "synthetic" : "kaspa";
|
|
280
|
-
if (existing && existing.kind !== configKind) {
|
|
281
|
-
console.error(`COLLISION DETECTED for ${name}. existing:`, existing, `configKind:`, configKind, `workspaceRoot:`, workspaceRoot, `keystoreJsonPath:`, keystoreJsonPath);
|
|
282
|
-
throw new CrossWorldAccountCollisionError({ accountId: name, worlds: [existing.kind, configKind] });
|
|
283
|
-
}
|
|
284
334
|
if (acc.type === "simulated") {
|
|
285
|
-
|
|
286
|
-
name,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
335
|
+
if (targetMode === "simulator") {
|
|
336
|
+
accounts.set(name, {
|
|
337
|
+
name,
|
|
338
|
+
kind: "synthetic",
|
|
339
|
+
executionMode: "simulator",
|
|
340
|
+
address: acc.address
|
|
341
|
+
});
|
|
342
|
+
}
|
|
291
343
|
} else {
|
|
292
344
|
if (!acc.network) {
|
|
293
345
|
throw new AccountNetworkMismatchError({ expected: "known network", actual: "undefined", detail: "in keystore.json" });
|
|
@@ -373,7 +425,8 @@ async function resolveHardkasAccountAddress(accountOrAddress, config, context =
|
|
|
373
425
|
}
|
|
374
426
|
if (!accountOrAddress.startsWith("kaspa:sim_")) {
|
|
375
427
|
try {
|
|
376
|
-
const
|
|
428
|
+
const { loadKaspaWasm } = await import("./signer-backend-LHWY7ULI.js");
|
|
429
|
+
const kaspa = await loadKaspaWasm();
|
|
377
430
|
try {
|
|
378
431
|
if (typeof kaspa.Address === "function" || kaspa.Address) {
|
|
379
432
|
new kaspa.Address(accountOrAddress);
|
|
@@ -387,6 +440,7 @@ async function resolveHardkasAccountAddress(accountOrAddress, config, context =
|
|
|
387
440
|
}
|
|
388
441
|
} catch (e) {
|
|
389
442
|
if (e instanceof Error && e.code === "HARDKAS_INVALID_ADDRESS") throw e;
|
|
443
|
+
if (e instanceof Error && e.code === "WASM_TOOLCHAIN_INTEGRITY_FAILED") throw e;
|
|
390
444
|
if (e instanceof Error && (e.code === "ERR_MODULE_NOT_FOUND" || (e instanceof Error ? e instanceof Error ? e.message : String(e) : String(e)).includes("Cannot find module") || (e instanceof Error ? e instanceof Error ? e.message : String(e) : String(e)).includes("kaspa-wasm"))) {
|
|
391
445
|
const err = new Error(
|
|
392
446
|
"ADDRESS_VALIDATOR_UNAVAILABLE: The Kaspa address validator backend is not available."
|
|
@@ -450,6 +504,7 @@ export {
|
|
|
450
504
|
listRealDevAccounts,
|
|
451
505
|
resolveRealAccountOrAddress,
|
|
452
506
|
resolveHardkasAccount,
|
|
507
|
+
assertAccountCompatible,
|
|
453
508
|
listHardkasAccounts,
|
|
454
509
|
resolveHardkasAccountAddress,
|
|
455
510
|
describeAccount
|
|
@@ -2,18 +2,14 @@
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import { pathToFileURL } from "url";
|
|
5
|
+
import { loadManagedKaspaWasmSync } from "@hardkas/core";
|
|
6
|
+
function loadKaspaWasmSync() {
|
|
7
|
+
return loadManagedKaspaWasmSync();
|
|
8
|
+
}
|
|
5
9
|
async function loadKaspaWasm(config) {
|
|
6
|
-
const provider = config?.provider || "
|
|
7
|
-
if (provider === "
|
|
8
|
-
|
|
9
|
-
return await import("kaspa-wasm");
|
|
10
|
-
} catch (error) {
|
|
11
|
-
const err = new Error(
|
|
12
|
-
"SIGNER_BACKEND_UNAVAILABLE: Official Kaspa WASM backend is required to sign transactions.\nInstall it via: npm install kaspa-wasm"
|
|
13
|
-
);
|
|
14
|
-
err.code = "SIGNER_BACKEND_UNAVAILABLE";
|
|
15
|
-
throw err;
|
|
16
|
-
}
|
|
10
|
+
const provider = config?.provider || "managed";
|
|
11
|
+
if (provider === "managed") {
|
|
12
|
+
return loadKaspaWasmSync();
|
|
17
13
|
}
|
|
18
14
|
if (provider === "local" || provider === "release-asset") {
|
|
19
15
|
if (!config?.path) {
|
|
@@ -52,13 +48,7 @@ async function loadKaspaWasm(config) {
|
|
|
52
48
|
}
|
|
53
49
|
}
|
|
54
50
|
function detectCapabilities(sdk) {
|
|
55
|
-
|
|
56
|
-
if (sdk.createTransaction && sdk.createTransaction.length >= 8) {
|
|
57
|
-
v1 = true;
|
|
58
|
-
}
|
|
59
|
-
if (sdk.createV1Transaction || sdk.Transaction && sdk.Transaction.prototype && !!Object.getOwnPropertyDescriptor(sdk.Transaction.prototype, "storageMass")) {
|
|
60
|
-
v1 = true;
|
|
61
|
-
}
|
|
51
|
+
const v1 = !!(sdk?.Transaction?.prototype && Object.getOwnPropertyDescriptor(sdk.Transaction.prototype, "storageMass"));
|
|
62
52
|
return { transactionV1Signing: v1 };
|
|
63
53
|
}
|
|
64
54
|
async function getKaspaSigningBackendStatus(config) {
|
|
@@ -67,11 +57,11 @@ async function getKaspaSigningBackendStatus(config) {
|
|
|
67
57
|
return {
|
|
68
58
|
available: true,
|
|
69
59
|
name: "Kaspa WASM SDK",
|
|
70
|
-
version: sdk.version
|
|
60
|
+
version: typeof sdk.version === "function" ? String(sdk.version()) : "unknown",
|
|
71
61
|
capabilities: detectCapabilities(sdk)
|
|
72
62
|
};
|
|
73
63
|
} catch (error) {
|
|
74
|
-
if (error.code === "WASM_RELEASE_ASSET_NOT_FOUND") {
|
|
64
|
+
if (error.code === "WASM_RELEASE_ASSET_NOT_FOUND" || error.code === "WASM_TOOLCHAIN_INTEGRITY_FAILED") {
|
|
75
65
|
throw error;
|
|
76
66
|
}
|
|
77
67
|
return {
|
|
@@ -84,6 +74,7 @@ async function getKaspaSigningBackendStatus(config) {
|
|
|
84
74
|
}
|
|
85
75
|
|
|
86
76
|
export {
|
|
77
|
+
loadKaspaWasmSync,
|
|
87
78
|
loadKaspaWasm,
|
|
88
79
|
detectCapabilities,
|
|
89
80
|
getKaspaSigningBackendStatus
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseWasmTxToRpc
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import {
|
|
5
|
-
resolveHardkasAccount
|
|
6
|
-
} from "./chunk-TRVIQTIO.js";
|
|
3
|
+
} from "./chunk-GM2BY4VS.js";
|
|
7
4
|
import {
|
|
8
5
|
loadKaspaWasm
|
|
9
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-BNF566PS.js";
|
|
7
|
+
import {
|
|
8
|
+
resolveHardkasAccount
|
|
9
|
+
} from "./chunk-7KKJXU73.js";
|
|
10
10
|
import {
|
|
11
11
|
KeystoreManager
|
|
12
12
|
} from "./chunk-YLG2NIAU.js";
|
|
@@ -15,10 +15,87 @@ import {
|
|
|
15
15
|
import fs from "fs";
|
|
16
16
|
import path from "path";
|
|
17
17
|
import crypto from "crypto";
|
|
18
|
-
import { deterministicCompare } from "@hardkas/core";
|
|
18
|
+
import { deterministicCompare, getNetworkPrefix as getNetworkPrefix2 } from "@hardkas/core";
|
|
19
19
|
|
|
20
20
|
// src/kaspa-wasm-signer.ts
|
|
21
21
|
import { calculateContentHash } from "@hardkas/artifacts";
|
|
22
|
+
import { getNetworkPrefix } from "@hardkas/core";
|
|
23
|
+
|
|
24
|
+
// src/wasm-transaction.ts
|
|
25
|
+
function assertValueConservation(input) {
|
|
26
|
+
const inputs = input.inputAmounts.reduce((a, b) => a + b, 0n);
|
|
27
|
+
const outputs = input.outputAmounts.reduce((a, b) => a + b, 0n);
|
|
28
|
+
if (input.feeSompi < 0n || inputs - outputs !== input.feeSompi) {
|
|
29
|
+
const err = new Error(
|
|
30
|
+
`TX_VALUE_NOT_CONSERVED: inputs ${inputs} - outputs ${outputs} = ${inputs - outputs} sompi, but the plan's fee is ${input.feeSompi}. Refusing to build a transaction whose difference would be paid as fee.`
|
|
31
|
+
);
|
|
32
|
+
err.code = "TX_VALUE_NOT_CONSERVED";
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function createBalancedTransaction(sdk, input) {
|
|
37
|
+
assertValueConservation({
|
|
38
|
+
inputAmounts: input.utxos.map((u) => u.utxoEntry.amount),
|
|
39
|
+
outputAmounts: input.outputs.map((o) => o.amountSompi),
|
|
40
|
+
feeSompi: input.feeSompi
|
|
41
|
+
});
|
|
42
|
+
return sdk.createTransaction(
|
|
43
|
+
[...input.utxos],
|
|
44
|
+
input.outputs.map((o) => o.output),
|
|
45
|
+
input.feeSompi
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
function createScriptTransaction(sdk, input) {
|
|
49
|
+
assertValueConservation({
|
|
50
|
+
inputAmounts: input.utxos.map((u) => u.utxoEntry.amount),
|
|
51
|
+
outputAmounts: input.outputs.map((o) => o.value),
|
|
52
|
+
feeSompi: input.feeSompi
|
|
53
|
+
});
|
|
54
|
+
return new sdk.Transaction({
|
|
55
|
+
version: 0,
|
|
56
|
+
inputs: input.utxos.map((u) => ({
|
|
57
|
+
previousOutpoint: { transactionId: u.outpoint.transactionId, index: u.outpoint.index },
|
|
58
|
+
signatureScript: "",
|
|
59
|
+
sequence: 0n,
|
|
60
|
+
sigOpCount: 1,
|
|
61
|
+
utxo: {
|
|
62
|
+
address: u.address,
|
|
63
|
+
outpoint: { transactionId: u.outpoint.transactionId, index: u.outpoint.index },
|
|
64
|
+
amount: u.utxoEntry.amount,
|
|
65
|
+
scriptPublicKey: u.utxoEntry.scriptPublicKey,
|
|
66
|
+
blockDaaScore: u.utxoEntry.blockDaaScore,
|
|
67
|
+
isCoinbase: u.utxoEntry.isCoinbase
|
|
68
|
+
}
|
|
69
|
+
})),
|
|
70
|
+
outputs: [...input.outputs],
|
|
71
|
+
lockTime: 0n,
|
|
72
|
+
subnetworkId: "0000000000000000000000000000000000000000",
|
|
73
|
+
gas: 0n,
|
|
74
|
+
payload: ""
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function toWasmScriptPublicKey(sdk, spk) {
|
|
78
|
+
if (spk && typeof spk === "object") {
|
|
79
|
+
const o = spk;
|
|
80
|
+
return new sdk.ScriptPublicKey(Number(o.version ?? 0), String(o.scriptPublicKey || o.script || ""));
|
|
81
|
+
}
|
|
82
|
+
const hex = String(spk ?? "");
|
|
83
|
+
if (/^[0-9a-fA-F]{72}$/.test(hex) && hex.startsWith("0000")) {
|
|
84
|
+
return new sdk.ScriptPublicKey(0, hex.slice(4));
|
|
85
|
+
}
|
|
86
|
+
return new sdk.ScriptPublicKey(0, hex);
|
|
87
|
+
}
|
|
88
|
+
function planOutputsWithChange(plan) {
|
|
89
|
+
const all = [...plan.outputs];
|
|
90
|
+
if (plan.change && BigInt(plan.change.amountSompi) > 0n) all.push(plan.change);
|
|
91
|
+
return all.map((o) => {
|
|
92
|
+
if (!o.address) throw new Error("Output is missing address.");
|
|
93
|
+
const amount = BigInt(o.amountSompi);
|
|
94
|
+
return { amountSompi: amount, output: { address: o.address, amount } };
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/kaspa-wasm-signer.ts
|
|
22
99
|
var KaspaWasmPrivateKeySigner = class {
|
|
23
100
|
constructor(options) {
|
|
24
101
|
this.options = options;
|
|
@@ -28,7 +105,7 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
28
105
|
async signTxPlan(input) {
|
|
29
106
|
const plan = input.planArtifact;
|
|
30
107
|
const sdk = await loadKaspaWasm(this.options.wasmConfig);
|
|
31
|
-
const { detectCapabilities } = await import("./signer-backend-
|
|
108
|
+
const { detectCapabilities } = await import("./signer-backend-LHWY7ULI.js");
|
|
32
109
|
const capabilities = detectCapabilities(sdk);
|
|
33
110
|
if (plan.computeBudget !== void 0 || plan.outputs.some((o) => o.covenant)) {
|
|
34
111
|
if (!capabilities.transactionV1Signing) {
|
|
@@ -56,7 +133,7 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
56
133
|
if (!pkValue && account.keystorePath) {
|
|
57
134
|
try {
|
|
58
135
|
const KeystoreManager2 = (await import("./keystore-CMWEKGBF.js")).KeystoreManager;
|
|
59
|
-
const DEV_ACCOUNTS_PASSWORD2 = (await import("./dev-accounts-
|
|
136
|
+
const DEV_ACCOUNTS_PASSWORD2 = (await import("./dev-accounts-BSWPKAAE.js")).DEV_ACCOUNTS_PASSWORD;
|
|
60
137
|
const keystore = await KeystoreManager2.loadEncryptedKeystore(account.keystorePath);
|
|
61
138
|
const unlock = await KeystoreManager2.decryptEncryptedKeystore(keystore, DEV_ACCOUNTS_PASSWORD2);
|
|
62
139
|
if (unlock.success && unlock.payload) {
|
|
@@ -75,8 +152,8 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
75
152
|
err.code = "INVALID_PRIVATE_KEY_MATERIAL";
|
|
76
153
|
throw err;
|
|
77
154
|
}
|
|
78
|
-
const expectedAddress = new sdk.PrivateKey(pkValue).toKeypair().toAddress(plan.networkId || "simnet").toString();
|
|
79
|
-
const { PrivateKeyAuthorizer } = await import("./authorizers-
|
|
155
|
+
const expectedAddress = new sdk.PrivateKey(pkValue).toKeypair().toAddress(getNetworkPrefix(plan.networkId || "simnet")).toString();
|
|
156
|
+
const { PrivateKeyAuthorizer } = await import("./authorizers-CP222OTW.js");
|
|
80
157
|
const sourceInputs = plan.inputs || plan.selectedUtxos || [];
|
|
81
158
|
for (let i = 0; i < numInputs; i++) {
|
|
82
159
|
if (!authorizers[i]) {
|
|
@@ -93,7 +170,6 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
93
170
|
}
|
|
94
171
|
}
|
|
95
172
|
try {
|
|
96
|
-
console.log("DEBUG PLAN INPUTS:", JSON.stringify(plan.inputs || plan.selectedUtxos, null, 2));
|
|
97
173
|
const sourceInputs = plan.inputs || plan.selectedUtxos || [];
|
|
98
174
|
const utxos = sourceInputs.map((u) => {
|
|
99
175
|
if (!u.outpoint.transactionId || u.outpoint.index === void 0) {
|
|
@@ -105,15 +181,27 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
105
181
|
"UTXO is missing scriptPublicKey. Real signing flows must never fabricate cryptographic state."
|
|
106
182
|
);
|
|
107
183
|
}
|
|
184
|
+
let spkHex = "";
|
|
185
|
+
let spkVersion = 0;
|
|
186
|
+
if (typeof spk === "object" && spk !== null) {
|
|
187
|
+
spkVersion = Number(spk.version ?? 0);
|
|
188
|
+
spkHex = String(spk.scriptPublicKey || spk.script || "");
|
|
189
|
+
} else {
|
|
190
|
+
spkHex = String(spk);
|
|
191
|
+
if (/^[0-9a-fA-F]{72}$/.test(spkHex) && spkHex.startsWith("0000")) {
|
|
192
|
+
spkVersion = parseInt(spkHex.slice(0, 4), 16) || 0;
|
|
193
|
+
spkHex = spkHex.slice(4);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
108
196
|
return {
|
|
109
|
-
address: plan.from.address,
|
|
197
|
+
address: u.address || plan.from.address,
|
|
110
198
|
outpoint: {
|
|
111
199
|
transactionId: u.outpoint.transactionId,
|
|
112
200
|
index: u.outpoint.index
|
|
113
201
|
},
|
|
114
202
|
utxoEntry: {
|
|
115
203
|
amount: BigInt(u.amountSompi),
|
|
116
|
-
scriptPublicKey: new sdk.ScriptPublicKey(
|
|
204
|
+
scriptPublicKey: new sdk.ScriptPublicKey(spkVersion, spkHex),
|
|
117
205
|
blockDaaScore: BigInt(u.blockDaaScore || "0"),
|
|
118
206
|
isCoinbase: !!u.isCoinbase
|
|
119
207
|
}
|
|
@@ -121,35 +209,28 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
121
209
|
});
|
|
122
210
|
const priorityFee = BigInt(plan.estimatedFeeSompi);
|
|
123
211
|
const createFreshTx = () => {
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
amountSompi: plan.change.amountSompi
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
const wasmOutputs = allOutputs.map((o, idx) => {
|
|
137
|
-
if (!o.address) throw new Error("Output is missing address.");
|
|
138
|
-
if (o.covenant && o.covenant.covenantId) {
|
|
139
|
-
const hash = new sdk.Hash(o.covenant.covenantId);
|
|
140
|
-
const binding = new sdk.CovenantBinding(o.covenant.authorizingInput, hash);
|
|
141
|
-
return sdk.PaymentOutput.withCovenant(new sdk.Address(o.address), BigInt(o.amountSompi), binding);
|
|
142
|
-
}
|
|
143
|
-
return new sdk.PaymentOutput(new sdk.Address(o.address), BigInt(o.amountSompi));
|
|
212
|
+
const isV1 = plan.txVersion === 1;
|
|
213
|
+
if (isV1 && !capabilities.transactionV1Signing) {
|
|
214
|
+
throw new Error("Transaction V1 signing is not supported by the installed kaspa-wasm version");
|
|
215
|
+
}
|
|
216
|
+
const allOutputs = [...plan.outputs];
|
|
217
|
+
if (plan.change && BigInt(plan.change.amountSompi) > 0n) {
|
|
218
|
+
allOutputs.push({
|
|
219
|
+
address: plan.change.address,
|
|
220
|
+
amountSompi: plan.change.amountSompi
|
|
144
221
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
222
|
+
}
|
|
223
|
+
const outputs = allOutputs.map((o) => {
|
|
224
|
+
if (!o.address) throw new Error("Output is missing address.");
|
|
225
|
+
const amount = BigInt(o.amountSompi);
|
|
226
|
+
if (isV1 && o.covenant && o.covenant.covenantId) {
|
|
227
|
+
const binding = new sdk.CovenantBinding(o.covenant.authorizingInput, new sdk.Hash(o.covenant.covenantId));
|
|
228
|
+
return { amountSompi: amount, output: sdk.PaymentOutput.withCovenant(new sdk.Address(o.address), amount, binding) };
|
|
229
|
+
}
|
|
230
|
+
return { amountSompi: amount, output: new sdk.PaymentOutput(new sdk.Address(o.address), amount) };
|
|
231
|
+
});
|
|
232
|
+
const unsignedTx = createBalancedTransaction(sdk, { utxos, outputs, feeSompi: priorityFee });
|
|
233
|
+
if (isV1) {
|
|
153
234
|
const genesisGroups = /* @__PURE__ */ new Map();
|
|
154
235
|
allOutputs.forEach((o, idx) => {
|
|
155
236
|
if (o.covenant && !o.covenant.covenantId) {
|
|
@@ -168,29 +249,6 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
168
249
|
if (plan.storageMass !== void 0) {
|
|
169
250
|
unsignedTx.storageMass = BigInt(plan.storageMass);
|
|
170
251
|
}
|
|
171
|
-
} else {
|
|
172
|
-
const allOutputs = [...plan.outputs];
|
|
173
|
-
if (plan.change) {
|
|
174
|
-
allOutputs.push({
|
|
175
|
-
address: plan.change.address,
|
|
176
|
-
amountSompi: plan.change.amountSompi
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
const wasmOutputs = allOutputs.map((o) => {
|
|
180
|
-
if (!o.address) throw new Error("Output is missing address.");
|
|
181
|
-
return {
|
|
182
|
-
address: o.address,
|
|
183
|
-
amount: BigInt(o.amountSompi)
|
|
184
|
-
};
|
|
185
|
-
});
|
|
186
|
-
console.log("DEBUG: Calling V0 createTransaction with:", { utxos: utxos.length, wasmOutputs: wasmOutputs.length, priorityFee });
|
|
187
|
-
const dummyChange = plan.change?.address || plan.from.address;
|
|
188
|
-
unsignedTx = sdk.createTransaction(
|
|
189
|
-
utxos,
|
|
190
|
-
wasmOutputs,
|
|
191
|
-
dummyChange,
|
|
192
|
-
priorityFee
|
|
193
|
-
);
|
|
194
252
|
}
|
|
195
253
|
const inputs = unsignedTx.inputs;
|
|
196
254
|
for (let i = 0; i < inputs.length; i++) {
|
|
@@ -202,7 +260,6 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
202
260
|
inputs[i].sigOpCount = 0;
|
|
203
261
|
}
|
|
204
262
|
}
|
|
205
|
-
console.log("DEBUG createFreshTx RETURNING:", unsignedTx?.constructor?.name);
|
|
206
263
|
return unsignedTx;
|
|
207
264
|
};
|
|
208
265
|
const inputOverrides = {};
|
|
@@ -244,10 +301,7 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
244
301
|
}
|
|
245
302
|
const finalTx = createFreshTx();
|
|
246
303
|
const signedTx = finalTx;
|
|
247
|
-
const
|
|
248
|
-
console.log("DEBUG wasmTxStr:", wasmTxStr);
|
|
249
|
-
console.log("DEBUG: about to parseWasmTxToRpc");
|
|
250
|
-
const rpcTx = parseWasmTxToRpc(wasmTxStr, signedTx, inputOverrides, plan);
|
|
304
|
+
const rpcTx = parseWasmTxToRpc(signedTx.serializeToObject(), signedTx, inputOverrides, plan);
|
|
251
305
|
const rawTx = JSON.stringify(rpcTx);
|
|
252
306
|
return {
|
|
253
307
|
signatureKind: "kaspa",
|
|
@@ -263,8 +317,6 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
263
317
|
}
|
|
264
318
|
};
|
|
265
319
|
} catch (error) {
|
|
266
|
-
console.error("DEBUG SIGNING ERROR:", error);
|
|
267
|
-
console.error("DEBUG STACK:", error?.stack || "NO STACK (raw panic)");
|
|
268
320
|
throw new Error(
|
|
269
321
|
`Kaspa WASM signing failed: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error, Object.getOwnPropertyNames(error))}`
|
|
270
322
|
);
|
|
@@ -290,6 +342,8 @@ async function ensureDevAccounts(workspaceDir) {
|
|
|
290
342
|
}
|
|
291
343
|
await getOrCreateDevAccount(workspaceDir, 0, "alice");
|
|
292
344
|
await getOrCreateDevAccount(workspaceDir, 1, "bob");
|
|
345
|
+
await getOrCreateDevAccount(workspaceDir, 2, "carol");
|
|
346
|
+
await getOrCreateDevAccount(workspaceDir, 3, "dave");
|
|
293
347
|
}
|
|
294
348
|
async function getOrCreateDevAccount(workspaceDir, index, alias) {
|
|
295
349
|
const devAccountsDir = path.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
@@ -314,70 +368,12 @@ async function getOrCreateDevAccount(workspaceDir, index, alias) {
|
|
|
314
368
|
const seedString = `${SIMNET_DETERMINISTIC_SEED}-${index}`;
|
|
315
369
|
const privateKeyHex = crypto.createHash("sha256").update(seedString).digest("hex");
|
|
316
370
|
const network = "simnet";
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
let kaspaWasm;
|
|
324
|
-
try {
|
|
325
|
-
kaspaWasm = await import(
|
|
326
|
-
/* @vite-ignore */
|
|
327
|
-
"kaspa-wasm"
|
|
328
|
-
);
|
|
329
|
-
} catch (e) {
|
|
330
|
-
console.warn(`
|
|
331
|
-
[Warning] kaspa-wasm is not installed. Required for simnet.`);
|
|
332
|
-
return { address: "", privateKey: "", publicKey: "" };
|
|
333
|
-
}
|
|
334
|
-
const privKey = new kaspaWasm.PrivateKey(privateKeyHex);
|
|
335
|
-
const kp = privKey.toKeypair();
|
|
336
|
-
address = kp.toAddress(network).toString();
|
|
337
|
-
publicKey = kp.publicKey;
|
|
338
|
-
privateKey = privateKeyHex;
|
|
339
|
-
} else {
|
|
340
|
-
let sdkModule;
|
|
341
|
-
try {
|
|
342
|
-
sdkModule = await import(
|
|
343
|
-
/* @vite-ignore */
|
|
344
|
-
"@kaspa/core-lib"
|
|
345
|
-
);
|
|
346
|
-
} catch (e) {
|
|
347
|
-
console.warn(`
|
|
348
|
-
[Warning] @kaspa/core-lib is not installed.`);
|
|
349
|
-
return { address: "", privateKey: "", publicKey: "" };
|
|
350
|
-
}
|
|
351
|
-
const sdk = sdkModule.default || sdkModule;
|
|
352
|
-
if (typeof sdk.initRuntime === "function") {
|
|
353
|
-
await sdk.initRuntime();
|
|
354
|
-
}
|
|
355
|
-
const privKey = new sdk.PrivateKey(privateKeyHex);
|
|
356
|
-
const pubKey = privKey.toPublicKey();
|
|
357
|
-
try {
|
|
358
|
-
address = pubKey.toAddress(network).toString();
|
|
359
|
-
} catch (e) {
|
|
360
|
-
const msg = e instanceof Error ? e instanceof Error ? e instanceof Error ? e.message : String(e) : String(e) : String(e);
|
|
361
|
-
if (msg.includes("Second argument must be") || msg.includes("Unsupported")) {
|
|
362
|
-
const err = new Error("DEV_ACCOUNT_BACKEND_UNSUPPORTED_NETWORK");
|
|
363
|
-
err.code = "DEV_ACCOUNT_BACKEND_UNSUPPORTED_NETWORK";
|
|
364
|
-
throw err;
|
|
365
|
-
}
|
|
366
|
-
throw e;
|
|
367
|
-
}
|
|
368
|
-
publicKey = pubKey.toString();
|
|
369
|
-
privateKey = privKey.toString();
|
|
370
|
-
}
|
|
371
|
-
} catch (e) {
|
|
372
|
-
const msg = e instanceof Error ? e instanceof Error ? e instanceof Error ? e.message : String(e) : String(e) : String(e);
|
|
373
|
-
if (msg === "DEV_ACCOUNT_BACKEND_UNSUPPORTED_NETWORK") {
|
|
374
|
-
throw e;
|
|
375
|
-
}
|
|
376
|
-
console.warn(`
|
|
377
|
-
[Warning] Could not generate dev account '${alias}'.
|
|
378
|
-
${msg}`);
|
|
379
|
-
return { address: "", privateKey: "", publicKey: "" };
|
|
380
|
-
}
|
|
371
|
+
const { loadKaspaWasm: loadKaspaWasm2 } = await import("./signer-backend-LHWY7ULI.js");
|
|
372
|
+
const kaspaWasm = await loadKaspaWasm2();
|
|
373
|
+
const kp = new kaspaWasm.PrivateKey(privateKeyHex).toKeypair();
|
|
374
|
+
const address = kp.toAddress(getNetworkPrefix2(network)).toString();
|
|
375
|
+
const publicKey = kp.publicKey;
|
|
376
|
+
const privateKey = privateKeyHex;
|
|
381
377
|
const accountData = {
|
|
382
378
|
address,
|
|
383
379
|
privateKey,
|
|
@@ -446,6 +442,11 @@ async function createDevSigner(workspaceDir, accountNameOrAddress) {
|
|
|
446
442
|
}
|
|
447
443
|
|
|
448
444
|
export {
|
|
445
|
+
assertValueConservation,
|
|
446
|
+
createBalancedTransaction,
|
|
447
|
+
createScriptTransaction,
|
|
448
|
+
toWasmScriptPublicKey,
|
|
449
|
+
planOutputsWithChange,
|
|
449
450
|
DEV_ACCOUNTS_PASSWORD,
|
|
450
451
|
ensureDevAccounts,
|
|
451
452
|
getOrCreateDevAccount,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/authorizers.ts
|
|
2
|
+
import { getNetworkPrefix } from "@hardkas/core";
|
|
2
3
|
var StaticSignatureScriptAuthorizer = class {
|
|
3
4
|
constructor(signatureScript) {
|
|
4
5
|
this.signatureScript = signatureScript;
|
|
@@ -30,7 +31,7 @@ var PrivateKeyAuthorizer = class {
|
|
|
30
31
|
const { wasm, wasmTransaction, inputIndex } = ctx;
|
|
31
32
|
const privateKey = new wasm.PrivateKey(this.privateKeyHex);
|
|
32
33
|
const networkId = context.plan.networkId || "simnet";
|
|
33
|
-
const expectedAddress = privateKey.toKeypair().toAddress(networkId).toString();
|
|
34
|
+
const expectedAddress = privateKey.toKeypair().toAddress(getNetworkPrefix(networkId)).toString();
|
|
34
35
|
if (planInput.address && expectedAddress !== planInput.address) {
|
|
35
36
|
throw new Error(
|
|
36
37
|
`PRIVATE_KEY_DOES_NOT_CONTROL_INPUT: The provided private key for account '${this.accountName}' derives to address '${expectedAddress}', but input ${context.inputIndex} is controlled by '${planInput.address}'.`
|
|
@@ -38,9 +39,7 @@ var PrivateKeyAuthorizer = class {
|
|
|
38
39
|
}
|
|
39
40
|
const tx = wasmTransaction;
|
|
40
41
|
try {
|
|
41
|
-
console.log("DEBUG: Calling signTransaction");
|
|
42
42
|
const signedTx = wasm.signTransaction(tx, [privateKey], false);
|
|
43
|
-
console.log("DEBUG: signTransaction OK");
|
|
44
43
|
const sigScript = signedTx.inputs[inputIndex].signatureScript;
|
|
45
44
|
if (!sigScript || sigScript.length === 0) {
|
|
46
45
|
throw new Error(`UNAUTHORIZED_TRANSACTION_INPUT: Kaspa WASM failed to generate a signature script for input ${inputIndex} using account '${this.accountName}'.`);
|
|
@@ -63,7 +62,7 @@ var LazyAccountAuthorizer = class {
|
|
|
63
62
|
accountName;
|
|
64
63
|
workspaceRoot;
|
|
65
64
|
async authorize(context) {
|
|
66
|
-
const { resolveHardkasAccount } = await import("./resolve-
|
|
65
|
+
const { resolveHardkasAccount } = await import("./resolve-KMMTZWC3.js");
|
|
67
66
|
const account = await resolveHardkasAccount({
|
|
68
67
|
nameOrAddress: this.accountName,
|
|
69
68
|
config: { cwd: this.workspaceRoot }
|
|
@@ -75,7 +74,7 @@ var LazyAccountAuthorizer = class {
|
|
|
75
74
|
if (!pkValue && account.keystorePath) {
|
|
76
75
|
try {
|
|
77
76
|
const { KeystoreManager } = await import("./keystore-CMWEKGBF.js");
|
|
78
|
-
const { DEV_ACCOUNTS_PASSWORD } = await import("./dev-accounts-
|
|
77
|
+
const { DEV_ACCOUNTS_PASSWORD } = await import("./dev-accounts-BSWPKAAE.js");
|
|
79
78
|
const keystore = await KeystoreManager.loadEncryptedKeystore(account.keystorePath);
|
|
80
79
|
const unlock = await KeystoreManager.decryptEncryptedKeystore(keystore, DEV_ACCOUNTS_PASSWORD);
|
|
81
80
|
if (unlock.success && unlock.payload) {
|