@qntx/wallet-engine 0.5.2 → 0.6.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,18 +1,19 @@
1
+ import { r as NetworkInfo } from "./registry-9oeWs_yZ.mjs";
1
2
  import { StoreApi } from "zustand/vanilla";
3
+ import "@scure/btc-signer";
2
4
  import { CooperativeExitResult, LightningInvoice, LightningPayment, SparkClient, SparkExitChain, SparkNetwork, SparkPayment, SparkStore, SparkTransfer } from "@qntx/spark";
5
+ import { decodeEvm } from "@qntx/wallet-chains/evm";
6
+ import { BackupHeader, BackupInfo, InitialFactor, UnlockFactor, Wallet, WalletInfo } from "@qntx/wallet";
7
+ import { LnurlPayRequest, LnurlWithdrawRequest } from "@qntx/spark/lnurl";
8
+ import { Event, Filter, NostrSigner } from "@qntx/nostr";
3
9
  import { NostrKeyRef, WalletSigner } from "@qntx/wallet/nostr";
4
10
  import { SparkSigner } from "@qntx/wallet/spark";
5
11
  import { PublicClient } from "viem";
6
12
  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
13
  import "@solana/kit";
13
14
  import { SparkAccountRef, SparkApproveIntent } from "@qntx/wallet/protocol";
14
- import "@scure/btc-signer/psbt.js";
15
15
  import "@qntx/wallet-chains";
16
+ import "@scure/btc-signer/psbt.js";
16
17
  //#region src/controller.d.ts
