@hardkas/toolkit 0.11.1-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/.turbo/turbo-build.log +4 -0
- package/LICENSE +21 -0
- package/dist/dag/consensus.d.ts +20 -0
- package/dist/dag/consensus.d.ts.map +1 -0
- package/dist/dag/consensus.js +131 -0
- package/dist/dag/consensus.js.map +1 -0
- package/dist/dag/index.d.ts +28 -0
- package/dist/dag/index.d.ts.map +1 -0
- package/dist/dag/index.js +57 -0
- package/dist/dag/index.js.map +1 -0
- package/dist/dag/store.d.ts +11 -0
- package/dist/dag/store.d.ts.map +1 -0
- package/dist/dag/store.js +32 -0
- package/dist/dag/store.js.map +1 -0
- package/dist/dag/topology.d.ts +14 -0
- package/dist/dag/topology.d.ts.map +1 -0
- package/dist/dag/topology.js +98 -0
- package/dist/dag/topology.js.map +1 -0
- package/dist/dag/types.d.ts +21 -0
- package/dist/dag/types.d.ts.map +1 -0
- package/dist/dag/types.js +2 -0
- package/dist/dag/types.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +34 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +111 -0
- package/dist/indexer.js.map +1 -0
- package/dist/jobs.d.ts +18 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +33 -0
- package/dist/jobs.js.map +1 -0
- package/dist/payment.d.ts +25 -0
- package/dist/payment.d.ts.map +1 -0
- package/dist/payment.js +66 -0
- package/dist/payment.js.map +1 -0
- package/dist/silver/index.d.ts +20 -0
- package/dist/silver/index.d.ts.map +1 -0
- package/dist/silver/index.js +94 -0
- package/dist/silver/index.js.map +1 -0
- package/dist/silver/templates.d.ts +2 -0
- package/dist/silver/templates.d.ts.map +1 -0
- package/dist/silver/templates.js +28 -0
- package/dist/silver/templates.js.map +1 -0
- package/dist/silver/types.d.ts +34 -0
- package/dist/silver/types.d.ts.map +1 -0
- package/dist/silver/types.js +2 -0
- package/dist/silver/types.js.map +1 -0
- package/dist/snapshot/backends/FsSnapshotBackend.d.ts +14 -0
- package/dist/snapshot/backends/FsSnapshotBackend.d.ts.map +1 -0
- package/dist/snapshot/backends/FsSnapshotBackend.js +82 -0
- package/dist/snapshot/backends/FsSnapshotBackend.js.map +1 -0
- package/dist/snapshot/backends/MemorySnapshotBackend.d.ts +13 -0
- package/dist/snapshot/backends/MemorySnapshotBackend.d.ts.map +1 -0
- package/dist/snapshot/backends/MemorySnapshotBackend.js +55 -0
- package/dist/snapshot/backends/MemorySnapshotBackend.js.map +1 -0
- package/dist/snapshot/index.d.ts +19 -0
- package/dist/snapshot/index.d.ts.map +1 -0
- package/dist/snapshot/index.js +84 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/snapshot/serialization.d.ts +18 -0
- package/dist/snapshot/serialization.d.ts.map +1 -0
- package/dist/snapshot/serialization.js +31 -0
- package/dist/snapshot/serialization.js.map +1 -0
- package/dist/snapshot/types.d.ts +44 -0
- package/dist/snapshot/types.d.ts.map +1 -0
- package/dist/snapshot/types.js +2 -0
- package/dist/snapshot/types.js.map +1 -0
- package/dist/stores/invoice-store.d.ts +26 -0
- package/dist/stores/invoice-store.d.ts.map +1 -0
- package/dist/stores/invoice-store.js +34 -0
- package/dist/stores/invoice-store.js.map +1 -0
- package/dist/stores/utxo-control-store.d.ts +23 -0
- package/dist/stores/utxo-control-store.d.ts.map +1 -0
- package/dist/stores/utxo-control-store.js +64 -0
- package/dist/stores/utxo-control-store.js.map +1 -0
- package/dist/utxos.d.ts +128 -0
- package/dist/utxos.d.ts.map +1 -0
- package/dist/utxos.js +331 -0
- package/dist/utxos.js.map +1 -0
- package/dist/wallet.d.ts +34 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +72 -0
- package/dist/wallet.js.map +1 -0
- package/package.json +28 -0
- package/src/dag/consensus.ts +150 -0
- package/src/dag/index.ts +73 -0
- package/src/dag/store.ts +40 -0
- package/src/dag/topology.ts +108 -0
- package/src/dag/types.ts +20 -0
- package/src/index.ts +9 -0
- package/src/indexer.ts +128 -0
- package/src/jobs.ts +46 -0
- package/src/payment.ts +81 -0
- package/src/silver/index.ts +102 -0
- package/src/silver/templates.ts +27 -0
- package/src/silver/types.ts +37 -0
- package/src/snapshot/backends/FsSnapshotBackend.ts +100 -0
- package/src/snapshot/backends/MemorySnapshotBackend.ts +65 -0
- package/src/snapshot/index.ts +102 -0
- package/src/snapshot/serialization.ts +35 -0
- package/src/snapshot/types.ts +45 -0
- package/src/stores/invoice-store.ts +53 -0
- package/src/stores/utxo-control-store.ts +78 -0
- package/src/utxos.ts +451 -0
- package/src/wallet.ts +93 -0
- package/test/silver.test.ts +59 -0
- package/test/snapshot.test.ts +99 -0
- package/test/toolkit.test.ts +109 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import { snapshotReplacer, snapshotReviver } from "../serialization.js";
|
|
3
|
+
export class MemorySnapshotBackend {
|
|
4
|
+
snapshots = new Map();
|
|
5
|
+
async save(id, name, state) {
|
|
6
|
+
const stateHashes = {};
|
|
7
|
+
const participants = Object.keys(state);
|
|
8
|
+
for (const [pId, pState] of Object.entries(state)) {
|
|
9
|
+
stateHashes[pId] = crypto.createHash("sha256").update(JSON.stringify(pState, snapshotReplacer)).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
const manifest = {
|
|
12
|
+
schema: "hardkas.snapshot.v1",
|
|
13
|
+
snapshotId: id,
|
|
14
|
+
name,
|
|
15
|
+
createdAt: Date.now(),
|
|
16
|
+
backend: "memory",
|
|
17
|
+
participants,
|
|
18
|
+
stateHashes,
|
|
19
|
+
claims: {
|
|
20
|
+
containsFullStateInline: true,
|
|
21
|
+
restorableLocalState: true,
|
|
22
|
+
networkStateCaptured: false
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
// Deep copy state to ensure isolation
|
|
26
|
+
const deepCopiedState = JSON.parse(JSON.stringify(state, snapshotReplacer), snapshotReviver);
|
|
27
|
+
this.snapshots.set(id, { manifest, state: deepCopiedState });
|
|
28
|
+
return manifest;
|
|
29
|
+
}
|
|
30
|
+
async load(id) {
|
|
31
|
+
const entry = this.snapshots.get(id);
|
|
32
|
+
if (!entry) {
|
|
33
|
+
throw new Error(`Snapshot ${id} not found in memory backend`);
|
|
34
|
+
}
|
|
35
|
+
// Return a deep copy to prevent mutation of the snapshot itself
|
|
36
|
+
return {
|
|
37
|
+
manifest: JSON.parse(JSON.stringify(entry.manifest)),
|
|
38
|
+
state: JSON.parse(JSON.stringify(entry.state, snapshotReplacer), snapshotReviver)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async list() {
|
|
42
|
+
return Array.from(this.snapshots.values()).map(s => s.manifest);
|
|
43
|
+
}
|
|
44
|
+
async delete(id) {
|
|
45
|
+
this.snapshots.delete(id);
|
|
46
|
+
}
|
|
47
|
+
async branch(baseId, newId, newName) {
|
|
48
|
+
const entry = this.snapshots.get(baseId);
|
|
49
|
+
if (!entry)
|
|
50
|
+
throw new Error(`Base snapshot ${baseId} not found`);
|
|
51
|
+
const newState = JSON.parse(JSON.stringify(entry.state, snapshotReplacer), snapshotReviver);
|
|
52
|
+
return this.save(newId, newName, newState);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=MemorySnapshotBackend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemorySnapshotBackend.js","sourceRoot":"","sources":["../../../src/snapshot/backends/MemorySnapshotBackend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SnapshotManifest, SnapshotParticipant } from "./types.js";
|
|
2
|
+
export interface SnapshotToolkitOptions {
|
|
3
|
+
backend?: "memory" | "filesystem";
|
|
4
|
+
dir?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class SnapshotToolkit {
|
|
7
|
+
private backend;
|
|
8
|
+
private participants;
|
|
9
|
+
private constructor();
|
|
10
|
+
static open(options?: SnapshotToolkitOptions): SnapshotToolkit;
|
|
11
|
+
register(id: string, participant: SnapshotParticipant): void;
|
|
12
|
+
create(name: string): Promise<SnapshotManifest>;
|
|
13
|
+
restore(id: string): Promise<void>;
|
|
14
|
+
branch(baseId: string, newName: string): Promise<SnapshotManifest>;
|
|
15
|
+
diff(idA: string, idB: string): Promise<Record<string, any>>;
|
|
16
|
+
compare(idA: string, idB: string): Promise<boolean>;
|
|
17
|
+
list(): Promise<SnapshotManifest[]>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/snapshot/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,gBAAgB,EAAE,mBAAmB,EAAiB,MAAM,YAAY,CAAC;AAKnG,MAAM,WAAW,sBAAsB;IACnC,OAAO,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,eAAe;IACxB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,YAAY,CAA0C;IAE9D,OAAO;WAQO,IAAI,CAAC,OAAO,GAAE,sBAA8C,GAAG,eAAe;IAIrF,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,mBAAmB,GAAG,IAAI;IAItD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAY/C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BlC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAKlE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAmB5D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKnD,IAAI,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;CAGnD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { MemorySnapshotBackend } from "./backends/MemorySnapshotBackend.js";
|
|
2
|
+
import { FsSnapshotBackend } from "./backends/FsSnapshotBackend.js";
|
|
3
|
+
import crypto from "crypto";
|
|
4
|
+
export class SnapshotToolkit {
|
|
5
|
+
backend;
|
|
6
|
+
participants = new Map();
|
|
7
|
+
constructor(options) {
|
|
8
|
+
if (options.backend === "filesystem") {
|
|
9
|
+
this.backend = new FsSnapshotBackend(options.dir);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
this.backend = new MemorySnapshotBackend();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
static open(options = { backend: "memory" }) {
|
|
16
|
+
return new SnapshotToolkit(options);
|
|
17
|
+
}
|
|
18
|
+
register(id, participant) {
|
|
19
|
+
this.participants.set(id, participant);
|
|
20
|
+
}
|
|
21
|
+
async create(name) {
|
|
22
|
+
const id = crypto.randomUUID();
|
|
23
|
+
const state = {};
|
|
24
|
+
// Snapshot all participants
|
|
25
|
+
for (const [pId, participant] of this.participants.entries()) {
|
|
26
|
+
state[pId] = await participant.snapshot();
|
|
27
|
+
}
|
|
28
|
+
return this.backend.save(id, name, state);
|
|
29
|
+
}
|
|
30
|
+
async restore(id) {
|
|
31
|
+
const { state } = await this.backend.load(id);
|
|
32
|
+
// 1. load state & restore (but don't reload yet in case something fails)
|
|
33
|
+
for (const [pId, pState] of Object.entries(state)) {
|
|
34
|
+
const participant = this.participants.get(pId);
|
|
35
|
+
if (!participant) {
|
|
36
|
+
throw new Error(`Cannot restore state for participant '${pId}' because it is not registered`);
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await participant.restore(pState);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
throw new Error(`Failed to restore participant '${pId}': ${err.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 2. reload all
|
|
46
|
+
for (const [pId, _] of Object.entries(state)) {
|
|
47
|
+
const participant = this.participants.get(pId);
|
|
48
|
+
try {
|
|
49
|
+
await participant.reload();
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
throw new Error(`Failed to reload participant '${pId}' after restoration: ${err.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async branch(baseId, newName) {
|
|
57
|
+
const newId = crypto.randomUUID();
|
|
58
|
+
return this.backend.branch(baseId, newId, newName);
|
|
59
|
+
}
|
|
60
|
+
async diff(idA, idB) {
|
|
61
|
+
const [a, b] = await Promise.all([this.backend.load(idA), this.backend.load(idB)]);
|
|
62
|
+
const differences = {};
|
|
63
|
+
const allParticipants = new Set([...a.manifest.participants, ...b.manifest.participants]);
|
|
64
|
+
for (const pId of allParticipants) {
|
|
65
|
+
const hashA = a.manifest.stateHashes[pId];
|
|
66
|
+
const hashB = b.manifest.stateHashes[pId];
|
|
67
|
+
if (hashA !== hashB) {
|
|
68
|
+
differences[pId] = {
|
|
69
|
+
a: a.state[pId] || null,
|
|
70
|
+
b: b.state[pId] || null
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return differences;
|
|
75
|
+
}
|
|
76
|
+
async compare(idA, idB) {
|
|
77
|
+
const diff = await this.diff(idA, idB);
|
|
78
|
+
return Object.keys(diff).length === 0;
|
|
79
|
+
}
|
|
80
|
+
async list() {
|
|
81
|
+
return this.backend.list();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/snapshot/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SnapshotState } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Custom JSON Replacer to safely serialize BigInt values.
|
|
4
|
+
*/
|
|
5
|
+
export declare function snapshotReplacer(key: string, value: any): any;
|
|
6
|
+
/**
|
|
7
|
+
* Custom JSON Reviver to safely deserialize BigInt values.
|
|
8
|
+
*/
|
|
9
|
+
export declare function snapshotReviver(key: string, value: any): any;
|
|
10
|
+
/**
|
|
11
|
+
* Serializes a SnapshotState map to a string safely supporting BigInt.
|
|
12
|
+
*/
|
|
13
|
+
export declare function serializeState(state: Record<string, SnapshotState>): string;
|
|
14
|
+
/**
|
|
15
|
+
* Deserializes a SnapshotState map from a string safely supporting BigInt.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deserializeState(data: string): Record<string, SnapshotState>;
|
|
18
|
+
//# sourceMappingURL=serialization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.d.ts","sourceRoot":"","sources":["../../src/snapshot/serialization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,CAK7D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,CAK5D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,MAAM,CAE3E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAE5E"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom JSON Replacer to safely serialize BigInt values.
|
|
3
|
+
*/
|
|
4
|
+
export function snapshotReplacer(key, value) {
|
|
5
|
+
if (typeof value === "bigint") {
|
|
6
|
+
return { $bigint: value.toString() };
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Custom JSON Reviver to safely deserialize BigInt values.
|
|
12
|
+
*/
|
|
13
|
+
export function snapshotReviver(key, value) {
|
|
14
|
+
if (value !== null && typeof value === "object" && typeof value.$bigint === "string") {
|
|
15
|
+
return BigInt(value.$bigint);
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Serializes a SnapshotState map to a string safely supporting BigInt.
|
|
21
|
+
*/
|
|
22
|
+
export function serializeState(state) {
|
|
23
|
+
return JSON.stringify(state, snapshotReplacer, 2);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Deserializes a SnapshotState map from a string safely supporting BigInt.
|
|
27
|
+
*/
|
|
28
|
+
export function deserializeState(data) {
|
|
29
|
+
return JSON.parse(data, snapshotReviver);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=serialization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialization.js","sourceRoot":"","sources":["../../src/snapshot/serialization.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,KAAU;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC;IACzC,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,KAAU;IACnD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACnF,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAoC;IAC/D,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface SnapshotState {
|
|
2
|
+
[key: string]: any;
|
|
3
|
+
}
|
|
4
|
+
export interface SnapshotParticipant {
|
|
5
|
+
/**
|
|
6
|
+
* Extracts the current internal state of the toolkit/store
|
|
7
|
+
*/
|
|
8
|
+
snapshot(): Promise<SnapshotState>;
|
|
9
|
+
/**
|
|
10
|
+
* Restores the internal state from the provided snapshot state
|
|
11
|
+
*/
|
|
12
|
+
restore(state: SnapshotState): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Reloads any caches, queues, or in-memory representations
|
|
15
|
+
* based on the newly restored state
|
|
16
|
+
*/
|
|
17
|
+
reload(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export interface SnapshotManifest {
|
|
20
|
+
schema: "hardkas.snapshot.v1";
|
|
21
|
+
snapshotId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
createdAt: number;
|
|
24
|
+
backend: "memory" | "filesystem";
|
|
25
|
+
path?: string;
|
|
26
|
+
participants: string[];
|
|
27
|
+
stateHashes: Record<string, string>;
|
|
28
|
+
claims: {
|
|
29
|
+
containsFullStateInline: boolean;
|
|
30
|
+
restorableLocalState: boolean;
|
|
31
|
+
networkStateCaptured: boolean;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface SnapshotBackend {
|
|
35
|
+
save(id: string, name: string, state: Record<string, SnapshotState>): Promise<SnapshotManifest>;
|
|
36
|
+
load(id: string): Promise<{
|
|
37
|
+
manifest: SnapshotManifest;
|
|
38
|
+
state: Record<string, SnapshotState>;
|
|
39
|
+
}>;
|
|
40
|
+
list(): Promise<SnapshotManifest[]>;
|
|
41
|
+
delete(id: string): Promise<void>;
|
|
42
|
+
branch(baseId: string, newId: string, newName: string): Promise<SnapshotManifest>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/snapshot/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAChC;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAEnC;;OAEG;IACH,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,qBAAqB,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,QAAQ,GAAG,YAAY,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,MAAM,EAAE;QACJ,uBAAuB,EAAE,OAAO,CAAC;QACjC,oBAAoB,EAAE,OAAO,CAAC;QAC9B,oBAAoB,EAAE,OAAO,CAAC;KACjC,CAAC;CACL;AAED,MAAM,WAAW,eAAe;IAC5B,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAChG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,gBAAgB,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;KAAE,CAAC,CAAC;IAChG,IAAI,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/snapshot/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface InvoiceRecord {
|
|
2
|
+
id: string;
|
|
3
|
+
merchantId: string;
|
|
4
|
+
amount: bigint;
|
|
5
|
+
currency: string;
|
|
6
|
+
status: 'pending' | 'paid' | 'cancelled';
|
|
7
|
+
uri: string;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class InvoiceStoreJson {
|
|
12
|
+
private store;
|
|
13
|
+
constructor(options: {
|
|
14
|
+
filePath: string;
|
|
15
|
+
});
|
|
16
|
+
save(invoice: InvoiceRecord): void;
|
|
17
|
+
get(id: string): InvoiceRecord | undefined;
|
|
18
|
+
list(): InvoiceRecord[];
|
|
19
|
+
listByMerchant(merchantId: string): InvoiceRecord[];
|
|
20
|
+
listPaid(): InvoiceRecord[];
|
|
21
|
+
stats(merchantId?: string): {
|
|
22
|
+
totalInvoices: number;
|
|
23
|
+
paidInvoices: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=invoice-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invoice-store.d.ts","sourceRoot":"","sources":["../../src/stores/invoice-store.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,gBAAgB;IACzB,OAAO,CAAC,KAAK,CAAiC;gBAElC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE;IAIlC,IAAI,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAKlC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAI1C,IAAI,IAAI,aAAa,EAAE;IAIvB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,EAAE;IAInD,QAAQ,IAAI,aAAa,EAAE;IAI3B,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM;;;;CAWnC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { DomainStoreJson } from '@hardkas/query-store';
|
|
2
|
+
export class InvoiceStoreJson {
|
|
3
|
+
store;
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.store = new DomainStoreJson(options);
|
|
6
|
+
}
|
|
7
|
+
save(invoice) {
|
|
8
|
+
invoice.updatedAt = new Date().toISOString();
|
|
9
|
+
this.store.save(invoice.id, invoice);
|
|
10
|
+
}
|
|
11
|
+
get(id) {
|
|
12
|
+
return this.store.get(id);
|
|
13
|
+
}
|
|
14
|
+
list() {
|
|
15
|
+
return this.store.list();
|
|
16
|
+
}
|
|
17
|
+
listByMerchant(merchantId) {
|
|
18
|
+
return this.store.list().filter(i => i.merchantId === merchantId);
|
|
19
|
+
}
|
|
20
|
+
listPaid() {
|
|
21
|
+
return this.store.list().filter(i => i.status === 'paid');
|
|
22
|
+
}
|
|
23
|
+
stats(merchantId) {
|
|
24
|
+
let invoices = this.store.list();
|
|
25
|
+
if (merchantId) {
|
|
26
|
+
invoices = invoices.filter(i => i.merchantId === merchantId);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
totalInvoices: invoices.length,
|
|
30
|
+
paidInvoices: invoices.filter(i => i.status === 'paid').length
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=invoice-store.js.map
|
|
@@ -0,0 +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;AAavD,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,23 @@
|
|
|
1
|
+
export interface UtxoControlState {
|
|
2
|
+
frozen: Record<string, {
|
|
3
|
+
reason?: string | undefined;
|
|
4
|
+
timestamp: number;
|
|
5
|
+
}>;
|
|
6
|
+
labels: Record<string, string>;
|
|
7
|
+
notes: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
export declare class UtxoControlStore {
|
|
10
|
+
private filePath;
|
|
11
|
+
private state;
|
|
12
|
+
constructor(filePath: string);
|
|
13
|
+
private load;
|
|
14
|
+
private save;
|
|
15
|
+
getState(): UtxoControlState;
|
|
16
|
+
freeze(utxoId: string, reason?: string): void;
|
|
17
|
+
unfreeze(utxoId: string): void;
|
|
18
|
+
setLabel(utxoId: string, label: string): void;
|
|
19
|
+
getLabel(utxoId: string): string | undefined;
|
|
20
|
+
setNote(utxoId: string, note: string): void;
|
|
21
|
+
getNote(utxoId: string): string | undefined;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=utxo-control-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utxo-control-store.d.ts","sourceRoot":"","sources":["../../src/stores/utxo-control-store.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAmB;gBAEpB,QAAQ,EAAE,MAAM;IAK5B,OAAO,CAAC,IAAI;IAgBZ,OAAO,CAAC,IAAI;IAYL,QAAQ,IAAI,gBAAgB;IAI5B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAK7C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK9B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK7C,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5C,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAK3C,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAGnD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
export class UtxoControlStore {
|
|
4
|
+
filePath;
|
|
5
|
+
state;
|
|
6
|
+
constructor(filePath) {
|
|
7
|
+
this.filePath = filePath;
|
|
8
|
+
this.state = this.load();
|
|
9
|
+
}
|
|
10
|
+
load() {
|
|
11
|
+
try {
|
|
12
|
+
if (fs.existsSync(this.filePath)) {
|
|
13
|
+
const data = fs.readFileSync(this.filePath, 'utf-8');
|
|
14
|
+
return JSON.parse(data);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
// Ignore read errors, initialize default
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
frozen: {},
|
|
22
|
+
labels: {},
|
|
23
|
+
notes: {}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
save() {
|
|
27
|
+
try {
|
|
28
|
+
const dir = path.dirname(this.filePath);
|
|
29
|
+
if (!fs.existsSync(dir)) {
|
|
30
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
+
}
|
|
32
|
+
fs.writeFileSync(this.filePath, JSON.stringify(this.state, null, 2), 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
console.warn(`Failed to save UTXO control state to ${this.filePath}`, e);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
getState() {
|
|
39
|
+
return JSON.parse(JSON.stringify(this.state)); // Deep copy
|
|
40
|
+
}
|
|
41
|
+
freeze(utxoId, reason) {
|
|
42
|
+
this.state.frozen[utxoId] = { reason, timestamp: Date.now() };
|
|
43
|
+
this.save();
|
|
44
|
+
}
|
|
45
|
+
unfreeze(utxoId) {
|
|
46
|
+
delete this.state.frozen[utxoId];
|
|
47
|
+
this.save();
|
|
48
|
+
}
|
|
49
|
+
setLabel(utxoId, label) {
|
|
50
|
+
this.state.labels[utxoId] = label;
|
|
51
|
+
this.save();
|
|
52
|
+
}
|
|
53
|
+
getLabel(utxoId) {
|
|
54
|
+
return this.state.labels[utxoId];
|
|
55
|
+
}
|
|
56
|
+
setNote(utxoId, note) {
|
|
57
|
+
this.state.notes[utxoId] = note;
|
|
58
|
+
this.save();
|
|
59
|
+
}
|
|
60
|
+
getNote(utxoId) {
|
|
61
|
+
return this.state.notes[utxoId];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=utxo-control-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utxo-control-store.js","sourceRoot":"","sources":["../../src/stores/utxo-control-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAQlC,MAAM,OAAO,gBAAgB;IACnB,QAAQ,CAAS;IACjB,KAAK,CAAmB;IAEhC,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC3B,CAAC;IAEO,IAAI;QACV,IAAI,CAAC;YACH,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjC,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBACrD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;YAC9C,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,yCAAyC;QAC3C,CAAC;QACD,OAAO;YACL,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,EAAE;YACV,KAAK,EAAE,EAAE;SACV,CAAC;IACJ,CAAC;IAEO,IAAI;QACV,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACzC,CAAC;YACD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAChF,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,wCAAwC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IAEM,QAAQ;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY;IAC7D,CAAC;IAEM,MAAM,CAAC,MAAc,EAAE,MAAe;QAC3C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAC9D,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAEM,QAAQ,CAAC,MAAc;QAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAEM,QAAQ,CAAC,MAAc,EAAE,KAAa;QAC3C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAEM,QAAQ,CAAC,MAAc;QAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAEM,OAAO,CAAC,MAAc,EAAE,IAAY;QACzC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE,CAAC;IACd,CAAC;IAEM,OAAO,CAAC,MAAc;QAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;CACF"}
|
package/dist/utxos.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Utxo } from "@hardkas/query";
|
|
2
|
+
export interface UtxoStatistics {
|
|
3
|
+
totalUtxos: number;
|
|
4
|
+
availableUtxos: number;
|
|
5
|
+
frozenUtxos: number;
|
|
6
|
+
totalValue: bigint;
|
|
7
|
+
availableValue: bigint;
|
|
8
|
+
frozenValue: bigint;
|
|
9
|
+
averageValue: bigint;
|
|
10
|
+
largest: bigint;
|
|
11
|
+
smallest: bigint;
|
|
12
|
+
includeFrozen: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface DustAnalysis {
|
|
15
|
+
dustUtxos: Utxo[];
|
|
16
|
+
normalUtxos: Utxo[];
|
|
17
|
+
totalDustValue: bigint;
|
|
18
|
+
dustPercentage: number;
|
|
19
|
+
}
|
|
20
|
+
export type Recommendation = "consolidate" | "mergeDust" | "splitLarge" | "none";
|
|
21
|
+
export interface WalletAnalysis {
|
|
22
|
+
utxoCount: number;
|
|
23
|
+
fragmentationScore: number;
|
|
24
|
+
dustCount: number;
|
|
25
|
+
totalValue: bigint;
|
|
26
|
+
recommendedActions: Recommendation[];
|
|
27
|
+
}
|
|
28
|
+
export interface ConsolidationPlan {
|
|
29
|
+
strategy: string;
|
|
30
|
+
inputs: Utxo[];
|
|
31
|
+
outputs: any[];
|
|
32
|
+
estimatedFee: bigint;
|
|
33
|
+
estimatedSavings: bigint;
|
|
34
|
+
warnings: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface SplitPlan extends ConsolidationPlan {
|
|
37
|
+
strategy: "split";
|
|
38
|
+
}
|
|
39
|
+
export interface MergePlan extends ConsolidationPlan {
|
|
40
|
+
strategy: "merge";
|
|
41
|
+
}
|
|
42
|
+
export interface SweepPlan extends ConsolidationPlan {
|
|
43
|
+
strategy: "sweep";
|
|
44
|
+
}
|
|
45
|
+
import { UtxoControlStore, UtxoControlState } from './stores/utxo-control-store.js';
|
|
46
|
+
export interface UtxoListOpts {
|
|
47
|
+
includeFrozen?: boolean | undefined;
|
|
48
|
+
}
|
|
49
|
+
export declare class WalletUtxoApi {
|
|
50
|
+
private readonly fetchUtxos;
|
|
51
|
+
private readonly address;
|
|
52
|
+
private readonly estimateFee;
|
|
53
|
+
private readonly store;
|
|
54
|
+
constructor(fetchUtxos: () => Promise<Utxo[]>, address: () => Promise<string>, estimateFee: (inputs: number, outputs: number) => Promise<bigint>, store: UtxoControlStore);
|
|
55
|
+
/**
|
|
56
|
+
* List UTXOs currently available in the wallet.
|
|
57
|
+
* Frozen UTXOs are excluded by default unless includeFrozen: true.
|
|
58
|
+
*/
|
|
59
|
+
list(opts?: UtxoListOpts): Promise<Utxo[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Return basic statistics about the UTXO set.
|
|
62
|
+
*/
|
|
63
|
+
statistics(opts?: UtxoListOpts): Promise<UtxoStatistics>;
|
|
64
|
+
/**
|
|
65
|
+
* Classify UTXOs into dust and normal based on a threshold.
|
|
66
|
+
* Default threshold: 10 KAS (1_000_000_000 sompi).
|
|
67
|
+
*/
|
|
68
|
+
analyzeDust(opts?: {
|
|
69
|
+
thresholdSompi?: bigint;
|
|
70
|
+
includeFrozen?: boolean | undefined;
|
|
71
|
+
}): Promise<DustAnalysis>;
|
|
72
|
+
/**
|
|
73
|
+
* High-level diagnostic analysis of the wallet's UTXO health.
|
|
74
|
+
*/
|
|
75
|
+
analyze(opts?: UtxoListOpts): Promise<WalletAnalysis>;
|
|
76
|
+
/**
|
|
77
|
+
* Plan a UTXO management operation without executing it.
|
|
78
|
+
*/
|
|
79
|
+
plan(opts: {
|
|
80
|
+
strategy: "consolidation" | "split" | "sweep";
|
|
81
|
+
includeFrozen?: boolean | undefined;
|
|
82
|
+
}): Promise<ConsolidationPlan>;
|
|
83
|
+
/**
|
|
84
|
+
* Convenience alias for planning a consolidation.
|
|
85
|
+
*/
|
|
86
|
+
consolidate(opts?: {
|
|
87
|
+
includeFrozen?: boolean | undefined;
|
|
88
|
+
}): Promise<ConsolidationPlan>;
|
|
89
|
+
private _assertNotFrozen;
|
|
90
|
+
/**
|
|
91
|
+
* Plans the splitting of a specific large UTXO.
|
|
92
|
+
* Supports symmetric splits (intoCount) OR asymmetric splits (outputs).
|
|
93
|
+
*/
|
|
94
|
+
splitPlan(opts: {
|
|
95
|
+
utxoId: string;
|
|
96
|
+
intoCount?: number;
|
|
97
|
+
outputs?: {
|
|
98
|
+
amountSompi: bigint;
|
|
99
|
+
}[];
|
|
100
|
+
includeFrozen?: boolean | undefined;
|
|
101
|
+
}): Promise<SplitPlan>;
|
|
102
|
+
/**
|
|
103
|
+
* Plans the explicit merging of specific UTXOs.
|
|
104
|
+
*/
|
|
105
|
+
mergePlan(opts: {
|
|
106
|
+
utxoIds: string[];
|
|
107
|
+
includeFrozen?: boolean | undefined;
|
|
108
|
+
}): Promise<MergePlan>;
|
|
109
|
+
/**
|
|
110
|
+
* Plans sweeping the entire wallet to a single destination address.
|
|
111
|
+
*/
|
|
112
|
+
sweepPlan(opts: {
|
|
113
|
+
destinationAddress: string;
|
|
114
|
+
includeFrozen?: boolean | undefined;
|
|
115
|
+
}): Promise<SweepPlan>;
|
|
116
|
+
controlState(): Promise<UtxoControlState>;
|
|
117
|
+
freeze(utxoId: string, reason?: string): Promise<void>;
|
|
118
|
+
unfreeze(utxoId: string): Promise<void>;
|
|
119
|
+
labels: {
|
|
120
|
+
set: (utxoId: string, label: string) => Promise<void>;
|
|
121
|
+
get: (utxoId: string) => Promise<string | undefined>;
|
|
122
|
+
};
|
|
123
|
+
notes: {
|
|
124
|
+
set: (utxoId: string, note: string) => Promise<void>;
|
|
125
|
+
get: (utxoId: string) => Promise<string | undefined>;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=utxos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utxos.d.ts","sourceRoot":"","sources":["../src/utxos.ts"],"names":[],"mappings":"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"}
|