@hardkas/accounts 0.8.5-alpha → 0.8.10-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/index.d.ts +16 -3
- package/dist/index.js +259 -28
- package/package.json +8 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HardkasConfig } from '@hardkas/config';
|
|
2
|
-
import { TxPlanArtifact, SignedTxArtifact, HardkasArtifactBase } from '@hardkas/artifacts';
|
|
2
|
+
import { TxPlanArtifact, SignedTxArtifact, ExternalHardkasSigner, HardkasArtifactBase } from '@hardkas/artifacts';
|
|
3
3
|
import { NetworkId } from '@hardkas/core';
|
|
4
4
|
|
|
5
5
|
type HardkasAccountKind = "simulated" | "kaspa-private-key" | "external-wallet" | "evm-private-key";
|
|
@@ -108,7 +108,7 @@ interface ResolveAccountOptions {
|
|
|
108
108
|
}
|
|
109
109
|
declare function resolveHardkasAccount(options: ResolveAccountOptions): HardkasAccount;
|
|
110
110
|
declare function listHardkasAccounts(config?: HardkasConfig): HardkasAccount[];
|
|
111
|
-
declare function resolveHardkasAccountAddress(accountOrAddress: string, config?: HardkasConfig, context?: "L1" | "L2"): string
|
|
111
|
+
declare function resolveHardkasAccountAddress(accountOrAddress: string, config?: HardkasConfig, context?: "L1" | "L2"): Promise<string>;
|
|
112
112
|
declare function describeAccount(account: HardkasAccount): Record<string, unknown>;
|
|
113
113
|
|
|
114
114
|
interface EvmExportResult {
|
|
@@ -222,6 +222,19 @@ declare function assertSigningNetworkAllowed(input: {
|
|
|
222
222
|
allowMainnet?: boolean | undefined;
|
|
223
223
|
}): void;
|
|
224
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Deterministic fixture signer for Docker testing on simnet.
|
|
227
|
+
* Never to be used with real funds or mainnet.
|
|
228
|
+
*/
|
|
229
|
+
declare class HardkasFixtureSigner implements ExternalHardkasSigner {
|
|
230
|
+
private networkId;
|
|
231
|
+
private readonly FIXTURE_PK;
|
|
232
|
+
constructor(networkId?: string);
|
|
233
|
+
private loadKaspa;
|
|
234
|
+
getAddress(): Promise<string>;
|
|
235
|
+
signTransaction(plan: TxPlanArtifact): Promise<SignedTxArtifact>;
|
|
236
|
+
}
|
|
237
|
+
|
|
225
238
|
interface RealAccountStore extends HardkasArtifactBase {
|
|
226
239
|
readonly schema: "hardkas.realAccountStore.v1";
|
|
227
240
|
readonly networkId: NetworkId;
|
|
@@ -375,4 +388,4 @@ declare function listDevAccountsSync(workspaceDir: string): {
|
|
|
375
388
|
address: string;
|
|
376
389
|
}[];
|
|
377
390
|
|
|
378
|
-
export { type CreateKaspaWalletOptions, DEV_ACCOUNTS_PASSWORD, type EncryptedKeystoreV2, type EvmExportResult, type GeneratedKaspaDevAccount, type HardkasAccount, type HardkasAccountKind, type HardkasBaseAccount, type HardkasEvmPrivateKeyAccount, type HardkasExternalWalletAccount, type HardkasKaspaPrivateKeyAccount, type HardkasSigner, type HardkasSignerKind, type HardkasSimulatedAccount, type HardkasTxPlanSigner, type KaspaKeyGenerator, KaspaSdkKeyGenerator, type KaspaSdkKeyGeneratorOptions, KaspaSdkRealTxSigner, type KaspaSdkRealTxSignerOptions, type KaspaSigningBackendStatus, KaspaWasmPrivateKeySigner, type KeystoreCipherParams, type KeystoreKdfParams, KeystoreManager, type KeystorePayload, type KeystoreUnlockResult, type RealAccountStore, type RealDevAccount, type RealTxSigner, type RealTxSigningInput, type RealTxSigningResult, type ResolveAccountOptions, type SignTxPlanInput, type SignTxPlanResult, SimulatedSigner, SimulatedTxPlanSigner, UnsupportedKaspaKeyGenerator, UnsupportedRealKaspaSigner, UnsupportedRealTxSigner, assertSigningNetworkAllowed, createEmptyRealAccountStore, createLocalKaspaWallet, describeAccount, ensureDevAccounts, getDefaultRealAccountsPath, getKaspaSigningBackendStatus, getOrCreateDevAccount, getRealDevAccount, getRequiredEnv, importRealDevAccount, listDevAccountsSync, listHardkasAccounts, listRealDevAccounts, loadKaspaWasm, loadOrCreateRealAccountStore, loadRealAccountStore, loadRealAccountStoreSync, prepareEvmAccountExport, removeRealDevAccount, resolveHardkasAccount, resolveHardkasAccountAddress, resolveRealAccountOrAddress, saveRealAccountStore, signTxPlanArtifact, validateAccountName, validateAddressPrefix };
|
|
391
|
+
export { type CreateKaspaWalletOptions, DEV_ACCOUNTS_PASSWORD, type EncryptedKeystoreV2, type EvmExportResult, type GeneratedKaspaDevAccount, type HardkasAccount, type HardkasAccountKind, type HardkasBaseAccount, type HardkasEvmPrivateKeyAccount, type HardkasExternalWalletAccount, HardkasFixtureSigner, type HardkasKaspaPrivateKeyAccount, type HardkasSigner, type HardkasSignerKind, type HardkasSimulatedAccount, type HardkasTxPlanSigner, type KaspaKeyGenerator, KaspaSdkKeyGenerator, type KaspaSdkKeyGeneratorOptions, KaspaSdkRealTxSigner, type KaspaSdkRealTxSignerOptions, type KaspaSigningBackendStatus, KaspaWasmPrivateKeySigner, type KeystoreCipherParams, type KeystoreKdfParams, KeystoreManager, type KeystorePayload, type KeystoreUnlockResult, type RealAccountStore, type RealDevAccount, type RealTxSigner, type RealTxSigningInput, type RealTxSigningResult, type ResolveAccountOptions, type SignTxPlanInput, type SignTxPlanResult, SimulatedSigner, SimulatedTxPlanSigner, UnsupportedKaspaKeyGenerator, UnsupportedRealKaspaSigner, UnsupportedRealTxSigner, assertSigningNetworkAllowed, createEmptyRealAccountStore, createLocalKaspaWallet, describeAccount, ensureDevAccounts, getDefaultRealAccountsPath, getKaspaSigningBackendStatus, getOrCreateDevAccount, getRealDevAccount, getRequiredEnv, importRealDevAccount, listDevAccountsSync, listHardkasAccounts, listRealDevAccounts, loadKaspaWasm, loadOrCreateRealAccountStore, loadRealAccountStore, loadRealAccountStoreSync, prepareEvmAccountExport, removeRealDevAccount, resolveHardkasAccount, resolveHardkasAccountAddress, resolveRealAccountOrAddress, saveRealAccountStore, signTxPlanArtifact, validateAccountName, validateAddressPrefix };
|
package/dist/index.js
CHANGED
|
@@ -277,11 +277,7 @@ function listHardkasAccounts(config) {
|
|
|
277
277
|
}
|
|
278
278
|
const keystoreDir = path2.join(process.cwd(), ".hardkas", "keystore");
|
|
279
279
|
if (fs2.existsSync(keystoreDir)) {
|
|
280
|
-
|
|
281
|
-
throw new Error(
|
|
282
|
-
"Workspace root/cwd is required for hermetic keystore path resolution"
|
|
283
|
-
);
|
|
284
|
-
}
|
|
280
|
+
const cwd = config?.cwd || process.cwd();
|
|
285
281
|
const files = fs2.readdirSync(keystoreDir);
|
|
286
282
|
for (const file of files) {
|
|
287
283
|
if (file.endsWith(".json")) {
|
|
@@ -312,13 +308,35 @@ function listHardkasAccounts(config) {
|
|
|
312
308
|
}
|
|
313
309
|
return Array.from(accounts.values());
|
|
314
310
|
}
|
|
315
|
-
function resolveHardkasAccountAddress(accountOrAddress, config, context = "L1") {
|
|
311
|
+
async function resolveHardkasAccountAddress(accountOrAddress, config, context = "L1") {
|
|
316
312
|
if (accountOrAddress.startsWith("kaspa:") || accountOrAddress.startsWith("kaspatest:") || accountOrAddress.startsWith("kaspasim:")) {
|
|
317
313
|
if (context === "L2") {
|
|
318
314
|
throw new Error(
|
|
319
315
|
`Invalid L2 address provided: ${accountOrAddress}. Expected EVM address or account alias.`
|
|
320
316
|
);
|
|
321
317
|
}
|
|
318
|
+
if (!accountOrAddress.startsWith("kaspa:sim_")) {
|
|
319
|
+
try {
|
|
320
|
+
const kaspa = await import("kaspa-wasm");
|
|
321
|
+
try {
|
|
322
|
+
if (typeof kaspa.Address === "function" || kaspa.Address) {
|
|
323
|
+
new kaspa.Address(accountOrAddress);
|
|
324
|
+
}
|
|
325
|
+
} catch (e) {
|
|
326
|
+
const err = new Error(`HARDKAS_INVALID_ADDRESS: Invalid Kaspa address format or checksum.`);
|
|
327
|
+
err.code = "HARDKAS_INVALID_ADDRESS";
|
|
328
|
+
throw err;
|
|
329
|
+
}
|
|
330
|
+
} catch (e) {
|
|
331
|
+
if (e.code === "HARDKAS_INVALID_ADDRESS") throw e;
|
|
332
|
+
if (e.code === "ERR_MODULE_NOT_FOUND" || e.message.includes("Cannot find module") || e.message.includes("kaspa-wasm")) {
|
|
333
|
+
const err = new Error("ADDRESS_VALIDATOR_UNAVAILABLE: The Kaspa address validator backend is not available.");
|
|
334
|
+
err.code = "ADDRESS_VALIDATOR_UNAVAILABLE";
|
|
335
|
+
throw err;
|
|
336
|
+
}
|
|
337
|
+
throw e;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
322
340
|
return accountOrAddress;
|
|
323
341
|
}
|
|
324
342
|
if (accountOrAddress.startsWith("0x") && accountOrAddress.length === 42) {
|
|
@@ -420,19 +438,21 @@ function getRequiredEnv(name) {
|
|
|
420
438
|
import {
|
|
421
439
|
createSimulatedSignedTxArtifact,
|
|
422
440
|
calculateContentHash as calculateContentHash2,
|
|
423
|
-
HARDKAS_VERSION as HARDKAS_VERSION2
|
|
441
|
+
HARDKAS_VERSION as HARDKAS_VERSION2,
|
|
442
|
+
createLineageTransition
|
|
424
443
|
} from "@hardkas/artifacts";
|
|
425
444
|
import { systemRuntimeContext } from "@hardkas/core";
|
|
426
445
|
|
|
427
446
|
// src/signer-backend.ts
|
|
428
447
|
async function loadKaspaWasm() {
|
|
429
448
|
try {
|
|
430
|
-
|
|
431
|
-
return sdk;
|
|
449
|
+
return await import("kaspa-wasm");
|
|
432
450
|
} catch (error) {
|
|
433
|
-
|
|
434
|
-
"Kaspa WASM
|
|
451
|
+
const err = new Error(
|
|
452
|
+
"SIGNER_BACKEND_UNAVAILABLE: Official Kaspa WASM backend is required to sign transactions.\nInstall it via: npm install kaspa-wasm"
|
|
435
453
|
);
|
|
454
|
+
err.code = "SIGNER_BACKEND_UNAVAILABLE";
|
|
455
|
+
throw err;
|
|
436
456
|
}
|
|
437
457
|
}
|
|
438
458
|
async function getKaspaSigningBackendStatus() {
|
|
@@ -454,6 +474,40 @@ async function getKaspaSigningBackendStatus() {
|
|
|
454
474
|
|
|
455
475
|
// src/kaspa-wasm-signer.ts
|
|
456
476
|
import { calculateContentHash } from "@hardkas/artifacts";
|
|
477
|
+
function toHex(arr) {
|
|
478
|
+
return Buffer.from(arr).toString("hex");
|
|
479
|
+
}
|
|
480
|
+
function parseWasmTxToRpc(wasmTxStr) {
|
|
481
|
+
let parsed = JSON.parse(wasmTxStr);
|
|
482
|
+
if (typeof parsed === "string") {
|
|
483
|
+
parsed = JSON.parse(parsed);
|
|
484
|
+
}
|
|
485
|
+
const txInner = parsed.tx ? parsed.tx.inner : parsed.inner;
|
|
486
|
+
if (!txInner) throw new Error("Could not find inner tx data");
|
|
487
|
+
return {
|
|
488
|
+
version: txInner.version || 0,
|
|
489
|
+
inputs: (txInner.inputs || []).map((i) => ({
|
|
490
|
+
previousOutpoint: {
|
|
491
|
+
transactionId: i.inner.previousOutpoint.inner.transactionId,
|
|
492
|
+
index: i.inner.previousOutpoint.inner.index
|
|
493
|
+
},
|
|
494
|
+
signatureScript: toHex(i.inner.signatureScript),
|
|
495
|
+
sequence: i.inner.sequence || 0,
|
|
496
|
+
sigOpCount: i.inner.sigOpCount || 1
|
|
497
|
+
})),
|
|
498
|
+
outputs: (txInner.outputs || []).map((o) => ({
|
|
499
|
+
amount: o.inner.value.toString(),
|
|
500
|
+
scriptPublicKey: {
|
|
501
|
+
version: parseInt(o.inner.scriptPublicKey.substring(0, 4), 16) || 0,
|
|
502
|
+
scriptPublicKey: o.inner.scriptPublicKey.substring(4)
|
|
503
|
+
}
|
|
504
|
+
})),
|
|
505
|
+
lockTime: txInner.lockTime || 0,
|
|
506
|
+
subnetworkId: txInner.subnetworkId || "0000000000000000000000000000000000000000",
|
|
507
|
+
gas: txInner.gas || 0,
|
|
508
|
+
payload: txInner.payload && txInner.payload.length > 0 ? toHex(txInner.payload) : ""
|
|
509
|
+
};
|
|
510
|
+
}
|
|
457
511
|
var KaspaWasmPrivateKeySigner = class {
|
|
458
512
|
constructor(options) {
|
|
459
513
|
this.options = options;
|
|
@@ -485,17 +539,26 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
485
539
|
"UTXO is missing scriptPublicKey. Real signing flows must never fabricate cryptographic state."
|
|
486
540
|
);
|
|
487
541
|
}
|
|
488
|
-
return
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
542
|
+
return {
|
|
543
|
+
address: plan.from.address,
|
|
544
|
+
outpoint: {
|
|
545
|
+
transactionId: u.outpoint.transactionId,
|
|
546
|
+
index: u.outpoint.index
|
|
547
|
+
},
|
|
548
|
+
utxoEntry: {
|
|
549
|
+
amount: BigInt(u.amountSompi),
|
|
550
|
+
scriptPublicKey: spk,
|
|
551
|
+
blockDaaScore: BigInt(u.blockDaaScore || "0"),
|
|
552
|
+
isCoinbase: !!u.isCoinbase
|
|
553
|
+
}
|
|
554
|
+
};
|
|
495
555
|
});
|
|
496
556
|
const outputs = plan.outputs.map((o) => {
|
|
497
557
|
if (!o.address) throw new Error("Output is missing address.");
|
|
498
|
-
return
|
|
558
|
+
return {
|
|
559
|
+
address: o.address,
|
|
560
|
+
amount: BigInt(o.amountSompi)
|
|
561
|
+
};
|
|
499
562
|
});
|
|
500
563
|
const changeAddress = plan.change?.address ? new sdk.Address(plan.change.address) : void 0;
|
|
501
564
|
const priorityFee = BigInt(plan.estimatedFeeSompi);
|
|
@@ -506,7 +569,7 @@ var KaspaWasmPrivateKeySigner = class {
|
|
|
506
569
|
priorityFee
|
|
507
570
|
);
|
|
508
571
|
const signedTx = sdk.signTransaction(unsignedTx, [privateKey], true);
|
|
509
|
-
const rawTx =
|
|
572
|
+
const rawTx = JSON.stringify(parseWasmTxToRpc(signedTx.toString()));
|
|
510
573
|
return {
|
|
511
574
|
signatureKind: "kaspa-private-key",
|
|
512
575
|
signerAddress: account.address || privateKey.toAddress(plan.networkId).toString(),
|
|
@@ -624,11 +687,15 @@ async function signTxPlanArtifact(input) {
|
|
|
624
687
|
format: result.signedTransaction?.format === "hex" ? "hex" : "unknown",
|
|
625
688
|
payload: result.signedTransaction?.payload || ""
|
|
626
689
|
},
|
|
690
|
+
lineage: createLineageTransition(planArtifact, "hardkas.signedTx"),
|
|
627
691
|
...planArtifact.workflowId ? { workflowId: planArtifact.workflowId } : {}
|
|
628
692
|
};
|
|
629
693
|
const contentHash = calculateContentHash2(artifact);
|
|
630
694
|
artifact.signedId = `signed-${contentHash.slice(0, 16)}`;
|
|
631
695
|
artifact.contentHash = contentHash;
|
|
696
|
+
if (artifact.lineage) {
|
|
697
|
+
artifact.lineage.artifactId = contentHash;
|
|
698
|
+
}
|
|
632
699
|
return artifact;
|
|
633
700
|
}
|
|
634
701
|
if (account.kind === "external-wallet") {
|
|
@@ -650,15 +717,17 @@ var KaspaSdkKeyGenerator = class {
|
|
|
650
717
|
constructor(options) {
|
|
651
718
|
this.networkId = options?.networkId || "simnet";
|
|
652
719
|
const rawLoader = options?.sdkLoader || (async () => {
|
|
653
|
-
return await import("kaspa");
|
|
720
|
+
return await import("kaspa-wasm");
|
|
654
721
|
});
|
|
655
722
|
this.sdkLoader = async () => {
|
|
656
723
|
try {
|
|
657
724
|
return await rawLoader();
|
|
658
725
|
} catch (e) {
|
|
659
|
-
|
|
660
|
-
"Kaspa cryptography adapter missing. Real account generation requires WASM execution.\
|
|
726
|
+
const err = new Error(
|
|
727
|
+
"WALLET_BACKEND_UNAVAILABLE: Kaspa cryptography adapter missing. Real account generation requires WASM execution.\nUse 'hardkas accounts real import' to add a test fixture manually for now."
|
|
661
728
|
);
|
|
729
|
+
err.code = "WALLET_BACKEND_UNAVAILABLE";
|
|
730
|
+
throw err;
|
|
662
731
|
}
|
|
663
732
|
};
|
|
664
733
|
}
|
|
@@ -667,11 +736,14 @@ var KaspaSdkKeyGenerator = class {
|
|
|
667
736
|
const network = options?.networkId || this.networkId;
|
|
668
737
|
try {
|
|
669
738
|
if (typeof sdk.PrivateKey === "function") {
|
|
670
|
-
const
|
|
671
|
-
const
|
|
672
|
-
const
|
|
673
|
-
const
|
|
674
|
-
const
|
|
739
|
+
const crypto3 = await import("crypto");
|
|
740
|
+
const randomBytes = crypto3.randomBytes(32);
|
|
741
|
+
const hex = randomBytes.toString("hex");
|
|
742
|
+
const privKey = new sdk.PrivateKey(hex);
|
|
743
|
+
const kp = privKey.toKeypair();
|
|
744
|
+
const address = kp.toAddress(network).toString();
|
|
745
|
+
const privateKeyStr = kp.privateKey;
|
|
746
|
+
const publicKeyStr = kp.publicKey;
|
|
675
747
|
return {
|
|
676
748
|
address,
|
|
677
749
|
publicKey: publicKeyStr,
|
|
@@ -697,6 +769,164 @@ async function createLocalKaspaWallet(options) {
|
|
|
697
769
|
return await keygen.generateAccount();
|
|
698
770
|
}
|
|
699
771
|
|
|
772
|
+
// src/fixture-signer.ts
|
|
773
|
+
import { calculateContentHash as calculateContentHash3, HARDKAS_VERSION as HARDKAS_VERSION3, ARTIFACT_VERSION as ARTIFACT_VERSION2, CURRENT_HASH_VERSION } from "@hardkas/artifacts";
|
|
774
|
+
function toHex2(arr) {
|
|
775
|
+
return Buffer.from(arr).toString("hex");
|
|
776
|
+
}
|
|
777
|
+
function parseWasmTxToRpc2(wasmTxStr) {
|
|
778
|
+
let parsed = JSON.parse(wasmTxStr);
|
|
779
|
+
while (typeof parsed === "string") {
|
|
780
|
+
parsed = JSON.parse(parsed);
|
|
781
|
+
}
|
|
782
|
+
const txInner = parsed.tx ? parsed.tx.inner : parsed.inner;
|
|
783
|
+
if (!txInner) throw new Error("Could not find inner tx data");
|
|
784
|
+
return {
|
|
785
|
+
version: txInner.version || 0,
|
|
786
|
+
inputs: (txInner.inputs || []).map((i) => ({
|
|
787
|
+
previousOutpoint: {
|
|
788
|
+
transactionId: i.inner.previousOutpoint.inner.transactionId,
|
|
789
|
+
index: i.inner.previousOutpoint.inner.index
|
|
790
|
+
},
|
|
791
|
+
signatureScript: toHex2(i.inner.signatureScript),
|
|
792
|
+
sequence: i.inner.sequence || 0,
|
|
793
|
+
sigOpCount: i.inner.sigOpCount || 1
|
|
794
|
+
})),
|
|
795
|
+
outputs: (txInner.outputs || []).map((o) => ({
|
|
796
|
+
value: o.inner.value,
|
|
797
|
+
scriptPublicKey: {
|
|
798
|
+
version: parseInt(o.inner.scriptPublicKey.substring(0, 4), 16) || 0,
|
|
799
|
+
script: o.inner.scriptPublicKey.substring(4)
|
|
800
|
+
}
|
|
801
|
+
})),
|
|
802
|
+
lockTime: txInner.lockTime || 0,
|
|
803
|
+
subnetworkId: txInner.subnetworkId || "0000000000000000000000000000000000000000",
|
|
804
|
+
gas: txInner.gas || 0,
|
|
805
|
+
payload: txInner.payload && txInner.payload.length > 0 ? toHex2(txInner.payload) : "",
|
|
806
|
+
mass: txInner.mass || 0
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
var HardkasFixtureSigner = class {
|
|
810
|
+
networkId;
|
|
811
|
+
// A deterministic, known private key exclusively for Docker tests.
|
|
812
|
+
FIXTURE_PK = "b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfef";
|
|
813
|
+
constructor(networkId = "simnet") {
|
|
814
|
+
this.networkId = networkId;
|
|
815
|
+
if (networkId === "mainnet") {
|
|
816
|
+
throw new Error("FixtureSigner cannot be used on mainnet.");
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
async loadKaspa() {
|
|
820
|
+
try {
|
|
821
|
+
return await import("kaspa-wasm");
|
|
822
|
+
} catch (e) {
|
|
823
|
+
const err = new Error("SIGNER_BACKEND_UNAVAILABLE: Official Kaspa WASM backend is required to sign transactions.\nInstall it via: npm install kaspa-wasm");
|
|
824
|
+
err.code = "SIGNER_BACKEND_UNAVAILABLE";
|
|
825
|
+
throw err;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
async getAddress() {
|
|
829
|
+
const kaspa = await this.loadKaspa();
|
|
830
|
+
const privKey = new kaspa.PrivateKey(this.FIXTURE_PK);
|
|
831
|
+
return privKey.toKeypair().toAddress(this.networkId).toString();
|
|
832
|
+
}
|
|
833
|
+
async signTransaction(plan) {
|
|
834
|
+
if (plan.networkId === "mainnet") {
|
|
835
|
+
throw new Error("FixtureSigner refuses to sign mainnet transactions.");
|
|
836
|
+
}
|
|
837
|
+
const kaspa = await this.loadKaspa();
|
|
838
|
+
const privateKey = new kaspa.PrivateKey(this.FIXTURE_PK);
|
|
839
|
+
const utxos = plan.inputs.map((u) => {
|
|
840
|
+
if (!u.outpoint.transactionId || u.outpoint.index === void 0) {
|
|
841
|
+
throw new Error(`UTXO is missing transactionId or index. Re-run tx plan.`);
|
|
842
|
+
}
|
|
843
|
+
const spk = u.scriptPublicKey;
|
|
844
|
+
if (!spk) {
|
|
845
|
+
throw new Error("UTXO is missing scriptPublicKey. Real signing flows must never fabricate cryptographic state.");
|
|
846
|
+
}
|
|
847
|
+
return {
|
|
848
|
+
address: plan.from.address,
|
|
849
|
+
outpoint: {
|
|
850
|
+
transactionId: u.outpoint.transactionId,
|
|
851
|
+
index: u.outpoint.index
|
|
852
|
+
},
|
|
853
|
+
utxoEntry: {
|
|
854
|
+
amount: BigInt(u.amountSompi),
|
|
855
|
+
scriptPublicKey: spk,
|
|
856
|
+
blockDaaScore: BigInt(u.blockDaaScore || "0"),
|
|
857
|
+
isCoinbase: !!u.isCoinbase
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
});
|
|
861
|
+
const outputs = plan.outputs.map((o) => {
|
|
862
|
+
if (!o.address) throw new Error("Output is missing address.");
|
|
863
|
+
return {
|
|
864
|
+
address: o.address,
|
|
865
|
+
amount: BigInt(o.amountSompi)
|
|
866
|
+
};
|
|
867
|
+
});
|
|
868
|
+
let changeAddress;
|
|
869
|
+
if (plan.change && plan.change.address) {
|
|
870
|
+
changeAddress = new kaspa.Address(plan.change.address);
|
|
871
|
+
} else {
|
|
872
|
+
changeAddress = new kaspa.Address(plan.from.address);
|
|
873
|
+
}
|
|
874
|
+
const priorityFee = BigInt(plan.estimatedFeeSompi || "0");
|
|
875
|
+
const unsignedTx = kaspa.createTransaction(
|
|
876
|
+
utxos,
|
|
877
|
+
outputs,
|
|
878
|
+
changeAddress,
|
|
879
|
+
priorityFee
|
|
880
|
+
);
|
|
881
|
+
const signedTx = kaspa.signTransaction(unsignedTx, [privateKey], true);
|
|
882
|
+
console.log("SIGNED TX TOSTRING:", signedTx.toString());
|
|
883
|
+
const rawTx = JSON.stringify(parseWasmTxToRpc2(signedTx.toString()));
|
|
884
|
+
const draft = {
|
|
885
|
+
schema: "hardkas.signedTx",
|
|
886
|
+
schemaVersion: "hardkas.artifact.v1",
|
|
887
|
+
hardkasVersion: HARDKAS_VERSION3,
|
|
888
|
+
version: ARTIFACT_VERSION2,
|
|
889
|
+
hashVersion: CURRENT_HASH_VERSION,
|
|
890
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
891
|
+
status: "signed",
|
|
892
|
+
txId: signedTx.id,
|
|
893
|
+
sourcePlanId: plan.planId,
|
|
894
|
+
networkId: plan.networkId,
|
|
895
|
+
mode: plan.mode,
|
|
896
|
+
from: plan.from,
|
|
897
|
+
to: plan.to,
|
|
898
|
+
amountSompi: plan.amountSompi,
|
|
899
|
+
unsignedPayloadHash: plan.contentHash,
|
|
900
|
+
signedTransaction: {
|
|
901
|
+
format: "hex",
|
|
902
|
+
payload: rawTx
|
|
903
|
+
},
|
|
904
|
+
metadata: {
|
|
905
|
+
signerBackend: "kaspa-wasm",
|
|
906
|
+
fixture: true,
|
|
907
|
+
networkGuard: "mainnet_rejected"
|
|
908
|
+
},
|
|
909
|
+
signatureMetadata: [
|
|
910
|
+
{
|
|
911
|
+
signer: "hardkas-local-docker-test-only",
|
|
912
|
+
signedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
913
|
+
}
|
|
914
|
+
],
|
|
915
|
+
lineage: {
|
|
916
|
+
artifactId: "",
|
|
917
|
+
lineageId: plan.lineage?.lineageId || plan.contentHash || "0".repeat(64),
|
|
918
|
+
parentArtifactId: plan.contentHash || plan.planId,
|
|
919
|
+
rootArtifactId: plan.contentHash || plan.planId
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
const hash = calculateContentHash3(draft, CURRENT_HASH_VERSION);
|
|
923
|
+
draft.signedId = `signed-${hash.slice(0, 16)}`;
|
|
924
|
+
draft.contentHash = hash;
|
|
925
|
+
if (draft.lineage) draft.lineage.artifactId = hash;
|
|
926
|
+
return draft;
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
|
|
700
930
|
// src/real-signer.ts
|
|
701
931
|
var UnsupportedRealTxSigner = class {
|
|
702
932
|
async sign() {
|
|
@@ -1067,6 +1297,7 @@ function listDevAccountsSync(workspaceDir) {
|
|
|
1067
1297
|
}
|
|
1068
1298
|
export {
|
|
1069
1299
|
DEV_ACCOUNTS_PASSWORD,
|
|
1300
|
+
HardkasFixtureSigner,
|
|
1070
1301
|
KaspaSdkKeyGenerator,
|
|
1071
1302
|
KaspaSdkRealTxSigner,
|
|
1072
1303
|
KaspaWasmPrivateKeySigner,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hardkas/accounts",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10-alpha",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"hash-wasm": "^4.12.0",
|
|
21
|
-
"
|
|
22
|
-
"@hardkas/
|
|
23
|
-
"@hardkas/
|
|
24
|
-
"@hardkas/
|
|
21
|
+
"kaspa-wasm": "0.13.0",
|
|
22
|
+
"@hardkas/artifacts": "0.8.10-alpha",
|
|
23
|
+
"@hardkas/core": "0.8.10-alpha",
|
|
24
|
+
"@hardkas/config": "0.8.10-alpha",
|
|
25
|
+
"@hardkas/localnet": "0.8.10-alpha"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"tsup": "^8.3.5",
|
|
@@ -40,8 +41,8 @@
|
|
|
40
41
|
},
|
|
41
42
|
"homepage": "https://github.com/KasLabDevs/HardKas/tree/main/packages/accounts#readme",
|
|
42
43
|
"scripts": {
|
|
43
|
-
"build": "tsup src/index.ts --format esm --dts --clean --external kaspa",
|
|
44
|
-
"dev": "tsup src/index.ts --format esm --watch --dts --external kaspa",
|
|
44
|
+
"build": "tsup src/index.ts --format esm --dts --clean --external kaspa-wasm",
|
|
45
|
+
"dev": "tsup src/index.ts --format esm --watch --dts --external kaspa-wasm",
|
|
45
46
|
"test": "vitest run",
|
|
46
47
|
"typecheck": "tsc --noEmit"
|
|
47
48
|
}
|