17
18
  type ControllerOptions<S> = {
18
19
  readonly name: string;
@@ -76,33 +77,166 @@ export declare function createMemoryEngineStore(backing?: {
76
77
  readonly collections?: Map<string, Map<string, Uint8Array>>;
77
78
  }): EngineStore;
78
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 = {
85
+ readonly id: string;
86
+ readonly name: string;
87
+ readonly family: AddressFamily;
88
+ readonly address: string;
89
+ readonly note?: string;
90
+ readonly createdAt: number;
91
+ readonly updatedAt: number;
92
+ };
93
+ type AddressBookTombstone = {
94
+ readonly id: string;
95
+ readonly deleted: true;
96
+ readonly updatedAt: number;
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>;
104
+ };
105
+ type AddressBookOptions = {
106
+ readonly openStore: () => Promise<EngineStore>;
107
+ readonly random: (n: number) => Uint8Array;
108
+ readonly now: () => number;
109
+ readonly logger: EngineLogger;
110
+ };
111
+ export declare class AddressBookController extends Controller<AddressBookState> {
112
+ private readonly options;
113
+ private engineStore;
114
+ /** Every record, tombstones included, by id. */
115
+ private records;
116
+ constructor(options: AddressBookOptions);
117
+ init(): Promise<void>;
118
+ destroy(): Promise<void>;
119
+ lock(): void;
120
+ /** Entries and tombstones, for the metadata document. */
121
+ syncEntries(): ReadonlyArray<AddressBookRecord>;
122
+ /**
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.
126
+ */
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;
144
+ private persist;
145
+ private requireStore;
146
+ }
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
79
158
  //#region src/chains/bitcoin.d.ts
80
159
  type Esplora = {
81
160
  readonly get: (path: string) => Promise<unknown>;
82
161
  readonly post: (path: string, body: string) => Promise<unknown>;
83
162
  };
84
163
  //#endregion
85
- //#region src/meta/doc.d.ts
164
+ //#region src/networks/controller.d.ts
165
+ type SolanaRpc = {
166
+ readonly call: (method: string, params: unknown[]) => Promise<unknown>;
167
+ };
86
168
  /**
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`.
169
+ * A host-endpoint-only client for cross-checking pre-signing reads. `not-needed`: the gateway is
170
+ * not this network's first endpoint, so the primary reads already come from the host.
171
+ * `unavailable`: a cross-check is needed but the network has no host endpoint.
89
172
  */
90
- type MetaDocWallet = {
91
- readonly name?: string;
92
- readonly updatedAt: number;
173
+ type Independent<T> = {
174
+ readonly kind: "not-needed";
175
+ } | {
176
+ readonly kind: "unavailable";
177
+ } | {
178
+ readonly kind: "available";
179
+ readonly client: T;
93
180
  };
94
- type MetaDocGroup = {
95
- readonly index: number;
96
- readonly name?: string;
97
- readonly hidden: boolean;
98
- readonly boundPubkey?: string;
99
- readonly updatedAt: number;
181
+ type NetworkControllerState = {
182
+ readonly customRpc: Record<string, ReadonlyArray<string>>;
100
183
  };
101
- type MetaDoc = {
102
- readonly v: 1;
103
- readonly wallet: MetaDocWallet;
104
- readonly groups: ReadonlyArray<MetaDocGroup>;
184
+ type Options$2 = {
185
+ readonly fetch: typeof globalThis.fetch;
186
+ readonly rpc: Readonly<Record<string, ReadonlyArray<string>>>;
187
+ readonly openStore: () => Promise<EngineStore>;
188
+ readonly logger: EngineLogger;
189
+ readonly gateway?: {
190
+ readonly url: string;
191
+ readonly installId: string;
192
+ } | undefined;
105
193
  };
194
+ declare class NetworkController extends Controller<NetworkControllerState> {
195
+ private readonly options;
196
+ private engineStore?;
197
+ private readonly clients;
198
+ private readonly gatewayBase;
199
+ private readonly gatewayOrigin;
200
+ /** Networks the gateway reported at unlock; empty while locked or when the read failed. */
201
+ private gatewayServed;
202
+ constructor(options: Options$2);
203
+ init(): Promise<void>;
204
+ private loadCustomRpc;
205
+ /** Reads the gateway's served networks; any failure leaves the gateway unused until next unlock. */
206
+ private loadGatewayNetworks;
207
+ /** `fetch` that adds the install id to requests bound for the gateway's origin only. */
208
+ private readonly gatewayFetch;
209
+ destroy(): Promise<void>;
210
+ lock(): void;
211
+ /**
212
+ * Ordered endpoints: a network with custom endpoints uses custom → host and never the gateway;
213
+ * otherwise a gateway-served network uses gateway → host; else host only.
214
+ */
215
+ endpoints(network: string): ReadonlyArray<string>;
216
+ info(network: string): NetworkInfo;
217
+ client(network: string): PublicClient;
218
+ /** True when the gateway heads this network's endpoint list (no custom endpoints, gateway-served). */
219
+ private gatewayFirst;
220
+ private independent;
221
+ /** Host-endpoint EVM client for cross-checking reads; never sends the install id. */
222
+ independentClient(network: string): Independent<PublicClient>;
223
+ independentSolanaRpc(network: string): Independent<SolanaRpc>;
224
+ independentEsplora(network: string): Independent<Esplora>;
225
+ /** Networks with at least one configured endpoint or served by the gateway, by family. */
226
+ enabled(family: "evm" | "solana" | "bitcoin"): ReadonlyArray<string>;
227
+ /** Solana JSON-RPC over the ordered endpoint list — kit's transport is single-endpoint. */
228
+ solanaRpc(network: string): SolanaRpc;
229
+ /** Bounded-concurrency + 429-aware ordered-failover Esplora client. */
230
+ esplora(network: string): Esplora;
231
+ /** Validate a custom endpoint's reported chain id, then persist it first in the list. */
232
+ addRpc(network: string, url: string): Promise<void>;
233
+ }
234
+ //#endregion
235
+ //#region src/chains/solana.d.ts
236
+ type SolanaFeeTier = "slow" | "normal" | "fast";
237
+ //#endregion
238
+ //#region src/requests/checks.d.ts
239
+ type RequestCheckCode = "decode-partial" | "nonce-mismatch" | "fee-mismatch" | "reads-unverified" | "fee-above-network" | "fee-exceeds-amount";
106
240
  //#endregion
107
241
  //#region src/spark/notes.d.ts
108
242
  /**
@@ -275,6 +409,460 @@ export declare class SparkController extends Controller<SparkControllerState> {
275
409
  }): Promise<CooperativeExitResult>;
276
410
  }
277
411
  //#endregion
412
+ //#region src/payments/action.d.ts
413
+ /**
414
+ * What the host shows for a Spark, Lightning or exit request; `family` tells it apart from a chain
415
+ * intent.
416
+ */
417
+ type SparkDisplay = {
418
+ readonly family: "spark";
419
+ readonly network: SparkNetwork;
420
+ readonly kind: "transfer" | "lightning" | "exit";
421
+ readonly amountSats: number;
422
+ readonly maxFeeSats?: number;
423
+ /** Spark address, Lightning payee public key (hex) or bitcoin address. */
424
+ readonly receiver: string;
425
+ /** Invoice description or the LNURL metadata text. */
426
+ readonly description?: string;
427
+ };
428
+ type SparkResult = {
429
+ readonly kind: "spark";
430
+ readonly transfer: SparkTransfer;
431
+ } | {
432
+ readonly kind: "lightning";
433
+ readonly payment: LightningPayment;
434
+ } | {
435
+ readonly kind: "withdraw";
436
+ readonly exit: CooperativeExitResult;
437
+ };
438
+ /**
439
+ * A validated, fixed Spark payment: everything that does not need the user's verification has
440
+ * happened (including any LNURL invoice fetch), so approval only re-checks expiry and executes.
441
+ */
442
+ type PreparedSparkAction = {
443
+ readonly display: SparkDisplay;
444
+ readonly checks: ReadonlyArray<RequestCheckCode>;
445
+ /** Why the action can no longer be paid at `now` (an expiring invoice), if so. */
446
+ readonly staleReason: (now: number) => string | undefined;
447
+ /** Runs the controller's approve-first payment call. */
448
+ readonly execute: (spark: SparkController, verify: UnlockFactor) => Promise<SparkResult>;
449
+ };
450
+ //#endregion
451
+ //#region src/payments/parse.d.ts
452
+ type PaymentOption = {
453
+ readonly kind: "spark";
454
+ readonly address: string;
455
+ } | {
456
+ readonly kind: "spark-invoice";
457
+ readonly invoice: string;
458
+ readonly amountSats?: number;
459
+ readonly memo?: string;
460
+ readonly expiresAt?: number;
461
+ } | {
462
+ readonly kind: "lightning";
463
+ readonly invoice: string;
464
+ readonly amountSats?: number;
465
+ readonly paymentHash: string;
466
+ readonly description?: string;
467
+ readonly expiresAt: number;
468
+ } | {
469
+ readonly kind: "lnurl-pay";
470
+ readonly request: LnurlPayRequest;
471
+ readonly address?: string;
472
+ } | {
473
+ readonly kind: "lnurl-withdraw";
474
+ readonly request: LnurlWithdrawRequest;
475
+ } | {
476
+ readonly kind: "bitcoin";
477
+ readonly address: string;
478
+ };
479
+ type PaymentRequest = {
480
+ readonly options: ReadonlyArray<PaymentOption>;
481
+ readonly amountSats?: number;
482
+ readonly label?: string;
483
+ readonly message?: string;
484
+ };
485
+ type PaymentParse = {
486
+ readonly kind: "request";
487
+ readonly request: PaymentRequest;
488
+ } | {
489
+ readonly kind: "lnurl";
490
+ readonly url: string;
491
+ readonly address?: string;
492
+ } | {
493
+ readonly kind: "unsupported";
494
+ readonly format: "bolt12" | "silent-payment" | "lnurl-auth" | "spark-token-invoice" | "nostr-wallet-connect";
495
+ } | {
496
+ readonly kind: "invalid";
497
+ readonly reason: "network" | "expired" | "amount-mismatch" | "malformed";
498
+ } | {
499
+ readonly kind: "unrecognized";
500
+ };
501
+ type ParseOptions = {
502
+ readonly network: SparkNetwork;
503
+ readonly now: number;
504
+ };
505
+ /** `input` → PaymentParse. Never throws: every failure is a structured result. */
506
+ export declare function parsePayment(input: string, options: ParseOptions): PaymentParse;
507
+ //#endregion
508
+ //#region src/payments/pay.d.ts
509
+ type PayParams = {
510
+ readonly account: SparkAccountRef;
511
+ readonly option: PaymentOption;
512
+ /** Caller-supplied amount — required by open invoices, the `spark` option, and LNURL flows. */
513
+ readonly amountSats?: number;
514
+ readonly comment?: string;
515
+ readonly maxFeeSats?: number;
516
+ readonly speed?: "FAST" | "MEDIUM" | "SLOW";
517
+ };
518
+ type WithdrawLnurlParams = {
519
+ readonly account: SparkAccountRef;
520
+ readonly option: Extract<PaymentOption, {
521
+ kind: "lnurl-withdraw";
522
+ }>;
523
+ readonly amountSats: number;
524
+ };
525
+ //#endregion
526
+ //#region src/payments/zap.d.ts
527
+ /** A profile zap names the recipient pubkey; an event zap carries the event it zaps. */
528
+ type ZapRecipient = {
529
+ readonly pubkey: string;
530
+ readonly lightning: string;
531
+ } | {
532
+ readonly event: Event;
533
+ readonly lightning: string;
534
+ };
535
+ type PrepareZapParams = {
536
+ /** Any `NostrSigner` — wallet key, external key, NIP-07/46 — signs the 9734. */
537
+ readonly signer: NostrSigner;
538
+ readonly recipient: ZapRecipient;
539
+ readonly amountSats: number;
540
+ readonly comment?: string | undefined;
541
+ readonly relays: ReadonlyArray<string>;
542
+ readonly network: SparkNetwork;
543
+ };
544
+ type PrepareZapContext = {
545
+ readonly fetch: typeof globalThis.fetch;
546
+ readonly now: () => number;
547
+ };
548
+ /** Everything a payer needs: the signed 9734, the invoice, and the note fields. */
549
+ type PreparedZap = {
550
+ readonly zapRequest: Event;
551
+ readonly invoice: string;
552
+ /** Hex payment hash decoded from the invoice. */
553
+ readonly paymentHash: string;
554
+ /** Millisecond expiry decoded from the invoice. */
555
+ readonly expiresAt: number;
556
+ readonly amountSats: number;
557
+ /** The LNURL service's 9735 signing pubkey. */
558
+ readonly receiptPubkey: string;
559
+ readonly recipientPubkey: string;
560
+ readonly eventId?: string;
561
+ readonly comment?: string;
562
+ /** The lud16 or LNURL as given. */
563
+ readonly lightning: string;
564
+ };
565
+ /** Everything up to payment: resolve LNURL, sign the 9734, request the invoice. */
566
+ 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];
570
+ type AccountRef = SignParams["request"]["account"];
571
+ type DisplayIntent = ReturnType<typeof decodeEvm>;
572
+ type TxState = "draft" | "signed" | "submitted" | "pending" | "confirmed" | "failed" | "rejected" | "dropped" | "replaced";
573
+ type TrackedTransaction = {
574
+ readonly id: string;
575
+ readonly family: "evm" | "solana" | "bitcoin";
576
+ readonly network: string;
577
+ readonly account: string;
578
+ readonly hash: string | undefined;
579
+ readonly state: TxState;
580
+ readonly intent: DisplayIntent | undefined;
581
+ readonly nonce: number;
582
+ /** Solana only — the blockhash expiry that turns an unconfirmed tx `dropped`. */
583
+ readonly lastValidBlockHeight?: number;
584
+ /** EVM replacement — the earlier transaction this one bumps or cancels. */
585
+ readonly replaces?: string;
586
+ /** CPFP — the unconfirmed parent this child pushes. */
587
+ readonly parentId?: string;
588
+ /** Bitcoin build record persisted at send for RBF/CPFP rebuilds. */
589
+ readonly btc?: {
590
+ readonly inputs: ReadonlyArray<{
591
+ readonly txid: string;
592
+ readonly vout: number;
593
+ readonly value: number;
594
+ readonly address: string;
595
+ }>;
596
+ readonly outputs: ReadonlyArray<{
597
+ readonly address: string;
598
+ readonly value: number;
599
+ readonly own: boolean;
600
+ }>;
601
+ readonly vsize: number;
602
+ readonly fee: number;
603
+ readonly feeRate: number;
604
+ };
605
+ readonly submittedAt: number;
606
+ readonly error: string | undefined;
607
+ };
608
+ type SendParams = {
609
+ readonly account: AccountRef;
610
+ readonly network: string;
611
+ readonly to: string;
612
+ readonly amount: bigint;
613
+ /** ERC-20 contract — when set, `to`/`amount` are the transfer recipient/units and value is 0. */
614
+ readonly token?: string;
615
+ readonly fee: EvmFeeTier | SolanaFeeTier;
616
+ /** SPL mint decimals — required when `token` is a Solana mint. */
617
+ readonly decimals?: number;
618
+ readonly verify: SignParams["verify"];
619
+ };
620
+ type PrepareParams = Omit<SendParams, "verify">;
621
+ type ChainRequestMethod = "send" | "speed-up" | "cancel" | "cpfp";
622
+ /** What a request asks for; replacements and CPFP name the original transaction. */
623
+ type ChainRequestInput = ({
624
+ readonly method: "send";
625
+ } & PrepareParams) | {
626
+ readonly method: "speed-up" | "cancel";
627
+ readonly txId: string;
628
+ } | {
629
+ readonly method: "cpfp";
630
+ readonly txId: string;
631
+ readonly feeRate: number;
632
+ };
633
+ /**
634
+ * A fully built, estimated and cross-checked send that has not been signed. The unsigned content
635
+ * lives in the closures and cannot change after `prepare` returns.
636
+ */
637
+ type PreparedRequest = {
638
+ /** Id of the tracked transaction `submit` creates. */
639
+ readonly txId: string;
640
+ readonly account: AccountRef;
641
+ readonly network: string;
642
+ /** The transaction being replaced or accelerated; absent for a plain send. */
643
+ readonly target?: string;
644
+ readonly intent: DisplayIntent;
645
+ readonly checks: ReadonlyArray<RequestCheckCode>;
646
+ /** Why the build can no longer be signed (nonce taken, blockhash expired), if so. */
647
+ readonly staleReason: () => string | undefined | Promise<string | undefined>;
648
+ /** Signs, finalizes, broadcasts and hands the transaction to tracking. */
649
+ readonly submit: (verify: SendParams["verify"]) => Promise<TrackedTransaction>;
650
+ /** Frees what `prepare` reserved; safe to call more than once. */
651
+ readonly release: () => void;
652
+ };
653
+ type Options$1 = {
654
+ readonly wallet: Wallet;
655
+ readonly networks: NetworkController;
656
+ readonly openStore: () => Promise<EngineStore>;
657
+ readonly random: (n: number) => Uint8Array;
658
+ readonly setTimer: (ms: number, fn: () => unknown) => () => void;
659
+ readonly now: () => number;
660
+ readonly logger: EngineLogger;
661
+ readonly pollMs?: number;
662
+ /** Test seam — defaults to the wallet's real chains proxy. */
663
+ readonly chains?: ReturnType<typeof chains>;
664
+ };
665
+ //#endregion
666
+ //#region src/transactions/controller.d.ts
667
+ type TransactionControllerState = {
668
+ readonly txs: Record<string, TrackedTransaction>;
669
+ };
670
+ declare class TransactionController extends Controller<TransactionControllerState> {
671
+ private readonly options;
672
+ private engineStore?;
673
+ private readonly timers;
674
+ private evmRoute;
675
+ private bitcoinRoute;
676
+ private locked;
677
+ constructor(options: Options$1);
678
+ init(): Promise<void>;
679
+ destroy(): Promise<void>;
680
+ lock(): void;
681
+ private persist;
682
+ private transition;
683
+ private routeCtx;
684
+ /**
685
+ * Internal entry for the request pipeline: builds, estimates and cross-checks a send without
686
+ * signing it. Bitcoin outputs it selects stay locked under `requestId` until the returned
687
+ * `release` runs or the broadcast moves them to the transaction.
688
+ */
689
+ prepareSend(params: PrepareParams, requestId: string): Promise<PreparedRequest>;
690
+ /**
691
+ * Internal entry for replacements and CPFP: builds, cross-checks and reserves what the
692
+ * replacement needs without signing it. The original must be `submitted` or `pending`.
693
+ */
694
+ prepareReplacement(input: Exclude<ChainRequestInput, {
695
+ method: "send";
696
+ }>, requestId: string): Promise<PreparedRequest>;
697
+ private track;
698
+ }
699
+ //#endregion
700
+ //#region src/requests/risk.d.ts
701
+ /**
702
+ * Recipient risk, pure: a recipient the wallet has never paid or saved is a `warning`; one that
703
+ * looks like a known address (same first and last four characters, different address) is a `danger`
704
+ * — the shape of an address-poisoning lookalike. Both can apply to one recipient.
705
+ */
706
+ type RiskFinding = {
707
+ readonly code: "first-recipient" | "address-poisoning";
708
+ readonly level: "warning" | "danger";
709
+ };
710
+ //#endregion
711
+ //#region src/requests/sign-log.d.ts
712
+ type SignLogOutcome = "submitted" | "rejected" | "failed" | "expired" | "invalidated";
713
+ type SignLogEntry = {
714
+ readonly id: string;
715
+ readonly at: number;
716
+ readonly origin: {
717
+ readonly kind: "internal";
718
+ };
719
+ readonly method: RequestMethod;
720
+ readonly account: AccountRef | SparkAccountRef;
721
+ readonly network: string;
722
+ readonly summary: string;
723
+ readonly verification: "verified" | "unverifiable";
724
+ readonly checks: ReadonlyArray<RequestCheckCode>;
725
+ readonly risk: ReadonlyArray<RiskFinding>;
726
+ readonly outcome: SignLogOutcome;
727
+ readonly error?: string;
728
+ };
729
+ //#endregion
730
+ //#region src/requests/controller.d.ts
731
+ /** A Spark, Lightning (including LNURL-pay and lightning addresses) or cooperative-exit payment. */
732
+ type PayInput = {
733
+ readonly method: "pay";
734
+ } & PayParams;
735
+ /** A prepared zap; its invoice was fetched by `engine.zaps.prepare` and is paid as-is. */
736
+ type ZapInput = {
737
+ readonly method: "zap";
738
+ readonly account: SparkAccountRef;
739
+ readonly zap: PreparedZap;
740
+ readonly maxFeeSats: number;
741
+ };
742
+ type RequestMethod = ChainRequestMethod | "pay" | "zap";
743
+ type RequestInput = ChainRequestInput | PayInput | ZapInput;
744
+ type RequestStage = "awaiting-approval" | "signing" | "submitted" | "rejected" | "failed" | "expired" | "invalidated";
745
+ type PendingRequest = {
746
+ readonly id: string;
747
+ readonly origin: {
748
+ readonly kind: "internal";
749
+ };
750
+ readonly method: RequestMethod;
751
+ readonly account: AccountRef | SparkAccountRef;
752
+ readonly network: string;
753
+ readonly stage: RequestStage;
754
+ /** Decoded locally from the exact content that will be signed; `family` tells the shapes apart. */
755
+ readonly display: DisplayIntent | SparkDisplay;
756
+ /** `unverifiable` as soon as any check is recorded; the host must ask for explicit confirmation. */
757
+ readonly verification: "verified" | "unverifiable";
758
+ readonly checks: ReadonlyArray<RequestCheckCode>;
759
+ /**
760
+ * Recipient risk; independent of `verification`. A `danger` needs the user's explicit
761
+ * acknowledgement.
762
+ */
763
+ readonly risk: ReadonlyArray<RiskFinding>;
764
+ readonly createdAt: number;
765
+ readonly expiresAt: number;
766
+ /** The transaction being replaced or accelerated; absent for a plain send. */
767
+ readonly target?: string;
768
+ /** The tracked transaction once submitted. */
769
+ readonly txId?: string;
770
+ readonly error?: string;
771
+ };
772
+ type RequestControllerState = {
773
+ readonly requests: Record<string, PendingRequest>;
774
+ };
775
+ type RequestControllerOptions = {
776
+ readonly transactions: TransactionController;
777
+ /** The full controller: its payment methods are reachable only through this pipeline. */
778
+ readonly spark: SparkController;
779
+ readonly addressBook: AddressBookController;
780
+ readonly openStore: () => Promise<EngineStore>;
781
+ readonly fetch: typeof globalThis.fetch;
782
+ readonly random: (n: number) => Uint8Array;
783
+ readonly now: () => number;
784
+ readonly setTimer: (ms: number, fn: () => unknown) => () => void;
785
+ readonly logger: EngineLogger;
786
+ };
787
+ /** Requests expire this long after creation. */
788
+ export declare const REQUEST_TTL_MS: number;
789
+ export declare class RequestController extends Controller<RequestControllerState> {
790
+ private readonly options;
791
+ private readonly entries;
792
+ private readonly log;
793
+ /** Original transaction id → the unfinished request replacing or accelerating it. */
794
+ private readonly targets;
795
+ private locked;
796
+ /** Bumped on every unlock and lock so work that outlives a session never touches the next one. */
797
+ private session;
798
+ constructor(options: RequestControllerOptions);
799
+ init(): Promise<void>;
800
+ destroy(): Promise<void>;
801
+ /** Locking is not consent: every unfinished request becomes `invalidated` and frees its resources. */
802
+ lock(): void;
803
+ /** The signature record, newest first. */
804
+ signLog(): Promise<ReadonlyArray<SignLogEntry>>;
805
+ clearSignLog(): Promise<void>;
806
+ prepare(input: RequestInput): Promise<PendingRequest>;
807
+ private build;
808
+ /**
809
+ * Signs and submits a request. Anything but a live, unexpired `send` request with this id throws
810
+ * before any signing; a stale build (nonce taken, blockhash expired) fails without signing.
811
+ */
812
+ approve(id: string, method: ChainRequestMethod, verify: SendParams["verify"]): Promise<TrackedTransaction>;
813
+ approve(id: string, method: "pay" | "zap", verify: SendParams["verify"]): Promise<SparkResult>;
814
+ /**
815
+ * A cancelled or refused verification is `rejected`; anything after it is `failed`. Chain
816
+ * requests record which step failed on the tracked transaction; for Spark, the approval round is
817
+ * the only source of `WalletError`.
818
+ */
819
+ private failureStage;
820
+ reject(id: string): void;
821
+ private requireRequest;
822
+ private put;
823
+ private unclaim;
824
+ /** Ends a request in a terminal stage and frees what it reserved. */
825
+ private finish;
826
+ /** Appends the final stage to the signature record; a failed write is a warning, not an error. */
827
+ private record;
828
+ private appendLog;
829
+ /**
830
+ * Addresses the wallet already knows for a family: contacts, plus recipients of tracked chain
831
+ * sends (Spark payments are not tracked).
832
+ */
833
+ private knownAddresses;
834
+ private chainRisk;
835
+ /** Only a Spark transfer has a recipient to check; Lightning and exits are not assessed. */
836
+ private sparkRisk;
837
+ }
838
+ //#endregion
839
+ //#region src/meta/doc.d.ts
840
+ /**
841
+ * Wallet metadata document — the plaintext exchanged over kind-30078 events between this wallet's
842
+ * own devices. One document per wallet; entry-level last-writer-wins on `updatedAt`.
843
+ */
844
+ type MetaDocWallet = {
845
+ readonly name?: string;
846
+ readonly updatedAt: number;
847
+ };
848
+ type MetaDocGroup = {
849
+ readonly index: number;
850
+ readonly name?: string;
851
+ readonly hidden: boolean;
852
+ readonly boundPubkey?: string;
853
+ readonly updatedAt: number;
854
+ };
855
+ /** The per-wallet part of the document: this wallet's name and account groups. */
856
+ type MetaDocTree = {
857
+ readonly wallet: MetaDocWallet;
858
+ readonly groups: ReadonlyArray<MetaDocGroup>;
859
+ };
860
+ /** Every wallet's document also carries the whole address book, which is shared across wallets. */
861
+ type MetaDoc = MetaDocTree & {
862
+ readonly v: 2;
863
+ readonly contacts: ReadonlyArray<AddressBookRecord>;
864
+ };
865
+ //#endregion
278
866
  //#region src/accounts/controller.d.ts
279
867
  type AccountGroup = {
280
868
  readonly walletId: string;
@@ -374,14 +962,14 @@ export declare class AccountTreeController extends Controller<AccountTreeState>
374
962
  groupIndex: number;
375
963
  } | undefined;
376
964
  /** The wallet's metadata document — every group, hidden included. */
377
- exportMeta(walletId: string): MetaDoc;
965
+ exportMeta(walletId: string): MetaDocTree;
378
966
  /**
379
967
  * Entry-level last-writer-wins merge: a newer remote `updatedAt` replaces the local entry, a tie
380
968
  * keeps local; remote-only groups are added. `selected`/hidden rules are re-validated — if the
381
969
  * merge hides the selected group it is re-selected, and a merge may never leave every group
382
970
  * hidden (the selected one stays visible and is re-stamped so the fix propagates).
383
971
  */
384
- mergeMeta(walletId: string, doc: MetaDoc): {
972
+ mergeMeta(walletId: string, doc: MetaDocTree): {
385
973
  changed: boolean;
386
974
  localNewer: boolean;
387
975
  };
@@ -389,80 +977,27 @@ export declare class AccountTreeController extends Controller<AccountTreeState>
389
977
  discoverSpark(walletId: string): Promise<number[]>;
390
978
  /**
391
979
  * 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>;
980
+ * after two consecutive fully-unused groups. A family error is reported on the group and does NOT
981
+ * count as unused (the group stays in `found` — it can't be proven empty).
982
+ */
983
+ discoverGroups(walletId: string): Promise<number[]>;
984
+ /** Refresh `totals` for every other visible group; per-group failures are logged and skipped. */
985
+ refreshTotals(): Promise<void>;
986
+ private onSparkState;
987
+ private setTotal;
988
+ private driveSpark;
989
+ private visibleGroups;
990
+ private validSelection;
991
+ private selectFallback;
992
+ private mutateGroups;
993
+ private mustWallet;
994
+ private mustGroup;
995
+ private checkedName;
996
+ private load;
997
+ /** Queues a store write behind the previous one — the last queued write always lands last. */
998
+ private persist;
999
+ private scheduleTotalsPersist;
1000
+ private flushTotals;
466
1001
  }
467
1002
  //#endregion
468
1003
  //#region src/assets/controller.d.ts
@@ -484,7 +1019,7 @@ type AccountBalances = {
484
1019
  type AssetsControllerState = {
485
1020
  readonly balances: Record<string, AccountBalances>;
486
1021
  };
487
- type Options$1 = {
1022
+ type Options = {
488
1023
  readonly networks: NetworkController;
489
1024
  readonly openStore: () => Promise<EngineStore>;
490
1025
  readonly setTimer: (ms: number, fn: () => unknown) => () => void;
@@ -497,7 +1032,7 @@ declare class AssetsController extends Controller<AssetsControllerState> {
497
1032
  private visible;
498
1033
  private cancelPoll;
499
1034
  private readonly customTokens;
500
- constructor(options: Options$1);
1035
+ constructor(options: Options);
501
1036
  init(): Promise<void>;
502
1037
  lock(): void;
503
1038
  destroy(): Promise<void>;
@@ -644,9 +1179,21 @@ type MetaSyncWalletStatus = {
644
1179
  type MetaSyncState = {
645
1180
  readonly wallets: Readonly<Record<string, MetaSyncWalletStatus>>;
646
1181
  };
1182
+ /** What the sync needs from the address book: its records, a merge, and a change subscription. */
1183
+ type AddressBookSync = {
1184
+ readonly syncEntries: () => ReadonlyArray<AddressBookRecord>;
1185
+ readonly merge: (remote: ReadonlyArray<AddressBookRecord>) => Promise<{
1186
+ readonly changed: boolean;
1187
+ readonly localNewer: boolean;
1188
+ }>;
1189
+ readonly store: {
1190
+ readonly subscribe: (listener: () => void) => () => void;
1191
+ };
1192
+ };
647
1193
  type MetaSyncControllerOptions = {
648
1194
  readonly wallet: Wallet;
649
1195
  readonly accounts: AccountTreeController;
1196
+ readonly addressBook: AddressBookSync;
650
1197
  /** `nostr(wallet)` — the signer is requested per wallet for its group-0 derived key. */
651
1198
  readonly nostr: {
652
1199
  readonly signer: (ref: NostrKeyRef) => WalletSigner;
@@ -670,6 +1217,7 @@ export declare class MetaSyncController extends Controller<MetaSyncState> {
670
1217
  private readonly timers;
671
1218
  private readonly inflight;
672
1219
  private accountsUnsubscribe;
1220
+ private addressBookUnsubscribe;
673
1221
  constructor(options: MetaSyncControllerOptions);
674
1222
  /** Unlocked: pull every wallet whose group-0 identity exists, then watch the tree. */
675
1223
  init(): Promise<void>;
@@ -685,6 +1233,8 @@ export declare class MetaSyncController extends Controller<MetaSyncState> {
685
1233
  private signerFor;
686
1234
  private setStatus;
687
1235
  private pullOne;
1236
+ /** The wallet's tree plus the shared address book — every wallet's document carries both. */
1237
+ private exportDoc;
688
1238
  private filterFor;
689
1239
  private acceptableEvent;
690
1240
  /** Snapshot the wallet's exported doc — the debounce publishes only on a real change. */
@@ -693,243 +1243,6 @@ export declare class MetaSyncController extends Controller<MetaSyncState> {
693
1243
  private publishNow;
694
1244
  }
695
1245
  //#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
1246
  //#region src/engine.d.ts
934
1247
  type EngineOptions = {
935
1248
  readonly wallet: Wallet;
@@ -942,8 +1255,19 @@ type EngineOptions = {
942
1255
  readonly openStore: () => Promise<EngineStore>;
943
1256
  /** Cloud file transport for the backup controller; absent → `unavailable`. */
944
1257
  readonly cloud?: CloudStore;
945
- /** RPC endpoints per network (CAIP-2), ordered by preference for the viem fallback transport. */
1258
+ /**
1259
+ * Host endpoints per network (CAIP-2), ordered by preference, for all three chain families: EVM
1260
+ * and Solana JSON-RPC URLs, and for Bitcoin the Esplora base URL.
1261
+ */
946
1262
  readonly rpc?: Readonly<Record<string, ReadonlyArray<string>>>;
1263
+ /**
1264
+ * The wallet gateway: tried first for the networks it serves (read at each unlock) unless a
1265
+ * network has custom endpoints. The install id is sent only to the gateway's origin.
1266
+ */
1267
+ readonly gateway?: {
1268
+ readonly url: string;
1269
+ readonly installId: string;
1270
+ };
947
1271
  /** Injected timer for transaction polling; defaults to setTimeout. */
948
1272
  readonly setTimer?: (ms: number, fn: () => void) => () => void;
949
1273
  /** Kind-30078 wallet-metadata sync; absent → every wallet reports `unavailable`. */
@@ -955,18 +1279,24 @@ type WalletEngine = {
955
1279
  readonly networks: NetworkController;
956
1280
  readonly assets: AssetsController;
957
1281
  readonly transactions: TransactionController;
958
- readonly spark: SparkController;
1282
+ readonly requests: RequestController;
1283
+ readonly addressBook: AddressBookController;
1284
+ /** Read and receive surface; every payment goes through `requests`. */
1285
+ readonly spark: Omit<SparkController, "send" | "payInvoice" | "paySparkInvoice" | "withdraw">;
959
1286
  readonly accounts: AccountTreeController;
960
1287
  readonly metaSync: MetaSyncController;
961
1288
  readonly backup: BackupController;
962
1289
  readonly zaps: {
963
1290
  readonly prepare: (params: PrepareZapParams) => Promise<PreparedZap>;
964
- readonly pay: (account: SparkAccountRef, prepared: PreparedZap, options: Omit<PayZapOptions, "now">) => Promise<LightningPayment>;
965
1291
  };
966
1292
  readonly payments: {
967
1293
  readonly parse: (input: string, network: SparkNetwork) => PaymentParse;
968
1294
  readonly resolve: (input: string, network: SparkNetwork) => Promise<PaymentParse>;
969
- readonly pay: (params: PayParams) => Promise<PayResult>;
1295
+ /** Receives funds from an LNURL-withdraw service; needs no verification factor. */
1296
+ readonly withdrawLnurl: (params: WithdrawLnurlParams) => Promise<{
1297
+ readonly kind: "lnurl-withdraw";
1298
+ readonly invoice: LightningInvoice;
1299
+ }>;
970
1300
  };
971
1301
  readonly unsubscribe: () => void;
972
1302
  };
@@ -1015,5 +1345,5 @@ export declare function resolvePayment(input: string, options: ResolveOptions):
1015
1345
  export declare function encodeExitChain(chain: SparkExitChain): Uint8Array;
1016
1346
  export declare function decodeExitChain(bytes: Uint8Array): SparkExitChain;
1017
1347
  //#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 };
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 };
1019
1349
  //# sourceMappingURL=index.d.mts.map