thunder-bridge 1.4.1 → 1.5.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/price.cjs ADDED
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/entry/price.ts
21
+ var price_exports = {};
22
+ __export(price_exports, {
23
+ AmountError: () => AmountError,
24
+ bitstamp: () => bitstamp,
25
+ coinbase: () => coinbase,
26
+ coinmate: () => coinmate,
27
+ kraken: () => kraken,
28
+ medianOf: () => medianOf,
29
+ minorScaleOf: () => minorScaleOf,
30
+ minorUnitsOf: () => minorUnitsOf,
31
+ msatFor: () => msatFor
32
+ });
33
+ module.exports = __toCommonJS(price_exports);
34
+
35
+ // src/errors.ts
36
+ var AmountError = class extends Error {
37
+ /**
38
+ * Whether a failure is one of these, without asking whether it is this exact
39
+ * class. Every entry point carries its own copy of the class, so a price
40
+ * refused inside `thunder-bridge/price` is not `instanceof` the `AmountError`
41
+ * imported from `thunder-bridge`. The name and the code are the same in every
42
+ * copy, so this holds where `instanceof` does not
43
+ */
44
+ static is(failure) {
45
+ return failure instanceof Error && failure.name === "AmountError" && "code" in failure;
46
+ }
47
+ code;
48
+ constructor(code, detail) {
49
+ super(`${code}: ${detail}`);
50
+ this.name = "AmountError";
51
+ this.code = code;
52
+ }
53
+ };
54
+
55
+ // src/currency.ts
56
+ var ISO_4217_MINOR_UNITS = {
57
+ BHD: 3,
58
+ BIF: 0,
59
+ CLP: 0,
60
+ CZK: 2,
61
+ DJF: 0,
62
+ EUR: 2,
63
+ GBP: 2,
64
+ ISK: 0,
65
+ IQD: 3,
66
+ JOD: 3,
67
+ JPY: 0,
68
+ KRW: 0,
69
+ KWD: 3,
70
+ LYD: 3,
71
+ OMR: 3,
72
+ PLN: 2,
73
+ PYG: 0,
74
+ RWF: 0,
75
+ TND: 3,
76
+ UGX: 0,
77
+ USD: 2,
78
+ VND: 0,
79
+ VUV: 0,
80
+ XAF: 0,
81
+ XOF: 0,
82
+ XPF: 0
83
+ };
84
+ function minorUnitsOf(currency) {
85
+ const digits = ISO_4217_MINOR_UNITS[currency.toUpperCase()];
86
+ if (digits === void 0) {
87
+ throw new AmountError(
88
+ "unknown-currency",
89
+ `${currency.toUpperCase()} is not a currency this knows the ISO 4217 minor unit of`
90
+ );
91
+ }
92
+ return digits;
93
+ }
94
+ function minorScaleOf(currency) {
95
+ return 10 ** minorUnitsOf(currency);
96
+ }
97
+
98
+ // src/price.ts
99
+ var MSAT_PER_BTC = 1e11;
100
+ var BASIS_POINTS = 1e4;
101
+ var DEFAULT_HOLD_SECS = 60;
102
+ var DEFAULT_MIN_VENUES = 2;
103
+ var DEFAULT_MAX_SPREAD_BPS = 200;
104
+ var MILLIS = 1e3;
105
+ function coinbase(baseUrl = "https://api.coinbase.com") {
106
+ return async (currency) => {
107
+ const asked = currency.toUpperCase();
108
+ const answer = await asJson(`${baseUrl}/v2/prices/BTC-${asked}/spot`, "coinbase");
109
+ const quoted = answer.data;
110
+ if (quoted?.currency !== asked) {
111
+ throw new Error(`coinbase quoted ${quoted?.currency} when asked for ${asked}`);
112
+ }
113
+ return asMinor(quoted.amount, "coinbase", asked);
114
+ };
115
+ }
116
+ function kraken(baseUrl = "https://api.kraken.com") {
117
+ return async (currency) => {
118
+ const asked = currency.toUpperCase();
119
+ const answer = await asJson(`${baseUrl}/0/public/Ticker?pair=XBT${asked}`, "kraken");
120
+ if (answer.error?.length) {
121
+ throw new Error(`kraken refused XBT${asked}: ${answer.error[0]}`);
122
+ }
123
+ const pairs = Object.values(answer.result ?? {});
124
+ if (pairs.length !== 1) {
125
+ throw new Error(`kraken answered ${pairs.length} pairs for XBT${asked}`);
126
+ }
127
+ return asMinor(pairs[0]?.c?.[0], "kraken", asked);
128
+ };
129
+ }
130
+ function bitstamp(baseUrl = "https://www.bitstamp.net") {
131
+ return async (currency) => {
132
+ const pair = `btc${currency.toLowerCase()}`;
133
+ const answer = await asJson(`${baseUrl}/api/v2/ticker/${pair}/`, "bitstamp");
134
+ if (Array.isArray(answer)) {
135
+ throw new Error(`bitstamp has no ${pair} pair`);
136
+ }
137
+ return asMinor(answer.last, "bitstamp", currency);
138
+ };
139
+ }
140
+ function coinmate(baseUrl = "https://coinmate.io") {
141
+ return async (currency) => {
142
+ const pair = `BTC_${currency.toUpperCase()}`;
143
+ const answer = await asJson(`${baseUrl}/api/ticker?currencyPair=${pair}`, "coinmate");
144
+ if (answer.error !== false) {
145
+ throw new Error(`coinmate refused ${pair}: ${answer.errorMessage ?? "no reason"}`);
146
+ }
147
+ return asMinor(answer.data?.last, "coinmate", currency);
148
+ };
149
+ }
150
+ function medianOf(tickers = [coinbase(), kraken(), bitstamp(), coinmate()], options = {}) {
151
+ const holdFor = (options.holdForSecs ?? DEFAULT_HOLD_SECS) * MILLIS;
152
+ const minVenues = options.minVenues ?? DEFAULT_MIN_VENUES;
153
+ const maxSpread = options.maxSpreadBps ?? DEFAULT_MAX_SPREAD_BPS;
154
+ const held = /* @__PURE__ */ new Map();
155
+ return async (currency) => {
156
+ const asked = currency.toUpperCase();
157
+ const remembered = held.get(asked);
158
+ if (remembered && Date.now() - remembered.at < holdFor) {
159
+ return remembered.price;
160
+ }
161
+ const answers = await Promise.allSettled(tickers.map((ticker) => ticker(asked)));
162
+ const quoted = answers.filter((answer) => answer.status === "fulfilled").map((answer) => answer.value).sort((one, other) => one - other);
163
+ if (quoted.length < minVenues) {
164
+ throw new Error(
165
+ `${quoted.length} of ${tickers.length} venues quoted ${asked}, ${minVenues} wanted: ${refusals(answers)}`
166
+ );
167
+ }
168
+ const spread = spreadBpsOf(quoted);
169
+ if (spread > maxSpread) {
170
+ throw new Error(
171
+ `venues disagree on ${asked} by ${Math.round(spread)} bps, more than the ${maxSpread} allowed: ${quoted.join(", ")}`
172
+ );
173
+ }
174
+ const price = middleOf(quoted);
175
+ held.set(asked, { at: Date.now(), price });
176
+ return price;
177
+ };
178
+ }
179
+ function msatFor(amountMinor, priceMinorPerBtc, options = {}) {
180
+ if (!Number.isInteger(amountMinor) || amountMinor <= 0) {
181
+ throw new Error("amountMinor must be a whole number of minor units above zero");
182
+ }
183
+ if (!Number.isFinite(priceMinorPerBtc) || priceMinorPerBtc <= 0) {
184
+ throw new Error(`${priceMinorPerBtc} is not a usable price`);
185
+ }
186
+ const spreadBps = options.spreadBps ?? 0;
187
+ if (!Number.isInteger(spreadBps) || spreadBps < 0) {
188
+ throw new Error("spreadBps must be a whole number of basis points, none or more");
189
+ }
190
+ const wanted = BigInt(amountMinor) * BigInt(MSAT_PER_BTC) * BigInt(BASIS_POINTS + spreadBps);
191
+ const per = BigInt(Math.round(priceMinorPerBtc)) * BigInt(BASIS_POINTS);
192
+ const msat = (wanted + per - 1n) / per;
193
+ if (msat > BigInt(Number.MAX_SAFE_INTEGER)) {
194
+ throw new Error(`${amountMinor} at ${priceMinorPerBtc} is more millisatoshi than exist`);
195
+ }
196
+ return Number(msat);
197
+ }
198
+ async function asJson(url, venue) {
199
+ const answer = await fetch(url, { headers: { accept: "application/json" } });
200
+ if (!answer.ok) {
201
+ throw new Error(`${venue} answered ${answer.status}`);
202
+ }
203
+ return answer.json();
204
+ }
205
+ function asMinor(quoted, venue, currency) {
206
+ const major = Number(quoted);
207
+ if (!Number.isFinite(major) || major <= 0) {
208
+ throw new Error(`${venue} quoted ${String(quoted)} for ${currency.toUpperCase()}`);
209
+ }
210
+ return Math.round(major * minorScaleOf(currency));
211
+ }
212
+ function spreadBpsOf(sorted) {
213
+ const lowest = sorted[0] ?? 0;
214
+ const highest = sorted[sorted.length - 1] ?? 0;
215
+ return lowest === 0 ? Number.POSITIVE_INFINITY : (highest - lowest) / lowest * BASIS_POINTS;
216
+ }
217
+ function middleOf(sorted) {
218
+ const middle = Math.floor(sorted.length / 2);
219
+ if (sorted.length % 2 === 1) {
220
+ return sorted[middle] ?? 0;
221
+ }
222
+ return Math.round(((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2);
223
+ }
224
+ function refusals(answers) {
225
+ return answers.filter((answer) => answer.status === "rejected").map(
226
+ (answer) => answer.reason instanceof Error ? answer.reason.message : String(answer.reason)
227
+ ).join("; ");
228
+ }
229
+ // Annotate the CommonJS export names for ESM import in node:
230
+ 0 && (module.exports = {
231
+ AmountError,
232
+ bitstamp,
233
+ coinbase,
234
+ coinmate,
235
+ kraken,
236
+ medianOf,
237
+ minorScaleOf,
238
+ minorUnitsOf,
239
+ msatFor
240
+ });
@@ -0,0 +1,17 @@
1
+ export { A as AmountError, a as AmountFault } from './errors-Dmh-Uoh8.cjs';
2
+ export { M as MedianOptions, T as Ticker, b as bitstamp, c as coinbase, a as coinmate, k as kraken, m as medianOf, d as msatFor } from './types-BNPmVnA7.cjs';
3
+
4
+ /**
5
+ * How many digits ISO 4217 gives the currency's minor unit, so 2 for a crown and a
6
+ * euro, 0 for a yen and 3 for a dinar.
7
+ *
8
+ * There is no sane default here, which is why an unlisted code throws rather than
9
+ * being treated as two. Assuming two turns 1000 yen into 10 and a dinar into a
10
+ * tenth of itself, and a payment library that guesses at this is a payment library
11
+ * that moves the wrong amount.
12
+ */
13
+ declare function minorUnitsOf(currency: string): number;
14
+ /** The scale that minor unit implies, so 100 for a crown, 1 for a yen, 1000 for a dinar */
15
+ declare function minorScaleOf(currency: string): number;
16
+
17
+ export { minorScaleOf, minorUnitsOf };
@@ -0,0 +1,17 @@
1
+ export { A as AmountError, a as AmountFault } from './errors-0vbVoISA.js';
2
+ export { M as MedianOptions, T as Ticker, b as bitstamp, c as coinbase, a as coinmate, k as kraken, m as medianOf, d as msatFor } from './types-BNPmVnA7.js';
3
+
4
+ /**
5
+ * How many digits ISO 4217 gives the currency's minor unit, so 2 for a crown and a
6
+ * euro, 0 for a yen and 3 for a dinar.
7
+ *
8
+ * There is no sane default here, which is why an unlisted code throws rather than
9
+ * being treated as two. Assuming two turns 1000 yen into 10 and a dinar into a
10
+ * tenth of itself, and a payment library that guesses at this is a payment library
11
+ * that moves the wrong amount.
12
+ */
13
+ declare function minorUnitsOf(currency: string): number;
14
+ /** The scale that minor unit implies, so 100 for a crown, 1 for a yen, 1000 for a dinar */
15
+ declare function minorScaleOf(currency: string): number;
16
+
17
+ export { minorScaleOf, minorUnitsOf };
package/dist/price.js ADDED
@@ -0,0 +1,205 @@
1
+ // src/errors.ts
2
+ var AmountError = class extends Error {
3
+ /**
4
+ * Whether a failure is one of these, without asking whether it is this exact
5
+ * class. Every entry point carries its own copy of the class, so a price
6
+ * refused inside `thunder-bridge/price` is not `instanceof` the `AmountError`
7
+ * imported from `thunder-bridge`. The name and the code are the same in every
8
+ * copy, so this holds where `instanceof` does not
9
+ */
10
+ static is(failure) {
11
+ return failure instanceof Error && failure.name === "AmountError" && "code" in failure;
12
+ }
13
+ code;
14
+ constructor(code, detail) {
15
+ super(`${code}: ${detail}`);
16
+ this.name = "AmountError";
17
+ this.code = code;
18
+ }
19
+ };
20
+
21
+ // src/currency.ts
22
+ var ISO_4217_MINOR_UNITS = {
23
+ BHD: 3,
24
+ BIF: 0,
25
+ CLP: 0,
26
+ CZK: 2,
27
+ DJF: 0,
28
+ EUR: 2,
29
+ GBP: 2,
30
+ ISK: 0,
31
+ IQD: 3,
32
+ JOD: 3,
33
+ JPY: 0,
34
+ KRW: 0,
35
+ KWD: 3,
36
+ LYD: 3,
37
+ OMR: 3,
38
+ PLN: 2,
39
+ PYG: 0,
40
+ RWF: 0,
41
+ TND: 3,
42
+ UGX: 0,
43
+ USD: 2,
44
+ VND: 0,
45
+ VUV: 0,
46
+ XAF: 0,
47
+ XOF: 0,
48
+ XPF: 0
49
+ };
50
+ function minorUnitsOf(currency) {
51
+ const digits = ISO_4217_MINOR_UNITS[currency.toUpperCase()];
52
+ if (digits === void 0) {
53
+ throw new AmountError(
54
+ "unknown-currency",
55
+ `${currency.toUpperCase()} is not a currency this knows the ISO 4217 minor unit of`
56
+ );
57
+ }
58
+ return digits;
59
+ }
60
+ function minorScaleOf(currency) {
61
+ return 10 ** minorUnitsOf(currency);
62
+ }
63
+
64
+ // src/price.ts
65
+ var MSAT_PER_BTC = 1e11;
66
+ var BASIS_POINTS = 1e4;
67
+ var DEFAULT_HOLD_SECS = 60;
68
+ var DEFAULT_MIN_VENUES = 2;
69
+ var DEFAULT_MAX_SPREAD_BPS = 200;
70
+ var MILLIS = 1e3;
71
+ function coinbase(baseUrl = "https://api.coinbase.com") {
72
+ return async (currency) => {
73
+ const asked = currency.toUpperCase();
74
+ const answer = await asJson(`${baseUrl}/v2/prices/BTC-${asked}/spot`, "coinbase");
75
+ const quoted = answer.data;
76
+ if (quoted?.currency !== asked) {
77
+ throw new Error(`coinbase quoted ${quoted?.currency} when asked for ${asked}`);
78
+ }
79
+ return asMinor(quoted.amount, "coinbase", asked);
80
+ };
81
+ }
82
+ function kraken(baseUrl = "https://api.kraken.com") {
83
+ return async (currency) => {
84
+ const asked = currency.toUpperCase();
85
+ const answer = await asJson(`${baseUrl}/0/public/Ticker?pair=XBT${asked}`, "kraken");
86
+ if (answer.error?.length) {
87
+ throw new Error(`kraken refused XBT${asked}: ${answer.error[0]}`);
88
+ }
89
+ const pairs = Object.values(answer.result ?? {});
90
+ if (pairs.length !== 1) {
91
+ throw new Error(`kraken answered ${pairs.length} pairs for XBT${asked}`);
92
+ }
93
+ return asMinor(pairs[0]?.c?.[0], "kraken", asked);
94
+ };
95
+ }
96
+ function bitstamp(baseUrl = "https://www.bitstamp.net") {
97
+ return async (currency) => {
98
+ const pair = `btc${currency.toLowerCase()}`;
99
+ const answer = await asJson(`${baseUrl}/api/v2/ticker/${pair}/`, "bitstamp");
100
+ if (Array.isArray(answer)) {
101
+ throw new Error(`bitstamp has no ${pair} pair`);
102
+ }
103
+ return asMinor(answer.last, "bitstamp", currency);
104
+ };
105
+ }
106
+ function coinmate(baseUrl = "https://coinmate.io") {
107
+ return async (currency) => {
108
+ const pair = `BTC_${currency.toUpperCase()}`;
109
+ const answer = await asJson(`${baseUrl}/api/ticker?currencyPair=${pair}`, "coinmate");
110
+ if (answer.error !== false) {
111
+ throw new Error(`coinmate refused ${pair}: ${answer.errorMessage ?? "no reason"}`);
112
+ }
113
+ return asMinor(answer.data?.last, "coinmate", currency);
114
+ };
115
+ }
116
+ function medianOf(tickers = [coinbase(), kraken(), bitstamp(), coinmate()], options = {}) {
117
+ const holdFor = (options.holdForSecs ?? DEFAULT_HOLD_SECS) * MILLIS;
118
+ const minVenues = options.minVenues ?? DEFAULT_MIN_VENUES;
119
+ const maxSpread = options.maxSpreadBps ?? DEFAULT_MAX_SPREAD_BPS;
120
+ const held = /* @__PURE__ */ new Map();
121
+ return async (currency) => {
122
+ const asked = currency.toUpperCase();
123
+ const remembered = held.get(asked);
124
+ if (remembered && Date.now() - remembered.at < holdFor) {
125
+ return remembered.price;
126
+ }
127
+ const answers = await Promise.allSettled(tickers.map((ticker) => ticker(asked)));
128
+ const quoted = answers.filter((answer) => answer.status === "fulfilled").map((answer) => answer.value).sort((one, other) => one - other);
129
+ if (quoted.length < minVenues) {
130
+ throw new Error(
131
+ `${quoted.length} of ${tickers.length} venues quoted ${asked}, ${minVenues} wanted: ${refusals(answers)}`
132
+ );
133
+ }
134
+ const spread = spreadBpsOf(quoted);
135
+ if (spread > maxSpread) {
136
+ throw new Error(
137
+ `venues disagree on ${asked} by ${Math.round(spread)} bps, more than the ${maxSpread} allowed: ${quoted.join(", ")}`
138
+ );
139
+ }
140
+ const price = middleOf(quoted);
141
+ held.set(asked, { at: Date.now(), price });
142
+ return price;
143
+ };
144
+ }
145
+ function msatFor(amountMinor, priceMinorPerBtc, options = {}) {
146
+ if (!Number.isInteger(amountMinor) || amountMinor <= 0) {
147
+ throw new Error("amountMinor must be a whole number of minor units above zero");
148
+ }
149
+ if (!Number.isFinite(priceMinorPerBtc) || priceMinorPerBtc <= 0) {
150
+ throw new Error(`${priceMinorPerBtc} is not a usable price`);
151
+ }
152
+ const spreadBps = options.spreadBps ?? 0;
153
+ if (!Number.isInteger(spreadBps) || spreadBps < 0) {
154
+ throw new Error("spreadBps must be a whole number of basis points, none or more");
155
+ }
156
+ const wanted = BigInt(amountMinor) * BigInt(MSAT_PER_BTC) * BigInt(BASIS_POINTS + spreadBps);
157
+ const per = BigInt(Math.round(priceMinorPerBtc)) * BigInt(BASIS_POINTS);
158
+ const msat = (wanted + per - 1n) / per;
159
+ if (msat > BigInt(Number.MAX_SAFE_INTEGER)) {
160
+ throw new Error(`${amountMinor} at ${priceMinorPerBtc} is more millisatoshi than exist`);
161
+ }
162
+ return Number(msat);
163
+ }
164
+ async function asJson(url, venue) {
165
+ const answer = await fetch(url, { headers: { accept: "application/json" } });
166
+ if (!answer.ok) {
167
+ throw new Error(`${venue} answered ${answer.status}`);
168
+ }
169
+ return answer.json();
170
+ }
171
+ function asMinor(quoted, venue, currency) {
172
+ const major = Number(quoted);
173
+ if (!Number.isFinite(major) || major <= 0) {
174
+ throw new Error(`${venue} quoted ${String(quoted)} for ${currency.toUpperCase()}`);
175
+ }
176
+ return Math.round(major * minorScaleOf(currency));
177
+ }
178
+ function spreadBpsOf(sorted) {
179
+ const lowest = sorted[0] ?? 0;
180
+ const highest = sorted[sorted.length - 1] ?? 0;
181
+ return lowest === 0 ? Number.POSITIVE_INFINITY : (highest - lowest) / lowest * BASIS_POINTS;
182
+ }
183
+ function middleOf(sorted) {
184
+ const middle = Math.floor(sorted.length / 2);
185
+ if (sorted.length % 2 === 1) {
186
+ return sorted[middle] ?? 0;
187
+ }
188
+ return Math.round(((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2);
189
+ }
190
+ function refusals(answers) {
191
+ return answers.filter((answer) => answer.status === "rejected").map(
192
+ (answer) => answer.reason instanceof Error ? answer.reason.message : String(answer.reason)
193
+ ).join("; ");
194
+ }
195
+ export {
196
+ AmountError,
197
+ bitstamp,
198
+ coinbase,
199
+ coinmate,
200
+ kraken,
201
+ medianOf,
202
+ minorScaleOf,
203
+ minorUnitsOf,
204
+ msatFor
205
+ };
@@ -0,0 +1,55 @@
1
+ /** An invoice a lightning address issued, with everything needed to watch and to prove it */
2
+ type Resolved = {
3
+ address: string;
4
+ bolt11: string;
5
+ verifyUrl: string;
6
+ paymentHash: string;
7
+ expiresAt: number;
8
+ };
9
+ /**
10
+ * Bech32-encode a pay endpoint as the `LNURL1` string LUD-01 defines, uppercase
11
+ * because that is the form it asks a QR to carry. An onion endpoint is http
12
+ * rather than https, which LUD-17 spells out, so both are taken here
13
+ */
14
+ declare function toLnurl(endpoint: string): string;
15
+
16
+ /** How the QR is drawn, which is the only thing about it worth configuring */
17
+ interface QrOptions {
18
+ /** SVG width and height in pixels, defaults to 256 */
19
+ size?: number;
20
+ /** Dark module color, defaults to `#000` */
21
+ color?: string;
22
+ }
23
+ /** Render a BOLT11 invoice or a lightning address as an SVG QR code */
24
+ declare function invoiceToSvg(destination: string, options?: QrOptions): string;
25
+ /** SVG data URL for an `<img>` `src` */
26
+ declare function invoiceToDataUrl(destination: string, options?: QrOptions): string;
27
+ /**
28
+ * Render your own LNURL-pay endpoint, the URL `serve.lnurlPay` is mounted on, as
29
+ * the QR a payer scans. It takes the endpoint URL and does the bech32 itself, so
30
+ * do not hand it the output of `toLnurl` - that is what this calls for you.
31
+ *
32
+ * Nothing is minted and nothing expires, so this is the code a tip jar prints
33
+ * once and an overlay shows all stream
34
+ */
35
+ declare function lnurlEndpointToSvg(endpoint: string, options?: QrOptions): string;
36
+ /** SVG data URL of the endpoint's QR, for an `<img>` `src` */
37
+ declare function lnurlEndpointToDataUrl(endpoint: string, options?: QrOptions): string;
38
+ /**
39
+ * Render the `spd` from `bankTransfer` as the QR a Czech banking app scans. The
40
+ * payload is a Short Payment Descriptor, so it carries the account, the amount
41
+ * and the reference the payer must leave on the transfer
42
+ */
43
+ declare function spdToSvg(spd: string, options?: QrOptions): string;
44
+ /** SVG data URL of the bank transfer's QR, for an `<img>` `src` */
45
+ declare function spdToDataUrl(spd: string, options?: QrOptions): string;
46
+ /**
47
+ * Render any rail's `Leg.qr` as an SVG QR code. Each rail states its own payload,
48
+ * a BOLT11 invoice under the `LIGHTNING` scheme or a Short Payment Descriptor as
49
+ * it stands, so this draws a leg without being told which rail made it
50
+ */
51
+ declare function qrToSvg(payload: string, options?: QrOptions): string;
52
+ /** SVG data URL of a leg's QR, for an `<img>` `src` */
53
+ declare function qrToDataUrl(payload: string, options?: QrOptions): string;
54
+
55
+ export { type QrOptions as Q, type Resolved as R, invoiceToSvg as a, lnurlEndpointToSvg as b, qrToSvg as c, spdToSvg as d, invoiceToDataUrl as i, lnurlEndpointToDataUrl as l, qrToDataUrl as q, spdToDataUrl as s, toLnurl as t };
@@ -0,0 +1,55 @@
1
+ /** An invoice a lightning address issued, with everything needed to watch and to prove it */
2
+ type Resolved = {
3
+ address: string;
4
+ bolt11: string;
5
+ verifyUrl: string;
6
+ paymentHash: string;
7
+ expiresAt: number;
8
+ };
9
+ /**
10
+ * Bech32-encode a pay endpoint as the `LNURL1` string LUD-01 defines, uppercase
11
+ * because that is the form it asks a QR to carry. An onion endpoint is http
12
+ * rather than https, which LUD-17 spells out, so both are taken here
13
+ */
14
+ declare function toLnurl(endpoint: string): string;
15
+
16
+ /** How the QR is drawn, which is the only thing about it worth configuring */
17
+ interface QrOptions {
18
+ /** SVG width and height in pixels, defaults to 256 */
19
+ size?: number;
20
+ /** Dark module color, defaults to `#000` */
21
+ color?: string;
22
+ }
23
+ /** Render a BOLT11 invoice or a lightning address as an SVG QR code */
24
+ declare function invoiceToSvg(destination: string, options?: QrOptions): string;
25
+ /** SVG data URL for an `<img>` `src` */
26
+ declare function invoiceToDataUrl(destination: string, options?: QrOptions): string;
27
+ /**
28
+ * Render your own LNURL-pay endpoint, the URL `serve.lnurlPay` is mounted on, as
29
+ * the QR a payer scans. It takes the endpoint URL and does the bech32 itself, so
30
+ * do not hand it the output of `toLnurl` - that is what this calls for you.
31
+ *
32
+ * Nothing is minted and nothing expires, so this is the code a tip jar prints
33
+ * once and an overlay shows all stream
34
+ */
35
+ declare function lnurlEndpointToSvg(endpoint: string, options?: QrOptions): string;
36
+ /** SVG data URL of the endpoint's QR, for an `<img>` `src` */
37
+ declare function lnurlEndpointToDataUrl(endpoint: string, options?: QrOptions): string;
38
+ /**
39
+ * Render the `spd` from `bankTransfer` as the QR a Czech banking app scans. The
40
+ * payload is a Short Payment Descriptor, so it carries the account, the amount
41
+ * and the reference the payer must leave on the transfer
42
+ */
43
+ declare function spdToSvg(spd: string, options?: QrOptions): string;
44
+ /** SVG data URL of the bank transfer's QR, for an `<img>` `src` */
45
+ declare function spdToDataUrl(spd: string, options?: QrOptions): string;
46
+ /**
47
+ * Render any rail's `Leg.qr` as an SVG QR code. Each rail states its own payload,
48
+ * a BOLT11 invoice under the `LIGHTNING` scheme or a Short Payment Descriptor as
49
+ * it stands, so this draws a leg without being told which rail made it
50
+ */
51
+ declare function qrToSvg(payload: string, options?: QrOptions): string;
52
+ /** SVG data URL of a leg's QR, for an `<img>` `src` */
53
+ declare function qrToDataUrl(payload: string, options?: QrOptions): string;
54
+
55
+ export { type QrOptions as Q, type Resolved as R, invoiceToSvg as a, lnurlEndpointToSvg as b, qrToSvg as c, spdToSvg as d, invoiceToDataUrl as i, lnurlEndpointToDataUrl as l, qrToDataUrl as q, spdToDataUrl as s, toLnurl as t };