@qntx/wallet-engine 0.0.0-stage → 0.5.2
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/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +58 -2
- package/dist/idb.d.mts +28 -0
- package/dist/idb.mjs +134 -0
- package/dist/idb.mjs.map +1 -0
- package/dist/index.d.mts +1019 -0
- package/dist/index.mjs +5374 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +80 -4
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,1019 @@
|
|
|
1
|
+
import { StoreApi } from "zustand/vanilla";
|
|
2
|
+
import { CooperativeExitResult, LightningInvoice, LightningPayment, SparkClient, SparkExitChain, SparkNetwork, SparkPayment, SparkStore, SparkTransfer } from "@qntx/spark";
|
|
3
|
+
import { NostrKeyRef, WalletSigner } from "@qntx/wallet/nostr";
|
|
4
|
+
import { SparkSigner } from "@qntx/wallet/spark";
|
|
5
|
+
import { PublicClient } from "viem";
|
|
6
|
+
import { chains } from "@qntx/wallet/chains";
|
|
7
|
+
import "@scure/btc-signer";
|
|
8
|
+
import { decodeEvm } from "@qntx/wallet-chains/evm";
|
|
9
|
+
import { BackupHeader, BackupInfo, InitialFactor, UnlockFactor, Wallet, WalletInfo } from "@qntx/wallet";
|
|
10
|
+
import { Event, Filter, NostrSigner } from "@qntx/nostr";
|
|
11
|
+
import { LnurlPayRequest, LnurlWithdrawRequest } from "@qntx/spark/lnurl";
|
|
12
|
+
import "@solana/kit";
|
|
13
|
+
import { SparkAccountRef, SparkApproveIntent } from "@qntx/wallet/protocol";
|
|
14
|
+
import "@scure/btc-signer/psbt.js";
|
|
15
|
+
import "@qntx/wallet-chains";
|
|
16
|
+
//#region src/controller.d.ts
|
|
17
|
+
type ControllerOptions<S> = {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly defaultState: () => S;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Controller base: a zustand vanilla store plus lifecycle. `update` applies a recipe that must
|
|
23
|
+
* return a NEW state object; returning the same reference is a no-op (no set, no notification).
|
|
24
|
+
* Persistence and migrations layer on later — this package carries only what its first user needs.
|
|
25
|
+
*/
|
|
26
|
+
export declare abstract class Controller<S extends Record<string, unknown>> {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly store: StoreApi<S>;
|
|
29
|
+
protected constructor(options: ControllerOptions<S>);
|
|
30
|
+
getState(): S;
|
|
31
|
+
protected update(recipe: (state: S) => S): void;
|
|
32
|
+
abstract init(): Promise<void>;
|
|
33
|
+
abstract destroy(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/events.d.ts
|
|
37
|
+
/** Log port: listener failures are reported here and never reach the emitter or peers. */
|
|
38
|
+
type EngineLogger = {
|
|
39
|
+
readonly warn: (message: string, error?: unknown) => void;
|
|
40
|
+
};
|
|
41
|
+
type EventBus<E> = {
|
|
42
|
+
/** Synchronous dispatch to every current listener in registration order. */
|
|
43
|
+
emit: <K extends keyof E>(type: K, payload: E[K]) => void;
|
|
44
|
+
on: <K extends keyof E>(type: K, listener: (payload: E[K]) => void) => () => void;
|
|
45
|
+
};
|
|
46
|
+
export declare function createEventBus<E>(logger: EngineLogger): EventBus<E>;
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/errors.d.ts
|
|
49
|
+
type EngineErrorCode = "closed" | "integrity" | "invalid-input";
|
|
50
|
+
/** Typed engine errors: store codes are `closed` and `integrity` (decrypt or bind failure). */
|
|
51
|
+
export declare class EngineStoreError extends Error {
|
|
52
|
+
readonly code: EngineErrorCode;
|
|
53
|
+
constructor(code: EngineErrorCode, message: string);
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/store/port.d.ts
|
|
57
|
+
/**
|
|
58
|
+
* Engine storage port: named collections of byte values. `collection` is synchronous and cheap —
|
|
59
|
+
* the same collection object backs every call, so guards (closed) live on the store.
|
|
60
|
+
*/
|
|
61
|
+
type EngineCollection = {
|
|
62
|
+
readonly get: (key: string) => Promise<Uint8Array | undefined>;
|
|
63
|
+
readonly put: (key: string, bytes: Uint8Array) => Promise<void>;
|
|
64
|
+
readonly delete: (key: string) => Promise<void>;
|
|
65
|
+
/** Every key in the collection, in an unspecified order. */
|
|
66
|
+
readonly list: () => Promise<ReadonlyArray<string>>;
|
|
67
|
+
};
|
|
68
|
+
type EngineStore = {
|
|
69
|
+
readonly collection: (name: string) => EngineCollection;
|
|
70
|
+
readonly close: () => Promise<void>;
|
|
71
|
+
};
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/store/memory.d.ts
|
|
74
|
+
/** In-memory EngineStore: the Node/test implementation — same contract, no durability. */
|
|
75
|
+
export declare function createMemoryEngineStore(backing?: {
|
|
76
|
+
readonly collections?: Map<string, Map<string, Uint8Array>>;
|
|
77
|
+
}): EngineStore;
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/chains/bitcoin.d.ts
|
|
80
|
+
type Esplora = {
|
|
81
|
+
readonly get: (path: string) => Promise<unknown>;
|
|
82
|
+
readonly post: (path: string, body: string) => Promise<unknown>;
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/meta/doc.d.ts
|
|
86
|
+
/**
|
|
87
|
+
* Wallet metadata document — the plaintext exchanged over kind-30078 events between this wallet's
|
|
88
|
+
* own devices. One document per wallet; entry-level last-writer-wins on `updatedAt`.
|
|
89
|
+
*/
|
|
90
|
+
type MetaDocWallet = {
|
|
91
|
+
readonly name?: string;
|
|
92
|
+
readonly updatedAt: number;
|
|
93
|
+
};
|
|
94
|
+
type MetaDocGroup = {
|
|
95
|
+
readonly index: number;
|
|
96
|
+
readonly name?: string;
|
|
97
|
+
readonly hidden: boolean;
|
|
98
|
+
readonly boundPubkey?: string;
|
|
99
|
+
readonly updatedAt: number;
|
|
100
|
+
};
|
|
101
|
+
type MetaDoc = {
|
|
102
|
+
readonly v: 1;
|
|
103
|
+
readonly wallet: MetaDocWallet;
|
|
104
|
+
readonly groups: ReadonlyArray<MetaDocGroup>;
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/spark/notes.d.ts
|
|
108
|
+
/**
|
|
109
|
+
* Local, engine-side metadata pinned to a history entry by transfer id. Written after the payment
|
|
110
|
+
* it annotates has already succeeded — a missing or malformed note must never affect the payment
|
|
111
|
+
* itself.
|
|
112
|
+
*/
|
|
113
|
+
type PaymentNote = {
|
|
114
|
+
readonly kind: "zap";
|
|
115
|
+
/** 64 lowercase hex. */
|
|
116
|
+
readonly recipientPubkey: string;
|
|
117
|
+
/** 64 lowercase hex — event zaps carry the zapped event's id. */
|
|
118
|
+
readonly eventId?: string;
|
|
119
|
+
/** At most 2000 characters. */
|
|
120
|
+
readonly comment?: string;
|
|
121
|
+
/** The lud16 or LNURL the zap resolved. */
|
|
122
|
+
readonly lightning: string;
|
|
123
|
+
};
|
|
124
|
+
type SparkActivity = SparkPayment & {
|
|
125
|
+
readonly note?: PaymentNote;
|
|
126
|
+
};
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/spark/store.d.ts
|
|
129
|
+
/**
|
|
130
|
+
* SparkStore over one EngineCollection: chains keyed by leafId, plus an index record listing them
|
|
131
|
+
* (`exitChains` is a full listing — the port has no secondary index).
|
|
132
|
+
*/
|
|
133
|
+
export declare function sparkStore(col: EngineCollection): SparkStore;
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/spark/controller.d.ts
|
|
136
|
+
type SparkAccountState = {
|
|
137
|
+
readonly status: "locked" | "connecting" | "ready" | "error";
|
|
138
|
+
readonly balanceSats?: number;
|
|
139
|
+
readonly identityPublicKey?: string;
|
|
140
|
+
readonly sparkAddress?: string;
|
|
141
|
+
readonly errorCategory?: string;
|
|
142
|
+
/** First history page (20 entries, newest first), refreshed alongside the balance. */
|
|
143
|
+
readonly recent?: ReadonlyArray<SparkActivity>;
|
|
144
|
+
};
|
|
145
|
+
type SparkControllerState = {
|
|
146
|
+
readonly accounts: Readonly<Record<string, SparkAccountState>>;
|
|
147
|
+
};
|
|
148
|
+
type SparkApis = {
|
|
149
|
+
readonly signer: (ref: SparkAccountRef) => SparkSigner;
|
|
150
|
+
readonly approve: (params: {
|
|
151
|
+
account: SparkAccountRef;
|
|
152
|
+
intent: SparkApproveIntent;
|
|
153
|
+
verify: UnlockFactor;
|
|
154
|
+
}) => Promise<unknown>;
|
|
155
|
+
};
|
|
156
|
+
type SparkClientFactory = (options: {
|
|
157
|
+
readonly ref: SparkAccountRef;
|
|
158
|
+
readonly store: ReturnType<typeof sparkStore>;
|
|
159
|
+
readonly signer: SparkSigner;
|
|
160
|
+
readonly fetch: typeof globalThis.fetch;
|
|
161
|
+
}) => SparkClient;
|
|
162
|
+
type SparkControllerOptions = {
|
|
163
|
+
readonly spark: SparkApis;
|
|
164
|
+
readonly fetch: typeof globalThis.fetch;
|
|
165
|
+
readonly random: (length: number) => Uint8Array;
|
|
166
|
+
readonly now: () => number;
|
|
167
|
+
readonly logger: EngineLogger;
|
|
168
|
+
readonly createClient: SparkClientFactory;
|
|
169
|
+
};
|
|
170
|
+
export declare class SparkController extends Controller<SparkControllerState> {
|
|
171
|
+
private readonly options;
|
|
172
|
+
private engineStore;
|
|
173
|
+
private readonly accounts;
|
|
174
|
+
private readonly clients;
|
|
175
|
+
private readonly recentSeq;
|
|
176
|
+
private readonly aborts;
|
|
177
|
+
constructor(options: SparkControllerOptions);
|
|
178
|
+
/** Engine calls this with the open store on unlock; `undefined` on lock. */
|
|
179
|
+
setStore(store: EngineStore | undefined): void;
|
|
180
|
+
/**
|
|
181
|
+
* Declarative account set: refs not yet tracked are patched `locked` and started when the store
|
|
182
|
+
* is open; tracked refs no longer listed are stopped and dropped; running refs stay untouched.
|
|
183
|
+
*/
|
|
184
|
+
setAccounts(refs: ReadonlyArray<SparkAccountRef>): void;
|
|
185
|
+
init(): Promise<void>;
|
|
186
|
+
destroy(): Promise<void>;
|
|
187
|
+
private stopAccount;
|
|
188
|
+
/** Lock: abort every event loop, close every client, reset state. */
|
|
189
|
+
lockAll(): Promise<void>;
|
|
190
|
+
private startAccount;
|
|
191
|
+
private runEvents;
|
|
192
|
+
private handleTransfer;
|
|
193
|
+
private notesCollection;
|
|
194
|
+
/** Attach locally written notes to a history page; malformed records warn and drop. */
|
|
195
|
+
private withNotes;
|
|
196
|
+
/**
|
|
197
|
+
* Refill `recent` from the first history page. A failure keeps the previous page — it never
|
|
198
|
+
* touches `status`/`errorCategory`. A per-account sequence keeps a slower older refresh from
|
|
199
|
+
* overwriting a newer one.
|
|
200
|
+
*/
|
|
201
|
+
private refreshRecent;
|
|
202
|
+
private patch;
|
|
203
|
+
private clientFor;
|
|
204
|
+
private guarded;
|
|
205
|
+
/**
|
|
206
|
+
* One-off balance + activity probe for an account that may not be running: builds a temporary
|
|
207
|
+
* client through the same factory on the account's own exit-chain collection (nothing diverges —
|
|
208
|
+
* `balance()`/`transfers.history` never write it), reads, and closes it in `finally`.
|
|
209
|
+
*/
|
|
210
|
+
inspect(ref: SparkAccountRef): Promise<{
|
|
211
|
+
balanceSats: number;
|
|
212
|
+
used: boolean;
|
|
213
|
+
}>;
|
|
214
|
+
/** History for one account: a client page merged with locally written notes by id. */
|
|
215
|
+
history(ref: SparkAccountRef, options?: {
|
|
216
|
+
readonly limit?: number;
|
|
217
|
+
readonly offset?: number;
|
|
218
|
+
}): Promise<{
|
|
219
|
+
readonly items: ReadonlyArray<SparkActivity>;
|
|
220
|
+
readonly nextOffset: number | undefined;
|
|
221
|
+
}>;
|
|
222
|
+
send(ref: SparkAccountRef, params: {
|
|
223
|
+
receiver: string;
|
|
224
|
+
amountSats: number;
|
|
225
|
+
verify: UnlockFactor;
|
|
226
|
+
}): Promise<SparkTransfer>;
|
|
227
|
+
registerLightningAddress(ref: SparkAccountRef, params: {
|
|
228
|
+
serverUrl: string;
|
|
229
|
+
domain: string;
|
|
230
|
+
username: string;
|
|
231
|
+
description: string;
|
|
232
|
+
}): Promise<{
|
|
233
|
+
lnurl: string;
|
|
234
|
+
lightningAddress: string;
|
|
235
|
+
}>;
|
|
236
|
+
unregisterLightningAddress(ref: SparkAccountRef, params: {
|
|
237
|
+
serverUrl: string;
|
|
238
|
+
domain: string;
|
|
239
|
+
username: string;
|
|
240
|
+
}): Promise<void>;
|
|
241
|
+
payInvoice(ref: SparkAccountRef, params: {
|
|
242
|
+
invoice: string;
|
|
243
|
+
amountSats?: number;
|
|
244
|
+
maxFeeSats: number;
|
|
245
|
+
verify: UnlockFactor;
|
|
246
|
+
/** Local annotation written under the payment's transfer id after it succeeds. */
|
|
247
|
+
note?: PaymentNote;
|
|
248
|
+
}): Promise<LightningPayment>;
|
|
249
|
+
/**
|
|
250
|
+
* Pays a Spark invoice address. The approval intent mirrors `send`: receiver identity and amount
|
|
251
|
+
* come from the parsed invoice (the supplied `amountSats` fills an open invoice). Then
|
|
252
|
+
* `client.invoices.pay` executes it.
|
|
253
|
+
*/
|
|
254
|
+
paySparkInvoice(ref: SparkAccountRef, params: {
|
|
255
|
+
invoice: string;
|
|
256
|
+
amountSats?: number;
|
|
257
|
+
verify: UnlockFactor;
|
|
258
|
+
}): Promise<SparkTransfer>;
|
|
259
|
+
createInvoice(ref: SparkAccountRef, params: {
|
|
260
|
+
amountSats: number;
|
|
261
|
+
memo?: string;
|
|
262
|
+
}): Promise<LightningInvoice>;
|
|
263
|
+
receiveAddress(ref: SparkAccountRef): Promise<string | undefined>;
|
|
264
|
+
/**
|
|
265
|
+
* Cooperative exit to an L1 address. The client exits `amountSats` of leaves and the payout is
|
|
266
|
+
* `amountSats - fee`, so the approval intent carries the PAYOUT: `amountSats: exit - fee` and
|
|
267
|
+
* `maxFeeSats: fee` — the kernel matches the exit output against the intent, not the leaf total.
|
|
268
|
+
*/
|
|
269
|
+
withdraw(ref: SparkAccountRef, params: {
|
|
270
|
+
address: string;
|
|
271
|
+
amountSats?: number;
|
|
272
|
+
speed: "FAST" | "MEDIUM" | "SLOW";
|
|
273
|
+
maxFeeSats?: number;
|
|
274
|
+
verify: UnlockFactor;
|
|
275
|
+
}): Promise<CooperativeExitResult>;
|
|
276
|
+
}
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/accounts/controller.d.ts
|
|
279
|
+
type AccountGroup = {
|
|
280
|
+
readonly walletId: string;
|
|
281
|
+
readonly index: number;
|
|
282
|
+
/** `discoverGroups` result — family key → used; a family error lives under `errors`. */
|
|
283
|
+
readonly used?: Readonly<Record<string, boolean>>;
|
|
284
|
+
readonly errors?: Readonly<Record<string, string>>;
|
|
285
|
+
readonly name?: string;
|
|
286
|
+
readonly hidden: boolean;
|
|
287
|
+
/** A social identity (64 lowercase hex) bound to this group; one identity binds one group. */
|
|
288
|
+
readonly boundPubkey?: string;
|
|
289
|
+
/** Last local modification time (ms); reconcile-created entries start at 0. */
|
|
290
|
+
readonly updatedAt: number;
|
|
291
|
+
};
|
|
292
|
+
type AccountTreeState = {
|
|
293
|
+
readonly status: "locked" | "ready";
|
|
294
|
+
readonly wallets: ReadonlyArray<{
|
|
295
|
+
readonly walletId: string;
|
|
296
|
+
readonly name?: string;
|
|
297
|
+
readonly updatedAt: number;
|
|
298
|
+
}>;
|
|
299
|
+
readonly groups: Readonly<Record<string, ReadonlyArray<AccountGroup>>>;
|
|
300
|
+
readonly selected: {
|
|
301
|
+
readonly walletId: string;
|
|
302
|
+
readonly groupIndex: number;
|
|
303
|
+
} | null;
|
|
304
|
+
readonly totals: Readonly<Record<string, number>>;
|
|
305
|
+
};
|
|
306
|
+
type DiscoveryNetworks = {
|
|
307
|
+
readonly client: (network: string) => {
|
|
308
|
+
readonly getTransactionCount: (p: {
|
|
309
|
+
readonly address: `0x${string}`;
|
|
310
|
+
readonly blockTag: "latest";
|
|
311
|
+
}) => Promise<number>;
|
|
312
|
+
readonly getBalance: (p: {
|
|
313
|
+
readonly address: `0x${string}`;
|
|
314
|
+
}) => Promise<bigint>;
|
|
315
|
+
};
|
|
316
|
+
readonly solanaRpc: (network: string) => {
|
|
317
|
+
readonly call: (method: string, params: unknown[]) => Promise<unknown>;
|
|
318
|
+
};
|
|
319
|
+
readonly esplora: (network: string) => Esplora;
|
|
320
|
+
readonly enabled: (family: "evm" | "solana" | "bitcoin") => ReadonlyArray<string>;
|
|
321
|
+
};
|
|
322
|
+
type AccountTreeControllerOptions = {
|
|
323
|
+
readonly wallet: Wallet;
|
|
324
|
+
readonly spark: SparkController;
|
|
325
|
+
readonly sparkNetwork: SparkNetwork;
|
|
326
|
+
readonly networks?: DiscoveryNetworks;
|
|
327
|
+
readonly chains?: {
|
|
328
|
+
readonly account: (params: {
|
|
329
|
+
readonly walletId: string;
|
|
330
|
+
readonly groupIndex: number;
|
|
331
|
+
readonly template: string;
|
|
332
|
+
readonly network: string;
|
|
333
|
+
}) => Promise<{
|
|
334
|
+
readonly address?: string;
|
|
335
|
+
readonly xpub?: string;
|
|
336
|
+
}>;
|
|
337
|
+
};
|
|
338
|
+
readonly logger: EngineLogger;
|
|
339
|
+
readonly now: () => number;
|
|
340
|
+
};
|
|
341
|
+
export declare class AccountTreeController extends Controller<AccountTreeState> {
|
|
342
|
+
private readonly options;
|
|
343
|
+
private col;
|
|
344
|
+
private sparkUnsubscribe;
|
|
345
|
+
private totalsTimer;
|
|
346
|
+
private persistChain;
|
|
347
|
+
constructor(options: AccountTreeControllerOptions);
|
|
348
|
+
setStore(store: EngineStore | undefined): void;
|
|
349
|
+
/** Load persisted state, reconcile with the snapshot, start driving Spark. */
|
|
350
|
+
init(): Promise<void>;
|
|
351
|
+
destroy(): Promise<void>;
|
|
352
|
+
/** Locked: stop every Spark client, flush pending persists, then drop to the empty state. */
|
|
353
|
+
lock(): Promise<void>;
|
|
354
|
+
/** Reconcile groups/names/selected with `wallet.getSnapshot().wallets`; persists on change. */
|
|
355
|
+
reconcile(): void;
|
|
356
|
+
addGroup(walletId: string): number;
|
|
357
|
+
addGroups(walletId: string, indices: ReadonlyArray<number>): void;
|
|
358
|
+
renameWallet(walletId: string, name: string): void;
|
|
359
|
+
renameGroup(walletId: string, index: number, name: string): void;
|
|
360
|
+
setHidden(walletId: string, index: number, hidden: boolean): void;
|
|
361
|
+
select(walletId: string, index: number): void;
|
|
362
|
+
/**
|
|
363
|
+
* Binds a social identity to one group. The identity is unique across the whole tree — every
|
|
364
|
+
* group in every wallet carrying it loses the binding (each entry re-stamped).
|
|
365
|
+
*/
|
|
366
|
+
bind(walletId: string, index: number, pubkey: string): void;
|
|
367
|
+
unbind(walletId: string, index: number): void;
|
|
368
|
+
/**
|
|
369
|
+
* Which group currently holds a pubkey — merges can leave the same identity on groups in two
|
|
370
|
+
* wallets; the newest `updatedAt` wins, ties resolve by snapshot wallet order then lower index.
|
|
371
|
+
*/
|
|
372
|
+
bindingOf(pubkey: string): {
|
|
373
|
+
walletId: string;
|
|
374
|
+
groupIndex: number;
|
|
375
|
+
} | undefined;
|
|
376
|
+
/** The wallet's metadata document — every group, hidden included. */
|
|
377
|
+
exportMeta(walletId: string): MetaDoc;
|
|
378
|
+
/**
|
|
379
|
+
* Entry-level last-writer-wins merge: a newer remote `updatedAt` replaces the local entry, a tie
|
|
380
|
+
* keeps local; remote-only groups are added. `selected`/hidden rules are re-validated — if the
|
|
381
|
+
* merge hides the selected group it is re-selected, and a merge may never leave every group
|
|
382
|
+
* hidden (the selected one stays visible and is re-stamped so the fix propagates).
|
|
383
|
+
*/
|
|
384
|
+
mergeMeta(walletId: string, doc: MetaDoc): {
|
|
385
|
+
changed: boolean;
|
|
386
|
+
localNewer: boolean;
|
|
387
|
+
};
|
|
388
|
+
/** Sequential inspect from 0 upward; index 0 always included; stop after 2 consecutive unused. */
|
|
389
|
+
discoverSpark(walletId: string): Promise<number[]>;
|
|
390
|
+
/**
|
|
391
|
+
* Per-group chain-family discovery — same stop rule as `discoverSpark`: group 0 always, stop
|
|
392
|
+
* after two consecutive fully-unused groups. A family error is reported on the group and does NOT
|
|
393
|
+
* count as unused (the group stays in `found` — it can't be proven empty).
|
|
394
|
+
*/
|
|
395
|
+
discoverGroups(walletId: string): Promise<number[]>;
|
|
396
|
+
/** Refresh `totals` for every other visible group; per-group failures are logged and skipped. */
|
|
397
|
+
refreshTotals(): Promise<void>;
|
|
398
|
+
private onSparkState;
|
|
399
|
+
private setTotal;
|
|
400
|
+
private driveSpark;
|
|
401
|
+
private visibleGroups;
|
|
402
|
+
private validSelection;
|
|
403
|
+
private selectFallback;
|
|
404
|
+
private mutateGroups;
|
|
405
|
+
private mustWallet;
|
|
406
|
+
private mustGroup;
|
|
407
|
+
private checkedName;
|
|
408
|
+
private load;
|
|
409
|
+
/** Queues a store write behind the previous one — the last queued write always lands last. */
|
|
410
|
+
private persist;
|
|
411
|
+
private scheduleTotalsPersist;
|
|
412
|
+
private flushTotals;
|
|
413
|
+
}
|
|
414
|
+
//#endregion
|
|
415
|
+
//#region src/networks/registry.d.ts
|
|
416
|
+
/**
|
|
417
|
+
* Built-in EVM network registry: the 8 mainnets from chains.mdx plus their main testnets. Each
|
|
418
|
+
* chain id was checked against the chain's official documentation (source URL per entry).
|
|
419
|
+
* Signing-time chainId always comes from this table, never from an RPC endpoint.
|
|
420
|
+
*/
|
|
421
|
+
type NetworkInfo = {
|
|
422
|
+
readonly id: string;
|
|
423
|
+
readonly chainId: number;
|
|
424
|
+
readonly name: string;
|
|
425
|
+
readonly symbol: string;
|
|
426
|
+
readonly multicall3?: string;
|
|
427
|
+
/** CAIP-2 family — `solana:<ref>` networks are validated against genesis-hash, not chainId. */
|
|
428
|
+
readonly family: "evm" | "solana" | "bitcoin";
|
|
429
|
+
readonly genesisHash?: string;
|
|
430
|
+
readonly decimals: number;
|
|
431
|
+
readonly testnet: boolean;
|
|
432
|
+
};
|
|
433
|
+
//#endregion
|
|
434
|
+
//#region src/networks/controller.d.ts
|
|
435
|
+
type SolanaRpc = {
|
|
436
|
+
readonly call: (method: string, params: unknown[]) => Promise<unknown>;
|
|
437
|
+
};
|
|
438
|
+
type NetworkControllerState = {
|
|
439
|
+
readonly customRpc: Record<string, ReadonlyArray<string>>;
|
|
440
|
+
};
|
|
441
|
+
type Options$2 = {
|
|
442
|
+
readonly fetch: typeof globalThis.fetch;
|
|
443
|
+
readonly rpc: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
444
|
+
readonly openStore: () => Promise<EngineStore>;
|
|
445
|
+
readonly logger: EngineLogger;
|
|
446
|
+
};
|
|
447
|
+
declare class NetworkController extends Controller<NetworkControllerState> {
|
|
448
|
+
private readonly options;
|
|
449
|
+
private engineStore?;
|
|
450
|
+
private readonly clients;
|
|
451
|
+
constructor(options: Options$2);
|
|
452
|
+
init(): Promise<void>;
|
|
453
|
+
destroy(): Promise<void>;
|
|
454
|
+
lock(): void;
|
|
455
|
+
endpoints(network: string): ReadonlyArray<string>;
|
|
456
|
+
info(network: string): NetworkInfo;
|
|
457
|
+
client(network: string): PublicClient;
|
|
458
|
+
/** Validate a custom endpoint's reported chain id, then persist it first in the list. */
|
|
459
|
+
/** Solana JSON-RPC over the ordered endpoint list — kit's transport is single-endpoint. */
|
|
460
|
+
/** Networks with at least one configured endpoint, filtered by family. */
|
|
461
|
+
enabled(family: "evm" | "solana" | "bitcoin"): ReadonlyArray<string>;
|
|
462
|
+
solanaRpc(network: string): SolanaRpc;
|
|
463
|
+
/** Bounded-concurrency + 429-aware ordered-failover Esplora client. */
|
|
464
|
+
esplora(network: string): Esplora;
|
|
465
|
+
addRpc(network: string, url: string): Promise<void>;
|
|
466
|
+
}
|
|
467
|
+
//#endregion
|
|
468
|
+
//#region src/assets/controller.d.ts
|
|
469
|
+
type TokenBalance = {
|
|
470
|
+
readonly address: string;
|
|
471
|
+
readonly symbol: string;
|
|
472
|
+
readonly decimals: number;
|
|
473
|
+
readonly balance: string;
|
|
474
|
+
/** False for user-added tokens until they appear in the built-in table. */
|
|
475
|
+
readonly verified: boolean;
|
|
476
|
+
};
|
|
477
|
+
type AccountBalances = {
|
|
478
|
+
readonly native: string;
|
|
479
|
+
readonly tokens: ReadonlyArray<TokenBalance>;
|
|
480
|
+
/** Bitcoin only — confirmed + own unconfirmed change; frozen 546/330-sat UTXOs shown apart. */
|
|
481
|
+
readonly unconfirmed?: string;
|
|
482
|
+
readonly frozen?: string;
|
|
483
|
+
};
|
|
484
|
+
type AssetsControllerState = {
|
|
485
|
+
readonly balances: Record<string, AccountBalances>;
|
|
486
|
+
};
|
|
487
|
+
type Options$1 = {
|
|
488
|
+
readonly networks: NetworkController;
|
|
489
|
+
readonly openStore: () => Promise<EngineStore>;
|
|
490
|
+
readonly setTimer: (ms: number, fn: () => unknown) => () => void;
|
|
491
|
+
readonly logger: EngineLogger;
|
|
492
|
+
};
|
|
493
|
+
declare class AssetsController extends Controller<AssetsControllerState> {
|
|
494
|
+
private readonly options;
|
|
495
|
+
private engineStore;
|
|
496
|
+
private readonly bitcoinUtxos;
|
|
497
|
+
private visible;
|
|
498
|
+
private cancelPoll;
|
|
499
|
+
private readonly customTokens;
|
|
500
|
+
constructor(options: Options$1);
|
|
501
|
+
init(): Promise<void>;
|
|
502
|
+
lock(): void;
|
|
503
|
+
destroy(): Promise<void>;
|
|
504
|
+
setVisible(visible: boolean): void;
|
|
505
|
+
private armPoll;
|
|
506
|
+
private refreshAll;
|
|
507
|
+
refresh(address: string, network: string): Promise<void>;
|
|
508
|
+
/** SOL via getBalance; SPL via getTokenAccountsByOwner (jsonParsed) for known mints. */
|
|
509
|
+
private refreshSolana;
|
|
510
|
+
private readTokens;
|
|
511
|
+
private balanceOfBatch;
|
|
512
|
+
private balanceOf;
|
|
513
|
+
private callString;
|
|
514
|
+
private callNumber;
|
|
515
|
+
/** Reads symbol/decimals on-chain, marks unverified, persists; throws when the calls fail. */
|
|
516
|
+
addToken(network: string, address: string): Promise<void>;
|
|
517
|
+
private persistToken;
|
|
518
|
+
private rpc;
|
|
519
|
+
private rpcHex;
|
|
520
|
+
/** BTC balance = confirmed spendable + own unconfirmed change; frozen 546/330 shown separately. */
|
|
521
|
+
refreshBitcoin(xpub: string, network: string): Promise<void>;
|
|
522
|
+
}
|
|
523
|
+
//#endregion
|
|
524
|
+
//#region src/backup/errors.d.ts
|
|
525
|
+
/** BackupController error categories — never carry file contents, digests or secrets. */
|
|
526
|
+
type BackupErrorCategory = "cloud" | "conflict" | "unavailable";
|
|
527
|
+
export declare class BackupControllerError extends Error {
|
|
528
|
+
readonly category: BackupErrorCategory;
|
|
529
|
+
constructor(category: BackupErrorCategory, message: string);
|
|
530
|
+
}
|
|
531
|
+
//#endregion
|
|
532
|
+
//#region src/backup/port.d.ts
|
|
533
|
+
/**
|
|
534
|
+
* Cloud-file storage port (design/backup.mdx "存储"): the cloud is an opaque key-value namespace —
|
|
535
|
+
* names are file names (`qntx-wallet-backup-<id>.json`), contents are envelope texts. The kernel
|
|
536
|
+
* never sees it; only the engine transports ciphertext across it.
|
|
537
|
+
*/
|
|
538
|
+
type CloudStore = {
|
|
539
|
+
/** Cloud account usable and the drive reachable. */
|
|
540
|
+
readonly available: () => Promise<boolean>;
|
|
541
|
+
/** Every file name in the namespace. */
|
|
542
|
+
readonly list: () => Promise<ReadonlyArray<string>>;
|
|
543
|
+
/** File contents, or undefined when the name does not exist. */
|
|
544
|
+
readonly read: (name: string) => Promise<string | undefined>;
|
|
545
|
+
/** Overwrites the whole file atomically from the caller's perspective. */
|
|
546
|
+
readonly write: (name: string, text: string) => Promise<void>;
|
|
547
|
+
/** Deleting a missing file succeeds. */
|
|
548
|
+
readonly delete: (name: string) => Promise<void>;
|
|
549
|
+
};
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/backup/controller.d.ts
|
|
552
|
+
type BackupControllerState = {
|
|
553
|
+
readonly status: "locked" | "unavailable" | "off" | "syncing" | "synced" | "conflict" | "error";
|
|
554
|
+
/** The retained backup key's public summary from the wallet snapshot. */
|
|
555
|
+
readonly backup: BackupInfo | null;
|
|
556
|
+
readonly uploadedAt: number | undefined;
|
|
557
|
+
readonly unprotected: {
|
|
558
|
+
readonly walletIds: ReadonlyArray<string>;
|
|
559
|
+
readonly nostrKeyIds: ReadonlyArray<string>;
|
|
560
|
+
};
|
|
561
|
+
readonly error: BackupErrorCategory | undefined;
|
|
562
|
+
};
|
|
563
|
+
type BackupControllerOptions = {
|
|
564
|
+
readonly wallet: Wallet;
|
|
565
|
+
readonly cloud: CloudStore | undefined;
|
|
566
|
+
readonly now: () => number;
|
|
567
|
+
readonly logger: EngineLogger;
|
|
568
|
+
};
|
|
569
|
+
export declare class BackupController extends Controller<BackupControllerState> {
|
|
570
|
+
private readonly wallet;
|
|
571
|
+
private readonly cloud;
|
|
572
|
+
private readonly now;
|
|
573
|
+
private readonly logger;
|
|
574
|
+
private engineStore;
|
|
575
|
+
private generation;
|
|
576
|
+
private current;
|
|
577
|
+
private rerun;
|
|
578
|
+
/** Set by restore() before the store opens — written as a record on the first reconcile. */
|
|
579
|
+
private pendingAdoption;
|
|
580
|
+
constructor(options: BackupControllerOptions);
|
|
581
|
+
init(): Promise<void>;
|
|
582
|
+
destroy(): Promise<void>;
|
|
583
|
+
/** The engine hands over the store on unlock and takes it back on lock. */
|
|
584
|
+
setStore(store: EngineStore | undefined): void;
|
|
585
|
+
/** Bumps the generation: an in-flight reconcile writes nothing after this point. */
|
|
586
|
+
lock(): void;
|
|
587
|
+
trigger(): void;
|
|
588
|
+
private triggerAsync;
|
|
589
|
+
/** Explicit sync — awaits the run so callers see its outcome. */
|
|
590
|
+
sync(): Promise<void>;
|
|
591
|
+
enable(options: {
|
|
592
|
+
readonly verify: UnlockFactor;
|
|
593
|
+
readonly factors: ReadonlyArray<UnlockFactor>;
|
|
594
|
+
}): Promise<void>;
|
|
595
|
+
disable(): Promise<void>;
|
|
596
|
+
/** Files under our prefix with a parseable header. */
|
|
597
|
+
list(): Promise<ReadonlyArray<{
|
|
598
|
+
readonly name: string;
|
|
599
|
+
readonly header: BackupHeader;
|
|
600
|
+
}>>;
|
|
601
|
+
restore(options: {
|
|
602
|
+
readonly name: string;
|
|
603
|
+
readonly backupFactor: UnlockFactor;
|
|
604
|
+
readonly factor?: InitialFactor;
|
|
605
|
+
}): Promise<ReadonlyArray<WalletInfo>>;
|
|
606
|
+
private reconcileOrThrow;
|
|
607
|
+
private readRecord;
|
|
608
|
+
private writeRecord;
|
|
609
|
+
private deleteRecord;
|
|
610
|
+
private snapshot;
|
|
611
|
+
private unprotected;
|
|
612
|
+
private coverage;
|
|
613
|
+
private setStatus;
|
|
614
|
+
/**
|
|
615
|
+
* Single-flight: a trigger during a run flags exactly one rerun inside the same promise, so
|
|
616
|
+
* `sync()`/`enable()` await real convergence instead of a mid-run snapshot.
|
|
617
|
+
*/
|
|
618
|
+
private reconcile;
|
|
619
|
+
private pass;
|
|
620
|
+
}
|
|
621
|
+
//#endregion
|
|
622
|
+
//#region src/meta/errors.d.ts
|
|
623
|
+
/** Meta-sync error categories — messages never carry event or document content. */
|
|
624
|
+
type MetaSyncErrorCategory = "network" | "internal" | "unsupported" | "corrupt";
|
|
625
|
+
export declare class MetaSyncError extends Error {
|
|
626
|
+
readonly category: MetaSyncErrorCategory;
|
|
627
|
+
constructor(category: MetaSyncErrorCategory, message: string, cause?: unknown);
|
|
628
|
+
}
|
|
629
|
+
//#endregion
|
|
630
|
+
//#region src/meta/controller.d.ts
|
|
631
|
+
type MetaSyncRelayPort = {
|
|
632
|
+
readonly fetch: (filter: Filter) => Promise<ReadonlyArray<Event>>;
|
|
633
|
+
readonly publish: (event: Event) => Promise<void>;
|
|
634
|
+
};
|
|
635
|
+
type MetaSyncConfig = {
|
|
636
|
+
/** The `d` tag that names this wallet's metadata document. */
|
|
637
|
+
readonly tag: string;
|
|
638
|
+
readonly relays: MetaSyncRelayPort;
|
|
639
|
+
};
|
|
640
|
+
type MetaSyncWalletStatus = {
|
|
641
|
+
readonly status: "unavailable" | "syncing" | "synced" | "error";
|
|
642
|
+
readonly error?: MetaSyncErrorCategory;
|
|
643
|
+
};
|
|
644
|
+
type MetaSyncState = {
|
|
645
|
+
readonly wallets: Readonly<Record<string, MetaSyncWalletStatus>>;
|
|
646
|
+
};
|
|
647
|
+
type MetaSyncControllerOptions = {
|
|
648
|
+
readonly wallet: Wallet;
|
|
649
|
+
readonly accounts: AccountTreeController;
|
|
650
|
+
/** `nostr(wallet)` — the signer is requested per wallet for its group-0 derived key. */
|
|
651
|
+
readonly nostr: {
|
|
652
|
+
readonly signer: (ref: NostrKeyRef) => WalletSigner;
|
|
653
|
+
};
|
|
654
|
+
readonly metaSync: MetaSyncConfig | undefined;
|
|
655
|
+
readonly logger: EngineLogger;
|
|
656
|
+
readonly now: () => number;
|
|
657
|
+
};
|
|
658
|
+
/**
|
|
659
|
+
* Wallet-metadata sync over kind-30078 addressable events: one NIP-44-encrypted document per
|
|
660
|
+
* wallet, authored by its group-0 derived key. Pull always precedes publish in an unlock session —
|
|
661
|
+
* replaceable events would otherwise let a stale local tree overwrite a newer remote document.
|
|
662
|
+
* Debounced publishes are per wallet; a generation counter cancels in-flight write-backs on lock.
|
|
663
|
+
*/
|
|
664
|
+
export declare class MetaSyncController extends Controller<MetaSyncState> {
|
|
665
|
+
private readonly options;
|
|
666
|
+
private generation;
|
|
667
|
+
private readonly pulled;
|
|
668
|
+
private readonly lastRemoteCreatedAt;
|
|
669
|
+
private readonly lastExported;
|
|
670
|
+
private readonly timers;
|
|
671
|
+
private readonly inflight;
|
|
672
|
+
private accountsUnsubscribe;
|
|
673
|
+
constructor(options: MetaSyncControllerOptions);
|
|
674
|
+
/** Unlocked: pull every wallet whose group-0 identity exists, then watch the tree. */
|
|
675
|
+
init(): Promise<void>;
|
|
676
|
+
destroy(): Promise<void>;
|
|
677
|
+
/** Locked: cancel debounces and in-flight write-backs, forget the pull/publish bookkeeping. */
|
|
678
|
+
lock(): Promise<void>;
|
|
679
|
+
/** Snapshot changed while unlocked: pull newly-seen wallets, drop removed ones. */
|
|
680
|
+
reconcile(): void;
|
|
681
|
+
/** Manual re-pull of every wallet that can sync. */
|
|
682
|
+
sync(): Promise<void>;
|
|
683
|
+
private pullAll;
|
|
684
|
+
private pubkeyFor;
|
|
685
|
+
private signerFor;
|
|
686
|
+
private setStatus;
|
|
687
|
+
private pullOne;
|
|
688
|
+
private filterFor;
|
|
689
|
+
private acceptableEvent;
|
|
690
|
+
/** Snapshot the wallet's exported doc — the debounce publishes only on a real change. */
|
|
691
|
+
private rememberExport;
|
|
692
|
+
private onTreeChange;
|
|
693
|
+
private publishNow;
|
|
694
|
+
}
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/payments/parse.d.ts
|
|
697
|
+
type PaymentOption = {
|
|
698
|
+
readonly kind: "spark";
|
|
699
|
+
readonly address: string;
|
|
700
|
+
} | {
|
|
701
|
+
readonly kind: "spark-invoice";
|
|
702
|
+
readonly invoice: string;
|
|
703
|
+
readonly amountSats?: number;
|
|
704
|
+
readonly memo?: string;
|
|
705
|
+
readonly expiresAt?: number;
|
|
706
|
+
} | {
|
|
707
|
+
readonly kind: "lightning";
|
|
708
|
+
readonly invoice: string;
|
|
709
|
+
readonly amountSats?: number;
|
|
710
|
+
readonly paymentHash: string;
|
|
711
|
+
readonly description?: string;
|
|
712
|
+
readonly expiresAt: number;
|
|
713
|
+
} | {
|
|
714
|
+
readonly kind: "lnurl-pay";
|
|
715
|
+
readonly request: LnurlPayRequest;
|
|
716
|
+
readonly address?: string;
|
|
717
|
+
} | {
|
|
718
|
+
readonly kind: "lnurl-withdraw";
|
|
719
|
+
readonly request: LnurlWithdrawRequest;
|
|
720
|
+
} | {
|
|
721
|
+
readonly kind: "bitcoin";
|
|
722
|
+
readonly address: string;
|
|
723
|
+
};
|
|
724
|
+
type PaymentRequest = {
|
|
725
|
+
readonly options: ReadonlyArray<PaymentOption>;
|
|
726
|
+
readonly amountSats?: number;
|
|
727
|
+
readonly label?: string;
|
|
728
|
+
readonly message?: string;
|
|
729
|
+
};
|
|
730
|
+
type PaymentParse = {
|
|
731
|
+
readonly kind: "request";
|
|
732
|
+
readonly request: PaymentRequest;
|
|
733
|
+
} | {
|
|
734
|
+
readonly kind: "lnurl";
|
|
735
|
+
readonly url: string;
|
|
736
|
+
readonly address?: string;
|
|
737
|
+
} | {
|
|
738
|
+
readonly kind: "unsupported";
|
|
739
|
+
readonly format: "bolt12" | "silent-payment" | "lnurl-auth" | "spark-token-invoice" | "nostr-wallet-connect";
|
|
740
|
+
} | {
|
|
741
|
+
readonly kind: "invalid";
|
|
742
|
+
readonly reason: "network" | "expired" | "amount-mismatch" | "malformed";
|
|
743
|
+
} | {
|
|
744
|
+
readonly kind: "unrecognized";
|
|
745
|
+
};
|
|
746
|
+
type ParseOptions = {
|
|
747
|
+
readonly network: SparkNetwork;
|
|
748
|
+
readonly now: number;
|
|
749
|
+
};
|
|
750
|
+
/** `input` → PaymentParse. Never throws: every failure is a structured result. */
|
|
751
|
+
export declare function parsePayment(input: string, options: ParseOptions): PaymentParse;
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/payments/pay.d.ts
|
|
754
|
+
type PayParams = {
|
|
755
|
+
readonly account: SparkAccountRef;
|
|
756
|
+
readonly option: PaymentOption;
|
|
757
|
+
/** Caller-supplied amount — required by open invoices, the `spark` option, and LNURL flows. */
|
|
758
|
+
readonly amountSats?: number;
|
|
759
|
+
readonly comment?: string;
|
|
760
|
+
readonly maxFeeSats?: number;
|
|
761
|
+
readonly speed?: "FAST" | "MEDIUM" | "SLOW";
|
|
762
|
+
readonly verify: UnlockFactor;
|
|
763
|
+
};
|
|
764
|
+
type PayResult = {
|
|
765
|
+
readonly kind: "spark";
|
|
766
|
+
readonly transfer: SparkTransfer;
|
|
767
|
+
} | {
|
|
768
|
+
readonly kind: "lightning";
|
|
769
|
+
readonly payment: LightningPayment;
|
|
770
|
+
} | {
|
|
771
|
+
readonly kind: "withdraw";
|
|
772
|
+
readonly exit: CooperativeExitResult;
|
|
773
|
+
} | {
|
|
774
|
+
readonly kind: "lnurl-withdraw";
|
|
775
|
+
readonly invoice: LightningInvoice;
|
|
776
|
+
};
|
|
777
|
+
//#endregion
|
|
778
|
+
//#region src/payments/zap.d.ts
|
|
779
|
+
/** A profile zap names the recipient pubkey; an event zap carries the event it zaps. */
|
|
780
|
+
type ZapRecipient = {
|
|
781
|
+
readonly pubkey: string;
|
|
782
|
+
readonly lightning: string;
|
|
783
|
+
} | {
|
|
784
|
+
readonly event: Event;
|
|
785
|
+
readonly lightning: string;
|
|
786
|
+
};
|
|
787
|
+
type PrepareZapParams = {
|
|
788
|
+
/** Any `NostrSigner` — wallet key, external key, NIP-07/46 — signs the 9734. */
|
|
789
|
+
readonly signer: NostrSigner;
|
|
790
|
+
readonly recipient: ZapRecipient;
|
|
791
|
+
readonly amountSats: number;
|
|
792
|
+
readonly comment?: string | undefined;
|
|
793
|
+
readonly relays: ReadonlyArray<string>;
|
|
794
|
+
readonly network: SparkNetwork;
|
|
795
|
+
};
|
|
796
|
+
type PrepareZapContext = {
|
|
797
|
+
readonly fetch: typeof globalThis.fetch;
|
|
798
|
+
readonly now: () => number;
|
|
799
|
+
};
|
|
800
|
+
/** Everything a payer needs: the signed 9734, the invoice, and the note fields. */
|
|
801
|
+
type PreparedZap = {
|
|
802
|
+
readonly zapRequest: Event;
|
|
803
|
+
readonly invoice: string;
|
|
804
|
+
/** Hex payment hash decoded from the invoice. */
|
|
805
|
+
readonly paymentHash: string;
|
|
806
|
+
/** Millisecond expiry decoded from the invoice. */
|
|
807
|
+
readonly expiresAt: number;
|
|
808
|
+
readonly amountSats: number;
|
|
809
|
+
/** The LNURL service's 9735 signing pubkey. */
|
|
810
|
+
readonly receiptPubkey: string;
|
|
811
|
+
readonly recipientPubkey: string;
|
|
812
|
+
readonly eventId?: string;
|
|
813
|
+
readonly comment?: string;
|
|
814
|
+
/** The lud16 or LNURL as given. */
|
|
815
|
+
readonly lightning: string;
|
|
816
|
+
};
|
|
817
|
+
type PayZapOptions = {
|
|
818
|
+
readonly maxFeeSats: number;
|
|
819
|
+
readonly verify: UnlockFactor;
|
|
820
|
+
readonly now: () => number;
|
|
821
|
+
};
|
|
822
|
+
/** Everything up to payment: resolve LNURL, sign the 9734, request the invoice. */
|
|
823
|
+
export declare function prepareZap(params: PrepareZapParams, context: PrepareZapContext): Promise<PreparedZap>;
|
|
824
|
+
/**
|
|
825
|
+
* Pays a prepared zap through the controller's approve-first path. A nearly-expired invoice is
|
|
826
|
+
* rejected before approval — the user may have sat on the confirm screen since `prepareZap`.
|
|
827
|
+
*/
|
|
828
|
+
export declare function payZap(spark: SparkController, account: SparkAccountRef, prepared: PreparedZap, options: PayZapOptions): Promise<LightningPayment>;
|
|
829
|
+
//#endregion
|
|
830
|
+
//#region src/chains/evm.d.ts
|
|
831
|
+
type EvmFeeTier = "slow" | "normal" | "fast";
|
|
832
|
+
//#endregion
|
|
833
|
+
//#region src/chains/solana.d.ts
|
|
834
|
+
type SolanaFeeTier = "slow" | "normal" | "fast";
|
|
835
|
+
//#endregion
|
|
836
|
+
//#region src/transactions/route.d.ts
|
|
837
|
+
type SignParams = Parameters<ReturnType<typeof chains>["sign"]>[0];
|
|
838
|
+
type AccountRef = SignParams["request"]["account"];
|
|
839
|
+
type DisplayIntent = ReturnType<typeof decodeEvm>;
|
|
840
|
+
type TxState = "draft" | "signed" | "submitted" | "pending" | "confirmed" | "failed" | "rejected" | "dropped" | "replaced";
|
|
841
|
+
type TrackedTransaction = {
|
|
842
|
+
readonly id: string;
|
|
843
|
+
readonly family: "evm" | "solana" | "bitcoin";
|
|
844
|
+
readonly network: string;
|
|
845
|
+
readonly account: string;
|
|
846
|
+
readonly hash: string | undefined;
|
|
847
|
+
readonly state: TxState;
|
|
848
|
+
readonly intent: DisplayIntent | undefined;
|
|
849
|
+
readonly nonce: number;
|
|
850
|
+
/** Solana only — the blockhash expiry that turns an unconfirmed tx `dropped`. */
|
|
851
|
+
readonly lastValidBlockHeight?: number;
|
|
852
|
+
/** EVM replacement — the earlier transaction this one bumps or cancels. */
|
|
853
|
+
readonly replaces?: string;
|
|
854
|
+
/** CPFP — the unconfirmed parent this child pushes. */
|
|
855
|
+
readonly parentId?: string;
|
|
856
|
+
/** Bitcoin build record persisted at send for RBF/CPFP rebuilds. */
|
|
857
|
+
readonly btc?: {
|
|
858
|
+
readonly inputs: ReadonlyArray<{
|
|
859
|
+
readonly txid: string;
|
|
860
|
+
readonly vout: number;
|
|
861
|
+
readonly value: number;
|
|
862
|
+
readonly address: string;
|
|
863
|
+
}>;
|
|
864
|
+
readonly outputs: ReadonlyArray<{
|
|
865
|
+
readonly address: string;
|
|
866
|
+
readonly value: number;
|
|
867
|
+
readonly own: boolean;
|
|
868
|
+
}>;
|
|
869
|
+
readonly vsize: number;
|
|
870
|
+
readonly fee: number;
|
|
871
|
+
readonly feeRate: number;
|
|
872
|
+
};
|
|
873
|
+
readonly submittedAt: number;
|
|
874
|
+
readonly error: string | undefined;
|
|
875
|
+
};
|
|
876
|
+
type SendParams = {
|
|
877
|
+
readonly account: AccountRef;
|
|
878
|
+
readonly network: string;
|
|
879
|
+
readonly to: string;
|
|
880
|
+
readonly amount: bigint;
|
|
881
|
+
/** ERC-20 contract — when set, `to`/`amount` are the transfer recipient/units and value is 0. */
|
|
882
|
+
readonly token?: string;
|
|
883
|
+
readonly fee: EvmFeeTier | SolanaFeeTier;
|
|
884
|
+
/** SPL mint decimals — required when `token` is a Solana mint. */
|
|
885
|
+
readonly decimals?: number;
|
|
886
|
+
readonly verify: SignParams["verify"];
|
|
887
|
+
};
|
|
888
|
+
type Options = {
|
|
889
|
+
readonly wallet: Wallet;
|
|
890
|
+
readonly networks: NetworkController;
|
|
891
|
+
readonly openStore: () => Promise<EngineStore>;
|
|
892
|
+
readonly random: (n: number) => Uint8Array;
|
|
893
|
+
readonly setTimer: (ms: number, fn: () => unknown) => () => void;
|
|
894
|
+
readonly now: () => number;
|
|
895
|
+
readonly logger: EngineLogger;
|
|
896
|
+
readonly pollMs?: number;
|
|
897
|
+
/** Test seam — defaults to the wallet's real chains proxy. */
|
|
898
|
+
readonly chains?: ReturnType<typeof chains>;
|
|
899
|
+
};
|
|
900
|
+
//#endregion
|
|
901
|
+
//#region src/transactions/controller.d.ts
|
|
902
|
+
type TransactionControllerState = {
|
|
903
|
+
readonly txs: Record<string, TrackedTransaction>;
|
|
904
|
+
};
|
|
905
|
+
declare class TransactionController extends Controller<TransactionControllerState> {
|
|
906
|
+
private readonly options;
|
|
907
|
+
private engineStore?;
|
|
908
|
+
private readonly timers;
|
|
909
|
+
private evmRoute;
|
|
910
|
+
private readonly bitcoinRoute;
|
|
911
|
+
private locked;
|
|
912
|
+
constructor(options: Options);
|
|
913
|
+
init(): Promise<void>;
|
|
914
|
+
destroy(): Promise<void>;
|
|
915
|
+
lock(): void;
|
|
916
|
+
private persist;
|
|
917
|
+
private transition;
|
|
918
|
+
private routeCtx;
|
|
919
|
+
send(params: SendParams): Promise<TrackedTransaction>;
|
|
920
|
+
/**
|
|
921
|
+
* Speed up or cancel a pending/submitted EVM transaction at the same nonce — a fresh signature is
|
|
922
|
+
* required and a rejection leaves the original untouched.
|
|
923
|
+
*/
|
|
924
|
+
replace(id: string, mode: "speed-up" | "cancel", verify: SendParams["verify"]): Promise<TrackedTransaction>;
|
|
925
|
+
/**
|
|
926
|
+
* CPFP — spend an unconfirmed tx's own outputs into a higher-fee child so the package mines.
|
|
927
|
+
* Bitcoin only; the child is linked via `parentId`.
|
|
928
|
+
*/
|
|
929
|
+
bumpChild(id: string, targetRate: number, verify: SendParams["verify"]): Promise<TrackedTransaction>;
|
|
930
|
+
private track;
|
|
931
|
+
}
|
|
932
|
+
//#endregion
|
|
933
|
+
//#region src/engine.d.ts
|
|
934
|
+
type EngineOptions = {
|
|
935
|
+
readonly wallet: Wallet;
|
|
936
|
+
/** Every Spark account the engine touches lives on this network. */
|
|
937
|
+
readonly sparkNetwork: SparkNetwork;
|
|
938
|
+
readonly fetch: typeof globalThis.fetch;
|
|
939
|
+
readonly random: (length: number) => Uint8Array;
|
|
940
|
+
readonly now: () => number;
|
|
941
|
+
readonly logger: EngineLogger;
|
|
942
|
+
readonly openStore: () => Promise<EngineStore>;
|
|
943
|
+
/** Cloud file transport for the backup controller; absent → `unavailable`. */
|
|
944
|
+
readonly cloud?: CloudStore;
|
|
945
|
+
/** RPC endpoints per network (CAIP-2), ordered by preference for the viem fallback transport. */
|
|
946
|
+
readonly rpc?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
947
|
+
/** Injected timer for transaction polling; defaults to setTimeout. */
|
|
948
|
+
readonly setTimer?: (ms: number, fn: () => void) => () => void;
|
|
949
|
+
/** Kind-30078 wallet-metadata sync; absent → every wallet reports `unavailable`. */
|
|
950
|
+
readonly metaSync?: MetaSyncConfig;
|
|
951
|
+
/** Test seam — defaults to the wallet's real spark proxy. */
|
|
952
|
+
readonly sparkApis?: SparkControllerOptions["spark"];
|
|
953
|
+
};
|
|
954
|
+
type WalletEngine = {
|
|
955
|
+
readonly networks: NetworkController;
|
|
956
|
+
readonly assets: AssetsController;
|
|
957
|
+
readonly transactions: TransactionController;
|
|
958
|
+
readonly spark: SparkController;
|
|
959
|
+
readonly accounts: AccountTreeController;
|
|
960
|
+
readonly metaSync: MetaSyncController;
|
|
961
|
+
readonly backup: BackupController;
|
|
962
|
+
readonly zaps: {
|
|
963
|
+
readonly prepare: (params: PrepareZapParams) => Promise<PreparedZap>;
|
|
964
|
+
readonly pay: (account: SparkAccountRef, prepared: PreparedZap, options: Omit<PayZapOptions, "now">) => Promise<LightningPayment>;
|
|
965
|
+
};
|
|
966
|
+
readonly payments: {
|
|
967
|
+
readonly parse: (input: string, network: SparkNetwork) => PaymentParse;
|
|
968
|
+
readonly resolve: (input: string, network: SparkNetwork) => Promise<PaymentParse>;
|
|
969
|
+
readonly pay: (params: PayParams) => Promise<PayResult>;
|
|
970
|
+
};
|
|
971
|
+
readonly unsubscribe: () => void;
|
|
972
|
+
};
|
|
973
|
+
export declare function createWalletEngine(options: EngineOptions): WalletEngine;
|
|
974
|
+
//#endregion
|
|
975
|
+
//#region src/backup/memory.d.ts
|
|
976
|
+
/** In-memory CloudStore for tests and the playground; swap the Map for cross-engine sharing. */
|
|
977
|
+
export declare function createMemoryCloudStore(backing?: Map<string, string>): CloudStore;
|
|
978
|
+
//#endregion
|
|
979
|
+
//#region src/spark/errors.d.ts
|
|
980
|
+
/** SparkController error categories — never carry request bytes or secrets. */
|
|
981
|
+
type SparkErrorCategory = "network" | "approval-required" | "insufficient-funds" | "rejected" | "internal";
|
|
982
|
+
export declare class SparkControllerError extends Error {
|
|
983
|
+
readonly category: SparkErrorCategory;
|
|
984
|
+
constructor(category: SparkErrorCategory, message: string, cause?: unknown);
|
|
985
|
+
}
|
|
986
|
+
//#endregion
|
|
987
|
+
//#region src/accounts/errors.d.ts
|
|
988
|
+
/** Account-tree error categories — messages never carry secrets. */
|
|
989
|
+
type AccountTreeErrorCategory = "invalid-input" | "rejected" | "not-found" | "locked";
|
|
990
|
+
export declare class AccountTreeError extends Error {
|
|
991
|
+
readonly category: AccountTreeErrorCategory;
|
|
992
|
+
constructor(category: AccountTreeErrorCategory, message: string, cause?: unknown);
|
|
993
|
+
}
|
|
994
|
+
//#endregion
|
|
995
|
+
//#region src/payments/errors.d.ts
|
|
996
|
+
/** Payment layer error categories — messages never carry remote or secret content. */
|
|
997
|
+
type PaymentErrorCategory = "invalid-input" | "network" | "rejected";
|
|
998
|
+
export declare class PaymentError extends Error {
|
|
999
|
+
readonly category: PaymentErrorCategory;
|
|
1000
|
+
constructor(category: PaymentErrorCategory, message: string, cause?: unknown);
|
|
1001
|
+
}
|
|
1002
|
+
//#endregion
|
|
1003
|
+
//#region src/payments/resolve.d.ts
|
|
1004
|
+
type ResolveOptions = ParseOptions & {
|
|
1005
|
+
readonly fetch: typeof globalThis.fetch;
|
|
1006
|
+
};
|
|
1007
|
+
/**
|
|
1008
|
+
* `parsePayment` plus LNURL dereferencing: an `lnurl` result is fetched and turned into a concrete
|
|
1009
|
+
* `lnurl-pay` / `lnurl-withdraw` request (`tag=login` endpoints report as unsupported). Network
|
|
1010
|
+
* failures throw `PaymentError("network")` — remote error bodies never enter the message.
|
|
1011
|
+
*/
|
|
1012
|
+
export declare function resolvePayment(input: string, options: ResolveOptions): Promise<PaymentParse>;
|
|
1013
|
+
//#endregion
|
|
1014
|
+
//#region src/spark/exit-chain-codec.d.ts
|
|
1015
|
+
export declare function encodeExitChain(chain: SparkExitChain): Uint8Array;
|
|
1016
|
+
export declare function decodeExitChain(bytes: Uint8Array): SparkExitChain;
|
|
1017
|
+
//#endregion
|
|
1018
|
+
export type { AccountGroup, AccountTreeState, BackupControllerState, BackupErrorCategory, CloudStore, ControllerOptions, EngineCollection, EngineErrorCode, EngineLogger, EngineOptions, EngineStore, EventBus, MetaDoc, MetaDocGroup, MetaDocWallet, MetaSyncConfig, MetaSyncRelayPort, MetaSyncState, MetaSyncWalletStatus, PayParams, PayResult, PayZapOptions, PaymentNote, PaymentOption, PaymentParse, PaymentRequest, PrepareZapContext, PrepareZapParams, PreparedZap, SparkAccountState, SparkActivity, SparkClientFactory, SparkControllerOptions, SparkControllerState, SparkErrorCategory, WalletEngine, ZapRecipient };
|
|
1019
|
+
//# sourceMappingURL=index.d.mts.map
|