thunder-bridge 1.5.0 → 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.
package/dist/nwc.cjs CHANGED
@@ -25,9 +25,9 @@ __export(nwc_exports, {
25
25
  nwcHoldInvoice: () => nwcHoldInvoice,
26
26
  nwcInvoice: () => nwcInvoice,
27
27
  nwcPay: () => nwcPay,
28
- nwcRail: () => nwcRail,
28
+ nwcRail: () => nwcRail2,
29
29
  nwcSettlement: () => nwcSettlement,
30
- nwcVerifyEndpoint: () => nwcVerifyEndpoint,
30
+ nwcVerifyEndpoint: () => nwcVerifyEndpoint2,
31
31
  nwcVerifyUrl: () => nwcVerifyUrl
32
32
  });
33
33
  module.exports = __toCommonJS(nwc_exports);
@@ -206,6 +206,7 @@ function rotate(value, by) {
206
206
 
207
207
  // ../core/bolt11.ts
208
208
  var BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
209
+ var BECH32_GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
209
210
  var CHECKSUM_WORDS = 6;
210
211
  var TIMESTAMP_WORDS = 7;
211
212
  var SIGNATURE_WORDS = 104;
@@ -223,18 +224,27 @@ var HRP_MULTIPLIER_MSAT = {
223
224
  n: MSAT_PER_BTC / 1e9,
224
225
  p: MSAT_PER_BTC / 1e12
225
226
  };
226
- var HRP_AMOUNT = /^ln[a-z]+?(\d+)([munp]?)$/;
227
+ var HRP = /^ln(?:bcrt|bc|tbs|tb|sb)(?:(\d+)([munp]?))?$/;
227
228
  function decodeInvoice(bolt11) {
228
229
  const parts = splitBech32(bolt11);
229
230
  if (parts === null) {
230
- return { paymentHash: null, descriptionHash: null, amountMsat: null, expiresAt: null };
231
+ return {
232
+ paymentHash: null,
233
+ descriptionHash: null,
234
+ amountMsat: null,
235
+ issuedAt: null,
236
+ expiresAt: null
237
+ };
231
238
  }
232
239
  const tagged = taggedFields(parts.words);
240
+ const issuedAt = issuedAtOf(parts.words);
241
+ const expiry = tagged.get(EXPIRY_TAG);
233
242
  return {
234
243
  paymentHash: hexTag(tagged, PAYMENT_HASH_TAG, PAYMENT_HASH_WORDS),
235
244
  descriptionHash: hexTag(tagged, DESCRIPTION_HASH_TAG, DESCRIPTION_HASH_WORDS),
236
245
  amountMsat: amountFromHrp(parts.hrp),
237
- expiresAt: expiryOf(parts.words, tagged)
246
+ issuedAt,
247
+ expiresAt: issuedAt === null ? null : issuedAt + (expiry ? readNumber(expiry) : DEFAULT_EXPIRY_SECS)
238
248
  };
239
249
  }
240
250
  function preimageMatchesHash(preimage, paymentHash) {
@@ -245,7 +255,7 @@ function preimageMatchesHash(preimage, paymentHash) {
245
255
  }
246
256
  function splitBech32(bolt11) {
247
257
  const lower = bolt11.toLowerCase();
248
- if (isBolt12Encoding(lower)) {
258
+ if (isBolt12Encoding(lower) || bolt11 !== lower && bolt11 !== bolt11.toUpperCase()) {
249
259
  return null;
250
260
  }
251
261
  const separator = lower.lastIndexOf("1");
@@ -264,7 +274,28 @@ function splitBech32(bolt11) {
264
274
  }
265
275
  words.push(value);
266
276
  }
267
- return { hrp: lower.slice(0, separator), words: words.slice(0, -CHECKSUM_WORDS) };
277
+ const hrp = lower.slice(0, separator);
278
+ if (!HRP.test(hrp) || bech32Polymod([...expandedHrp(hrp), ...words]) !== 1) {
279
+ return null;
280
+ }
281
+ return { hrp, words: words.slice(0, -CHECKSUM_WORDS) };
282
+ }
283
+ function expandedHrp(hrp) {
284
+ const codes = [...hrp].map((char) => char.charCodeAt(0));
285
+ return [...codes.map((code) => code >> 5), 0, ...codes.map((code) => code & 31)];
286
+ }
287
+ function bech32Polymod(values) {
288
+ let checksum = 1;
289
+ for (const value of values) {
290
+ const top = checksum >> 25;
291
+ checksum = (checksum & 33554431) << 5 ^ value;
292
+ for (let bit = 0; bit < 5; bit++) {
293
+ if (top >> bit & 1) {
294
+ checksum ^= BECH32_GENERATOR[bit];
295
+ }
296
+ }
297
+ }
298
+ return checksum;
268
299
  }
269
300
  function isBolt12Encoding(lower) {
270
301
  return lower.startsWith("lno") || lower.startsWith("lni") || lower.startsWith("lnr");
@@ -294,19 +325,17 @@ function hexTag(fields, tag, expectedWords) {
294
325
  }
295
326
  return bytesToHex(wordsToBytes(words));
296
327
  }
297
- function expiryOf(words, fields) {
328
+ function issuedAtOf(words) {
298
329
  if (words.length < TIMESTAMP_WORDS + SIGNATURE_WORDS) {
299
330
  return null;
300
331
  }
301
- const expiry = fields.get(EXPIRY_TAG);
302
- return readNumber(words.slice(0, TIMESTAMP_WORDS)) + (expiry ? readNumber(expiry) : DEFAULT_EXPIRY_SECS);
332
+ return readNumber(words.slice(0, TIMESTAMP_WORDS));
303
333
  }
304
334
  function amountFromHrp(hrp) {
305
- const match = HRP_AMOUNT.exec(hrp);
306
- if (match === null) {
335
+ const [, digits, multiplier] = HRP.exec(hrp) ?? [];
336
+ if (digits === void 0 || multiplier === "p" && !digits.endsWith("0")) {
307
337
  return null;
308
338
  }
309
- const [, digits, multiplier] = match;
310
339
  return multiplier ? Number(digits) * HRP_MULTIPLIER_MSAT[multiplier] : Number(digits) * MSAT_PER_BTC;
311
340
  }
312
341
  function readNumber(words) {
@@ -336,49 +365,138 @@ var WalletRefused = class extends Error {
336
365
  }
337
366
  };
338
367
 
368
+ // ../core/hmac.ts
369
+ async function hmacHex(secret, payload) {
370
+ const raw = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
371
+ const key = await crypto.subtle.importKey(
372
+ "raw",
373
+ asBuffer(raw),
374
+ { name: "HMAC", hash: "SHA-256" },
375
+ false,
376
+ ["sign"]
377
+ );
378
+ const message = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
379
+ return bytesToHex(new Uint8Array(await crypto.subtle.sign("HMAC", key, asBuffer(message))));
380
+ }
381
+ function asBuffer(bytes) {
382
+ return bytes instanceof Uint8Array && bytes.buffer instanceof ArrayBuffer ? bytes : new Uint8Array(bytes);
383
+ }
384
+
339
385
  // ../core/sealed.ts
340
- var VERSION = "v1";
386
+ var VERSION = "v2";
387
+ var UNPADDED_VERSION = "v1";
388
+ var PURPOSE_VERSION = "v3";
341
389
  var IV_BYTES = 12;
342
390
  var MIN_SECRET_CHARS = 32;
343
391
  var MAX_PLAIN_BYTES = 3e3;
344
- var INFO = new TextEncoder().encode("thunder-bridge/sealed");
345
- async function seal(secret, plaintext) {
346
- const body = new TextEncoder().encode(plaintext);
347
- if (body.length > MAX_PLAIN_BYTES) {
348
- throw new Error(`sealed takes at most ${MAX_PLAIN_BYTES} bytes, this was ${body.length}`);
392
+ var SIZE_CLASSES = [256, 1024, MAX_PLAIN_BYTES];
393
+ var END_OF_TEXT = 128;
394
+ var INFO = "thunder-bridge/sealed";
395
+ async function seal(secret, plaintext, paymentHash) {
396
+ return await sealUnder(
397
+ await keyFor(secret, INFO),
398
+ plaintext,
399
+ crypto.getRandomValues(new Uint8Array(IV_BYTES)),
400
+ VERSION,
401
+ boundTo(paymentHash)
402
+ );
403
+ }
404
+ async function sealFor(purpose, secret, plaintext, { stable = false } = {}) {
405
+ return await sealUnder(
406
+ await keyFor(secret, `${INFO}/${purpose}`),
407
+ plaintext,
408
+ stable ? await nonceOf(secret, plaintext) : crypto.getRandomValues(new Uint8Array(IV_BYTES)),
409
+ PURPOSE_VERSION,
410
+ new TextEncoder().encode(PURPOSE_VERSION)
411
+ );
412
+ }
413
+ async function unsealFor(purpose, secret, sealed) {
414
+ if (!sealed.startsWith(`${PURPOSE_VERSION}.`)) {
415
+ return await unseal(secret, sealed);
349
416
  }
350
- const key = await keyFor(secret);
351
- const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
352
- const cipher = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, body));
417
+ return await opened(
418
+ await keyFor(secret, `${INFO}/${purpose}`),
419
+ sealed.slice(PURPOSE_VERSION.length + 1),
420
+ new TextEncoder().encode(PURPOSE_VERSION)
421
+ );
422
+ }
423
+ async function nonceOf(secret, plaintext) {
424
+ const derived = await hmacHex(secret, `sealed-nonce|${plaintext}`);
425
+ const nonce = new Uint8Array(IV_BYTES);
426
+ for (let at = 0; at < IV_BYTES; at++) {
427
+ nonce[at] = Number.parseInt(derived.slice(at * 2, at * 2 + 2), 16);
428
+ }
429
+ return nonce;
430
+ }
431
+ async function sealUnder(key, plaintext, iv, version, additionalData) {
432
+ const body = padToClass(plaintext);
433
+ const cipher = new Uint8Array(
434
+ await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData }, key, body)
435
+ );
353
436
  const joined2 = new Uint8Array(iv.length + cipher.length);
354
437
  joined2.set(iv);
355
438
  joined2.set(cipher, iv.length);
356
- return `${VERSION}.${toBase64Url(joined2)}`;
439
+ return `${version}.${toBase64Url(joined2)}`;
357
440
  }
358
- async function unseal(secret, sealed) {
359
- const key = await keyFor(secret);
360
- if (!sealed.startsWith(`${VERSION}.`)) {
441
+ async function unseal(secret, sealed, paymentHash) {
442
+ const key = await keyFor(secret, INFO);
443
+ const padded2 = sealed.startsWith(`${VERSION}.`);
444
+ const unbound = !padded2 && sealed.startsWith(`${UNPADDED_VERSION}.`);
445
+ if (!padded2 && (!unbound || paymentHash !== void 0)) {
361
446
  return null;
362
447
  }
363
- const joined2 = fromBase64Url(sealed.slice(VERSION.length + 1));
448
+ return await opened(
449
+ key,
450
+ sealed.slice(VERSION.length + 1),
451
+ padded2 ? boundTo(paymentHash) : void 0
452
+ );
453
+ }
454
+ async function opened(key, encoded, additionalData) {
455
+ const joined2 = fromBase64Url(encoded);
364
456
  if (joined2 === null || joined2.length <= IV_BYTES) {
365
457
  return null;
366
458
  }
367
459
  try {
368
- const body = await crypto.subtle.decrypt(
369
- { name: "AES-GCM", iv: joined2.slice(0, IV_BYTES) },
370
- key,
371
- joined2.slice(IV_BYTES)
460
+ const body = new Uint8Array(
461
+ await crypto.subtle.decrypt(
462
+ { name: "AES-GCM", iv: joined2.slice(0, IV_BYTES), additionalData },
463
+ key,
464
+ joined2.slice(IV_BYTES)
465
+ )
372
466
  );
373
- return new TextDecoder().decode(body);
467
+ const text = additionalData === void 0 ? body : unpad(body);
468
+ return text === null ? null : new TextDecoder().decode(text);
374
469
  } catch {
375
470
  return null;
376
471
  }
377
472
  }
378
- async function keyFor(secret) {
473
+ function refuseAWeakSecret(secret) {
379
474
  if (secret.length < MIN_SECRET_CHARS) {
380
475
  throw new Error(`the sealing secret needs ${MIN_SECRET_CHARS} characters of randomness`);
381
476
  }
477
+ }
478
+ function boundTo(paymentHash) {
479
+ return new TextEncoder().encode(
480
+ paymentHash === void 0 ? VERSION : `${VERSION}|${paymentHash.toLowerCase()}`
481
+ );
482
+ }
483
+ function padToClass(plaintext) {
484
+ const text = new TextEncoder().encode(plaintext);
485
+ const size = SIZE_CLASSES.find((limit) => text.length <= limit);
486
+ if (size === void 0) {
487
+ throw new Error(`sealed takes at most ${MAX_PLAIN_BYTES} bytes, this was ${text.length}`);
488
+ }
489
+ const body = new Uint8Array(size + 1);
490
+ body.set(text);
491
+ body[text.length] = END_OF_TEXT;
492
+ return body;
493
+ }
494
+ function unpad(body) {
495
+ const end = body.lastIndexOf(END_OF_TEXT);
496
+ return end === -1 ? null : body.subarray(0, end);
497
+ }
498
+ async function keyFor(secret, info) {
499
+ refuseAWeakSecret(secret);
382
500
  const material = await crypto.subtle.importKey(
383
501
  "raw",
384
502
  new TextEncoder().encode(secret),
@@ -387,7 +505,12 @@ async function keyFor(secret) {
387
505
  ["deriveKey"]
388
506
  );
389
507
  return crypto.subtle.deriveKey(
390
- { name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: INFO },
508
+ {
509
+ name: "HKDF",
510
+ hash: "SHA-256",
511
+ salt: new Uint8Array(0),
512
+ info: new TextEncoder().encode(info)
513
+ },
391
514
  material,
392
515
  { name: "AES-GCM", length: 256 },
393
516
  false,
@@ -421,7 +544,7 @@ function fromBase64Url(text) {
421
544
  var import_chacha = require("@noble/ciphers/chacha.js");
422
545
  var import_secp256k1 = require("@noble/curves/secp256k1.js");
423
546
  var import_hkdf = require("@noble/hashes/hkdf.js");
424
- var import_hmac = require("@noble/hashes/hmac.js");
547
+ var import_hmac2 = require("@noble/hashes/hmac.js");
425
548
  var import_sha2 = require("@noble/hashes/sha2.js");
426
549
  var NIP44_SALT = new TextEncoder().encode("nip44-v2");
427
550
  var NIP44_VERSION = 2;
@@ -479,7 +602,7 @@ function encryptNip44(conversationKey, plaintext) {
479
602
  function sealNip44(conversationKey, plaintext, nonce) {
480
603
  const keys = messageKeysFrom(conversationKey, nonce);
481
604
  const ciphertext = (0, import_chacha.chacha20)(keys.chachaKey, keys.chachaNonce, padded(plaintext));
482
- const mac = (0, import_hmac.hmac)(import_sha2.sha256, keys.hmacKey, joined(nonce, ciphertext));
605
+ const mac = (0, import_hmac2.hmac)(import_sha2.sha256, keys.hmacKey, joined(nonce, ciphertext));
483
606
  return toBase64(joined(Uint8Array.of(NIP44_VERSION), nonce, ciphertext, mac));
484
607
  }
485
608
  function decryptNip44(conversationKey, payload) {
@@ -497,7 +620,7 @@ function decryptNip44(conversationKey, payload) {
497
620
  const ciphertext = raw.subarray(1 + NONCE_BYTES, raw.length - MAC_BYTES);
498
621
  const mac = raw.subarray(raw.length - MAC_BYTES);
499
622
  const keys = messageKeysFrom(conversationKey, nonce);
500
- if (!sameBytes((0, import_hmac.hmac)(import_sha2.sha256, keys.hmacKey, joined(nonce, ciphertext)), mac)) {
623
+ if (!sameBytes((0, import_hmac2.hmac)(import_sha2.sha256, keys.hmacKey, joined(nonce, ciphertext)), mac)) {
501
624
  throw new Error("nip44 payload failed its mac, so it was not written with this key");
502
625
  }
503
626
  return unpadded((0, import_chacha.chacha20)(keys.chachaKey, keys.chachaNonce, ciphertext));
@@ -796,6 +919,15 @@ async function amountNow(amount) {
796
919
 
797
920
  // ../core/ed25519.ts
798
921
  var PKCS8_HEADER = hexToBytes("302e020100300506032b657004220420");
922
+ var SMALL_ORDER = [
923
+ "00".repeat(32),
924
+ `01${"00".repeat(31)}`,
925
+ "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
926
+ "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
927
+ `ec${"ff".repeat(30)}7f`,
928
+ `ed${"ff".repeat(30)}7f`,
929
+ `ee${"ff".repeat(30)}7f`
930
+ ];
799
931
 
800
932
  // src/webhook.ts
801
933
  var VERIFY_CHALLENGE = "verify-challenge";
@@ -803,7 +935,7 @@ async function answerVerifyChallenge(request) {
803
935
  if (request.method !== "POST") {
804
936
  return null;
805
937
  }
806
- const nonce = nonceOf(await request.clone().text(), VERIFY_CHALLENGE);
938
+ const nonce = nonceOf2(await request.clone().text(), VERIFY_CHALLENGE);
807
939
  return nonce === null ? null : answered(nonce);
808
940
  }
809
941
  function answered(nonce) {
@@ -811,7 +943,7 @@ function answered(nonce) {
811
943
  headers: { "content-type": "application/json" }
812
944
  });
813
945
  }
814
- function nonceOf(text, type) {
946
+ function nonceOf2(text, type) {
815
947
  try {
816
948
  const said = JSON.parse(text);
817
949
  if (said["type"] !== type || typeof said["nonce"] !== "string") {
@@ -957,7 +1089,7 @@ function nwcVerifyEndpoint(config) {
957
1089
  if (sealed === null) {
958
1090
  return Response.json({ settled: false }, { status: 400 });
959
1091
  }
960
- const paymentHash = await unseal(config.secret, sealed);
1092
+ const paymentHash = await unsealFor("nwc-verify", config.secret, sealed);
961
1093
  if (paymentHash === null) {
962
1094
  return Response.json({ settled: false }, { status: 403 });
963
1095
  }
@@ -972,7 +1104,7 @@ function nwcVerifyEndpoint(config) {
972
1104
  }
973
1105
  async function nwcVerifyUrl(endpoint, paymentHash, secret) {
974
1106
  const sealed = new URL(endpoint);
975
- sealed.searchParams.set(HASH, await seal(secret, paymentHash));
1107
+ sealed.searchParams.set(HASH, await sealFor("nwc-verify", secret, paymentHash));
976
1108
  return sealed.toString();
977
1109
  }
978
1110
  async function askWallet(connection, method, params, timeoutMs = DEFAULT_ASK_TIMEOUT_MS) {
@@ -1126,11 +1258,16 @@ function nwcRail(gateway, config) {
1126
1258
  expiresAt: invoice.expiresAt,
1127
1259
  trigger: config.trigger,
1128
1260
  replay: config.replay,
1129
- sealed: await config.sealed?.(order),
1261
+ sealed: config.sealed ? await seal(
1262
+ config.sealed.secret,
1263
+ JSON.stringify(config.sealed.data(order)),
1264
+ invoice.paymentHash
1265
+ ) : void 0,
1130
1266
  webhookUrl: config.webhookUrl
1131
1267
  });
1132
1268
  return {
1133
1269
  id: watched.id,
1270
+ paymentHash: invoice.paymentHash,
1134
1271
  rail: config.name ?? "lightning",
1135
1272
  scan: invoice.bolt11,
1136
1273
  qr: toLightningUri(invoice.bolt11),
@@ -1138,6 +1275,10 @@ function nwcRail(gateway, config) {
1138
1275
  };
1139
1276
  };
1140
1277
  }
1278
+
1279
+ // src/entry/nwc.ts
1280
+ var nwcRail2 = nwcRail;
1281
+ var nwcVerifyEndpoint2 = nwcVerifyEndpoint;
1141
1282
  // Annotate the CommonJS export names for ESM import in node:
1142
1283
  0 && (module.exports = {
1143
1284
  askWallet,
package/dist/nwc.d.cts CHANGED
@@ -1,120 +1,19 @@
1
- import { A as Amount, T as Ticker } from './types-BNPmVnA7.cjs';
2
- import { R as RailConfig, O as Order, T as ThunderBridge, a as Rail } from './client-CzGZcByI.cjs';
1
+ import { n as nwcRail$1, f as nwcVerifyEndpoint$1 } from './bank-Bg6RjuO-.cjs';
2
+ export { N as NwcConnection, g as NwcInvoice, h as NwcRailConfig, i as NwcVerifyConfig, j as askWallet, k as nwcConnection, l as nwcHoldInvoice, m as nwcInvoice, o as nwcPay, p as nwcSettlement, q as nwcVerifyUrl } from './bank-Bg6RjuO-.cjs';
3
3
  import './qr-CF-YeXU1.cjs';
4
- import './bank-B3mHISn1.cjs';
4
+ import './types-DYZ9EkmJ.cjs';
5
5
 
6
- /** A wallet reachable over NIP-47, as its `nostr+walletconnect://` URI describes it */
7
- interface NwcConnection {
8
- /** The wallet service's public key, which is what its answers have to be signed by */
9
- walletPubkey: string;
10
- /** Where to reach it, tried in order until one answers */
11
- relays: string[];
12
- /** Our own private key on this connection, and the only thing that authorises it */
13
- secret: string;
14
- }
15
- /** A minted invoice and everything needed to watch it */
16
- interface NwcInvoice {
17
- bolt11: string;
18
- paymentHash: string;
19
- expiresAt: number;
20
- }
21
- /** The endpoint the gateway polls for an NWC payment, answering off your own wallet */
22
- interface NwcVerifyConfig {
23
- /** The wallet this endpoint speaks for. It never leaves this process */
24
- connection: NwcConnection;
25
- /** The secret the payment hash was sealed with, and nothing else uses it */
26
- secret: string;
27
- /** How often the gateway should ask, in seconds, sent as `Cache-Control: max-age`, `5` by default */
28
- pollEverySecs?: number;
29
- /** How long one `lookup_invoice` may take before the wallet counts as unreachable, `10_000` by default */
30
- askTimeoutMs?: number;
31
- }
32
6
  /**
33
- * Read a `nostr+walletconnect://` URI. Refuses a relay that is not `wss`, for the
34
- * reason the gateway refuses a verify URL that is not https
35
- */
36
- declare function nwcConnection(uri: string): NwcConnection;
37
- /** Mint an invoice on the connected wallet, decoded so the caller need not trust its word */
38
- declare function nwcInvoice(connection: NwcConnection, amountMsat: number, description: string, timeoutMs?: number): Promise<NwcInvoice>;
39
- /**
40
- * Mint a hold invoice on a hash the wallet does not hold the preimage for, which
41
- * is what lets an operator be paid only by paying somebody else first. The hash
42
- * has to come from the recipient's own invoice, and the invoice that comes back
43
- * is decoded rather than believed
44
- */
45
- declare function nwcHoldInvoice(connection: NwcConnection, held: {
46
- paymentHash: string;
47
- amountMsat: number;
48
- description: string;
49
- expirySecs: number;
50
- minCltvExpiryDelta?: number;
51
- }, timeoutMs?: number): Promise<NwcInvoice>;
52
- /**
53
- * The preimage the wallet released for this hash, null while it has released
54
- * none. A preimage that does not hash to what was asked for is a lie rather than
55
- * an answer, so it throws instead of being passed on
56
- */
57
- declare function nwcSettlement(connection: NwcConnection, paymentHash: string, timeoutMs?: number): Promise<string | null>;
58
- /**
59
- * Pay an invoice and keep the preimage the network handed back. Whoever pays
60
- * learns it, which is what makes delivery provable to a recipient publishing no
61
- * LUD-21 of their own. A preimage that does not hash to the invoice's own hash is
62
- * a lie rather than a receipt, so it throws instead of being passed on
63
- */
64
- declare function nwcPay(connection: NwcConnection, bolt11: string, timeoutMs?: number): Promise<string>;
65
- /**
66
- * A verify endpoint of your own that asks your wallet over NIP-47, so the gateway
67
- * polls you and never learns the connection, the relay, or which wallet it is.
68
- *
69
- * `nwcVerifyUrl` seals the payment hash into the query with your secret, which is
70
- * what stops a stranger driving your wallet through this handler. It answers the
71
- * LUD-21 shape the gateway already speaks, so nothing on that side changes.
7
+ * The NWC rail as a free function, which is how it was reached before 2.2.0
72
8
  *
73
- * A wallet it cannot reach answers `502` rather than "not settled", because those
74
- * are different claims and only one of them is true.
75
- */
76
- declare function nwcVerifyEndpoint(config: NwcVerifyConfig): (request: Request) => Promise<Response>;
77
- /**
78
- * The URL to hand the gateway, with the payment hash sealed inside it. Point it at
79
- * wherever `nwcVerifyEndpoint` is mounted
80
- */
81
- declare function nwcVerifyUrl(endpoint: string, paymentHash: string, secret: string): Promise<string>;
82
- /**
83
- * One NIP-47 call, for a method this SDK does not wrap. The wallet's own info
84
- * event lists what it will answer, and anything it refuses comes back as a
85
- * `WalletRefused` whose `reason` says which kind of refusal it was
9
+ * @deprecated Use `gateway.rails.nwc(config)`, the same rail beside every other one
86
10
  */
87
- declare function askWallet(connection: NwcConnection, method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<Record<string, unknown>>;
88
- /** A Lightning rail minting on a wallet of your own over NIP-47, bound once per shop */
89
- interface NwcRailConfig extends RailConfig {
90
- /** The wallet that mints, which never leaves this process */
91
- connection: NwcConnection;
92
- /**
93
- * What to charge for one order, the order's own price converted at `rate` by
94
- * default. Give it a function and the price is whatever you say
95
- */
96
- amount?: (order: Order) => Amount;
97
- /** Where the default conversion gets its rate, the median of four venues by default */
98
- rate?: Ticker;
99
- /** Where `nwcVerifyEndpoint` is mounted, and the secret the hash is sealed with */
100
- verifyThrough: {
101
- endpoint: string;
102
- secret: string;
103
- };
104
- /** What the payer's wallet shows, the order's reference by default */
105
- description?: (order: Order) => string;
106
- /** Sealed before the gateway sees it, the way the blind Lightning rail does */
107
- sealed?: (order: Order) => string | Promise<string>;
108
- }
11
+ declare const nwcRail: typeof nwcRail$1;
109
12
  /**
110
- * Sell for Lightning against a wallet of your own over NIP-47, for a wallet that
111
- * has no LUD-21 address to be watched at. Your node mints the invoice and releases
112
- * the preimage, so the proof comes from one hop nearer than any hosted address can
113
- * manage, and the gateway sees a hash and a URL of yours.
13
+ * The NWC verify endpoint as a free function, which is how it was reached before 2.2.0
114
14
  *
115
- * This rail lives here rather than on `gateway.rails` because NIP-47 needs the
116
- * nostr crypto in this module, and a browser showing a QR should not download it
15
+ * @deprecated Use `gateway.serve.nwcVerify(config)`, beside every other verify endpoint
117
16
  */
118
- declare function nwcRail(gateway: ThunderBridge, config: NwcRailConfig): Rail;
17
+ declare const nwcVerifyEndpoint: typeof nwcVerifyEndpoint$1;
119
18
 
120
- export { type NwcConnection, type NwcInvoice, type NwcRailConfig, type NwcVerifyConfig, askWallet, nwcConnection, nwcHoldInvoice, nwcInvoice, nwcPay, nwcRail, nwcSettlement, nwcVerifyEndpoint, nwcVerifyUrl };
19
+ export { nwcRail, nwcVerifyEndpoint };
package/dist/nwc.d.ts CHANGED
@@ -1,120 +1,19 @@
1
- import { A as Amount, T as Ticker } from './types-BNPmVnA7.js';
2
- import { R as RailConfig, O as Order, T as ThunderBridge, a as Rail } from './client-Cc5gtjGV.js';
1
+ import { n as nwcRail$1, f as nwcVerifyEndpoint$1 } from './bank-B5MxT_6u.js';
2
+ export { N as NwcConnection, g as NwcInvoice, h as NwcRailConfig, i as NwcVerifyConfig, j as askWallet, k as nwcConnection, l as nwcHoldInvoice, m as nwcInvoice, o as nwcPay, p as nwcSettlement, q as nwcVerifyUrl } from './bank-B5MxT_6u.js';
3
3
  import './qr-CF-YeXU1.js';
4
- import './bank-B3mHISn1.js';
4
+ import './types-DYZ9EkmJ.js';
5
5
 
6
- /** A wallet reachable over NIP-47, as its `nostr+walletconnect://` URI describes it */
7
- interface NwcConnection {
8
- /** The wallet service's public key, which is what its answers have to be signed by */
9
- walletPubkey: string;
10
- /** Where to reach it, tried in order until one answers */
11
- relays: string[];
12
- /** Our own private key on this connection, and the only thing that authorises it */
13
- secret: string;
14
- }
15
- /** A minted invoice and everything needed to watch it */
16
- interface NwcInvoice {
17
- bolt11: string;
18
- paymentHash: string;
19
- expiresAt: number;
20
- }
21
- /** The endpoint the gateway polls for an NWC payment, answering off your own wallet */
22
- interface NwcVerifyConfig {
23
- /** The wallet this endpoint speaks for. It never leaves this process */
24
- connection: NwcConnection;
25
- /** The secret the payment hash was sealed with, and nothing else uses it */
26
- secret: string;
27
- /** How often the gateway should ask, in seconds, sent as `Cache-Control: max-age`, `5` by default */
28
- pollEverySecs?: number;
29
- /** How long one `lookup_invoice` may take before the wallet counts as unreachable, `10_000` by default */
30
- askTimeoutMs?: number;
31
- }
32
6
  /**
33
- * Read a `nostr+walletconnect://` URI. Refuses a relay that is not `wss`, for the
34
- * reason the gateway refuses a verify URL that is not https
35
- */
36
- declare function nwcConnection(uri: string): NwcConnection;
37
- /** Mint an invoice on the connected wallet, decoded so the caller need not trust its word */
38
- declare function nwcInvoice(connection: NwcConnection, amountMsat: number, description: string, timeoutMs?: number): Promise<NwcInvoice>;
39
- /**
40
- * Mint a hold invoice on a hash the wallet does not hold the preimage for, which
41
- * is what lets an operator be paid only by paying somebody else first. The hash
42
- * has to come from the recipient's own invoice, and the invoice that comes back
43
- * is decoded rather than believed
44
- */
45
- declare function nwcHoldInvoice(connection: NwcConnection, held: {
46
- paymentHash: string;
47
- amountMsat: number;
48
- description: string;
49
- expirySecs: number;
50
- minCltvExpiryDelta?: number;
51
- }, timeoutMs?: number): Promise<NwcInvoice>;
52
- /**
53
- * The preimage the wallet released for this hash, null while it has released
54
- * none. A preimage that does not hash to what was asked for is a lie rather than
55
- * an answer, so it throws instead of being passed on
56
- */
57
- declare function nwcSettlement(connection: NwcConnection, paymentHash: string, timeoutMs?: number): Promise<string | null>;
58
- /**
59
- * Pay an invoice and keep the preimage the network handed back. Whoever pays
60
- * learns it, which is what makes delivery provable to a recipient publishing no
61
- * LUD-21 of their own. A preimage that does not hash to the invoice's own hash is
62
- * a lie rather than a receipt, so it throws instead of being passed on
63
- */
64
- declare function nwcPay(connection: NwcConnection, bolt11: string, timeoutMs?: number): Promise<string>;
65
- /**
66
- * A verify endpoint of your own that asks your wallet over NIP-47, so the gateway
67
- * polls you and never learns the connection, the relay, or which wallet it is.
68
- *
69
- * `nwcVerifyUrl` seals the payment hash into the query with your secret, which is
70
- * what stops a stranger driving your wallet through this handler. It answers the
71
- * LUD-21 shape the gateway already speaks, so nothing on that side changes.
7
+ * The NWC rail as a free function, which is how it was reached before 2.2.0
72
8
  *
73
- * A wallet it cannot reach answers `502` rather than "not settled", because those
74
- * are different claims and only one of them is true.
75
- */
76
- declare function nwcVerifyEndpoint(config: NwcVerifyConfig): (request: Request) => Promise<Response>;
77
- /**
78
- * The URL to hand the gateway, with the payment hash sealed inside it. Point it at
79
- * wherever `nwcVerifyEndpoint` is mounted
80
- */
81
- declare function nwcVerifyUrl(endpoint: string, paymentHash: string, secret: string): Promise<string>;
82
- /**
83
- * One NIP-47 call, for a method this SDK does not wrap. The wallet's own info
84
- * event lists what it will answer, and anything it refuses comes back as a
85
- * `WalletRefused` whose `reason` says which kind of refusal it was
9
+ * @deprecated Use `gateway.rails.nwc(config)`, the same rail beside every other one
86
10
  */
87
- declare function askWallet(connection: NwcConnection, method: string, params: Record<string, unknown>, timeoutMs?: number): Promise<Record<string, unknown>>;
88
- /** A Lightning rail minting on a wallet of your own over NIP-47, bound once per shop */
89
- interface NwcRailConfig extends RailConfig {
90
- /** The wallet that mints, which never leaves this process */
91
- connection: NwcConnection;
92
- /**
93
- * What to charge for one order, the order's own price converted at `rate` by
94
- * default. Give it a function and the price is whatever you say
95
- */
96
- amount?: (order: Order) => Amount;
97
- /** Where the default conversion gets its rate, the median of four venues by default */
98
- rate?: Ticker;
99
- /** Where `nwcVerifyEndpoint` is mounted, and the secret the hash is sealed with */
100
- verifyThrough: {
101
- endpoint: string;
102
- secret: string;
103
- };
104
- /** What the payer's wallet shows, the order's reference by default */
105
- description?: (order: Order) => string;
106
- /** Sealed before the gateway sees it, the way the blind Lightning rail does */
107
- sealed?: (order: Order) => string | Promise<string>;
108
- }
11
+ declare const nwcRail: typeof nwcRail$1;
109
12
  /**
110
- * Sell for Lightning against a wallet of your own over NIP-47, for a wallet that
111
- * has no LUD-21 address to be watched at. Your node mints the invoice and releases
112
- * the preimage, so the proof comes from one hop nearer than any hosted address can
113
- * manage, and the gateway sees a hash and a URL of yours.
13
+ * The NWC verify endpoint as a free function, which is how it was reached before 2.2.0
114
14
  *
115
- * This rail lives here rather than on `gateway.rails` because NIP-47 needs the
116
- * nostr crypto in this module, and a browser showing a QR should not download it
15
+ * @deprecated Use `gateway.serve.nwcVerify(config)`, beside every other verify endpoint
117
16
  */
118
- declare function nwcRail(gateway: ThunderBridge, config: NwcRailConfig): Rail;
17
+ declare const nwcVerifyEndpoint: typeof nwcVerifyEndpoint$1;
119
18
 
120
- export { type NwcConnection, type NwcInvoice, type NwcRailConfig, type NwcVerifyConfig, askWallet, nwcConnection, nwcHoldInvoice, nwcInvoice, nwcPay, nwcRail, nwcSettlement, nwcVerifyEndpoint, nwcVerifyUrl };
19
+ export { nwcRail, nwcVerifyEndpoint };