@qntx/wallet-engine 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,20 +1,19 @@
1
- import { r as NetworkInfo } from "./registry-9oeWs_yZ.mjs";
1
+ import { r as NetworkInfo } from "./registry-LcFSPrE1.mjs";
2
2
  import { StoreApi } from "zustand/vanilla";
3
+ import { SignRequest } from "@qntx/wallet-chains";
4
+ import { BackupHeader, BackupInfo, InitialFactor, UnlockFactor, Wallet, WalletInfo } from "@qntx/wallet";
3
5
  import "@scure/btc-signer";
4
6
  import { CooperativeExitResult, LightningInvoice, LightningPayment, SparkClient, SparkExitChain, SparkNetwork, SparkPayment, SparkStore, SparkTransfer } from "@qntx/spark";
5
7
  import { decodeEvm } from "@qntx/wallet-chains/evm";
6
- import { BackupHeader, BackupInfo, InitialFactor, UnlockFactor, Wallet, WalletInfo } from "@qntx/wallet";
7
8
  import { LnurlPayRequest, LnurlWithdrawRequest } from "@qntx/spark/lnurl";
8
9
  import { Event, Filter, NostrSigner } from "@qntx/nostr";
9
- import { NostrKeyRef, WalletSigner } from "@qntx/wallet/nostr";
10
+ import { chains } from "@qntx/wallet/chains";
11
+ import { WalletSyncSigner } from "@qntx/wallet/nostr";
10
12
  import { SparkSigner } from "@qntx/wallet/spark";
11
13
  import { PublicClient } from "viem";
12
- import { chains } from "@qntx/wallet/chains";
13
14
  import "@solana/kit";
14
- import { SparkAccountRef, SparkApproveIntent } from "@qntx/wallet/protocol";
15
- import "@qntx/wallet-chains";
16
15
  import "@scure/btc-signer/psbt.js";
