@hardkas/accounts 0.11.2-alpha → 0.11.6-alpha
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-TYSAH7ZC.js +10 -0
- package/dist/chunk-3XXJ6UUU.js +99 -0
- package/dist/chunk-6DPO5V3N.js +90 -0
- package/dist/chunk-ILASLDZU.js +472 -0
- package/dist/chunk-R5P3WIWR.js +103 -0
- package/dist/chunk-WXMOC6WN.js +456 -0
- package/dist/chunk-YLG2NIAU.js +166 -0
- package/dist/dev-accounts-UULCTDJB.js +18 -0
- package/dist/index.d.ts +54 -28
- package/dist/index.js +96 -1183
- package/dist/internal/wasm-rpc-serialization.d.ts +5 -0
- package/dist/internal/wasm-rpc-serialization.js +6 -0
- package/dist/keystore-CMWEKGBF.js +6 -0
- package/dist/resolve-EXYT2GAC.js +12 -0
- package/dist/{signer-backend-T4JT2RCK.js → signer-backend-W3LNCQA3.js} +1 -1
- package/package.json +16 -8
- package/dist/chunk-7QKDZQBR.js +0 -46
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// src/internal/wasm-rpc-serialization.ts
|
|
2
|
+
function parseWasmTxToRpc(wasmTxStr, signedTx, inputOverrides, plan) {
|
|
3
|
+
let parsed;
|
|
4
|
+
try {
|
|
5
|
+
parsed = JSON.parse(wasmTxStr);
|
|
6
|
+
} catch (e) {
|
|
7
|
+
throw new Error("Failed to parse WASM transaction JSON: " + String(e));
|
|
8
|
+
}
|
|
9
|
+
while (typeof parsed === "string") {
|
|
10
|
+
parsed = JSON.parse(parsed);
|
|
11
|
+
}
|
|
12
|
+
const txInner = parsed.outputs ? parsed : parsed.tx ? parsed.tx.inner : parsed.inner;
|
|
13
|
+
if (!txInner) throw new Error("Could not find inner tx data");
|
|
14
|
+
const version = txInner.version || 0;
|
|
15
|
+
const numInputs = txInner.inputs ? txInner.inputs.length : 0;
|
|
16
|
+
if (inputOverrides) {
|
|
17
|
+
for (const idxStr of Object.keys(inputOverrides)) {
|
|
18
|
+
const idx = parseInt(idxStr, 10);
|
|
19
|
+
if (isNaN(idx) || idx < 0 || idx >= numInputs) {
|
|
20
|
+
throw new Error(`INVALID_UNLOCKER_INPUT_INDEX: Unlocker provided for non-existent input index ${idxStr}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function toHex(arr) {
|
|
25
|
+
if (!arr) return "";
|
|
26
|
+
return Buffer.from(arr).toString("hex");
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
version,
|
|
30
|
+
inputs: (txInner.inputs || []).map((i, idx) => {
|
|
31
|
+
const isFlattened = !!i.previousOutpoint || !!i.transactionId;
|
|
32
|
+
const prevOut = isFlattened ? i.previousOutpoint || i : i.inner.previousOutpoint.inner;
|
|
33
|
+
const originalSigScript = isFlattened ? i.signatureScript || "" : toHex(i.inner.signatureScript);
|
|
34
|
+
const originalSigOpCount = isFlattened ? i.sigOpCount : i.inner.sigOpCount;
|
|
35
|
+
const computeBudget = isFlattened ? i.computeBudget : i.inner.computeBudget;
|
|
36
|
+
const override = inputOverrides ? inputOverrides[idx] : void 0;
|
|
37
|
+
const finalSigScript = override ? override.signatureScript : originalSigScript;
|
|
38
|
+
if (!finalSigScript || finalSigScript.length === 0 || !/^[0-9a-fA-F]+$/.test(finalSigScript)) {
|
|
39
|
+
throw new Error(`INVALID_SIGNATURE_SCRIPT: Missing or invalid hex signature script at input ${idx}`);
|
|
40
|
+
}
|
|
41
|
+
const sigOpCount = version === 1 ? 0 : originalSigOpCount !== void 0 ? originalSigOpCount : 1;
|
|
42
|
+
if (version === 1 && sigOpCount !== 0) {
|
|
43
|
+
throw new Error("INVALID_V1_SIG_OP_COUNT: V1 transactions must have sigOpCount = 0.");
|
|
44
|
+
}
|
|
45
|
+
const overrideBudget = plan?.computeBudget;
|
|
46
|
+
const finalComputeBudget = overrideBudget !== void 0 ? Number(overrideBudget) : computeBudget !== void 0 && computeBudget !== 0 ? computeBudget : 0;
|
|
47
|
+
return {
|
|
48
|
+
previousOutpoint: {
|
|
49
|
+
transactionId: prevOut.transactionId || prevOut.transactionId,
|
|
50
|
+
index: prevOut.index || prevOut.index
|
|
51
|
+
},
|
|
52
|
+
signatureScript: finalSigScript,
|
|
53
|
+
sequence: isFlattened ? i.sequence || 0 : i.inner.sequence || 0,
|
|
54
|
+
sigOpCount,
|
|
55
|
+
computeBudget: finalComputeBudget
|
|
56
|
+
};
|
|
57
|
+
}),
|
|
58
|
+
outputs: (txInner.outputs || []).map((o, idx) => {
|
|
59
|
+
const isFlattened = !!o.scriptPublicKey || !!o.script_public_key || !!o.value || !!o.amount;
|
|
60
|
+
const innerOut = isFlattened ? o : o.inner;
|
|
61
|
+
const scriptObj = innerOut.scriptPublicKey || innerOut.script_public_key;
|
|
62
|
+
const ret = {
|
|
63
|
+
amount: (innerOut.value || innerOut.amount || 0).toString(),
|
|
64
|
+
scriptPublicKey: {
|
|
65
|
+
version: typeof scriptObj === "string" ? parseInt(scriptObj.substring(0, 4), 16) || 0 : scriptObj.version || 0,
|
|
66
|
+
scriptPublicKey: typeof scriptObj === "string" ? scriptObj.substring(4) : scriptObj.scriptPublicKey || scriptObj.script || ""
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
if (innerOut.covenant) {
|
|
70
|
+
ret.covenant = {
|
|
71
|
+
authorizingInput: innerOut.covenant.authorizingInput !== void 0 ? innerOut.covenant.authorizingInput : 0,
|
|
72
|
+
covenantId: typeof innerOut.covenant.covenantId === "string" ? innerOut.covenant.covenantId : ""
|
|
73
|
+
};
|
|
74
|
+
} else if (signedTx && typeof signedTx.outputs === "function") {
|
|
75
|
+
const outputs = signedTx.outputs();
|
|
76
|
+
if (outputs && outputs[idx] && outputs[idx].covenant) {
|
|
77
|
+
const cov = outputs[idx].covenant;
|
|
78
|
+
ret.covenant = {
|
|
79
|
+
authorizingInput: cov.authorizingInput !== void 0 ? cov.authorizingInput : 0,
|
|
80
|
+
covenantId: typeof cov.covenantId === "string" ? cov.covenantId : cov.covenantId.toString()
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
} else if (signedTx && signedTx.outputs && signedTx.outputs[idx] && signedTx.outputs[idx].covenant) {
|
|
84
|
+
const cov = signedTx.outputs[idx].covenant;
|
|
85
|
+
ret.covenant = {
|
|
86
|
+
authorizingInput: cov.authorizingInput !== void 0 ? cov.authorizingInput : 0,
|
|
87
|
+
covenantId: typeof cov.covenantId === "string" ? cov.covenantId : cov.covenantId.toString()
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return ret;
|
|
91
|
+
}),
|
|
92
|
+
lockTime: txInner.lockTime || 0,
|
|
93
|
+
subnetworkId: txInner.subnetworkId || "0000000000000000000000000000000000000000",
|
|
94
|
+
gas: txInner.gas || 0,
|
|
95
|
+
mass: txInner.mass || 0,
|
|
96
|
+
storageMass: txInner.storageMass || 0,
|
|
97
|
+
payload: txInner.payload && txInner.payload.length > 0 ? typeof txInner.payload === "string" ? txInner.payload : toHex(txInner.payload) : ""
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export {
|
|
102
|
+
parseWasmTxToRpc
|
|
103
|
+
};
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseWasmTxToRpc
|
|
3
|
+
} from "./chunk-R5P3WIWR.js";
|
|
4
|
+
import {
|
|
5
|
+
resolveHardkasAccount
|
|
6
|
+
} from "./chunk-ILASLDZU.js";
|
|
7
|
+
import {
|
|
8
|
+
loadKaspaWasm
|
|
9
|
+
} from "./chunk-6DPO5V3N.js";
|
|
10
|
+
import {
|
|
11
|
+
KeystoreManager
|
|
12
|
+
} from "./chunk-YLG2NIAU.js";
|
|
13
|
+
|
|
14
|
+
// src/dev-accounts.ts
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
import path from "path";
|
|
17
|
+
import crypto from "crypto";
|
|
18
|
+
import { deterministicCompare } from "@hardkas/core";
|
|
19
|
+
|
|
20
|
+
// src/kaspa-wasm-signer.ts
|
|
21
|
+
import { calculateContentHash } from "@hardkas/artifacts";
|
|
22
|
+
var KaspaWasmPrivateKeySigner = class {
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.options = options;
|
|
25
|
+
}
|
|
26
|
+
options;
|
|
27
|
+
kind = "kaspa-private-key";
|
|
28
|
+
async signTxPlan(input) {
|
|
29
|
+
const plan = input.planArtifact;
|
|
30
|
+
const sdk = await loadKaspaWasm(this.options.wasmConfig);
|
|
31
|
+
const { detectCapabilities } = await import("./signer-backend-W3LNCQA3.js");
|
|
32
|
+
const capabilities = detectCapabilities(sdk);
|
|
33
|
+
if (plan.computeBudget !== void 0 || plan.outputs.some((o) => o.covenant)) {
|
|
34
|
+
if (!capabilities.transactionV1Signing) {
|
|
35
|
+
throw new Error("Transaction V1 signing is not supported by the installed kaspa-wasm version");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
assertSigningNetworkAllowed({
|
|
39
|
+
network: plan.networkId,
|
|
40
|
+
mode: plan.mode,
|
|
41
|
+
allowMainnet: this.options.allowMainnet
|
|
42
|
+
});
|
|
43
|
+
let authorizers = input.authorizers ? { ...input.authorizers } : {};
|
|
44
|
+
const numInputs = plan.inputs?.length || plan.selectedUtxos?.length || 0;
|
|
45
|
+
if (this.options.account) {
|
|
46
|
+
const account = this.options.account;
|
|
47
|
+
let pkValue = account.privateKeyEnv ? process.env[account.privateKeyEnv] : void 0;
|
|
48
|
+
if (!pkValue && account.privateKey) {
|
|
49
|
+
if (plan.networkId === "mainnet") {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Mainnet guard: Unsafe plaintext privateKey fallback is forbidden on mainnet for account '${account.name}'. Use privateKeyEnv instead.`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
pkValue = account.privateKey;
|
|
55
|
+
}
|
|
56
|
+
if (!pkValue && account.keystorePath) {
|
|
57
|
+
try {
|
|
58
|
+
const KeystoreManager2 = (await import("./keystore-CMWEKGBF.js")).KeystoreManager;
|
|
59
|
+
const DEV_ACCOUNTS_PASSWORD2 = (await import("./dev-accounts-UULCTDJB.js")).DEV_ACCOUNTS_PASSWORD;
|
|
60
|
+
const keystore = await KeystoreManager2.loadEncryptedKeystore(account.keystorePath);
|
|
61
|
+
const unlock = await KeystoreManager2.decryptEncryptedKeystore(keystore, DEV_ACCOUNTS_PASSWORD2);
|
|
62
|
+
if (unlock.success && unlock.payload) {
|
|
63
|
+
pkValue = unlock.payload.privateKey;
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!pkValue) {
|
|
69
|
+
const err = new Error(`DEV_ACCOUNT_KEY_UNAVAILABLE: Missing required private key for account '${account.name}'.`);
|
|
70
|
+
err.code = "DEV_ACCOUNT_KEY_UNAVAILABLE";
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
if (typeof pkValue !== "string" || pkValue.trim() === "" || !/^[0-9a-fA-F]{64}$/.test(pkValue)) {
|
|
74
|
+
const err = new Error("INVALID_PRIVATE_KEY_MATERIAL: Private key must be a valid 64-character hex string.");
|
|
75
|
+
err.code = "INVALID_PRIVATE_KEY_MATERIAL";
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
const expectedAddress = new sdk.PrivateKey(pkValue).toKeypair().toAddress(plan.networkId || "simnet").toString();
|
|
79
|
+
const { PrivateKeyAuthorizer } = await import("./authorizers-TYSAH7ZC.js");
|
|
80
|
+
const sourceInputs = plan.inputs || plan.selectedUtxos || [];
|
|
81
|
+
for (let i = 0; i < numInputs; i++) {
|
|
82
|
+
if (!authorizers[i]) {
|
|
83
|
+
const inputAddress = sourceInputs[i]?.address;
|
|
84
|
+
if (inputAddress === expectedAddress) {
|
|
85
|
+
authorizers[i] = new PrivateKeyAuthorizer(account.name, pkValue);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (let i = 0; i < numInputs; i++) {
|
|
91
|
+
if (!authorizers[i]) {
|
|
92
|
+
throw new Error(`MISSING_INPUT_AUTHORIZER: No authorizer was provided for input index ${i}.`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
console.log("DEBUG PLAN INPUTS:", JSON.stringify(plan.inputs || plan.selectedUtxos, null, 2));
|
|
97
|
+
const sourceInputs = plan.inputs || plan.selectedUtxos || [];
|
|
98
|
+
const utxos = sourceInputs.map((u) => {
|
|
99
|
+
if (!u.outpoint.transactionId || u.outpoint.index === void 0) {
|
|
100
|
+
throw new Error(`UTXO is missing transactionId or index. Re-run tx plan.`);
|
|
101
|
+
}
|
|
102
|
+
const spk = u.scriptPublicKey;
|
|
103
|
+
if (!spk) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
"UTXO is missing scriptPublicKey. Real signing flows must never fabricate cryptographic state."
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
address: plan.from.address,
|
|
110
|
+
outpoint: {
|
|
111
|
+
transactionId: u.outpoint.transactionId,
|
|
112
|
+
index: u.outpoint.index
|
|
113
|
+
},
|
|
114
|
+
utxoEntry: {
|
|
115
|
+
amount: BigInt(u.amountSompi),
|
|
116
|
+
scriptPublicKey: new sdk.ScriptPublicKey(parseInt(spk.substring(0, 4), 16) || 0, spk.substring(4)),
|
|
117
|
+
blockDaaScore: BigInt(u.blockDaaScore || "0"),
|
|
118
|
+
isCoinbase: !!u.isCoinbase
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
const priorityFee = BigInt(plan.estimatedFeeSompi);
|
|
123
|
+
const createFreshTx = () => {
|
|
124
|
+
let unsignedTx;
|
|
125
|
+
if (plan.txVersion === 1) {
|
|
126
|
+
if (!capabilities.transactionV1Signing) {
|
|
127
|
+
throw new Error("Transaction V1 signing is not supported by the installed kaspa-wasm version");
|
|
128
|
+
}
|
|
129
|
+
const allOutputs = [...plan.outputs];
|
|
130
|
+
if (plan.change) {
|
|
131
|
+
allOutputs.push({
|
|
132
|
+
address: plan.change.address,
|
|
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));
|
|
144
|
+
});
|
|
145
|
+
console.log("DEBUG: Calling V1 createTransaction with:", { utxos: utxos.length, wasmOutputs: wasmOutputs.length, priorityFee });
|
|
146
|
+
console.log("DEBUG KASPA-WASM PATH:", import.meta.resolve("kaspa-wasm"));
|
|
147
|
+
console.log("DEBUG CREATE TRANSACTION FN:", sdk.createTransaction.toString());
|
|
148
|
+
unsignedTx = sdk.createTransaction(
|
|
149
|
+
utxos,
|
|
150
|
+
wasmOutputs,
|
|
151
|
+
priorityFee
|
|
152
|
+
);
|
|
153
|
+
const genesisGroups = /* @__PURE__ */ new Map();
|
|
154
|
+
allOutputs.forEach((o, idx) => {
|
|
155
|
+
if (o.covenant && !o.covenant.covenantId) {
|
|
156
|
+
const authIn = o.covenant.authorizingInput;
|
|
157
|
+
if (!genesisGroups.has(authIn)) genesisGroups.set(authIn, []);
|
|
158
|
+
genesisGroups.get(authIn).push(idx);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
if (genesisGroups.size > 0) {
|
|
162
|
+
const groupsArray = Array.from(genesisGroups.entries()).map(([authIn, outIndices]) => {
|
|
163
|
+
return new sdk.GenesisCovenantGroup(authIn, outIndices);
|
|
164
|
+
});
|
|
165
|
+
unsignedTx.populateGenesisCovenants(groupsArray);
|
|
166
|
+
}
|
|
167
|
+
unsignedTx.version = 1;
|
|
168
|
+
if (plan.storageMass !== void 0) {
|
|
169
|
+
unsignedTx.storageMass = BigInt(plan.storageMass);
|
|
170
|
+
}
|
|
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
|
+
}
|
|
195
|
+
const inputs = unsignedTx.inputs;
|
|
196
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
197
|
+
if (plan.computeBudget !== void 0) {
|
|
198
|
+
const val = Number(plan.computeBudget);
|
|
199
|
+
inputs[i].computeBudget = val;
|
|
200
|
+
}
|
|
201
|
+
if (unsignedTx.version === 1) {
|
|
202
|
+
inputs[i].sigOpCount = 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
console.log("DEBUG createFreshTx RETURNING:", unsignedTx?.constructor?.name);
|
|
206
|
+
return unsignedTx;
|
|
207
|
+
};
|
|
208
|
+
const inputOverrides = {};
|
|
209
|
+
if (!authorizers || Object.keys(authorizers).length === 0) {
|
|
210
|
+
throw new Error("MISSING_INPUT_AUTHORIZER: No authorizers were provided for the transaction.");
|
|
211
|
+
}
|
|
212
|
+
const dummyTx = createFreshTx();
|
|
213
|
+
const numInputsWasm = dummyTx.inputs.length;
|
|
214
|
+
for (let i = 0; i < numInputsWasm; i++) {
|
|
215
|
+
const authorizer = authorizers[i];
|
|
216
|
+
if (!authorizer) {
|
|
217
|
+
throw new Error(`MISSING_INPUT_AUTHORIZER: No authorizer was provided for input index ${i}.`);
|
|
218
|
+
}
|
|
219
|
+
const txForAuth = createFreshTx();
|
|
220
|
+
const authorization = await authorizer.authorize({
|
|
221
|
+
inputIndex: i,
|
|
222
|
+
plan,
|
|
223
|
+
wasmTransaction: txForAuth,
|
|
224
|
+
wasm: sdk
|
|
225
|
+
});
|
|
226
|
+
let sigScript;
|
|
227
|
+
if (authorization.kind === "signature-script") {
|
|
228
|
+
sigScript = authorization.signatureScript;
|
|
229
|
+
} else if (authorization.kind === "wasm-signer") {
|
|
230
|
+
sigScript = await authorization.signer.signInput({
|
|
231
|
+
inputIndex: i,
|
|
232
|
+
plan,
|
|
233
|
+
wasmTransaction: txForAuth,
|
|
234
|
+
wasm: sdk
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
if (!sigScript || sigScript.trim() === "") {
|
|
238
|
+
throw new Error(`INVALID_SIGNATURE_SCRIPT: Authorizer for input ${i} failed to return a signatureScript.`);
|
|
239
|
+
}
|
|
240
|
+
if (!/^[0-9a-fA-F]+$/.test(sigScript)) {
|
|
241
|
+
throw new Error(`INVALID_SIGNATURE_SCRIPT: Authorizer for input ${i} returned a non-hex signatureScript.`);
|
|
242
|
+
}
|
|
243
|
+
inputOverrides[i] = { signatureScript: sigScript };
|
|
244
|
+
}
|
|
245
|
+
const finalTx = createFreshTx();
|
|
246
|
+
const signedTx = finalTx;
|
|
247
|
+
const wasmTxStr = typeof signedTx.serializeToJSON === "function" ? signedTx.serializeToJSON() : signedTx.toString();
|
|
248
|
+
console.log("DEBUG wasmTxStr:", wasmTxStr);
|
|
249
|
+
console.log("DEBUG: about to parseWasmTxToRpc");
|
|
250
|
+
const rpcTx = parseWasmTxToRpc(wasmTxStr, signedTx, inputOverrides, plan);
|
|
251
|
+
const rawTx = JSON.stringify(rpcTx);
|
|
252
|
+
return {
|
|
253
|
+
signatureKind: "kaspa-private-key",
|
|
254
|
+
signerAddress: input.accountName || plan.from.address || "authorized",
|
|
255
|
+
signedTransaction: {
|
|
256
|
+
format: "hex",
|
|
257
|
+
payload: rawTx
|
|
258
|
+
},
|
|
259
|
+
txId: signedTx.id,
|
|
260
|
+
signature: {
|
|
261
|
+
// We use the txid as the signature identifier in the artifact
|
|
262
|
+
value: signedTx.id || calculateContentHash(plan)
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error("DEBUG SIGNING ERROR:", error);
|
|
267
|
+
console.error("DEBUG STACK:", error?.stack || "NO STACK (raw panic)");
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Kaspa WASM signing failed: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error, Object.getOwnPropertyNames(error))}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
function assertSigningNetworkAllowed(input) {
|
|
275
|
+
const isMainnet = input.network === "mainnet";
|
|
276
|
+
if (isMainnet && !input.allowMainnet) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
"Mainnet signing is disabled by default. Use --allow-mainnet-signing only if you understand the risks."
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/dev-accounts.ts
|
|
284
|
+
var DEV_ACCOUNTS_PASSWORD = "hardkas-local-dev";
|
|
285
|
+
var SIMNET_DETERMINISTIC_SEED = "hardkas-deterministic-simnet-seed-v1";
|
|
286
|
+
async function ensureDevAccounts(workspaceDir) {
|
|
287
|
+
const devAccountsDir = path.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
288
|
+
if (!fs.existsSync(devAccountsDir)) {
|
|
289
|
+
await fs.promises.mkdir(devAccountsDir, { recursive: true });
|
|
290
|
+
}
|
|
291
|
+
await getOrCreateDevAccount(workspaceDir, 0, "alice");
|
|
292
|
+
await getOrCreateDevAccount(workspaceDir, 1, "bob");
|
|
293
|
+
}
|
|
294
|
+
async function getOrCreateDevAccount(workspaceDir, index, alias) {
|
|
295
|
+
const devAccountsDir = path.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
296
|
+
const filePath = path.join(devAccountsDir, `${alias}.json`);
|
|
297
|
+
if (fs.existsSync(filePath)) {
|
|
298
|
+
const keystore2 = await KeystoreManager.loadEncryptedKeystore(filePath);
|
|
299
|
+
const unlock = await KeystoreManager.decryptEncryptedKeystore(
|
|
300
|
+
keystore2,
|
|
301
|
+
DEV_ACCOUNTS_PASSWORD
|
|
302
|
+
);
|
|
303
|
+
if (!unlock.success || !unlock.payload) {
|
|
304
|
+
throw new Error(
|
|
305
|
+
`Failed to decrypt dev account ${alias}. Expected password: ${DEV_ACCOUNTS_PASSWORD}`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
address: unlock.payload.address,
|
|
310
|
+
privateKey: unlock.payload.privateKey,
|
|
311
|
+
publicKey: unlock.payload.publicKey
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const seedString = `${SIMNET_DETERMINISTIC_SEED}-${index}`;
|
|
315
|
+
const privateKeyHex = crypto.createHash("sha256").update(seedString).digest("hex");
|
|
316
|
+
const network = "simnet";
|
|
317
|
+
const isSimnet = ["simnet", "kaspasim", "local"].includes(network);
|
|
318
|
+
let address = "";
|
|
319
|
+
let privateKey = "";
|
|
320
|
+
let publicKey = "";
|
|
321
|
+
try {
|
|
322
|
+
if (isSimnet) {
|
|
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
|
+
}
|
|
381
|
+
const accountData = {
|
|
382
|
+
address,
|
|
383
|
+
privateKey,
|
|
384
|
+
publicKey
|
|
385
|
+
};
|
|
386
|
+
if (!fs.existsSync(devAccountsDir)) {
|
|
387
|
+
await fs.promises.mkdir(devAccountsDir, { recursive: true });
|
|
388
|
+
}
|
|
389
|
+
const payload = {
|
|
390
|
+
address: accountData.address,
|
|
391
|
+
privateKey: accountData.privateKey,
|
|
392
|
+
network: "simnet"
|
|
393
|
+
};
|
|
394
|
+
if (accountData.publicKey) {
|
|
395
|
+
payload.publicKey = accountData.publicKey;
|
|
396
|
+
}
|
|
397
|
+
const keystore = await KeystoreManager.createEncryptedKeystore(
|
|
398
|
+
payload,
|
|
399
|
+
DEV_ACCOUNTS_PASSWORD,
|
|
400
|
+
{
|
|
401
|
+
label: alias,
|
|
402
|
+
network: "simnet"
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
await KeystoreManager.saveEncryptedKeystore(filePath, keystore);
|
|
406
|
+
return accountData;
|
|
407
|
+
}
|
|
408
|
+
function listDevAccountsSync(workspaceDir) {
|
|
409
|
+
const devAccountsDir = path.join(workspaceDir, ".hardkas", "dev-accounts");
|
|
410
|
+
if (!fs.existsSync(devAccountsDir)) {
|
|
411
|
+
return [];
|
|
412
|
+
}
|
|
413
|
+
const accounts = [];
|
|
414
|
+
const files = fs.readdirSync(devAccountsDir);
|
|
415
|
+
for (const file of files) {
|
|
416
|
+
if (file.endsWith(".json")) {
|
|
417
|
+
const name = path.basename(file, ".json");
|
|
418
|
+
try {
|
|
419
|
+
const data = fs.readFileSync(path.join(devAccountsDir, file), "utf-8");
|
|
420
|
+
const keystore = JSON.parse(data);
|
|
421
|
+
if (keystore.type === "hardkas.encryptedKeystore.v2") {
|
|
422
|
+
accounts.push({
|
|
423
|
+
name,
|
|
424
|
+
address: keystore.metadata?.address || ""
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
} catch (e) {
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
accounts.sort((a, b) => deterministicCompare(a.name, b.name));
|
|
432
|
+
return accounts;
|
|
433
|
+
}
|
|
434
|
+
async function createDevSigner(workspaceDir, accountNameOrAddress) {
|
|
435
|
+
const account = resolveHardkasAccount({
|
|
436
|
+
nameOrAddress: accountNameOrAddress,
|
|
437
|
+
config: { cwd: workspaceDir }
|
|
438
|
+
});
|
|
439
|
+
if (account.kind !== "kaspa-private-key") {
|
|
440
|
+
throw new Error(`Account '${accountNameOrAddress}' is not a private key account, cannot create local dev signer.`);
|
|
441
|
+
}
|
|
442
|
+
return new KaspaWasmPrivateKeySigner({
|
|
443
|
+
account,
|
|
444
|
+
allowMainnet: false
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export {
|
|
449
|
+
DEV_ACCOUNTS_PASSWORD,
|
|
450
|
+
ensureDevAccounts,
|
|
451
|
+
getOrCreateDevAccount,
|
|
452
|
+
listDevAccountsSync,
|
|
453
|
+
createDevSigner,
|
|
454
|
+
KaspaWasmPrivateKeySigner,
|
|
455
|
+
assertSigningNetworkAllowed
|
|
456
|
+
};
|