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/README.md +49 -24
- package/dist/{client-Cc5gtjGV.d.ts → bank-B5MxT_6u.d.ts} +367 -42
- package/dist/{client-CzGZcByI.d.cts → bank-Bg6RjuO-.d.cts} +367 -42
- package/dist/bank.cjs +196 -0
- package/dist/bank.d.cts +4 -2
- package/dist/bank.d.ts +4 -2
- package/dist/bank.js +195 -0
- package/dist/{errors-Dmh-Uoh8.d.cts → errors-DJmsalYZ.d.cts} +3 -3
- package/dist/{errors-0vbVoISA.d.ts → errors-VX0R18oE.d.ts} +3 -3
- package/dist/index.cjs +1313 -395
- package/dist/index.d.cts +19 -10
- package/dist/index.d.ts +19 -10
- package/dist/index.js +1312 -395
- package/dist/nwc.cjs +183 -42
- package/dist/nwc.d.cts +10 -111
- package/dist/nwc.d.ts +10 -111
- package/dist/nwc.js +180 -39
- package/dist/price.d.cts +2 -2
- package/dist/price.d.ts +2 -2
- package/dist/qr.cjs +20 -23
- package/dist/qr.js +20 -23
- package/dist/{types-BNPmVnA7.d.cts → types-DYZ9EkmJ.d.cts} +11 -2
- package/dist/{types-BNPmVnA7.d.ts → types-DYZ9EkmJ.d.ts} +11 -2
- package/openapi.yaml +30 -40
- package/package.json +1 -1
- package/dist/bank-B3mHISn1.d.cts +0 -92
- package/dist/bank-B3mHISn1.d.ts +0 -92
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: () =>
|
|
28
|
+
nwcRail: () => nwcRail2,
|
|
29
29
|
nwcSettlement: () => nwcSettlement,
|
|
30
|
-
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
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
328
|
+
function issuedAtOf(words) {
|
|
298
329
|
if (words.length < TIMESTAMP_WORDS + SIGNATURE_WORDS) {
|
|
299
330
|
return null;
|
|
300
331
|
}
|
|
301
|
-
|
|
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
|
|
306
|
-
if (
|
|
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 = "
|
|
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
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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 `${
|
|
439
|
+
return `${version}.${toBase64Url(joined2)}`;
|
|
357
440
|
}
|
|
358
|
-
async function unseal(secret, sealed) {
|
|
359
|
-
const key = await keyFor(secret);
|
|
360
|
-
|
|
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
|
-
|
|
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 =
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
|
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,
|
|
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,
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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 {
|
|
2
|
-
|
|
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 './
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
17
|
+
declare const nwcVerifyEndpoint: typeof nwcVerifyEndpoint$1;
|
|
119
18
|
|
|
120
|
-
export {
|
|
19
|
+
export { nwcRail, nwcVerifyEndpoint };
|
package/dist/nwc.d.ts
CHANGED
|
@@ -1,120 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
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 './
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
17
|
+
declare const nwcVerifyEndpoint: typeof nwcVerifyEndpoint$1;
|
|
119
18
|
|
|
120
|
-
export {
|
|
19
|
+
export { nwcRail, nwcVerifyEndpoint };
|