thunder-bridge 2.1.1 → 2.2.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.
@@ -15,6 +15,120 @@ type Verified = {
15
15
  /** Carries one request to an address ask() already verified, so nothing resolves the name again */
16
16
  type Send = (url: string, sent: Sent, signal: AbortSignal, at: readonly Verified[]) => Promise<Response>;
17
17
 
18
+ /** A wallet reachable over NIP-47, as its `nostr+walletconnect://` URI describes it */
19
+ interface NwcConnection {
20
+ /** The wallet service's public key, which is what its answers have to be signed by */
21
+ walletPubkey: string;
22
+ /** Where to reach it, tried in order until one answers */
23
+ relays: string[];
24
+ /** Our own private key on this connection, and the only thing that authorises it */
25
+ secret: string;
26
+ }
27
+ /** A minted invoice and everything needed to watch it */
28
+ interface NwcInvoice {
29
+ bolt11: string;
30
+ paymentHash: string;
31
+ expiresAt: number;
32
+ }
33
+ /** The endpoint the gateway polls for an NWC payment, answering off your own wallet */
34
+ interface NwcVerifyConfig {
35
+ /** The wallet this endpoint speaks for. It never leaves this process */
36
+ connection: NwcConnection;
37
+ /** The secret the payment hash was sealed with, and nothing else uses it */
38
+ secret: string;
39
+ /** How often the gateway should ask, in seconds, sent as `Cache-Control: max-age`, `5` by default */
40
+ pollEverySecs?: number;
41
+ /** How long one `lookup_invoice` may take before the wallet counts as unreachable, `10_000` by default */
42
+ askTimeoutMs?: number;
43
+ }
44
+ /**
45
+ * Read a `nostr+walletconnect://` URI. Refuses a relay that is not `wss`, for the
46
+ * reason the gateway refuses a verify URL that is not https
47
+ */
48
+ declare function nwcConnection(uri: string): NwcConnection;
49
+ /** Mint an invoice on the connected wallet, decoded so the caller need not trust its word */
50
+ declare function nwcInvoice(connection: NwcConnection, amountMsat: number, description: string, timeoutMs?: number): Promise<NwcInvoice>;
51
+ /**
52
+ * Mint a hold invoice on a hash the wallet does not hold the preimage for, which
53
+ * is what lets an operator be paid only by paying somebody else first. The hash
54
+ * has to come from the recipient's own invoice, and the invoice that comes back
55
+ * is decoded rather than believed
56
+ */
57
+ declare function nwcHoldInvoice(connection: NwcConnection, held: {
58
+ paymentHash: string;
59
+ amountMsat: number;
60
+ description: string;
61
+ expirySecs: number;
62
+ minCltvExpiryDelta?: number;
63
+ }, timeoutMs?: number): Promise<NwcInvoice>;
64
+ /**
65
+ * The preimage the wallet released for this hash, null while it has released
66
+ * none. A preimage that does not hash to what was asked for is a lie rather than
67
+ * an answer, so it throws instead of being passed on
68
+ */
69
+ declare function nwcSettlement(connection: NwcConnection, paymentHash: string, timeoutMs?: number): Promise<string | null>;
70
+ /**
71
+ * Pay an invoice and keep the preimage the network handed back. Whoever pays
72
+ * learns it, which is what makes delivery provable to a recipient publishing no
73
+ * LUD-21 of their own. A preimage that does not hash to the invoice's own hash is
74
+ * a lie rather than a receipt, so it throws instead of being passed on
75
+ */
76
+ declare function nwcPay(connection: NwcConnection, bolt11: string, timeoutMs?: number): Promise<string>;
77
+ /**
78
+ * A verify endpoint of your own that asks your wallet over NIP-47, so the gateway
79
+ * polls you and never learns the connection, the relay, or which wallet it is.
80
+ *
81
+ * `nwcVerifyUrl` seals the payment hash into the query with your secret, which is
82
+ * what stops a stranger driving your wallet through this handler. It answers the
83
+ * LUD-21 shape the gateway already speaks, so nothing on that side changes.
84
+ *
85
+ * A wallet it cannot reach answers `502` rather than "not settled", because those
86
+ * are different claims and only one of them is true.
87
+ */
88
+ declare function nwcVerifyEndpoint(config: NwcVerifyConfig): (request: Request) => Promise<Response>;
89
+ /**
90
+ * The URL to hand the gateway, with the payment hash sealed inside it. Point it at
91
+ * wherever `nwcVerifyEndpoint` is mounted
92
+ */
93
+ declare function nwcVerifyUrl(endpoint: string, paymentHash: string, secret: string): Promise<string>;
94
+ /**
95
+ * One NIP-47 call, for a method this SDK does not wrap. The wallet's own info
96
+ * event lists what it will answer, and anything it refuses comes back as a
97
+ * `WalletRefused` whose `reason` says which kind of refusal it was
98
+ */
99
+ declare function askWallet(connection: NwcConnection, method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<Record<string, unknown>>;
100
+ /** A Lightning rail minting on a wallet of your own over NIP-47, bound once per shop */
101
+ interface NwcRailConfig extends RailConfig {
102
+ /** The wallet that mints, which never leaves this process */
103
+ connection: NwcConnection;
104
+ /**
105
+ * What to charge for one order, the order's own price converted at `rate` by
106
+ * default. Give it a function and the price is whatever you say
107
+ */
108
+ amount?: (order: Order) => Amount;
109
+ /** Where the default conversion gets its rate, the median of four venues by default */
110
+ rate?: Ticker;
111
+ /** Where `nwcVerifyEndpoint` is mounted, and the secret the hash is sealed with */
112
+ verifyThrough: {
113
+ endpoint: string;
114
+ secret: string;
115
+ };
116
+ /** What the payer's wallet shows, the order's reference by default */
117
+ description?: (order: Order) => string;
118
+ /** Sealed before the gateway sees it, the way the blind Lightning rail does */
119
+ sealed?: {
120
+ secret: string;
121
+ data: (order: Order) => unknown;
122
+ };
123
+ }
124
+ /**
125
+ * Sell for Lightning against a wallet of your own over NIP-47, for a wallet that
126
+ * has no LUD-21 address to be watched at. Your node mints the invoice and releases
127
+ * the preimage, so the proof comes from one hop nearer than any hosted address can
128
+ * manage, and the gateway sees a hash and a URL of yours
129
+ */
130
+ declare function nwcRail(gateway: ThunderBridge, config: NwcRailConfig): Rail;
131
+
18
132
  /** What a shop knows about a sale before any rail exists */
19
133
  interface Order {
20
134
  /** The bank matches it on the statement, and Lightning keys idempotency on it */
@@ -139,6 +253,13 @@ declare class Rails {
139
253
  blindLightning(config: BlindLightningRailConfig): Rail;
140
254
  /** A bank transfer, proved the way a Lightning payment is */
141
255
  bank(config: BankRailConfig): Rail;
256
+ /**
257
+ * Lightning against a wallet of your own over NIP-47, for a wallet that has no
258
+ * LUD-21 address to be watched at. Your node mints the invoice and releases the
259
+ * preimage, so the proof comes from one hop nearer than any hosted address can
260
+ * manage, and the gateway sees a hash and a URL of yours
261
+ */
262
+ nwc(config: NwcRailConfig): Rail;
142
263
  /**
143
264
  * One bank transfer without building a rail first, for a shop that asks for
144
265
  * them one at a time rather than beside another payment method
@@ -358,26 +479,39 @@ interface Provable {
358
479
  bolt11?: string | null;
359
480
  }
360
481
  /**
361
- * A report `carriesProof` has already accepted, so the preimage is there and the
362
- * status is settled. Nothing downstream of the check needs a null guard
482
+ * A report `agreesWithItself` has already accepted, so the preimage is there and
483
+ * the status is settled. Nothing downstream of the check needs a null guard
363
484
  */
364
- type Proven<T extends Provable> = T & {
485
+ type SelfConsistent<T extends Provable> = T & {
365
486
  status: "paid";
366
487
  preimage: string;
367
488
  };
368
489
  /**
369
- * Whether a report proves what it claims: it says paid, and it carries a preimage
490
+ * What `SelfConsistent` was called before 2.2.0
491
+ *
492
+ * @deprecated Use `SelfConsistent`, which says the report was checked against itself and nothing else
493
+ */
494
+ type Proven<T extends Provable> = SelfConsistent<T>;
495
+ /**
496
+ * Whether a report agrees with itself: it says paid, and it carries a preimage
370
497
  * that hashes to the payment hash it itself names. Where an invoice comes with it,
371
498
  * the invoice's own hash has to agree too.
372
499
  *
373
500
  * A payment, a settlement delivered to a webhook and a frame off a trigger all
374
- * answer this, because all three carry those fields and no other question about
375
- * one matters.
501
+ * answer this, because all three carry those fields.
502
+ *
503
+ * It asks nobody anything and holds the report against nothing you hold, so a
504
+ * gateway that invents a preimage and names its hash passes it. Checking against
505
+ * the hash you hold is what `payment` and `settled` do, and only
506
+ * `proveSettlement` asks the recipient
507
+ */
508
+ declare function agreesWithItself<T extends Provable>(report: T): report is SelfConsistent<T>;
509
+ /**
510
+ * What `agreesWithItself` was called before 2.2.0
376
511
  *
377
- * It asks nobody anything, so it costs no round trip and is not a proof of
378
- * arrival. Only `proveSettlement` asks the recipient
512
+ * @deprecated Use `agreesWithItself`, because it proves nothing beyond the report itself
379
513
  */
380
- declare function carriesProof<T extends Provable>(report: T): report is Proven<T>;
514
+ declare const carriesProof: typeof agreesWithItself;
381
515
  /**
382
516
  * The most an operator may add over the recipient's own amount, in millisatoshi.
383
517
  * The proportion is what routing and the liquidity behind it costs, and the base
@@ -402,9 +536,15 @@ declare function wrapFeeCeiling(amountMsat: number, allowance?: WrapAllowance):
402
536
  */
403
537
  declare function proveWrapped(wrapped: string, recipient: string, allowance?: WrapAllowance): void;
404
538
 
405
- /** How far the gateway's clock may drift from yours before a webhook is refused */
539
+ /**
540
+ * How far the gateway's clock may drift from yours before a webhook is refused,
541
+ * and the URL you registered when a proxy in front of you hands requests on under
542
+ * another one. A delivery is signed for the URL it was sent to, so one made for
543
+ * somebody else's endpoint is refused here
544
+ */
406
545
  type WebhookOptions = {
407
546
  toleranceSecs?: number;
547
+ url?: string;
408
548
  };
409
549
  /**
410
550
  * What checks a delivery: the hex the gateway publishes at `/webhook-key`. There
@@ -434,7 +574,7 @@ interface WebhookHandlers {
434
574
  * A settlement that proves itself: it says paid and its preimage hashes to the
435
575
  * payment hash it names. This is the only callback a shop needs
436
576
  */
437
- onSettled?: (settlement: Proven<Settlement>) => void | Promise<void>;
577
+ onSettled?: (settlement: SelfConsistent<Settlement>) => void | Promise<void>;
438
578
  /**
439
579
  * A delivery that carries no proof, so an expiry. Left unset, the handler
440
580
  * answers `202` and does nothing, because acting on an unproven claim is the
@@ -450,14 +590,21 @@ interface WebhookHandlers {
450
590
  credential?: WebhookCredential;
451
591
  /** How far the gateway's clock may drift from yours, five minutes by default */
452
592
  toleranceSecs?: number;
593
+ /**
594
+ * The URL you registered, when a proxy in front of you hands the request on
595
+ * under another host or scheme. Left unset, the request's own URL is what the
596
+ * delivery has to have been signed for
597
+ */
598
+ url?: string;
453
599
  }
454
600
  /**
455
601
  * Everything one gateway lets you mount, in one place so a caller never has to
456
602
  * know which handler needs the gateway and which does not. Most of these took it
457
603
  * as a config field before, and reaching them through the gateway deleted it.
458
604
  *
459
- * `verify` and `bankVerify` need nothing from the gateway and are here anyway,
460
- * because a reader looking for a handler should find every handler in one list
605
+ * `lightningVerify`, `bankVerify` and `nwcVerify` need nothing from the gateway and
606
+ * are here anyway, because a reader looking for a handler should find every handler
607
+ * in one list
461
608
  */
462
609
  declare class Serve {
463
610
  private readonly gateway;
@@ -478,13 +625,24 @@ declare class Serve {
478
625
  * A verify endpoint of your own that asks the recipient's wallet for you, so
479
626
  * the gateway polls you and never the wallet
480
627
  */
628
+ lightningVerify(config: LightningVerifyConfig): Handler;
629
+ /**
630
+ * What `lightningVerify` was called before 2.2.0
631
+ *
632
+ * @deprecated Use `lightningVerify`, beside `bankVerify` and `nwcVerify`
633
+ */
481
634
  verify(config: LightningVerifyConfig): Handler;
482
635
  /** The verify endpoint a bank rail is polled at, answering off your own statement */
483
636
  bankVerify(config: BankVerifyConfig): Handler;
637
+ /**
638
+ * The verify endpoint an NWC rail is polled at, asking your own wallet over
639
+ * NIP-47, so the gateway never learns the connection, the relay or the wallet
640
+ */
641
+ nwcVerify(config: NwcVerifyConfig): Handler;
484
642
  /**
485
643
  * The whole webhook route: it answers the gateway's challenge, checks the
486
644
  * signature against the key the gateway publishes, refuses a settlement that
487
- * proves nothing, and calls you for the one that does.
645
+ * proves nothing, and calls you once for the one that does.
488
646
  *
489
647
  * `export const POST = gateway.serve.webhook({ onSettled: fulfil })` is the
490
648
  * entire integration
@@ -706,6 +864,10 @@ declare class ThunderBridge {
706
864
  * the recipient's own wallet minted it, so a payer who pays the loser afterwards
707
865
  * really does pay twice and that shows up on `follow` as a second settlement to
708
866
  * refund.
867
+ *
868
+ * A leg the gateway is caught lying about before any leg is paid ends the wait
869
+ * with its `GatewayCheatError`, even when another leg is paid after it, so a
870
+ * detected cheat is never traded for a later win.
709
871
  */
710
872
  firstSettled(held: Held[], options?: WaitOptions): Promise<Payment | null>;
711
873
  /**
@@ -893,4 +1055,4 @@ interface BankAgentConfig {
893
1055
  */
894
1056
  declare function bankAgent(config: BankAgentConfig): () => void;
895
1057
 
896
- export { type AttendOptions as A, type BankAgentConfig as B, type Credit as C, type WebhookOptions as D, type WrapAllowance as E, type FollowOptions as F, answerVerifyChallenge as G, type Handler as H, carriesProof as I, invoiceFrom as J, proveOrigin as K, type Leg as L, type Minted as M, proveSettlement as N, type Order as O, type PaymentRequest as P, proveWrapped as Q, type RailConfig as R, type Statement as S, ThunderBridge as T, relayedVerifyUrl as U, wrapFeeCeiling as V, type WaitOptions as W, type BankOrder as a, type BankTransfer as b, type BankTransferParams as c, type BankVerifyConfig as d, bankAgent as e, type Rail as f, type ThunderBridgeOptions as g, type BankRailConfig as h, type BlindLightningRailConfig as i, type CreateOptions as j, type LightningRailConfig as k, type LightningVerifyConfig as l, type PaymentRequestInit as m, type PaymentRequestOptions as n, type Provable as o, type Proven as p, Rails as q, type Range as r, type Relayed as s, type Send as t, Serve as u, type TicketOptions as v, type TriggerConfig as w, type WatchTicketConfig as x, type WebhookCredential as y, type WebhookHandlers as z };
1058
+ export { type WebhookOptions as $, type AttendOptions as A, type BankAgentConfig as B, type Credit as C, type Proven as D, type RailConfig as E, type FollowOptions as F, Rails as G, type Handler as H, type Range as I, type Relayed as J, type SelfConsistent as K, type Leg as L, type Minted as M, type NwcConnection as N, type Order as O, type PaymentRequest as P, type Send as Q, type Rail as R, type Statement as S, ThunderBridge as T, Serve as U, type TicketOptions as V, type WaitOptions as W, type TriggerConfig as X, type WatchTicketConfig as Y, type WebhookCredential as Z, type WebhookHandlers as _, type BankOrder as a, type WrapAllowance as a0, agreesWithItself as a1, answerVerifyChallenge as a2, carriesProof as a3, invoiceFrom as a4, proveOrigin as a5, proveSettlement as a6, proveWrapped as a7, relayedVerifyUrl as a8, wrapFeeCeiling as a9, type BankTransfer as b, type BankTransferParams as c, type BankVerifyConfig as d, bankAgent as e, nwcVerifyEndpoint as f, type NwcInvoice as g, type NwcRailConfig as h, type NwcVerifyConfig as i, askWallet as j, nwcConnection as k, nwcHoldInvoice as l, nwcInvoice as m, nwcRail as n, nwcPay as o, nwcSettlement as p, nwcVerifyUrl as q, type ThunderBridgeOptions as r, type BankRailConfig as s, type BlindLightningRailConfig as t, type CreateOptions as u, type LightningRailConfig as v, type LightningVerifyConfig as w, type PaymentRequestInit as x, type PaymentRequestOptions as y, type Provable as z };
package/dist/bank.cjs CHANGED
@@ -150,7 +150,6 @@ var INITIAL_STATE = new Uint32Array([
150
150
 
151
151
  // ../core/sealed.ts
152
152
  var MIN_SECRET_CHARS = 32;
153
- var INFO = new TextEncoder().encode("thunder-bridge/sealed");
154
153
  function refuseAWeakSecret(secret) {
155
154
  if (secret.length < MIN_SECRET_CHARS) {
156
155
  throw new Error(`the sealing secret needs ${MIN_SECRET_CHARS} characters of randomness`);
package/dist/bank.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Statement } from './bank-CEIuNhV_.cjs';
2
- export { B as BankAgentConfig, a as BankOrder, b as BankTransfer, c as BankTransferParams, d as BankVerifyConfig, C as Credit, e as bankAgent } from './bank-CEIuNhV_.cjs';
1
+ import { S as Statement } from './bank-Bg6RjuO-.cjs';
2
+ export { B as BankAgentConfig, a as BankOrder, b as BankTransfer, c as BankTransferParams, d as BankVerifyConfig, C as Credit, e as bankAgent } from './bank-Bg6RjuO-.cjs';
3
3
  import './qr-CF-YeXU1.cjs';
4
4
  import './types-DYZ9EkmJ.cjs';
5
5
 
package/dist/bank.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as Statement } from './bank-M8flcMks.js';
2
- export { B as BankAgentConfig, a as BankOrder, b as BankTransfer, c as BankTransferParams, d as BankVerifyConfig, C as Credit, e as bankAgent } from './bank-M8flcMks.js';
1
+ import { S as Statement } from './bank-B5MxT_6u.js';
2
+ export { B as BankAgentConfig, a as BankOrder, b as BankTransfer, c as BankTransferParams, d as BankVerifyConfig, C as Credit, e as bankAgent } from './bank-B5MxT_6u.js';
3
3
  import './qr-CF-YeXU1.js';
4
4
  import './types-DYZ9EkmJ.js';
5
5
 
package/dist/bank.js CHANGED
@@ -123,7 +123,6 @@ var INITIAL_STATE = new Uint32Array([
123
123
 
124
124
  // ../core/sealed.ts
125
125
  var MIN_SECRET_CHARS = 32;
126
- var INFO = new TextEncoder().encode("thunder-bridge/sealed");
127
126
  function refuseAWeakSecret(secret) {
128
127
  if (secret.length < MIN_SECRET_CHARS) {
129
128
  throw new Error(`the sealing secret needs ${MIN_SECRET_CHARS} characters of randomness`);
@@ -123,4 +123,4 @@ declare class NoWalletAvailableError extends ProblemError {
123
123
  }, wallets: WalletFailure[]);
124
124
  }
125
125
 
126
- export { AmountError as A, type GatewayCheatCode as G, type IdempotencyConflict as I, NoWalletAvailableError as N, ProblemError as P, UnverifiedRecipientError as U, type WrapRefusalCode as W, type AmountFault as a, GatewayCheatError as b, IdempotencyConflictError as c, WrapRefusedError as d };
126
+ export { AmountError as A, GatewayCheatError as G, type IdempotencyConflict as I, NoWalletAvailableError as N, ProblemError as P, UnverifiedRecipientError as U, type WrapRefusalCode as W, type AmountFault as a, type GatewayCheatCode as b, IdempotencyConflictError as c, WrapRefusedError as d };
@@ -123,4 +123,4 @@ declare class NoWalletAvailableError extends ProblemError {
123
123
  }, wallets: WalletFailure[]);
124
124
  }
125
125
 
126
- export { AmountError as A, type GatewayCheatCode as G, type IdempotencyConflict as I, NoWalletAvailableError as N, ProblemError as P, UnverifiedRecipientError as U, type WrapRefusalCode as W, type AmountFault as a, GatewayCheatError as b, IdempotencyConflictError as c, WrapRefusedError as d };
126
+ export { AmountError as A, GatewayCheatError as G, type IdempotencyConflict as I, NoWalletAvailableError as N, ProblemError as P, UnverifiedRecipientError as U, type WrapRefusalCode as W, type AmountFault as a, type GatewayCheatCode as b, IdempotencyConflictError as c, WrapRefusedError as d };