thunder-bridge 2.2.0 → 3.0.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 +93 -412
- package/dist/bank.cjs +13 -3
- package/dist/bank.d.cts +9 -3
- package/dist/bank.d.ts +9 -3
- package/dist/bank.js +18 -3
- package/dist/index.cjs +254 -113
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +259 -112
- package/dist/{bank-B5MxT_6u.d.ts → nwc-BivXGSPY.d.ts} +80 -118
- package/dist/{bank-Bg6RjuO-.d.cts → nwc-h_-cGvMb.d.cts} +80 -118
- package/dist/nwc.cjs +10 -424
- package/dist/nwc.d.cts +2 -18
- package/dist/nwc.d.ts +2 -18
- package/dist/nwc.js +10 -422
- package/openapi.yaml +1 -1
- package/package.json +8 -2
package/dist/nwc.cjs
CHANGED
|
@@ -25,9 +25,7 @@ __export(nwc_exports, {
|
|
|
25
25
|
nwcHoldInvoice: () => nwcHoldInvoice,
|
|
26
26
|
nwcInvoice: () => nwcInvoice,
|
|
27
27
|
nwcPay: () => nwcPay,
|
|
28
|
-
nwcRail: () => nwcRail2,
|
|
29
28
|
nwcSettlement: () => nwcSettlement,
|
|
30
|
-
nwcVerifyEndpoint: () => nwcVerifyEndpoint2,
|
|
31
29
|
nwcVerifyUrl: () => nwcVerifyUrl
|
|
32
30
|
});
|
|
33
31
|
module.exports = __toCommonJS(nwc_exports);
|
|
@@ -383,8 +381,6 @@ function asBuffer(bytes) {
|
|
|
383
381
|
}
|
|
384
382
|
|
|
385
383
|
// ../core/sealed.ts
|
|
386
|
-
var VERSION = "v2";
|
|
387
|
-
var UNPADDED_VERSION = "v1";
|
|
388
384
|
var PURPOSE_VERSION = "v3";
|
|
389
385
|
var IV_BYTES = 12;
|
|
390
386
|
var MIN_SECRET_CHARS = 32;
|
|
@@ -392,15 +388,6 @@ var MAX_PLAIN_BYTES = 3e3;
|
|
|
392
388
|
var SIZE_CLASSES = [256, 1024, MAX_PLAIN_BYTES];
|
|
393
389
|
var END_OF_TEXT = 128;
|
|
394
390
|
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
391
|
async function sealFor(purpose, secret, plaintext, { stable = false } = {}) {
|
|
405
392
|
return await sealUnder(
|
|
406
393
|
await keyFor(secret, `${INFO}/${purpose}`),
|
|
@@ -410,16 +397,6 @@ async function sealFor(purpose, secret, plaintext, { stable = false } = {}) {
|
|
|
410
397
|
new TextEncoder().encode(PURPOSE_VERSION)
|
|
411
398
|
);
|
|
412
399
|
}
|
|
413
|
-
async function unsealFor(purpose, secret, sealed) {
|
|
414
|
-
if (!sealed.startsWith(`${PURPOSE_VERSION}.`)) {
|
|
415
|
-
return await unseal(secret, sealed);
|
|
416
|
-
}
|
|
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
400
|
async function nonceOf(secret, plaintext) {
|
|
424
401
|
const derived = await hmacHex(secret, `sealed-nonce|${plaintext}`);
|
|
425
402
|
const nonce = new Uint8Array(IV_BYTES);
|
|
@@ -438,48 +415,11 @@ async function sealUnder(key, plaintext, iv, version, additionalData) {
|
|
|
438
415
|
joined2.set(cipher, iv.length);
|
|
439
416
|
return `${version}.${toBase64Url(joined2)}`;
|
|
440
417
|
}
|
|
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)) {
|
|
446
|
-
return null;
|
|
447
|
-
}
|
|
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);
|
|
456
|
-
if (joined2 === null || joined2.length <= IV_BYTES) {
|
|
457
|
-
return null;
|
|
458
|
-
}
|
|
459
|
-
try {
|
|
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
|
-
)
|
|
466
|
-
);
|
|
467
|
-
const text = additionalData === void 0 ? body : unpad(body);
|
|
468
|
-
return text === null ? null : new TextDecoder().decode(text);
|
|
469
|
-
} catch {
|
|
470
|
-
return null;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
418
|
function refuseAWeakSecret(secret) {
|
|
474
419
|
if (secret.length < MIN_SECRET_CHARS) {
|
|
475
420
|
throw new Error(`the sealing secret needs ${MIN_SECRET_CHARS} characters of randomness`);
|
|
476
421
|
}
|
|
477
422
|
}
|
|
478
|
-
function boundTo(paymentHash) {
|
|
479
|
-
return new TextEncoder().encode(
|
|
480
|
-
paymentHash === void 0 ? VERSION : `${VERSION}|${paymentHash.toLowerCase()}`
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
423
|
function padToClass(plaintext) {
|
|
484
424
|
const text = new TextEncoder().encode(plaintext);
|
|
485
425
|
const size = SIZE_CLASSES.find((limit) => text.length <= limit);
|
|
@@ -491,10 +431,6 @@ function padToClass(plaintext) {
|
|
|
491
431
|
body[text.length] = END_OF_TEXT;
|
|
492
432
|
return body;
|
|
493
433
|
}
|
|
494
|
-
function unpad(body) {
|
|
495
|
-
const end = body.lastIndexOf(END_OF_TEXT);
|
|
496
|
-
return end === -1 ? null : body.subarray(0, end);
|
|
497
|
-
}
|
|
498
434
|
async function keyFor(secret, info) {
|
|
499
435
|
refuseAWeakSecret(secret);
|
|
500
436
|
const material = await crypto.subtle.importKey(
|
|
@@ -524,21 +460,6 @@ function toBase64Url(bytes) {
|
|
|
524
460
|
}
|
|
525
461
|
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
526
462
|
}
|
|
527
|
-
function fromBase64Url(text) {
|
|
528
|
-
if (text.length === 0 || !/^[A-Za-z0-9_-]+$/.test(text)) {
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
try {
|
|
532
|
-
const binary = atob(text.replace(/-/g, "+").replace(/_/g, "/"));
|
|
533
|
-
const bytes = new Uint8Array(binary.length);
|
|
534
|
-
for (let at = 0; at < bytes.length; at++) {
|
|
535
|
-
bytes[at] = binary.charCodeAt(at);
|
|
536
|
-
}
|
|
537
|
-
return bytes;
|
|
538
|
-
} catch {
|
|
539
|
-
return null;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
463
|
|
|
543
464
|
// src/nostr.ts
|
|
544
465
|
var import_chacha = require("@noble/ciphers/chacha.js");
|
|
@@ -695,274 +616,6 @@ function fromBase64(text) {
|
|
|
695
616
|
return bytes;
|
|
696
617
|
}
|
|
697
618
|
|
|
698
|
-
// src/qr.ts
|
|
699
|
-
var import_uqr = require("uqr");
|
|
700
|
-
function toLightningUri(destination) {
|
|
701
|
-
const scanned = isLnAddress(destination) ? destination : destination.toUpperCase();
|
|
702
|
-
return `LIGHTNING:${scanned}`;
|
|
703
|
-
}
|
|
704
|
-
function isLnAddress(destination) {
|
|
705
|
-
const at = destination.indexOf("@");
|
|
706
|
-
return at > 0 && destination.slice(at + 1).includes(".");
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// src/errors.ts
|
|
710
|
-
var AmountError = class extends Error {
|
|
711
|
-
/**
|
|
712
|
-
* Whether a failure is one of these, without asking whether it is this exact
|
|
713
|
-
* class. Every entry point carries its own copy of the class, so a price
|
|
714
|
-
* refused inside `thunder-bridge/price` is not `instanceof` the `AmountError`
|
|
715
|
-
* imported from `thunder-bridge`. The name and the code are the same in every
|
|
716
|
-
* copy, so this holds where `instanceof` does not
|
|
717
|
-
*/
|
|
718
|
-
static is(failure) {
|
|
719
|
-
return failure instanceof Error && failure.name === "AmountError" && "code" in failure;
|
|
720
|
-
}
|
|
721
|
-
code;
|
|
722
|
-
constructor(code, detail) {
|
|
723
|
-
super(`${code}: ${detail}`);
|
|
724
|
-
this.name = "AmountError";
|
|
725
|
-
this.code = code;
|
|
726
|
-
}
|
|
727
|
-
};
|
|
728
|
-
|
|
729
|
-
// src/currency.ts
|
|
730
|
-
var ISO_4217_MINOR_UNITS = {
|
|
731
|
-
BHD: 3,
|
|
732
|
-
BIF: 0,
|
|
733
|
-
CLP: 0,
|
|
734
|
-
CZK: 2,
|
|
735
|
-
DJF: 0,
|
|
736
|
-
EUR: 2,
|
|
737
|
-
GBP: 2,
|
|
738
|
-
ISK: 0,
|
|
739
|
-
IQD: 3,
|
|
740
|
-
JOD: 3,
|
|
741
|
-
JPY: 0,
|
|
742
|
-
KRW: 0,
|
|
743
|
-
KWD: 3,
|
|
744
|
-
LYD: 3,
|
|
745
|
-
OMR: 3,
|
|
746
|
-
PLN: 2,
|
|
747
|
-
PYG: 0,
|
|
748
|
-
RWF: 0,
|
|
749
|
-
TND: 3,
|
|
750
|
-
UGX: 0,
|
|
751
|
-
USD: 2,
|
|
752
|
-
VND: 0,
|
|
753
|
-
VUV: 0,
|
|
754
|
-
XAF: 0,
|
|
755
|
-
XOF: 0,
|
|
756
|
-
XPF: 0
|
|
757
|
-
};
|
|
758
|
-
function minorUnitsOf(currency) {
|
|
759
|
-
const digits = ISO_4217_MINOR_UNITS[currency.toUpperCase()];
|
|
760
|
-
if (digits === void 0) {
|
|
761
|
-
throw new AmountError(
|
|
762
|
-
"unknown-currency",
|
|
763
|
-
`${currency.toUpperCase()} is not a currency this knows the ISO 4217 minor unit of`
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
return digits;
|
|
767
|
-
}
|
|
768
|
-
function minorScaleOf(currency) {
|
|
769
|
-
return 10 ** minorUnitsOf(currency);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// src/price.ts
|
|
773
|
-
var MSAT_PER_BTC2 = 1e11;
|
|
774
|
-
var BASIS_POINTS = 1e4;
|
|
775
|
-
var DEFAULT_HOLD_SECS = 60;
|
|
776
|
-
var DEFAULT_MIN_VENUES = 2;
|
|
777
|
-
var DEFAULT_MAX_SPREAD_BPS = 200;
|
|
778
|
-
var MILLIS = 1e3;
|
|
779
|
-
function coinbase(baseUrl = "https://api.coinbase.com") {
|
|
780
|
-
return async (currency) => {
|
|
781
|
-
const asked = currency.toUpperCase();
|
|
782
|
-
const answer = await asJson(`${baseUrl}/v2/prices/BTC-${asked}/spot`, "coinbase");
|
|
783
|
-
const quoted = answer.data;
|
|
784
|
-
if (quoted?.currency !== asked) {
|
|
785
|
-
throw new Error(`coinbase quoted ${quoted?.currency} when asked for ${asked}`);
|
|
786
|
-
}
|
|
787
|
-
return asMinor(quoted.amount, "coinbase", asked);
|
|
788
|
-
};
|
|
789
|
-
}
|
|
790
|
-
function kraken(baseUrl = "https://api.kraken.com") {
|
|
791
|
-
return async (currency) => {
|
|
792
|
-
const asked = currency.toUpperCase();
|
|
793
|
-
const answer = await asJson(`${baseUrl}/0/public/Ticker?pair=XBT${asked}`, "kraken");
|
|
794
|
-
if (answer.error?.length) {
|
|
795
|
-
throw new Error(`kraken refused XBT${asked}: ${answer.error[0]}`);
|
|
796
|
-
}
|
|
797
|
-
const pairs = Object.values(answer.result ?? {});
|
|
798
|
-
if (pairs.length !== 1) {
|
|
799
|
-
throw new Error(`kraken answered ${pairs.length} pairs for XBT${asked}`);
|
|
800
|
-
}
|
|
801
|
-
return asMinor(pairs[0]?.c?.[0], "kraken", asked);
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
function bitstamp(baseUrl = "https://www.bitstamp.net") {
|
|
805
|
-
return async (currency) => {
|
|
806
|
-
const pair = `btc${currency.toLowerCase()}`;
|
|
807
|
-
const answer = await asJson(`${baseUrl}/api/v2/ticker/${pair}/`, "bitstamp");
|
|
808
|
-
if (Array.isArray(answer)) {
|
|
809
|
-
throw new Error(`bitstamp has no ${pair} pair`);
|
|
810
|
-
}
|
|
811
|
-
return asMinor(answer.last, "bitstamp", currency);
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
function coinmate(baseUrl = "https://coinmate.io") {
|
|
815
|
-
return async (currency) => {
|
|
816
|
-
const pair = `BTC_${currency.toUpperCase()}`;
|
|
817
|
-
const answer = await asJson(`${baseUrl}/api/ticker?currencyPair=${pair}`, "coinmate");
|
|
818
|
-
if (answer.error !== false) {
|
|
819
|
-
throw new Error(`coinmate refused ${pair}: ${answer.errorMessage ?? "no reason"}`);
|
|
820
|
-
}
|
|
821
|
-
return asMinor(answer.data?.last, "coinmate", currency);
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
function medianOf(tickers = [coinbase(), kraken(), bitstamp(), coinmate()], options = {}) {
|
|
825
|
-
const holdFor = (options.holdForSecs ?? DEFAULT_HOLD_SECS) * MILLIS;
|
|
826
|
-
const minVenues = options.minVenues ?? DEFAULT_MIN_VENUES;
|
|
827
|
-
const maxSpread = options.maxSpreadBps ?? DEFAULT_MAX_SPREAD_BPS;
|
|
828
|
-
const held = /* @__PURE__ */ new Map();
|
|
829
|
-
return async (currency) => {
|
|
830
|
-
const asked = currency.toUpperCase();
|
|
831
|
-
const remembered = held.get(asked);
|
|
832
|
-
if (remembered && Date.now() - remembered.at < holdFor) {
|
|
833
|
-
return remembered.price;
|
|
834
|
-
}
|
|
835
|
-
const answers = await Promise.allSettled(tickers.map((ticker) => ticker(asked)));
|
|
836
|
-
const quoted = answers.filter((answer) => answer.status === "fulfilled").map((answer) => answer.value).sort((one, other) => one - other);
|
|
837
|
-
if (quoted.length < minVenues) {
|
|
838
|
-
throw new Error(
|
|
839
|
-
`${quoted.length} of ${tickers.length} venues quoted ${asked}, ${minVenues} wanted: ${refusals(answers)}`
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
|
-
const spread = spreadBpsOf(quoted);
|
|
843
|
-
if (spread > maxSpread) {
|
|
844
|
-
throw new Error(
|
|
845
|
-
`venues disagree on ${asked} by ${Math.round(spread)} bps, more than the ${maxSpread} allowed: ${quoted.join(", ")}`
|
|
846
|
-
);
|
|
847
|
-
}
|
|
848
|
-
const price = middleOf(quoted);
|
|
849
|
-
held.set(asked, { at: Date.now(), price });
|
|
850
|
-
return price;
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
function msatFor(amountMinor, priceMinorPerBtc, options = {}) {
|
|
854
|
-
if (!Number.isInteger(amountMinor) || amountMinor <= 0) {
|
|
855
|
-
throw new Error("amountMinor must be a whole number of minor units above zero");
|
|
856
|
-
}
|
|
857
|
-
if (!Number.isFinite(priceMinorPerBtc) || priceMinorPerBtc <= 0) {
|
|
858
|
-
throw new Error(`${priceMinorPerBtc} is not a usable price`);
|
|
859
|
-
}
|
|
860
|
-
const spreadBps = options.spreadBps ?? 0;
|
|
861
|
-
if (!Number.isInteger(spreadBps) || spreadBps < 0) {
|
|
862
|
-
throw new Error("spreadBps must be a whole number of basis points, none or more");
|
|
863
|
-
}
|
|
864
|
-
const wanted = BigInt(amountMinor) * BigInt(MSAT_PER_BTC2) * BigInt(BASIS_POINTS + spreadBps);
|
|
865
|
-
const per = BigInt(Math.round(priceMinorPerBtc)) * BigInt(BASIS_POINTS);
|
|
866
|
-
const msat2 = (wanted + per - 1n) / per;
|
|
867
|
-
if (msat2 > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
868
|
-
throw new Error(`${amountMinor} at ${priceMinorPerBtc} is more millisatoshi than exist`);
|
|
869
|
-
}
|
|
870
|
-
return Number(msat2);
|
|
871
|
-
}
|
|
872
|
-
async function asJson(url, venue) {
|
|
873
|
-
const answer = await fetch(url, { headers: { accept: "application/json" } });
|
|
874
|
-
if (!answer.ok) {
|
|
875
|
-
throw new Error(`${venue} answered ${answer.status}`);
|
|
876
|
-
}
|
|
877
|
-
return answer.json();
|
|
878
|
-
}
|
|
879
|
-
function asMinor(quoted, venue, currency) {
|
|
880
|
-
const major = Number(quoted);
|
|
881
|
-
if (!Number.isFinite(major) || major <= 0) {
|
|
882
|
-
throw new Error(`${venue} quoted ${String(quoted)} for ${currency.toUpperCase()}`);
|
|
883
|
-
}
|
|
884
|
-
return Math.round(major * minorScaleOf(currency));
|
|
885
|
-
}
|
|
886
|
-
function spreadBpsOf(sorted) {
|
|
887
|
-
const lowest = sorted[0] ?? 0;
|
|
888
|
-
const highest = sorted[sorted.length - 1] ?? 0;
|
|
889
|
-
return lowest === 0 ? Number.POSITIVE_INFINITY : (highest - lowest) / lowest * BASIS_POINTS;
|
|
890
|
-
}
|
|
891
|
-
function middleOf(sorted) {
|
|
892
|
-
const middle = Math.floor(sorted.length / 2);
|
|
893
|
-
if (sorted.length % 2 === 1) {
|
|
894
|
-
return sorted[middle] ?? 0;
|
|
895
|
-
}
|
|
896
|
-
return Math.round(((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2);
|
|
897
|
-
}
|
|
898
|
-
function refusals(answers) {
|
|
899
|
-
return answers.filter((answer) => answer.status === "rejected").map(
|
|
900
|
-
(answer) => answer.reason instanceof Error ? answer.reason.message : String(answer.reason)
|
|
901
|
-
).join("; ");
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
// src/amount.ts
|
|
905
|
-
var MSAT_PER_SAT = 1e3;
|
|
906
|
-
var MAX_SATS = Math.floor(Number.MAX_SAFE_INTEGER / MSAT_PER_SAT);
|
|
907
|
-
function msat(exact) {
|
|
908
|
-
if (!Number.isSafeInteger(exact) || exact < 1) {
|
|
909
|
-
throw new AmountError(
|
|
910
|
-
"not-whole-millisatoshi",
|
|
911
|
-
`${exact} is not a payable count of millisatoshi`
|
|
912
|
-
);
|
|
913
|
-
}
|
|
914
|
-
return exact;
|
|
915
|
-
}
|
|
916
|
-
async function amountNow(amount) {
|
|
917
|
-
return typeof amount === "function" ? msat(await amount()) : msat(amount);
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
// ../core/ed25519.ts
|
|
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
|
-
];
|
|
931
|
-
|
|
932
|
-
// src/webhook.ts
|
|
933
|
-
var VERIFY_CHALLENGE = "verify-challenge";
|
|
934
|
-
async function answerVerifyChallenge(request) {
|
|
935
|
-
if (request.method !== "POST") {
|
|
936
|
-
return null;
|
|
937
|
-
}
|
|
938
|
-
const nonce = nonceOf2(await request.clone().text(), VERIFY_CHALLENGE);
|
|
939
|
-
return nonce === null ? null : answered(nonce);
|
|
940
|
-
}
|
|
941
|
-
function answered(nonce) {
|
|
942
|
-
return new Response(JSON.stringify({ nonce }), {
|
|
943
|
-
headers: { "content-type": "application/json" }
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
function nonceOf2(text, type) {
|
|
947
|
-
try {
|
|
948
|
-
const said = JSON.parse(text);
|
|
949
|
-
if (said["type"] !== type || typeof said["nonce"] !== "string") {
|
|
950
|
-
return null;
|
|
951
|
-
}
|
|
952
|
-
return said["nonce"];
|
|
953
|
-
} catch {
|
|
954
|
-
return null;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
// src/rail.ts
|
|
959
|
-
async function msatForOrder(order, amount, rate) {
|
|
960
|
-
if (amount !== void 0) {
|
|
961
|
-
return await amountNow(amount(order));
|
|
962
|
-
}
|
|
963
|
-
return msat(msatFor(order.amountMinor, await (rate ?? medianOf())(order.currency)));
|
|
964
|
-
}
|
|
965
|
-
|
|
966
619
|
// src/nwc.ts
|
|
967
620
|
var REQUEST_KIND = 23194;
|
|
968
621
|
var RESPONSE_KIND = 23195;
|
|
@@ -971,7 +624,6 @@ var SCHEME = "nostr+walletconnect:";
|
|
|
971
624
|
var HEX_32_BYTES = /^[0-9a-f]{64}$/i;
|
|
972
625
|
var DEFAULT_ASK_TIMEOUT_MS = 1e4;
|
|
973
626
|
var PAY_TIMEOUT_MS = 9e4;
|
|
974
|
-
var DEFAULT_POLL_EVERY_SECS = 5;
|
|
975
627
|
var HASH = "h";
|
|
976
628
|
var REFUSAL_BY_CODE = {
|
|
977
629
|
INSUFFICIENT_BALANCE: "amount-not-accepted",
|
|
@@ -997,13 +649,13 @@ function nwcConnection(uri) {
|
|
|
997
649
|
return { walletPubkey: walletPubkey.toLowerCase(), relays, secret: secret.toLowerCase() };
|
|
998
650
|
}
|
|
999
651
|
async function nwcInvoice(connection, amountMsat, description, timeoutMs = DEFAULT_ASK_TIMEOUT_MS) {
|
|
1000
|
-
const
|
|
652
|
+
const answered = await askWallet(
|
|
1001
653
|
connection,
|
|
1002
654
|
"make_invoice",
|
|
1003
655
|
{ amount: amountMsat, description },
|
|
1004
656
|
timeoutMs
|
|
1005
657
|
);
|
|
1006
|
-
const bolt11 = String(
|
|
658
|
+
const bolt11 = String(answered.invoice ?? "");
|
|
1007
659
|
const issued = decodeInvoice(bolt11);
|
|
1008
660
|
if (issued.paymentHash === null || issued.expiresAt === null) {
|
|
1009
661
|
throw new WalletRefused("invoice-refused", "the nwc wallet issued an invoice we cannot decode");
|
|
@@ -1017,7 +669,7 @@ async function nwcInvoice(connection, amountMsat, description, timeoutMs = DEFAU
|
|
|
1017
669
|
return { bolt11, paymentHash: issued.paymentHash, expiresAt: issued.expiresAt };
|
|
1018
670
|
}
|
|
1019
671
|
async function nwcHoldInvoice(connection, held, timeoutMs = DEFAULT_ASK_TIMEOUT_MS) {
|
|
1020
|
-
const
|
|
672
|
+
const answered = await askWallet(
|
|
1021
673
|
connection,
|
|
1022
674
|
"make_hold_invoice",
|
|
1023
675
|
{
|
|
@@ -1029,7 +681,7 @@ async function nwcHoldInvoice(connection, held, timeoutMs = DEFAULT_ASK_TIMEOUT_
|
|
|
1029
681
|
},
|
|
1030
682
|
timeoutMs
|
|
1031
683
|
);
|
|
1032
|
-
const bolt11 = String(
|
|
684
|
+
const bolt11 = String(answered.invoice ?? "");
|
|
1033
685
|
const issued = decodeInvoice(bolt11);
|
|
1034
686
|
if (issued.paymentHash === null || issued.expiresAt === null) {
|
|
1035
687
|
throw new WalletRefused("invoice-refused", "the nwc wallet held an invoice we cannot decode");
|
|
@@ -1049,13 +701,13 @@ async function nwcHoldInvoice(connection, held, timeoutMs = DEFAULT_ASK_TIMEOUT_
|
|
|
1049
701
|
return { bolt11, paymentHash: issued.paymentHash, expiresAt: issued.expiresAt };
|
|
1050
702
|
}
|
|
1051
703
|
async function nwcSettlement(connection, paymentHash, timeoutMs = DEFAULT_ASK_TIMEOUT_MS) {
|
|
1052
|
-
const
|
|
704
|
+
const answered = await askWallet(
|
|
1053
705
|
connection,
|
|
1054
706
|
"lookup_invoice",
|
|
1055
707
|
{ payment_hash: paymentHash },
|
|
1056
708
|
timeoutMs
|
|
1057
709
|
);
|
|
1058
|
-
const preimage =
|
|
710
|
+
const preimage = answered.preimage;
|
|
1059
711
|
if (typeof preimage !== "string" || preimage.length === 0) {
|
|
1060
712
|
return null;
|
|
1061
713
|
}
|
|
@@ -1069,39 +721,13 @@ async function nwcPay(connection, bolt11, timeoutMs = PAY_TIMEOUT_MS) {
|
|
|
1069
721
|
if (owed.paymentHash === null) {
|
|
1070
722
|
throw new WalletRefused("invoice-refused", "the invoice to pay carries no payment hash");
|
|
1071
723
|
}
|
|
1072
|
-
const
|
|
1073
|
-
const preimage =
|
|
724
|
+
const answered = await askWallet(connection, "pay_invoice", { invoice: bolt11 }, timeoutMs);
|
|
725
|
+
const preimage = answered.preimage;
|
|
1074
726
|
if (typeof preimage !== "string" || !preimageMatchesHash(preimage, owed.paymentHash)) {
|
|
1075
727
|
throw new Error(`the nwc wallet paid and returned no preimage hashing to ${owed.paymentHash}`);
|
|
1076
728
|
}
|
|
1077
729
|
return preimage;
|
|
1078
730
|
}
|
|
1079
|
-
function nwcVerifyEndpoint(config) {
|
|
1080
|
-
const paced = {
|
|
1081
|
-
"cache-control": `max-age=${config.pollEverySecs ?? DEFAULT_POLL_EVERY_SECS}`
|
|
1082
|
-
};
|
|
1083
|
-
return async (request) => {
|
|
1084
|
-
const consented = await answerVerifyChallenge(request);
|
|
1085
|
-
if (consented !== null) {
|
|
1086
|
-
return consented;
|
|
1087
|
-
}
|
|
1088
|
-
const sealed = new URL(request.url).searchParams.get(HASH);
|
|
1089
|
-
if (sealed === null) {
|
|
1090
|
-
return Response.json({ settled: false }, { status: 400 });
|
|
1091
|
-
}
|
|
1092
|
-
const paymentHash = await unsealFor("nwc-verify", config.secret, sealed);
|
|
1093
|
-
if (paymentHash === null) {
|
|
1094
|
-
return Response.json({ settled: false }, { status: 403 });
|
|
1095
|
-
}
|
|
1096
|
-
const preimage = await nwcSettlement(config.connection, paymentHash, config.askTimeoutMs).catch(
|
|
1097
|
-
() => void 0
|
|
1098
|
-
);
|
|
1099
|
-
if (preimage === void 0) {
|
|
1100
|
-
return Response.json({ settled: false }, { status: 502 });
|
|
1101
|
-
}
|
|
1102
|
-
return Response.json({ settled: preimage !== null, preimage }, { headers: paced });
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
731
|
async function nwcVerifyUrl(endpoint, paymentHash, secret) {
|
|
1106
732
|
const sealed = new URL(endpoint);
|
|
1107
733
|
sealed.searchParams.set(HASH, await sealFor("nwc-verify", secret, paymentHash));
|
|
@@ -1151,7 +777,7 @@ async function overFirstRelayThatAnswers(connection, request, timeoutMs) {
|
|
|
1151
777
|
throw new WalletRefused("unreachable", `no nwc relay answered: ${String(lastFailure)}`);
|
|
1152
778
|
}
|
|
1153
779
|
function overRelay(relay, connection, request, deadline) {
|
|
1154
|
-
return new Promise((
|
|
780
|
+
return new Promise((resolve, reject) => {
|
|
1155
781
|
const socket = new WebSocket(relay);
|
|
1156
782
|
const subscription = request.id.slice(0, 16);
|
|
1157
783
|
const expired = () => {
|
|
@@ -1195,7 +821,7 @@ function overRelay(relay, connection, request, deadline) {
|
|
|
1195
821
|
return;
|
|
1196
822
|
}
|
|
1197
823
|
close();
|
|
1198
|
-
|
|
824
|
+
resolve(answer);
|
|
1199
825
|
});
|
|
1200
826
|
});
|
|
1201
827
|
}
|
|
@@ -1241,44 +867,6 @@ function isSecureRelay(relay) {
|
|
|
1241
867
|
return false;
|
|
1242
868
|
}
|
|
1243
869
|
}
|
|
1244
|
-
function nwcRail(gateway, config) {
|
|
1245
|
-
return async (order) => {
|
|
1246
|
-
const invoice = await nwcInvoice(
|
|
1247
|
-
config.connection,
|
|
1248
|
-
await msatForOrder(order, config.amount, config.rate),
|
|
1249
|
-
config.description?.(order) ?? order.reference
|
|
1250
|
-
);
|
|
1251
|
-
const watched = await gateway.watch({
|
|
1252
|
-
paymentHash: invoice.paymentHash,
|
|
1253
|
-
verifyUrl: await nwcVerifyUrl(
|
|
1254
|
-
config.verifyThrough.endpoint,
|
|
1255
|
-
invoice.paymentHash,
|
|
1256
|
-
config.verifyThrough.secret
|
|
1257
|
-
),
|
|
1258
|
-
expiresAt: invoice.expiresAt,
|
|
1259
|
-
trigger: config.trigger,
|
|
1260
|
-
replay: config.replay,
|
|
1261
|
-
sealed: config.sealed ? await seal(
|
|
1262
|
-
config.sealed.secret,
|
|
1263
|
-
JSON.stringify(config.sealed.data(order)),
|
|
1264
|
-
invoice.paymentHash
|
|
1265
|
-
) : void 0,
|
|
1266
|
-
webhookUrl: config.webhookUrl
|
|
1267
|
-
});
|
|
1268
|
-
return {
|
|
1269
|
-
id: watched.id,
|
|
1270
|
-
paymentHash: invoice.paymentHash,
|
|
1271
|
-
rail: config.name ?? "lightning",
|
|
1272
|
-
scan: invoice.bolt11,
|
|
1273
|
-
qr: toLightningUri(invoice.bolt11),
|
|
1274
|
-
expiresAt: invoice.expiresAt
|
|
1275
|
-
};
|
|
1276
|
-
};
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
|
-
// src/entry/nwc.ts
|
|
1280
|
-
var nwcRail2 = nwcRail;
|
|
1281
|
-
var nwcVerifyEndpoint2 = nwcVerifyEndpoint;
|
|
1282
870
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1283
871
|
0 && (module.exports = {
|
|
1284
872
|
askWallet,
|
|
@@ -1286,8 +874,6 @@ var nwcVerifyEndpoint2 = nwcVerifyEndpoint;
|
|
|
1286
874
|
nwcHoldInvoice,
|
|
1287
875
|
nwcInvoice,
|
|
1288
876
|
nwcPay,
|
|
1289
|
-
nwcRail,
|
|
1290
877
|
nwcSettlement,
|
|
1291
|
-
nwcVerifyEndpoint,
|
|
1292
878
|
nwcVerifyUrl
|
|
1293
879
|
});
|
package/dist/nwc.d.cts
CHANGED
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
|
|
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
|
-
import './qr-CF-YeXU1.cjs';
|
|
1
|
+
export { N as NwcConnection, a1 as NwcInvoice, l as NwcRailConfig, m as NwcVerifyConfig, a2 as askWallet, a3 as nwcConnection, a4 as nwcHoldInvoice, a5 as nwcInvoice, a6 as nwcPay, a7 as nwcSettlement, a8 as nwcVerifyUrl } from './nwc-h_-cGvMb.cjs';
|
|
4
2
|
import './types-DYZ9EkmJ.cjs';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* The NWC rail as a free function, which is how it was reached before 2.2.0
|
|
8
|
-
*
|
|
9
|
-
* @deprecated Use `gateway.rails.nwc(config)`, the same rail beside every other one
|
|
10
|
-
*/
|
|
11
|
-
declare const nwcRail: typeof nwcRail$1;
|
|
12
|
-
/**
|
|
13
|
-
* The NWC verify endpoint as a free function, which is how it was reached before 2.2.0
|
|
14
|
-
*
|
|
15
|
-
* @deprecated Use `gateway.serve.nwcVerify(config)`, beside every other verify endpoint
|
|
16
|
-
*/
|
|
17
|
-
declare const nwcVerifyEndpoint: typeof nwcVerifyEndpoint$1;
|
|
18
|
-
|
|
19
|
-
export { nwcRail, nwcVerifyEndpoint };
|
|
3
|
+
import './qr-CF-YeXU1.cjs';
|
package/dist/nwc.d.ts
CHANGED
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
|
|
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
|
-
import './qr-CF-YeXU1.js';
|
|
1
|
+
export { N as NwcConnection, a1 as NwcInvoice, l as NwcRailConfig, m as NwcVerifyConfig, a2 as askWallet, a3 as nwcConnection, a4 as nwcHoldInvoice, a5 as nwcInvoice, a6 as nwcPay, a7 as nwcSettlement, a8 as nwcVerifyUrl } from './nwc-BivXGSPY.js';
|
|
4
2
|
import './types-DYZ9EkmJ.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* The NWC rail as a free function, which is how it was reached before 2.2.0
|
|
8
|
-
*
|
|
9
|
-
* @deprecated Use `gateway.rails.nwc(config)`, the same rail beside every other one
|
|
10
|
-
*/
|
|
11
|
-
declare const nwcRail: typeof nwcRail$1;
|
|
12
|
-
/**
|
|
13
|
-
* The NWC verify endpoint as a free function, which is how it was reached before 2.2.0
|
|
14
|
-
*
|
|
15
|
-
* @deprecated Use `gateway.serve.nwcVerify(config)`, beside every other verify endpoint
|
|
16
|
-
*/
|
|
17
|
-
declare const nwcVerifyEndpoint: typeof nwcVerifyEndpoint$1;
|
|
18
|
-
|
|
19
|
-
export { nwcRail, nwcVerifyEndpoint };
|
|
3
|
+
import './qr-CF-YeXU1.js';
|