17
- //#region src/controller.d.ts
16
+ import { SparkAccountRef, SparkApproveIntent } from "@qntx/wallet/protocol";
18
17
  type ControllerOptions<S> = {
19
18
  readonly name: string;
20
19
  readonly defaultState: () => S;
@@ -33,8 +32,6 @@ export declare abstract class Controller<S extends Record<string, unknown>> {
33
32
  abstract init(): Promise<void>;
34
33
  abstract destroy(): Promise<void>;
35
34
  }
36
- //#endregion
37
- //#region src/events.d.ts
38
35
  /** Log port: listener failures are reported here and never reach the emitter or peers. */
39
36
  type EngineLogger = {
40
37
  readonly warn: (message: string, error?: unknown) => void;
@@ -45,16 +42,12 @@ type EventBus<E> = {
45
42
  on: <K extends keyof E>(type: K, listener: (payload: E[K]) => void) => () => void;
46
43
  };
47
44
  export declare function createEventBus<E>(logger: EngineLogger): EventBus<E>;
48
- //#endregion
49
- //#region src/errors.d.ts
50
45
  type EngineErrorCode = "closed" | "integrity" | "invalid-input";
51
46
  /** Typed engine errors: store codes are `closed` and `integrity` (decrypt or bind failure). */
52
47
  export declare class EngineStoreError extends Error {
53
48
  readonly code: EngineErrorCode;
54
49
  constructor(code: EngineErrorCode, message: string);
55
50
  }
56
- //#endregion
57
- //#region src/store/port.d.ts
58
51
  /**
59
52
  * Engine storage port: named collections of byte values. `collection` is synchronous and cheap —
60
53
  * the same collection object backs every call, so guards (closed) live on the store.
@@ -70,98 +63,113 @@ type EngineStore = {
70
63
  readonly collection: (name: string) => EngineCollection;
71
64
  readonly close: () => Promise<void>;
72
65
  };
73
- //#endregion
74
- //#region src/store/memory.d.ts
75
66
  /** In-memory EngineStore: the Node/test implementation — same contract, no durability. */
76
67
  export declare function createMemoryEngineStore(backing?: {
77
68
  readonly collections?: Map<string, Map<string, Uint8Array>>;
78
69
  }): EngineStore;
79
- //#endregion
80
- //#region src/addresses/normalize.d.ts
81
- type AddressFamily = "evm" | "solana" | "bitcoin" | "spark";
82
- //#endregion
83
- //#region src/addresses/record.d.ts
84
- type AddressBookEntry = {
70
+ type ConnectionOrigin = {
71
+ readonly kind: "browser";
72
+ readonly origin: string;
73
+ };
74
+ type ConnectionScope = {
75
+ /** CAIP-10 */
76
+ readonly accounts: ReadonlyArray<string>;
77
+ readonly methods: ReadonlyArray<string>;
78
+ readonly notifications: ReadonlyArray<string>;
79
+ };
80
+ type ConnectionSession = {
85
81
  readonly id: string;
86
- readonly name: string;
87
- readonly family: AddressFamily;
88
- readonly address: string;
89
- readonly note?: string;
82
+ readonly origin: ConnectionOrigin;
83
+ /** Keyed by CAIP-2. */
84
+ readonly scopes: Readonly<Record<string, ConnectionScope>>;
85
+ /** The session's current EVM network; only dApp sessions have one. */
86
+ readonly currentNetwork: string;
90
87
  readonly createdAt: number;
91
- readonly updatedAt: number;
88
+ readonly lastUsedAt: number;
92
89
  };
93
- type AddressBookTombstone = {
90
+ /** `connect` asks for accounts; `network` asks to add a network the session does not cover. */
91
+ type PendingConnection = {
94
92
  readonly id: string;
95
- readonly deleted: true;
96
- readonly updatedAt: number;
93
+ readonly origin: ConnectionOrigin;
94
+ readonly kind: "connect" | "network";
95
+ readonly network?: string;
96
+ readonly createdAt: number;
97
97
  };
98
- type AddressBookRecord = AddressBookEntry | AddressBookTombstone;
99
- //#endregion
100
- //#region src/addresses/controller.d.ts
101
- type AddressBookState = {
102
- /** Live entries only; tombstones stay internal. */
103
- readonly entries: Record<string, AddressBookEntry>;
98
+ type AccountGroup$1 = {
99
+ readonly walletId: string;
100
+ readonly groupIndex: number;
104
101
  };
105
- type AddressBookOptions = {
102
+ type ConnectionState = {
103
+ readonly sessions: Readonly<Record<string, ConnectionSession>>;
104
+ readonly pending: Readonly<Record<string, PendingConnection>>;
105
+ };
106
+ type ConnectionControllerOptions = {
106
107
  readonly openStore: () => Promise<EngineStore>;
107
108
  readonly random: (n: number) => Uint8Array;
108
109
  readonly now: () => number;
109
110
  readonly logger: EngineLogger;
110
- };
111
- export declare class AddressBookController extends Controller<AddressBookState> {
111
+ /** Development builds also accept `http://localhost`. */
112
+ readonly allowLocalhost: boolean;
113
+ /** The group the app currently shows; sessions are bound to it. */
114
+ readonly selectedGroup: () => AccountGroup$1 | undefined;
115
+ /** The group's EVM address on `network`. */
116
+ readonly evmAddress: (group: AccountGroup$1, network: string) => Promise<string>;
117
+ readonly enabledNetworks: () => ReadonlyArray<string>;
118
+ /** Voids a page's unfinished requests (revoked, or its network changed). */
119
+ readonly invalidate: (origin: string) => void;
120
+ };
121
+ export declare class ConnectionController extends Controller<ConnectionState> {
112
122
  private readonly options;
113
123
  private engineStore;
114
- /** Every record, tombstones included, by id. */
115
- private records;
116
- constructor(options: AddressBookOptions);
124
+ private readonly waiters;
125
+ private group;
126
+ constructor(options: ConnectionControllerOptions);
117
127
  init(): Promise<void>;
118
128
  destroy(): Promise<void>;
129
+ /** Locking is not consent: every pending connection is rejected. */
119
130
  lock(): void;
120
- /** Entries and tombstones, for the metadata document. */
121
- syncEntries(): ReadonlyArray<AddressBookRecord>;
131
+ /** Throws `4100` unless `origin` is `https://host[:port]` (or localhost in development). */
132
+ checkOrigin(origin: string): ConnectionOrigin;
133
+ session(origin: string): ConnectionSession | undefined;
134
+ pending(): ReadonlyArray<PendingConnection>;
135
+ /** The group sessions are bound to: the host's last `setActiveGroup`, else the app's selection. */
136
+ activeGroup(): AccountGroup$1 | undefined;
137
+ /** Whether `network` is an EVM network the wallet has enabled. */
138
+ isEnabled(network: string): boolean;
139
+ /** The origin's authorized addresses on its current network; empty when not connected. */
140
+ accounts(origin: string): ReadonlyArray<string>;
141
+ /** Throws `4100` unless the origin's session covers `method` on `network`. */
142
+ authorize(origin: string, network: string, method: string): ConnectionSession;
143
+ /** Resolves with the session once the host approves; rejects with `4001` when it refuses. */
144
+ request(origin: string, kind: PendingConnection["kind"], network?: string): Promise<ConnectionSession>;
122
145
  /**
123
- * Merges records from another device (last writer wins, tombstones win over older entries).
124
- * Returns whether anything local changed and whether the merged set holds something `remote`
125
- * lacks, i.e. whether the remote copy needs a newer publish.
146
+ * The host's approval, by id: the accounts granted (EVM addresses of the active group) and the
147
+ * network.
126
148
  */
127
- merge(remote: ReadonlyArray<AddressBookRecord>): Promise<{
128
- readonly changed: boolean;
129
- readonly localNewer: boolean;
130
- }>;
131
- add(params: {
132
- readonly name: string;
133
- readonly family: AddressFamily;
134
- readonly address: string;
135
- readonly note?: string;
136
- }): Promise<AddressBookEntry>;
137
- rename(id: string, name: string): Promise<AddressBookEntry>;
138
- /** Sets or clears (empty or `undefined`) the note. */
139
- setNote(id: string, note: string | undefined): Promise<AddressBookEntry>;
140
- /** Removes the entry, leaving a tombstone so other devices drop it too. */
141
- remove(id: string): Promise<void>;
142
- private checkedNote;
143
- private requireEntry;
149
+ approveConnection(id: string, grant: {
150
+ readonly accounts: ReadonlyArray<string>;
151
+ readonly network: string;
152
+ }): Promise<ConnectionSession>;
153
+ rejectConnection(id: string): void;
154
+ /** Ends the origin's session; its pending connection and unfinished requests are void. */
155
+ revoke(origin: string): Promise<void>;
156
+ /** Switches within the session's scope; the origin's unfinished requests are void. */
157
+ setCurrentNetwork(origin: string, network: string): Promise<void>;
158
+ touch(origin: string): Promise<void>;
159
+ /**
160
+ * The host switched account group: every session's accounts become the new group's addresses. A
161
+ * session that cannot get one (no address derivable) is left with an empty list and has to be
162
+ * authorized again.
163
+ */
164
+ setActiveGroup(walletId: string, groupIndex: number): Promise<void>;
165
+ private dropPending;
144
166
  private persist;
145
167
  private requireStore;
146
168
  }
147
- //#endregion
148
- //#region src/addresses/errors.d.ts
149
- type AddressBookErrorCategory = "invalid-input" | "duplicate" | "not-found" | "locked" | "limit";
150
- export declare class AddressBookError extends Error {
151
- readonly category: AddressBookErrorCategory;
152
- constructor(category: AddressBookErrorCategory, message: string);
153
- }
154
- //#endregion
155
- //#region src/chains/evm.d.ts
156
- type EvmFeeTier = "slow" | "normal" | "fast";
157
- //#endregion
158
- //#region src/chains/bitcoin.d.ts
159
169
  type Esplora = {
160
170
  readonly get: (path: string) => Promise<unknown>;
161
171
  readonly post: (path: string, body: string) => Promise<unknown>;
162
172
  };
163
- //#endregion
164
- //#region src/networks/controller.d.ts
165
173
  type SolanaRpc = {
166
174
  readonly call: (method: string, params: unknown[]) => Promise<unknown>;
167
175
  };
@@ -231,14 +239,71 @@ declare class NetworkController extends Controller<NetworkControllerState> {
231
239
  /** Validate a custom endpoint's reported chain id, then persist it first in the list. */
232
240
  addRpc(network: string, url: string): Promise<void>;
233
241
  }
234
- //#endregion
235
- //#region src/chains/solana.d.ts
242
+ type AddressFamily = "evm" | "solana" | "bitcoin" | "spark";
243
+ type AddressBookEntry = {
244
+ readonly id: string;
245
+ readonly name: string;
246
+ readonly family: AddressFamily;
247
+ readonly address: string;
248
+ readonly note?: string;
249
+ readonly createdAt: number;
250
+ readonly updatedAt: number;
251
+ };
252
+ type AddressBookTombstone = {
253
+ readonly id: string;
254
+ readonly deleted: true;
255
+ readonly updatedAt: number;
256
+ };
257
+ type AddressBookRecord = AddressBookEntry | AddressBookTombstone;
258
+ type AddressBookState = {
259
+ /** Live entries only; tombstones stay internal. */
260
+ readonly entries: Record<string, AddressBookEntry>;
261
+ };
262
+ type AddressBookOptions = {
263
+ readonly openStore: () => Promise<EngineStore>;
264
+ readonly random: (n: number) => Uint8Array;
265
+ readonly now: () => number;
266
+ readonly logger: EngineLogger;
267
+ };
268
+ export declare class AddressBookController extends Controller<AddressBookState> {
269
+ private readonly options;
270
+ private engineStore;
271
+ /** Every record, tombstones included, by id. */
272
+ private records;
273
+ constructor(options: AddressBookOptions);
274
+ init(): Promise<void>;
275
+ destroy(): Promise<void>;
276
+ lock(): void;
277
+ /** Entries and tombstones, for the metadata document. */
278
+ syncEntries(): ReadonlyArray<AddressBookRecord>;
279
+ /**
280
+ * Merges records from another device (last writer wins, tombstones win over older entries).
281
+ * Returns whether anything local changed and whether the merged set holds something `remote`
282
+ * lacks, i.e. whether the remote copy needs a newer publish.
283
+ */
284
+ merge(remote: ReadonlyArray<AddressBookRecord>): Promise<{
285
+ readonly changed: boolean;
286
+ readonly localNewer: boolean;
287
+ }>;
288
+ add(params: {
289
+ readonly name: string;
290
+ readonly family: AddressFamily;
291
+ readonly address: string;
292
+ readonly note?: string;
293
+ }): Promise<AddressBookEntry>;
294
+ rename(id: string, name: string): Promise<AddressBookEntry>;
295
+ /** Sets or clears (empty or `undefined`) the note. */
296
+ setNote(id: string, note: string | undefined): Promise<AddressBookEntry>;
297
+ /** Removes the entry, leaving a tombstone so other devices drop it too. */
298
+ remove(id: string): Promise<void>;
299
+ private checkedNote;
300
+ private requireEntry;
301
+ private persist;
302
+ private requireStore;
303
+ }
304
+ type EvmFeeTier = "slow" | "normal" | "fast";
236
305
  type SolanaFeeTier = "slow" | "normal" | "fast";
237
- //#endregion
238
- //#region src/requests/checks.d.ts
239
306
  type RequestCheckCode = "decode-partial" | "nonce-mismatch" | "fee-mismatch" | "reads-unverified" | "fee-above-network" | "fee-exceeds-amount";
240
- //#endregion
241
- //#region src/spark/notes.d.ts
242
307
  /**
243
308
  * Local, engine-side metadata pinned to a history entry by transfer id. Written after the payment
244
309
  * it annotates has already succeeded — a missing or malformed note must never affect the payment
@@ -258,15 +323,11 @@ type PaymentNote = {
258
323
  type SparkActivity = SparkPayment & {
259
324
  readonly note?: PaymentNote;
260
325
  };
261
- //#endregion
262
- //#region src/spark/store.d.ts
263
326
  /**
264
327
  * SparkStore over one EngineCollection: chains keyed by leafId, plus an index record listing them
265
328
  * (`exitChains` is a full listing — the port has no secondary index).
266
329
  */
267
330
  export declare function sparkStore(col: EngineCollection): SparkStore;
268
- //#endregion
269
- //#region src/spark/controller.d.ts
270
331
  type SparkAccountState = {
271
332
  readonly status: "locked" | "connecting" | "ready" | "error";
272
333
  readonly balanceSats?: number;
@@ -394,6 +455,10 @@ export declare class SparkController extends Controller<SparkControllerState> {
394
455
  amountSats: number;
395
456
  memo?: string;
396
457
  }): Promise<LightningInvoice>;
458
+ /** A fresh single-use L1 deposit address (with the SO's proof of possession). */
459
+ newDepositAddress(ref: SparkAccountRef): Promise<string>;
460
+ /** Claims a confirmed L1 deposit paying one of the account's unused deposit addresses. */
461
+ claimDeposit(ref: SparkAccountRef, transaction: Uint8Array): Promise<void>;
397
462
  receiveAddress(ref: SparkAccountRef): Promise<string | undefined>;
398
463
  /**
399
464
  * Cooperative exit to an L1 address. The client exits `amountSats` of leaves and the payout is
@@ -408,8 +473,6 @@ export declare class SparkController extends Controller<SparkControllerState> {
408
473
  verify: UnlockFactor;
409
474
  }): Promise<CooperativeExitResult>;
410
475
  }
411
- //#endregion
412
- //#region src/payments/action.d.ts
413
476
  /**
414
477
  * What the host shows for a Spark, Lightning or exit request; `family` tells it apart from a chain
415
478
  * intent.
@@ -447,8 +510,6 @@ type PreparedSparkAction = {
447
510
  /** Runs the controller's approve-first payment call. */
448
511
  readonly execute: (spark: SparkController, verify: UnlockFactor) => Promise<SparkResult>;
449
512
  };
450
- //#endregion
451
- //#region src/payments/parse.d.ts
452
513
  type PaymentOption = {
453
514
  readonly kind: "spark";
454
515
  readonly address: string;
@@ -504,8 +565,6 @@ type ParseOptions = {
504
565
  };
505
566
  /** `input` → PaymentParse. Never throws: every failure is a structured result. */
506
567
  export declare function parsePayment(input: string, options: ParseOptions): PaymentParse;
507
- //#endregion
508
- //#region src/payments/pay.d.ts
509
568
  type PayParams = {
510
569
  readonly account: SparkAccountRef;
511
570
  readonly option: PaymentOption;
@@ -522,8 +581,6 @@ type WithdrawLnurlParams = {
522
581
  }>;
523
582
  readonly amountSats: number;
524
583
  };
525
- //#endregion
526
- //#region src/payments/zap.d.ts
527
584
  /** A profile zap names the recipient pubkey; an event zap carries the event it zaps. */
528
585
  type ZapRecipient = {
529
586
  readonly pubkey: string;
@@ -564,12 +621,18 @@ type PreparedZap = {
564
621
  };
565
622
  /** Everything up to payment: resolve LNURL, sign the 9734, request the invoice. */
566
623
  export declare function prepareZap(params: PrepareZapParams, context: PrepareZapContext): Promise<PreparedZap>;
567
- //#endregion
568
- //#region src/transactions/route.d.ts
569
- type SignParams = Parameters<ReturnType<typeof chains>["sign"]>[0];
624
+ type ChainApis = ReturnType<typeof chains>;
625
+ type SignParams = Parameters<ChainApis["sign"]>[0];
570
626
  type AccountRef = SignParams["request"]["account"];
571
627
  type DisplayIntent = ReturnType<typeof decodeEvm>;
572
628
  type TxState = "draft" | "signed" | "submitted" | "pending" | "confirmed" | "failed" | "rejected" | "dropped" | "replaced";
629
+ /** A bitcoin transaction that funds the wallet's own Spark account; it is claimed once confirmed. */
630
+ type SparkDepositTag = {
631
+ readonly walletId: string;
632
+ readonly groupIndex: number;
633
+ readonly network: SparkNetwork;
634
+ readonly claimed?: boolean;
635
+ };
573
636
  type TrackedTransaction = {
574
637
  readonly id: string;
575
638
  readonly family: "evm" | "solana" | "bitcoin";
@@ -585,6 +648,8 @@ type TrackedTransaction = {
585
648
  readonly replaces?: string;
586
649
  /** CPFP — the unconfirmed parent this child pushes. */
587
650
  readonly parentId?: string;
651
+ /** Set on a Spark deposit; `claimed` flips once the deposit is credited on Spark. */
652
+ readonly sparkDeposit?: SparkDepositTag;
588
653
  /** Bitcoin build record persisted at send for RBF/CPFP rebuilds. */
589
654
  readonly btc?: {
590
655
  readonly inputs: ReadonlyArray<{
@@ -650,6 +715,14 @@ type PreparedRequest = {
650
715
  /** Frees what `prepare` reserved; safe to call more than once. */
651
716
  readonly release: () => void;
652
717
  };
718
+ type SignatureResult = {
719
+ readonly kind: "evm.personal-message" | "evm.typed-data";
720
+ readonly signature: Uint8Array;
721
+ };
722
+ /** A prepared message signature: nothing is broadcast or tracked, `submit` returns the signature. */
723
+ type PreparedMessage = Omit<PreparedRequest, "submit" | "txId"> & {
724
+ readonly submit: (verify: SendParams["verify"]) => Promise<SignatureResult>;
725
+ };
653
726
  type Options$1 = {
654
727
  readonly wallet: Wallet;
655
728
  readonly networks: NetworkController;
@@ -662,8 +735,6 @@ type Options$1 = {
662
735
  /** Test seam — defaults to the wallet's real chains proxy. */
663
736
  readonly chains?: ReturnType<typeof chains>;
664
737
  };
665
- //#endregion
666
- //#region src/transactions/controller.d.ts
667
738
  type TransactionControllerState = {
668
739
  readonly txs: Record<string, TrackedTransaction>;
669
740
  };
@@ -686,7 +757,17 @@ declare class TransactionController extends Controller<TransactionControllerStat
686
757
  * signing it. Bitcoin outputs it selects stay locked under `requestId` until the returned
687
758
  * `release` runs or the broadcast moves them to the transaction.
688
759
  */
689
- prepareSend(params: PrepareParams, requestId: string): Promise<PreparedRequest>;
760
+ prepareSend(params: PrepareParams, requestId: string, sparkDeposit?: SparkDepositTag): Promise<PreparedRequest>;
761
+ /** The group's next unused `btc.bip84` receive address on a bitcoin `network`. */
762
+ nextReceiveAddress(walletId: string, groupIndex: number, network: string): Promise<string>;
763
+ /** Records that a Spark deposit was credited; persisted with the transaction. */
764
+ markDepositClaimed(id: string): Promise<void>;
765
+ /**
766
+ * Internal entry for a dApp's EVM signature request: the request is normalized first, a
767
+ * transaction is rebuilt with fresh nonce and fees (the dApp's own are discarded), and a message
768
+ * is decoded and signed as is.
769
+ */
770
+ prepareDapp(account: AccountRef, request: SignRequest): Promise<PreparedRequest | PreparedMessage>;
690
771
  /**
691
772
  * Internal entry for replacements and CPFP: builds, cross-checks and reserves what the
692
773
  * replacement needs without signing it. The original must be `submitted` or `pending`.
@@ -696,8 +777,6 @@ declare class TransactionController extends Controller<TransactionControllerStat
696
777
  }>, requestId: string): Promise<PreparedRequest>;
697
778
  private track;
698
779
  }
699
- //#endregion
700
- //#region src/requests/risk.d.ts
701
780
  /**
702
781
  * Recipient risk, pure: a recipient the wallet has never paid or saved is a `warning`; one that
703
782
  * looks like a known address (same first and last four characters, different address) is a `danger`
@@ -707,15 +786,20 @@ type RiskFinding = {
707
786
  readonly code: "first-recipient" | "address-poisoning";
708
787
  readonly level: "warning" | "danger";
709
788
  };
710
- //#endregion
711
- //#region src/requests/sign-log.d.ts
789
+ /** A web page; `origin` is `scheme://host[:port]`, computed by the host from the top-level URL. */
790
+ type BrowserOrigin = {
791
+ readonly kind: "browser";
792
+ readonly origin: string;
793
+ };
794
+ type RequestOrigin = {
795
+ readonly kind: "internal";
796
+ } | BrowserOrigin;
797
+ type RequestMethod = ChainRequestMethod | "pay" | "zap" | "evm-sign" | "spark-deposit" | "spark-exit";
712
798
  type SignLogOutcome = "submitted" | "rejected" | "failed" | "expired" | "invalidated";
713
799
  type SignLogEntry = {
714
800
  readonly id: string;
715
801
  readonly at: number;
716
- readonly origin: {
717
- readonly kind: "internal";
718
- };
802
+ readonly origin: RequestOrigin;
719
803
  readonly method: RequestMethod;
720
804
  readonly account: AccountRef | SparkAccountRef;
721
805
  readonly network: string;
@@ -726,8 +810,6 @@ type SignLogEntry = {
726
810
  readonly outcome: SignLogOutcome;
727
811
  readonly error?: string;
728
812
  };
729
- //#endregion
730
- //#region src/requests/controller.d.ts
731
813
  /** A Spark, Lightning (including LNURL-pay and lightning addresses) or cooperative-exit payment. */
732
814
  type PayInput = {
733
815
  readonly method: "pay";
@@ -739,14 +821,41 @@ type ZapInput = {
739
821
  readonly zap: PreparedZap;
740
822
  readonly maxFeeSats: number;
741
823
  };
742
- type RequestMethod = ChainRequestMethod | "pay" | "zap";
743
- type RequestInput = ChainRequestInput | PayInput | ZapInput;
824
+ /**
825
+ * A dApp's EVM request: a transaction (rebuilt here with fresh nonce and fees), a personal message
826
+ * or EIP-712 typed data.
827
+ */
828
+ type EvmSignInput = {
829
+ readonly method: "evm-sign";
830
+ readonly origin: BrowserOrigin;
831
+ readonly account: AccountRef;
832
+ readonly network: string;
833
+ readonly request: SignRequest;
834
+ };
835
+ /**
836
+ * Funds the wallet's own Spark account from a bitcoin account of the same wallet; the transaction
837
+ * is an ordinary bitcoin send to a fresh Spark deposit address and is claimed once confirmed.
838
+ */
839
+ type SparkDepositInput = {
840
+ readonly method: "spark-deposit";
841
+ readonly account: SparkAccountRef;
842
+ readonly from: AccountRef;
843
+ readonly amountSats: number;
844
+ readonly fee: PrepareParams["fee"];
845
+ };
846
+ /** A cooperative exit to the group's own next unused `btc.bip84` receive address. */
847
+ type SparkExitInput = {
848
+ readonly method: "spark-exit";
849
+ readonly account: SparkAccountRef;
850
+ readonly amountSats: number;
851
+ readonly maxFeeSats: number;
852
+ readonly speed?: "FAST" | "MEDIUM" | "SLOW";
853
+ };
854
+ type RequestInput = ChainRequestInput | PayInput | ZapInput | EvmSignInput | SparkDepositInput | SparkExitInput;
744
855
  type RequestStage = "awaiting-approval" | "signing" | "submitted" | "rejected" | "failed" | "expired" | "invalidated";
745
856
  type PendingRequest = {
746
857
  readonly id: string;
747
- readonly origin: {
748
- readonly kind: "internal";
749
- };
858
+ readonly origin: RequestOrigin;
750
859
  readonly method: RequestMethod;
751
860
  readonly account: AccountRef | SparkAccountRef;
752
861
  readonly network: string;
@@ -790,6 +899,7 @@ export declare class RequestController extends Controller<RequestControllerState
790
899
  private readonly options;
791
900
  private readonly entries;
792
901
  private readonly log;
902
+ private readonly settlers;
793
903
  /** Original transaction id → the unfinished request replacing or accelerating it. */
794
904
  private readonly targets;
795
905
  private locked;
@@ -800,17 +910,30 @@ export declare class RequestController extends Controller<RequestControllerState
800
910
  destroy(): Promise<void>;
801
911
  /** Locking is not consent: every unfinished request becomes `invalidated` and frees its resources. */
802
912
  lock(): void;
913
+ /** A page navigated away, was revoked or changed network: its unfinished requests are void. */
914
+ invalidateOrigin(origin: string): void;
803
915
  /** The signature record, newest first. */
804
916
  signLog(): Promise<ReadonlyArray<SignLogEntry>>;
805
917
  clearSignLog(): Promise<void>;
806
918
  prepare(input: RequestInput): Promise<PendingRequest>;
919
+ /**
920
+ * Resolves with what `approve` returned, or rejects with `RequestEndedError` when the request
921
+ * ends any other way — how a dApp's pending call learns the outcome.
922
+ */
923
+ outcome(id: string): Promise<TrackedTransaction | SparkResult | SignatureResult>;
924
+ private openSettler;
925
+ private settle;
926
+ /** The bitcoin network `account`'s Spark network settles on; any other pairing is refused. */
927
+ private l1Network;
928
+ private sparkBuilt;
807
929
  private build;
808
930
  /**
809
931
  * Signs and submits a request. Anything but a live, unexpired `send` request with this id throws
810
932
  * before any signing; a stale build (nonce taken, blockhash expired) fails without signing.
811
933
  */
812
- approve(id: string, method: ChainRequestMethod, verify: SendParams["verify"]): Promise<TrackedTransaction>;
813
- approve(id: string, method: "pay" | "zap", verify: SendParams["verify"]): Promise<SparkResult>;
934
+ approve(id: string, method: ChainRequestMethod | "spark-deposit", verify: SendParams["verify"]): Promise<TrackedTransaction>;
935
+ approve(id: string, method: "pay" | "zap" | "spark-exit", verify: SendParams["verify"]): Promise<SparkResult>;
936
+ approve(id: string, method: "evm-sign", verify: SendParams["verify"]): Promise<TrackedTransaction | SignatureResult>;
814
937
  /**
815
938
  * A cancelled or refused verification is `rejected`; anything after it is `failed`. Chain
816
939
  * requests record which step failed on the tracked transaction; for Spark, the approval round is
@@ -835,8 +958,62 @@ export declare class RequestController extends Controller<RequestControllerState
835
958
  /** Only a Spark transfer has a recipient to check; Lightning and exits are not assessed. */
836
959
  private sparkRisk;
837
960
  }
838
- //#endregion
839
- //#region src/meta/doc.d.ts
961
+ type Eip1193Request = {
962
+ readonly method: string;
963
+ readonly params?: unknown;
964
+ };
965
+ type Eip1193Event = {
966
+ readonly name: "connect";
967
+ readonly origin: string;
968
+ readonly chainId: string;
969
+ } | {
970
+ readonly name: "accountsChanged";
971
+ readonly origin: string;
972
+ readonly accounts: ReadonlyArray<string>;
973
+ } | {
974
+ readonly name: "chainChanged";
975
+ readonly origin: string;
976
+ readonly chainId: string;
977
+ } | {
978
+ readonly name: "disconnect";
979
+ readonly origin: string;
980
+ };
981
+ type Eip1193HandlerOptions = {
982
+ readonly connections: ConnectionController;
983
+ readonly requests: RequestController;
984
+ readonly networks: NetworkController;
985
+ /** The network an unconnected page sees. */
986
+ readonly defaultNetwork: () => string;
987
+ };
988
+ export declare class Eip1193Handler {
989
+ private readonly options;
990
+ private readonly listeners;
991
+ private unsubscribe;
992
+ constructor(options: Eip1193HandlerOptions);
993
+ /** Events for the host to forward to pages; derived from changes to the sessions. */
994
+ subscribe(listener: (event: Eip1193Event) => void): () => void;
995
+ handle(origin: string, request: Eip1193Request): Promise<unknown>;
996
+ private requestAccounts;
997
+ private permissions;
998
+ private switchChain;
999
+ private authorizedAccount;
1000
+ private personalSign;
1001
+ private typedData;
1002
+ private sendTransaction;
1003
+ private signingSession;
1004
+ private sign;
1005
+ private read;
1006
+ }
1007
+ /** EIP-1193 / EIP-1474 / EIP-3326 error: `code` is what the page's promise rejects with. */
1008
+ export declare class ProviderError extends Error {
1009
+ readonly code: number;
1010
+ constructor(code: number, message: string);
1011
+ }
1012
+ type AddressBookErrorCategory = "invalid-input" | "duplicate" | "not-found" | "locked" | "limit";
1013
+ export declare class AddressBookError extends Error {
1014
+ readonly category: AddressBookErrorCategory;
1015
+ constructor(category: AddressBookErrorCategory, message: string);
1016
+ }
840
1017
  /**
841
1018
  * Wallet metadata document — the plaintext exchanged over kind-30078 events between this wallet's
842
1019
  * own devices. One document per wallet; entry-level last-writer-wins on `updatedAt`.
@@ -862,8 +1039,6 @@ type MetaDoc = MetaDocTree & {
862
1039
  readonly v: 2;
863
1040
  readonly contacts: ReadonlyArray<AddressBookRecord>;
864
1041
  };
865
- //#endregion
866
- //#region src/accounts/controller.d.ts
867
1042
  type AccountGroup = {
868
1043
  readonly walletId: string;
869
1044
  readonly index: number;
@@ -999,8 +1174,6 @@ export declare class AccountTreeController extends Controller<AccountTreeState>
999
1174
  private scheduleTotalsPersist;
1000
1175
  private flushTotals;
1001
1176
  }
1002
- //#endregion
1003
- //#region src/assets/controller.d.ts
1004
1177
  type TokenBalance = {
1005
1178
  readonly address: string;
1006
1179
  readonly symbol: string;
@@ -1045,26 +1218,19 @@ declare class AssetsController extends Controller<AssetsControllerState> {
1045
1218
  private readTokens;
1046
1219
  private balanceOfBatch;
1047
1220
  private balanceOf;
1048
- private callString;
1049
- private callNumber;
1221
+ private callToken;
1050
1222
  /** Reads symbol/decimals on-chain, marks unverified, persists; throws when the calls fail. */
1051
1223
  addToken(network: string, address: string): Promise<void>;
1052
1224
  private persistToken;
1053
- private rpc;
1054
- private rpcHex;
1055
1225
  /** BTC balance = confirmed spendable + own unconfirmed change; frozen 546/330 shown separately. */
1056
1226
  refreshBitcoin(xpub: string, network: string): Promise<void>;
1057
1227
  }
1058
- //#endregion
1059
- //#region src/backup/errors.d.ts
1060
1228
  /** BackupController error categories — never carry file contents, digests or secrets. */
1061
1229
  type BackupErrorCategory = "cloud" | "conflict" | "unavailable";
1062
1230
  export declare class BackupControllerError extends Error {
1063
1231
  readonly category: BackupErrorCategory;
1064
1232
  constructor(category: BackupErrorCategory, message: string);
1065
1233
  }
1066
- //#endregion
1067
- //#region src/backup/port.d.ts
1068
1234
  /**
1069
1235
  * Cloud-file storage port (design/backup.mdx "存储"): the cloud is an opaque key-value namespace —
1070
1236
  * names are file names (`qntx-wallet-backup-<id>.json`), contents are envelope texts. The kernel
@@ -1082,8 +1248,6 @@ type CloudStore = {
1082
1248
  /** Deleting a missing file succeeds. */
1083
1249
  readonly delete: (name: string) => Promise<void>;
1084
1250
  };
1085
- //#endregion
1086
- //#region src/backup/controller.d.ts
1087
1251
  type BackupControllerState = {
1088
1252
  readonly status: "locked" | "unavailable" | "off" | "syncing" | "synced" | "conflict" | "error";
1089
1253
  /** The retained backup key's public summary from the wallet snapshot. */
@@ -1091,7 +1255,6 @@ type BackupControllerState = {
1091
1255
  readonly uploadedAt: number | undefined;
1092
1256
  readonly unprotected: {
1093
1257
  readonly walletIds: ReadonlyArray<string>;
1094
- readonly nostrKeyIds: ReadonlyArray<string>;
1095
1258
  };
1096
1259
  readonly error: BackupErrorCategory | undefined;
1097
1260
  };
@@ -1153,16 +1316,12 @@ export declare class BackupController extends Controller<BackupControllerState>
1153
1316
  private reconcile;
1154
1317
  private pass;
1155
1318
  }
1156
- //#endregion
1157
- //#region src/meta/errors.d.ts
1158
1319
  /** Meta-sync error categories — messages never carry event or document content. */
1159
1320
  type MetaSyncErrorCategory = "network" | "internal" | "unsupported" | "corrupt";
1160
1321
  export declare class MetaSyncError extends Error {
1161
1322
  readonly category: MetaSyncErrorCategory;
1162
1323
  constructor(category: MetaSyncErrorCategory, message: string, cause?: unknown);
1163
1324
  }
1164
- //#endregion
1165
- //#region src/meta/controller.d.ts
1166
1325
  type MetaSyncRelayPort = {
1167
1326
  readonly fetch: (filter: Filter) => Promise<ReadonlyArray<Event>>;
1168
1327
  readonly publish: (event: Event) => Promise<void>;
@@ -1173,7 +1332,7 @@ type MetaSyncConfig = {
1173
1332
  readonly relays: MetaSyncRelayPort;
1174
1333
  };
1175
1334
  type MetaSyncWalletStatus = {
1176
- readonly status: "unavailable" | "syncing" | "synced" | "error";
1335
+ readonly status: "syncing" | "synced" | "error";
1177
1336
  readonly error?: MetaSyncErrorCategory;
1178
1337
  };
1179
1338
  type MetaSyncState = {
@@ -1194,9 +1353,9 @@ type MetaSyncControllerOptions = {
1194
1353
  readonly wallet: Wallet;
1195
1354
  readonly accounts: AccountTreeController;
1196
1355
  readonly addressBook: AddressBookSync;
1197
- /** `nostr(wallet)` — the signer is requested per wallet for its group-0 derived key. */
1356
+ /** `nostr(wallet).syncSigner` — each wallet signs metadata with its dedicated sync key. */
1198
1357
  readonly nostr: {
1199
- readonly signer: (ref: NostrKeyRef) => WalletSigner;
1358
+ readonly syncSigner: (walletId: string) => WalletSyncSigner;
1200
1359
  };
1201
1360
  readonly metaSync: MetaSyncConfig | undefined;
1202
1361
  readonly logger: EngineLogger;
@@ -1204,9 +1363,10 @@ type MetaSyncControllerOptions = {
1204
1363
  };
1205
1364
  /**
1206
1365
  * Wallet-metadata sync over kind-30078 addressable events: one NIP-44-encrypted document per
1207
- * wallet, authored by its group-0 derived key. Pull always precedes publish in an unlock session —
1208
- * replaceable events would otherwise let a stale local tree overwrite a newer remote document.
1209
- * Debounced publishes are per wallet; a generation counter cancels in-flight write-backs on lock.
1366
+ * wallet, authored by its dedicated sync key (WD-070). Pull always precedes publish in an unlock
1367
+ * session — replaceable events would otherwise let a stale local tree overwrite a newer remote
1368
+ * document. Debounced publishes are per wallet; a generation counter cancels in-flight write-backs
1369
+ * on lock.
1210
1370
  */
1211
1371
  export declare class MetaSyncController extends Controller<MetaSyncState> {
1212
1372
  private readonly options;
@@ -1229,8 +1389,6 @@ export declare class MetaSyncController extends Controller<MetaSyncState> {
1229
1389
  /** Manual re-pull of every wallet that can sync. */
1230
1390
  sync(): Promise<void>;
1231
1391
  private pullAll;
1232
- private pubkeyFor;
1233
- private signerFor;
1234
1392
  private setStatus;
1235
1393
  private pullOne;
1236
1394
  /** The wallet's tree plus the shared address book — every wallet's document carries both. */
@@ -1242,8 +1400,6 @@ export declare class MetaSyncController extends Controller<MetaSyncState> {
1242
1400
  private onTreeChange;
1243
1401
  private publishNow;
1244
1402
  }
1245
- //#endregion
1246
- //#region src/engine.d.ts
1247
1403
  type EngineOptions = {
1248
1404
  readonly wallet: Wallet;
1249
1405
  /** Every Spark account the engine touches lives on this network. */
@@ -1269,8 +1425,10 @@ type EngineOptions = {
1269
1425
  readonly installId: string;
1270
1426
  };
1271
1427
  /** Injected timer for transaction polling; defaults to setTimeout. */
1428
+ /** Development builds: also accept `http://localhost` as a dApp origin. */
1429
+ readonly allowLocalhostOrigins?: boolean;
1272
1430
  readonly setTimer?: (ms: number, fn: () => void) => () => void;
1273
- /** Kind-30078 wallet-metadata sync; absent → every wallet reports `unavailable`. */
1431
+ /** Kind-30078 wallet-metadata sync; absent → no wallet metadata is synced. */
1274
1432
  readonly metaSync?: MetaSyncConfig;
1275
1433
  /** Test seam — defaults to the wallet's real spark proxy. */
1276
1434
  readonly sparkApis?: SparkControllerOptions["spark"];
@@ -1281,6 +1439,8 @@ type WalletEngine = {
1281
1439
  readonly transactions: TransactionController;
1282
1440
  readonly requests: RequestController;
1283
1441
  readonly addressBook: AddressBookController;
1442
+ readonly connections: ConnectionController;
1443
+ readonly eip1193: Eip1193Handler;
1284
1444
  /** Read and receive surface; every payment goes through `requests`. */
1285
1445
  readonly spark: Omit<SparkController, "send" | "payInvoice" | "paySparkInvoice" | "withdraw">;
1286
1446
  readonly accounts: AccountTreeController;
@@ -1301,36 +1461,26 @@ type WalletEngine = {
1301
1461
  readonly unsubscribe: () => void;
1302
1462
  };
1303
1463
  export declare function createWalletEngine(options: EngineOptions): WalletEngine;
1304
- //#endregion
1305
- //#region src/backup/memory.d.ts
1306
1464
  /** In-memory CloudStore for tests and the playground; swap the Map for cross-engine sharing. */
1307
1465
  export declare function createMemoryCloudStore(backing?: Map<string, string>): CloudStore;
1308
- //#endregion
1309
- //#region src/spark/errors.d.ts
1310
1466
  /** SparkController error categories — never carry request bytes or secrets. */
1311
1467
  type SparkErrorCategory = "network" | "approval-required" | "insufficient-funds" | "rejected" | "internal";
1312
1468
  export declare class SparkControllerError extends Error {
1313
1469
  readonly category: SparkErrorCategory;
1314
1470
  constructor(category: SparkErrorCategory, message: string, cause?: unknown);
1315
1471
  }
1316
- //#endregion
1317
- //#region src/accounts/errors.d.ts
1318
1472
  /** Account-tree error categories — messages never carry secrets. */
1319
1473
  type AccountTreeErrorCategory = "invalid-input" | "rejected" | "not-found" | "locked";
1320
1474
  export declare class AccountTreeError extends Error {
1321
1475
  readonly category: AccountTreeErrorCategory;
1322
1476
  constructor(category: AccountTreeErrorCategory, message: string, cause?: unknown);
1323
1477
  }
1324
- //#endregion
1325
- //#region src/payments/errors.d.ts
1326
1478
  /** Payment layer error categories — messages never carry remote or secret content. */
1327
1479
  type PaymentErrorCategory = "invalid-input" | "network" | "rejected";
1328
1480
  export declare class PaymentError extends Error {
1329
1481
  readonly category: PaymentErrorCategory;
1330
1482
  constructor(category: PaymentErrorCategory, message: string, cause?: unknown);
1331
1483
  }
1332
- //#endregion
1333
- //#region src/payments/resolve.d.ts
1334
1484
  type ResolveOptions = ParseOptions & {
1335
1485
  readonly fetch: typeof globalThis.fetch;
1336
1486
  };
@@ -1340,10 +1490,7 @@ type ResolveOptions = ParseOptions & {
1340
1490
  * failures throw `PaymentError("network")` — remote error bodies never enter the message.
1341
1491
  */
1342
1492
  export declare function resolvePayment(input: string, options: ResolveOptions): Promise<PaymentParse>;
1343
- //#endregion
1344
- //#region src/spark/exit-chain-codec.d.ts
1345
1493
  export declare function encodeExitChain(chain: SparkExitChain): Uint8Array;
1346
1494
  export declare function decodeExitChain(bytes: Uint8Array): SparkExitChain;
1347
- //#endregion
1348
- export type { AccountGroup, AccountTreeState, AddressBookEntry, AddressBookErrorCategory, AddressBookState, AddressFamily, BackupControllerState, BackupErrorCategory, CloudStore, ControllerOptions, EngineCollection, EngineErrorCode, EngineLogger, EngineOptions, EngineStore, EventBus, MetaDoc, MetaDocGroup, MetaDocTree, MetaDocWallet, MetaSyncConfig, MetaSyncRelayPort, MetaSyncState, MetaSyncWalletStatus, PayInput, PayParams, PaymentNote, PaymentOption, PaymentParse, PaymentRequest, PendingRequest, PrepareZapContext, PrepareZapParams, PreparedSparkAction, PreparedZap, RequestCheckCode, RequestControllerState, RequestInput, RequestMethod, RequestStage, RiskFinding, SignLogEntry, SignLogOutcome, SparkAccountState, SparkActivity, SparkClientFactory, SparkControllerOptions, SparkControllerState, SparkDisplay, SparkErrorCategory, SparkResult, WalletEngine, WithdrawLnurlParams, ZapRecipient };
1495
+ export type { AccountGroup, AccountTreeState, AddressBookEntry, AddressBookErrorCategory, AddressBookState, AddressFamily, BackupControllerState, BackupErrorCategory, BrowserOrigin, CloudStore, AccountGroup$1 as ConnectionAccountGroup, ConnectionOrigin, ConnectionScope, ConnectionSession, ConnectionState, ControllerOptions, Eip1193Event, Eip1193Request, EngineCollection, EngineErrorCode, EngineLogger, EngineOptions, EngineStore, EventBus, EvmSignInput, MetaDoc, MetaDocGroup, MetaDocTree, MetaDocWallet, MetaSyncConfig, MetaSyncRelayPort, MetaSyncState, MetaSyncWalletStatus, PayInput, PayParams, PaymentNote, PaymentOption, PaymentParse, PaymentRequest, PendingConnection, PendingRequest, PrepareZapContext, PrepareZapParams, PreparedSparkAction, PreparedZap, RequestCheckCode, RequestControllerState, RequestInput, RequestMethod, RequestOrigin, RequestStage, RiskFinding, SignLogEntry, SignLogOutcome, SignatureResult, SparkAccountState, SparkActivity, SparkClientFactory, SparkControllerOptions, SparkControllerState, SparkDepositInput, SparkDisplay, SparkErrorCategory, SparkExitInput, SparkResult, WalletEngine, WithdrawLnurlParams, ZapRecipient };
1349
1496
  //# sourceMappingURL=index.d.mts.map