@hardkas/toolkit 0.11.1-alpha → 0.11.4-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/fee-estimator.d.ts +9 -0
- package/dist/fee-estimator.d.ts.map +1 -0
- package/dist/fee-estimator.js +32 -0
- package/dist/fee-estimator.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/indexer.d.ts +3 -1
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +13 -2
- package/dist/indexer.js.map +1 -1
- package/dist/jobs.d.ts +3 -0
- package/dist/jobs.d.ts.map +1 -1
- package/dist/jobs.js +16 -1
- package/dist/jobs.js.map +1 -1
- package/dist/payment.d.ts +4 -2
- package/dist/payment.d.ts.map +1 -1
- package/dist/payment.js +30 -12
- package/dist/payment.js.map +1 -1
- package/dist/query-api.d.ts +22 -0
- package/dist/query-api.d.ts.map +1 -0
- package/dist/query-api.js +116 -0
- package/dist/query-api.js.map +1 -0
- package/dist/snapshot/serialization.d.ts +0 -9
- package/dist/snapshot/serialization.d.ts.map +1 -1
- package/dist/snapshot/serialization.js +0 -12
- package/dist/snapshot/serialization.js.map +1 -1
- package/dist/stores/invoice-store.d.ts +15 -1
- package/dist/stores/invoice-store.d.ts.map +1 -1
- package/dist/stores/invoice-store.js.map +1 -1
- package/dist/subscriptions.d.ts +22 -0
- package/dist/subscriptions.d.ts.map +1 -0
- package/dist/subscriptions.js +91 -0
- package/dist/subscriptions.js.map +1 -0
- package/dist/wallet.d.ts +81 -2
- package/dist/wallet.d.ts.map +1 -1
- package/dist/wallet.js +328 -13
- package/dist/wallet.js.map +1 -1
- package/package.json +27 -11
- package/.turbo/turbo-build.log +0 -4
- package/src/dag/consensus.ts +0 -150
- package/src/dag/index.ts +0 -73
- package/src/dag/store.ts +0 -40
- package/src/dag/topology.ts +0 -108
- package/src/dag/types.ts +0 -20
- package/src/index.ts +0 -9
- package/src/indexer.ts +0 -128
- package/src/jobs.ts +0 -46
- package/src/payment.ts +0 -81
- package/src/silver/index.ts +0 -102
- package/src/silver/templates.ts +0 -27
- package/src/silver/types.ts +0 -37
- package/src/snapshot/backends/FsSnapshotBackend.ts +0 -100
- package/src/snapshot/backends/MemorySnapshotBackend.ts +0 -65
- package/src/snapshot/index.ts +0 -102
- package/src/snapshot/serialization.ts +0 -35
- package/src/snapshot/types.ts +0 -45
- package/src/stores/invoice-store.ts +0 -53
- package/src/stores/utxo-control-store.ts +0 -78
- package/src/utxos.ts +0 -451
- package/src/wallet.ts +0 -93
- package/test/silver.test.ts +0 -59
- package/test/snapshot.test.ts +0 -99
- package/test/toolkit.test.ts +0 -109
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoice-store.js","sourceRoot":"","sources":["../../src/stores/invoice-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"invoice-store.js","sourceRoot":"","sources":["../../src/stores/invoice-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAsBvD,MAAM,OAAO,gBAAgB;IACjB,KAAK,CAAiC;IAE9C,YAAY,OAA6B;QACrC,IAAI,CAAC,KAAK,GAAG,IAAI,eAAe,CAAgB,OAAO,CAAC,CAAC;IAC7D,CAAC;IAEM,IAAI,CAAC,OAAsB;QAC9B,OAAO,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC7C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAEM,GAAG,CAAC,EAAU;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAEM,IAAI;QACP,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAEM,cAAc,CAAC,UAAkB;QACpC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;IACtE,CAAC;IAEM,QAAQ;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IAC9D,CAAC;IAEM,KAAK,CAAC,UAAmB;QAC5B,IAAI,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,UAAU,EAAE,CAAC;YACb,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;QACjE,CAAC;QAED,OAAO;YACH,aAAa,EAAE,QAAQ,CAAC,MAAM;YAC9B,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM;SACjE,CAAC;IACN,CAAC;CACJ"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { KaspaRpcClient } from "@hardkas/kaspa-rpc";
|
|
2
|
+
export interface WalletSubscriptionEvent {
|
|
3
|
+
type: "transaction";
|
|
4
|
+
txid: string;
|
|
5
|
+
details?: any;
|
|
6
|
+
}
|
|
7
|
+
export type WalletWatchHandler = (event: WalletSubscriptionEvent) => void | Promise<void>;
|
|
8
|
+
export declare class WalletSubscriptionManager {
|
|
9
|
+
private rpc;
|
|
10
|
+
private getAddress;
|
|
11
|
+
private seenTxIds;
|
|
12
|
+
private txIdQueue;
|
|
13
|
+
private readonly MAX_DEDUPE_CACHE;
|
|
14
|
+
private handlers;
|
|
15
|
+
private activeRpcSubscription?;
|
|
16
|
+
constructor(rpc: KaspaRpcClient, getAddress: () => Promise<string>);
|
|
17
|
+
watch(handler: WalletWatchHandler): Promise<{
|
|
18
|
+
unwatch: () => Promise<void>;
|
|
19
|
+
}>;
|
|
20
|
+
private handleRawUtxosChanged;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=subscriptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscriptions.d.ts","sourceRoot":"","sources":["../src/subscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,MAAM,WAAW,uBAAuB;IACpC,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;CACjB;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1F,qBAAa,yBAAyB;IAStB,OAAO,CAAC,GAAG;IAAkB,OAAO,CAAC,UAAU;IAR3D,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAGzC,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,qBAAqB,CAAC,CAAmD;gBAE7D,GAAG,EAAE,cAAc,EAAU,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC;IAErE,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IA6B1F,OAAO,CAAC,qBAAqB;CAwDhC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { logger } from "@hardkas/observability";
|
|
2
|
+
export class WalletSubscriptionManager {
|
|
3
|
+
rpc;
|
|
4
|
+
getAddress;
|
|
5
|
+
seenTxIds = new Set();
|
|
6
|
+
txIdQueue = []; // Simple LRU tracking
|
|
7
|
+
MAX_DEDUPE_CACHE = 1000;
|
|
8
|
+
// Maintain a list of handlers per address
|
|
9
|
+
handlers = new Set();
|
|
10
|
+
activeRpcSubscription;
|
|
11
|
+
constructor(rpc, getAddress) {
|
|
12
|
+
this.rpc = rpc;
|
|
13
|
+
this.getAddress = getAddress;
|
|
14
|
+
}
|
|
15
|
+
async watch(handler) {
|
|
16
|
+
this.handlers.add(handler);
|
|
17
|
+
// If this is the first handler, spin up the underlying RPC subscription
|
|
18
|
+
if (this.handlers.size === 1 && !this.activeRpcSubscription) {
|
|
19
|
+
const addr = await this.getAddress();
|
|
20
|
+
const subscription = await this.rpc.subscribeToUtxosChanged([addr], (data) => {
|
|
21
|
+
this.handleRawUtxosChanged(data);
|
|
22
|
+
});
|
|
23
|
+
this.activeRpcSubscription = {
|
|
24
|
+
unsubscribe: async () => {
|
|
25
|
+
await subscription.unsubscribe();
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
unwatch: async () => {
|
|
31
|
+
this.handlers.delete(handler);
|
|
32
|
+
if (this.handlers.size === 0 && this.activeRpcSubscription) {
|
|
33
|
+
await this.activeRpcSubscription.unsubscribe().catch(() => { });
|
|
34
|
+
this.activeRpcSubscription = undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
handleRawUtxosChanged(data) {
|
|
40
|
+
// Data usually has 'added' and 'removed' arrays containing KaspaRpcUtxo
|
|
41
|
+
const added = data?.added || data?.utxosAdded || [];
|
|
42
|
+
const removed = data?.removed || data?.utxosRemoved || [];
|
|
43
|
+
// Extract all unique txids from this event
|
|
44
|
+
const incomingTxids = new Set();
|
|
45
|
+
const extractTxid = (utxos) => {
|
|
46
|
+
for (const u of utxos) {
|
|
47
|
+
const txid = u?.outpoint?.transactionId || u?.outpoint?.transaction_id || u?.outpoint?.txId;
|
|
48
|
+
if (txid) {
|
|
49
|
+
incomingTxids.add(txid);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
extractTxid(added);
|
|
54
|
+
extractTxid(removed);
|
|
55
|
+
for (const txid of incomingTxids) {
|
|
56
|
+
if (this.seenTxIds.has(txid)) {
|
|
57
|
+
logger.debug("WalletSubscriptionManager deduplicated event", { txid });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
// Mark as seen
|
|
61
|
+
this.seenTxIds.add(txid);
|
|
62
|
+
this.txIdQueue.push(txid);
|
|
63
|
+
if (this.txIdQueue.length > this.MAX_DEDUPE_CACHE) {
|
|
64
|
+
const oldest = this.txIdQueue.shift();
|
|
65
|
+
if (oldest)
|
|
66
|
+
this.seenTxIds.delete(oldest);
|
|
67
|
+
}
|
|
68
|
+
// Dispatch to all wallet-level handlers
|
|
69
|
+
const event = {
|
|
70
|
+
type: "transaction",
|
|
71
|
+
txid,
|
|
72
|
+
details: data
|
|
73
|
+
};
|
|
74
|
+
for (const handler of this.handlers) {
|
|
75
|
+
try {
|
|
76
|
+
// Safe execution
|
|
77
|
+
const res = handler(event);
|
|
78
|
+
if (res instanceof Promise) {
|
|
79
|
+
res.catch(err => {
|
|
80
|
+
logger.error("Wallet watch async handler threw error", { error: err });
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
logger.error("Wallet watch sync handler threw error", { error: err });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=subscriptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../src/subscriptions.ts"],"names":[],"mappings":"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"}
|
package/dist/wallet.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import { WalletQueryProvider } from '@hardkas/query';
|
|
1
2
|
import { WalletUtxoApi } from './utxos.js';
|
|
3
|
+
import { WalletWatchHandler } from './subscriptions.js';
|
|
4
|
+
import { FeePriority } from './fee-estimator.js';
|
|
2
5
|
export interface WalletToolkitOptions {
|
|
3
6
|
storePath?: string;
|
|
4
7
|
network?: "simnet" | "testnet" | "mainnet";
|
|
8
|
+
provider?: WalletQueryProvider;
|
|
9
|
+
rpc?: any;
|
|
10
|
+
signer?: any;
|
|
11
|
+
coinbaseMaturity?: bigint;
|
|
5
12
|
}
|
|
6
13
|
export declare class WalletToolkit {
|
|
7
14
|
readonly name: string;
|
|
@@ -11,24 +18,96 @@ export declare class WalletToolkit {
|
|
|
11
18
|
private addressManager;
|
|
12
19
|
private walletQuery;
|
|
13
20
|
private _utxosApi;
|
|
21
|
+
private _subscriptionManager?;
|
|
14
22
|
private constructor();
|
|
15
23
|
static open(name: string, options?: WalletToolkitOptions): WalletToolkit;
|
|
16
24
|
create(): Promise<void>;
|
|
17
|
-
|
|
25
|
+
receive(): Promise<string>;
|
|
18
26
|
balance(): Promise<bigint>;
|
|
19
27
|
get utxos(): WalletUtxoApi;
|
|
20
28
|
history(): Promise<any[]>;
|
|
29
|
+
watch(cb: WalletWatchHandler): Promise<{
|
|
30
|
+
unwatch: () => void;
|
|
31
|
+
}>;
|
|
21
32
|
estimateFee(opts: {
|
|
22
33
|
to: string;
|
|
23
34
|
amount: bigint;
|
|
24
|
-
|
|
35
|
+
priority?: FeePriority;
|
|
36
|
+
feeRate?: bigint;
|
|
37
|
+
}): Promise<{
|
|
38
|
+
fee: bigint;
|
|
39
|
+
feeRate: bigint;
|
|
40
|
+
estimatedMass: bigint;
|
|
41
|
+
estimatedFee: bigint;
|
|
42
|
+
totalOut: bigint;
|
|
43
|
+
plan: any;
|
|
44
|
+
evidence: "dynamic" | "heuristic";
|
|
45
|
+
mempoolSize?: number;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* @deprecated Use hardkas.tx.plan directly or the specific operation helpers like send() or consolidate()
|
|
49
|
+
*/
|
|
25
50
|
planSend(opts: {
|
|
26
51
|
to: string;
|
|
27
52
|
amount: bigint;
|
|
53
|
+
priority?: FeePriority;
|
|
54
|
+
feeRate?: bigint;
|
|
28
55
|
}): Promise<any>;
|
|
56
|
+
/**
|
|
57
|
+
* @deprecated sendSimulated is moved to testing patterns. Use hardkas.localnet.mine() and standard send() flows.
|
|
58
|
+
*/
|
|
29
59
|
sendSimulated(opts: {
|
|
30
60
|
to: string;
|
|
31
61
|
amount: bigint;
|
|
62
|
+
priority?: FeePriority;
|
|
63
|
+
feeRate?: bigint;
|
|
32
64
|
}): Promise<string>;
|
|
65
|
+
private signAndBroadcast;
|
|
66
|
+
send(opts: {
|
|
67
|
+
to: string;
|
|
68
|
+
amount: bigint;
|
|
69
|
+
priority?: FeePriority;
|
|
70
|
+
feeRate?: bigint;
|
|
71
|
+
}): Promise<{
|
|
72
|
+
txId: string;
|
|
73
|
+
plan: any;
|
|
74
|
+
}>;
|
|
75
|
+
payMany(opts: {
|
|
76
|
+
outputs: {
|
|
77
|
+
address: string;
|
|
78
|
+
amount: bigint;
|
|
79
|
+
}[];
|
|
80
|
+
priority?: FeePriority;
|
|
81
|
+
feeRate?: bigint;
|
|
82
|
+
}): Promise<{
|
|
83
|
+
txId: string;
|
|
84
|
+
plan: any;
|
|
85
|
+
}>;
|
|
86
|
+
sweep(opts: {
|
|
87
|
+
to: string;
|
|
88
|
+
priority?: FeePriority;
|
|
89
|
+
feeRate?: bigint;
|
|
90
|
+
}): Promise<{
|
|
91
|
+
txId: string;
|
|
92
|
+
plan: any;
|
|
93
|
+
}>;
|
|
94
|
+
consolidate(opts: {
|
|
95
|
+
to?: string;
|
|
96
|
+
priority?: FeePriority;
|
|
97
|
+
feeRate?: bigint;
|
|
98
|
+
maxUtxos?: number;
|
|
99
|
+
}): Promise<{
|
|
100
|
+
txId: string;
|
|
101
|
+
plan: any;
|
|
102
|
+
}>;
|
|
103
|
+
split(opts: {
|
|
104
|
+
outputsCount: number;
|
|
105
|
+
amountPerOutput: bigint;
|
|
106
|
+
priority?: FeePriority;
|
|
107
|
+
feeRate?: bigint;
|
|
108
|
+
}): Promise<{
|
|
109
|
+
txId: string;
|
|
110
|
+
plan: any;
|
|
111
|
+
}>;
|
|
33
112
|
}
|
|
34
113
|
//# sourceMappingURL=wallet.d.ts.map
|
package/dist/wallet.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wallet.d.ts","sourceRoot":"","sources":["../src/wallet.ts"],"names":[],"mappings":"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"}
|
package/dist/wallet.js
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
import { WalletManagerImpl, WalletStateStoreJson, AddressManager } from '@hardkas/accounts';
|
|
2
2
|
import { WalletQuery } from '@hardkas/query';
|
|
3
|
+
import { buildPaymentPlan, toTxBuilderUtxo } from '@hardkas/tx-builder';
|
|
3
4
|
import { WalletUtxoApi } from './utxos.js';
|
|
4
5
|
import { UtxoControlStore } from './stores/utxo-control-store.js';
|
|
6
|
+
import { logger, metrics } from '@hardkas/observability';
|
|
7
|
+
import { WalletSubscriptionManager } from './subscriptions.js';
|
|
8
|
+
import { calculateDynamicFeeRate } from './fee-estimator.js';
|
|
9
|
+
metrics.register({
|
|
10
|
+
name: "wallet_tx_generated_total",
|
|
11
|
+
help: "Total tx generated by wallet",
|
|
12
|
+
type: "counter"
|
|
13
|
+
});
|
|
14
|
+
metrics.register({
|
|
15
|
+
name: "wallet_tx_submitted_total",
|
|
16
|
+
help: "Total tx submitted by wallet",
|
|
17
|
+
type: "counter"
|
|
18
|
+
});
|
|
19
|
+
metrics.register({
|
|
20
|
+
name: "wallet_tx_failed_total",
|
|
21
|
+
help: "Total tx failed by wallet",
|
|
22
|
+
type: "counter"
|
|
23
|
+
});
|
|
5
24
|
export class WalletToolkit {
|
|
6
25
|
name;
|
|
7
26
|
store;
|
|
@@ -10,18 +29,66 @@ export class WalletToolkit {
|
|
|
10
29
|
addressManager;
|
|
11
30
|
walletQuery;
|
|
12
31
|
_utxosApi;
|
|
32
|
+
_subscriptionManager;
|
|
13
33
|
constructor(name, store, options) {
|
|
14
34
|
this.name = name;
|
|
15
35
|
this.store = store;
|
|
16
36
|
this.options = options;
|
|
17
37
|
this.walletManager = new WalletManagerImpl();
|
|
18
38
|
this.addressManager = AddressManager;
|
|
19
|
-
|
|
39
|
+
let provider = options.provider;
|
|
40
|
+
if (!provider && options.rpc) {
|
|
41
|
+
provider = {
|
|
42
|
+
source: "rpc",
|
|
43
|
+
async getBalances(addresses) {
|
|
44
|
+
const res = {};
|
|
45
|
+
for (const addr of addresses) {
|
|
46
|
+
const bal = await options.rpc.getBalanceByAddress(addr);
|
|
47
|
+
res[addr] = bal.balanceSompi;
|
|
48
|
+
}
|
|
49
|
+
return res;
|
|
50
|
+
},
|
|
51
|
+
async getUtxos(addresses) {
|
|
52
|
+
const res = {};
|
|
53
|
+
for (const addr of addresses) {
|
|
54
|
+
const utxos = await options.rpc.getUtxosByAddress(addr);
|
|
55
|
+
res[addr] = utxos.map((u) => ({
|
|
56
|
+
transactionId: u.outpoint.transactionId,
|
|
57
|
+
outputIndex: u.outpoint.index,
|
|
58
|
+
amountSompi: u.amountSompi,
|
|
59
|
+
scriptPublicKey: u.scriptPublicKey,
|
|
60
|
+
address: u.address,
|
|
61
|
+
blockDaaScore: u.blockDaaScore,
|
|
62
|
+
isCoinbase: u.isCoinbase
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
return res;
|
|
66
|
+
},
|
|
67
|
+
async getHistory() {
|
|
68
|
+
return { items: [] };
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
else if (!provider) {
|
|
73
|
+
provider = {
|
|
74
|
+
source: "dummy",
|
|
75
|
+
async getBalances() { return {}; },
|
|
76
|
+
async getUtxos() { return {}; },
|
|
77
|
+
async getHistory() { return { items: [] }; }
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
this.walletQuery = new WalletQuery({ provider, network: options.network || "simnet" });
|
|
20
81
|
const utxoControlPath = options.storePath
|
|
21
82
|
? options.storePath.replace('.json', '-utxo-control.json')
|
|
22
83
|
: 'default-utxo-control.json';
|
|
23
84
|
const utxoStore = new UtxoControlStore(utxoControlPath);
|
|
24
|
-
this._utxosApi = new WalletUtxoApi(async () =>
|
|
85
|
+
this._utxosApi = new WalletUtxoApi(async () => {
|
|
86
|
+
const addr = await this.receive();
|
|
87
|
+
const res = await this.walletQuery.getUtxos([addr]);
|
|
88
|
+
if (!res.ok)
|
|
89
|
+
throw new Error(res.error || "Degraded query result");
|
|
90
|
+
return res.utxos[addr] || [];
|
|
91
|
+
}, async () => this.receive(), async (inputs, outputs) => BigInt(inputs * 1000 + outputs * 1000), // Dummy fee estimation
|
|
25
92
|
utxoStore);
|
|
26
93
|
}
|
|
27
94
|
static open(name, options = {}) {
|
|
@@ -31,7 +98,7 @@ export class WalletToolkit {
|
|
|
31
98
|
async create() {
|
|
32
99
|
this.walletManager.create({ walletId: this.name });
|
|
33
100
|
}
|
|
34
|
-
async
|
|
101
|
+
async receive() {
|
|
35
102
|
// High level facade: gets receive address
|
|
36
103
|
const seedRef = this.walletManager.getSeedRef(this.name);
|
|
37
104
|
return this.addressManager.derive({
|
|
@@ -43,30 +110,278 @@ export class WalletToolkit {
|
|
|
43
110
|
}).address;
|
|
44
111
|
}
|
|
45
112
|
async balance() {
|
|
46
|
-
const addr = await this.
|
|
113
|
+
const addr = await this.receive();
|
|
47
114
|
return (await this.walletQuery.getBalance([addr]));
|
|
48
115
|
}
|
|
49
116
|
get utxos() {
|
|
50
117
|
return this._utxosApi;
|
|
51
118
|
}
|
|
52
119
|
async history() {
|
|
53
|
-
const addr = await this.
|
|
54
|
-
|
|
120
|
+
const addr = await this.receive();
|
|
121
|
+
// Return local observed history (best-effort locally + remote if available)
|
|
122
|
+
const remoteHistory = await this.walletQuery.getHistory({ addresses: [addr] });
|
|
123
|
+
// Enhance with local mock/artifacts if needed, but per specs:
|
|
124
|
+
// "txs enviadas por HardKAS, UTXO changes observados, receipts locales"
|
|
125
|
+
// For now, we return remoteHistory items + potentially local state from utxos store
|
|
126
|
+
const items = remoteHistory?.items || remoteHistory || [];
|
|
127
|
+
return items;
|
|
128
|
+
}
|
|
129
|
+
async watch(cb) {
|
|
130
|
+
if (!this.options.rpc || !this.options.rpc.subscribeToUtxosChanged) {
|
|
131
|
+
throw new Error("RPC client with subscription support is required for watch()");
|
|
132
|
+
}
|
|
133
|
+
if (!this._subscriptionManager) {
|
|
134
|
+
this._subscriptionManager = new WalletSubscriptionManager(this.options.rpc, async () => this.receive());
|
|
135
|
+
}
|
|
136
|
+
return this._subscriptionManager.watch(cb);
|
|
55
137
|
}
|
|
56
138
|
async estimateFee(opts) {
|
|
57
|
-
const
|
|
58
|
-
|
|
139
|
+
const addr = await this.receive();
|
|
140
|
+
const availableUtxos = await this.utxos.list();
|
|
141
|
+
let finalFeeRate = opts.feeRate;
|
|
142
|
+
let evidence = "heuristic";
|
|
143
|
+
let mempoolSize;
|
|
144
|
+
if (!finalFeeRate) {
|
|
145
|
+
const dynamic = await calculateDynamicFeeRate(this.options.rpc, opts.priority || "normal");
|
|
146
|
+
finalFeeRate = dynamic.feeRate;
|
|
147
|
+
evidence = dynamic.evidence;
|
|
148
|
+
mempoolSize = dynamic.mempoolSize;
|
|
149
|
+
}
|
|
150
|
+
// Fetch current DAA score for coinbase maturity filtering
|
|
151
|
+
let virtualDaaScore;
|
|
152
|
+
let networkId;
|
|
153
|
+
if (this.options.rpc?.getBlockDagInfo) {
|
|
154
|
+
try {
|
|
155
|
+
const dagInfo = await this.options.rpc.getBlockDagInfo();
|
|
156
|
+
virtualDaaScore = dagInfo.virtualDaaScore;
|
|
157
|
+
networkId = dagInfo.networkId;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// If DAG info unavailable, proceed without maturity filtering
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const plan = buildPaymentPlan({
|
|
164
|
+
fromAddress: addr,
|
|
165
|
+
outputs: [{ address: opts.to, amountSompi: opts.amount }],
|
|
166
|
+
availableUtxos: availableUtxos,
|
|
167
|
+
feeRateSompiPerMass: finalFeeRate || 1n,
|
|
168
|
+
changeAddress: addr,
|
|
169
|
+
coinbaseMaturity: networkId === "mainnet" ? 1000n : 100n,
|
|
170
|
+
...(virtualDaaScore !== undefined ? { virtualDaaScore } : {}),
|
|
171
|
+
...(networkId !== undefined ? { networkId } : {})
|
|
172
|
+
});
|
|
173
|
+
const exactMass = BigInt(plan.estimatedMass);
|
|
59
174
|
return {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
175
|
+
fee: plan.estimatedFeeSompi,
|
|
176
|
+
feeRate: finalFeeRate || 1n,
|
|
177
|
+
estimatedMass: exactMass,
|
|
178
|
+
estimatedFee: plan.estimatedFeeSompi,
|
|
179
|
+
totalOut: opts.amount + plan.estimatedFeeSompi,
|
|
180
|
+
plan,
|
|
181
|
+
evidence,
|
|
182
|
+
...(mempoolSize !== undefined ? { mempoolSize } : {})
|
|
63
183
|
};
|
|
64
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* @deprecated Use hardkas.tx.plan directly or the specific operation helpers like send() or consolidate()
|
|
187
|
+
*/
|
|
65
188
|
async planSend(opts) {
|
|
66
|
-
return {
|
|
189
|
+
return {
|
|
190
|
+
message: "This is a stub, as requested by P61 audit. Real planning requires UTXO orchestration."
|
|
191
|
+
};
|
|
67
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* @deprecated sendSimulated is moved to testing patterns. Use hardkas.localnet.mine() and standard send() flows.
|
|
195
|
+
*/
|
|
68
196
|
async sendSimulated(opts) {
|
|
69
|
-
return `
|
|
197
|
+
return `sim_tx_${Date.now()}`;
|
|
198
|
+
}
|
|
199
|
+
async signAndBroadcast(plan) {
|
|
200
|
+
if (!this.options.signer) {
|
|
201
|
+
const err = new Error("MISSING_SIGNER: WalletToolkit requires an explicit signer to sign transactions.");
|
|
202
|
+
err.code = "MISSING_SIGNER";
|
|
203
|
+
throw err;
|
|
204
|
+
}
|
|
205
|
+
if (!this.options.rpc) {
|
|
206
|
+
throw new Error("RPC client is required for broadcast.");
|
|
207
|
+
}
|
|
208
|
+
const addr = await this.receive();
|
|
209
|
+
const fakeArtifact = {
|
|
210
|
+
schema: "hardkas.txPlan",
|
|
211
|
+
planId: "wallet-toolkit-inline",
|
|
212
|
+
networkId: this.options.network || "simnet",
|
|
213
|
+
mode: "real",
|
|
214
|
+
from: { address: addr },
|
|
215
|
+
to: { address: plan.outputs[0]?.address || addr },
|
|
216
|
+
amountSompi: plan.outputs.reduce((acc, out) => acc + BigInt(out.amountSompi), 0n).toString(),
|
|
217
|
+
estimatedFeeSompi: plan.estimatedFeeSompi.toString(),
|
|
218
|
+
inputs: plan.inputs,
|
|
219
|
+
outputs: plan.outputs,
|
|
220
|
+
change: plan.change,
|
|
221
|
+
};
|
|
222
|
+
const result = await (this.options.signer.signTxPlan
|
|
223
|
+
? this.options.signer.signTxPlan({ planArtifact: fakeArtifact })
|
|
224
|
+
: this.options.signer.signTransaction(fakeArtifact));
|
|
225
|
+
const rawTx = JSON.parse(result.signedTransaction.payload);
|
|
226
|
+
const submitRes = await this.options.rpc.submitTransaction(rawTx);
|
|
227
|
+
metrics.inc("wallet_tx_submitted_total");
|
|
228
|
+
logger.info("Wallet tx submitted successfully", { txId: submitRes.transactionId || result.txId });
|
|
229
|
+
// Return full artifact/evidence format
|
|
230
|
+
return {
|
|
231
|
+
txId: submitRes.transactionId || result.txId,
|
|
232
|
+
plan
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
async send(opts) {
|
|
236
|
+
return this.payMany({
|
|
237
|
+
outputs: [{ address: opts.to, amount: opts.amount }],
|
|
238
|
+
...(opts.priority !== undefined ? { priority: opts.priority } : {}),
|
|
239
|
+
...(opts.feeRate !== undefined ? { feeRate: opts.feeRate } : {})
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
async payMany(opts) {
|
|
243
|
+
const addr = await this.receive();
|
|
244
|
+
const availableUtxos = await this.utxos.list();
|
|
245
|
+
let finalFeeRate = opts.feeRate;
|
|
246
|
+
if (!finalFeeRate) {
|
|
247
|
+
const dynamic = await calculateDynamicFeeRate(this.options.rpc, opts.priority || "normal");
|
|
248
|
+
finalFeeRate = dynamic.feeRate;
|
|
249
|
+
}
|
|
250
|
+
// Fetch current DAA score for coinbase maturity filtering
|
|
251
|
+
let virtualDaaScore;
|
|
252
|
+
let networkId;
|
|
253
|
+
if (this.options.rpc?.getBlockDagInfo) {
|
|
254
|
+
try {
|
|
255
|
+
const dagInfo = await this.options.rpc.getBlockDagInfo();
|
|
256
|
+
virtualDaaScore = dagInfo.virtualDaaScore;
|
|
257
|
+
networkId = dagInfo.networkId;
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// If DAG info unavailable, proceed without maturity filtering
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const builderOutputs = opts.outputs.map(o => ({ address: o.address, amountSompi: o.amount }));
|
|
264
|
+
const mappedUtxos = availableUtxos.map(u => toTxBuilderUtxo(u));
|
|
265
|
+
const plan = buildPaymentPlan({
|
|
266
|
+
fromAddress: addr,
|
|
267
|
+
outputs: builderOutputs,
|
|
268
|
+
availableUtxos: mappedUtxos,
|
|
269
|
+
feeRateSompiPerMass: finalFeeRate || 1n,
|
|
270
|
+
changeAddress: addr,
|
|
271
|
+
coinbaseMaturity: networkId === "mainnet" ? 1000n : 100n,
|
|
272
|
+
...(virtualDaaScore !== undefined ? { virtualDaaScore } : {}),
|
|
273
|
+
...(networkId !== undefined ? { networkId } : {})
|
|
274
|
+
});
|
|
275
|
+
return this.signAndBroadcast(plan);
|
|
276
|
+
}
|
|
277
|
+
async sweep(opts) {
|
|
278
|
+
const addr = await this.receive();
|
|
279
|
+
let availableUtxos = await this.utxos.list();
|
|
280
|
+
if (availableUtxos.length === 0)
|
|
281
|
+
throw new Error("No UTXOs to sweep");
|
|
282
|
+
let finalFeeRate = opts.feeRate;
|
|
283
|
+
if (!finalFeeRate) {
|
|
284
|
+
const dynamic = await calculateDynamicFeeRate(this.options.rpc, opts.priority || "normal");
|
|
285
|
+
finalFeeRate = dynamic.feeRate;
|
|
286
|
+
}
|
|
287
|
+
// Filter immature coinbase UTXOs
|
|
288
|
+
if (this.options.rpc?.getBlockDagInfo) {
|
|
289
|
+
try {
|
|
290
|
+
const dagInfo = await this.options.rpc.getBlockDagInfo();
|
|
291
|
+
const virtualDaaScore = dagInfo.virtualDaaScore;
|
|
292
|
+
if (virtualDaaScore !== undefined) {
|
|
293
|
+
if (this.options.coinbaseMaturity === undefined) {
|
|
294
|
+
throw new Error("COINBASE_MATURITY_UNRESOLVED: WalletToolkit requires coinbaseMaturity to be explicitly provided");
|
|
295
|
+
}
|
|
296
|
+
const COINBASE_MATURITY = this.options.coinbaseMaturity;
|
|
297
|
+
availableUtxos = availableUtxos.filter((u) => {
|
|
298
|
+
if (u.isCoinbase && u.blockDaaScore !== undefined &&
|
|
299
|
+
(virtualDaaScore - BigInt(u.blockDaaScore)) < COINBASE_MATURITY) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
return true;
|
|
303
|
+
});
|
|
304
|
+
if (availableUtxos.length === 0)
|
|
305
|
+
throw new Error("No mature UTXOs to sweep");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch (e) {
|
|
309
|
+
if (e.message?.includes("No mature UTXOs"))
|
|
310
|
+
throw e;
|
|
311
|
+
// If DAG info unavailable, proceed without maturity filtering
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
const { estimateTransactionMass } = await import("@hardkas/tx-builder");
|
|
315
|
+
const massRes = estimateTransactionMass({
|
|
316
|
+
inputCount: availableUtxos.length,
|
|
317
|
+
outputs: [{ address: opts.to }],
|
|
318
|
+
payloadBytes: 0,
|
|
319
|
+
hasChange: false
|
|
320
|
+
});
|
|
321
|
+
const fee = massRes.mass * (finalFeeRate || 1n);
|
|
322
|
+
const totalValue = availableUtxos.reduce((acc, u) => acc + BigInt(u.amountSompi), 0n);
|
|
323
|
+
const sendValue = totalValue - fee;
|
|
324
|
+
if (sendValue <= 0n) {
|
|
325
|
+
throw new Error("Insufficient funds to cover sweep fee");
|
|
326
|
+
}
|
|
327
|
+
const plan = {
|
|
328
|
+
inputs: availableUtxos,
|
|
329
|
+
outputs: [{ address: opts.to, amountSompi: sendValue }],
|
|
330
|
+
estimatedMass: massRes.mass,
|
|
331
|
+
estimatedFeeSompi: fee
|
|
332
|
+
};
|
|
333
|
+
return this.signAndBroadcast(plan);
|
|
334
|
+
}
|
|
335
|
+
async consolidate(opts) {
|
|
336
|
+
const addr = await this.receive();
|
|
337
|
+
const availableUtxos = await this.utxos.list();
|
|
338
|
+
const maxUtxos = opts.maxUtxos || 100;
|
|
339
|
+
if (availableUtxos.length <= 1) {
|
|
340
|
+
throw new Error("Not enough UTXOs to consolidate");
|
|
341
|
+
}
|
|
342
|
+
// Sort by smallest first to consolidate dust
|
|
343
|
+
const sortedUtxos = [...availableUtxos].sort((a, b) => {
|
|
344
|
+
const diff = BigInt(a.amountSompi) - BigInt(b.amountSompi);
|
|
345
|
+
return diff < 0n ? -1 : diff > 0n ? 1 : 0;
|
|
346
|
+
});
|
|
347
|
+
const selectedUtxos = sortedUtxos.slice(0, maxUtxos);
|
|
348
|
+
const toAddress = opts.to || addr;
|
|
349
|
+
let finalFeeRate = opts.feeRate;
|
|
350
|
+
if (!finalFeeRate) {
|
|
351
|
+
const dynamic = await calculateDynamicFeeRate(this.options.rpc, opts.priority || "normal");
|
|
352
|
+
finalFeeRate = dynamic.feeRate;
|
|
353
|
+
}
|
|
354
|
+
const { estimateTransactionMass } = await import("@hardkas/tx-builder");
|
|
355
|
+
const massRes = estimateTransactionMass({
|
|
356
|
+
inputCount: selectedUtxos.length,
|
|
357
|
+
outputs: [{ address: toAddress }],
|
|
358
|
+
payloadBytes: 0,
|
|
359
|
+
hasChange: false
|
|
360
|
+
});
|
|
361
|
+
const fee = massRes.mass * (finalFeeRate || 1n);
|
|
362
|
+
const totalValue = selectedUtxos.reduce((acc, u) => acc + BigInt(u.amountSompi), 0n);
|
|
363
|
+
const sendValue = totalValue - fee;
|
|
364
|
+
if (sendValue <= 0n) {
|
|
365
|
+
throw new Error("Insufficient funds to cover consolidate fee");
|
|
366
|
+
}
|
|
367
|
+
const plan = {
|
|
368
|
+
inputs: selectedUtxos,
|
|
369
|
+
outputs: [{ address: toAddress, amountSompi: sendValue }],
|
|
370
|
+
estimatedMass: massRes.mass,
|
|
371
|
+
estimatedFeeSompi: fee
|
|
372
|
+
};
|
|
373
|
+
return this.signAndBroadcast(plan);
|
|
374
|
+
}
|
|
375
|
+
async split(opts) {
|
|
376
|
+
if (opts.outputsCount <= 0)
|
|
377
|
+
throw new Error("outputsCount must be greater than 0");
|
|
378
|
+
const addr = await this.receive();
|
|
379
|
+
const outputs = Array(opts.outputsCount).fill({ address: addr, amount: opts.amountPerOutput });
|
|
380
|
+
return this.payMany({
|
|
381
|
+
outputs,
|
|
382
|
+
...(opts.priority !== undefined ? { priority: opts.priority } : {}),
|
|
383
|
+
...(opts.feeRate !== undefined ? { feeRate: opts.feeRate } : {})
|
|
384
|
+
});
|
|
70
385
|
}
|
|
71
386
|
}
|
|
72
387
|
//# sourceMappingURL=wallet.js.map
|