thunder-bridge 1.4.2 → 2.1.1
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 +145 -133
- package/dist/bank-CEIuNhV_.d.cts +896 -0
- package/dist/bank-M8flcMks.d.ts +896 -0
- package/dist/bank.cjs +338 -0
- package/dist/bank.d.cts +46 -0
- package/dist/bank.d.ts +46 -0
- package/dist/bank.js +310 -0
- package/dist/errors-DriqW-lp.d.cts +126 -0
- package/dist/errors-ZEUT-MhO.d.ts +126 -0
- package/dist/index.cjs +2108 -1235
- package/dist/index.d.cts +13 -505
- package/dist/index.d.ts +13 -505
- package/dist/index.js +2088 -1193
- package/dist/{server.cjs → nwc.cjs} +376 -720
- package/dist/nwc.d.cts +122 -0
- package/dist/nwc.d.ts +122 -0
- package/dist/{server.js → nwc.js} +368 -695
- package/dist/price.cjs +240 -0
- package/dist/price.d.cts +17 -0
- package/dist/price.d.ts +17 -0
- package/dist/price.js +205 -0
- package/dist/qr-CF-YeXU1.d.cts +55 -0
- package/dist/qr-CF-YeXU1.d.ts +55 -0
- package/dist/qr.cjs +259 -0
- package/dist/qr.d.cts +1 -0
- package/dist/qr.d.ts +1 -0
- package/dist/qr.js +224 -0
- package/dist/types-DYZ9EkmJ.d.cts +261 -0
- package/dist/types-DYZ9EkmJ.d.ts +261 -0
- package/openapi.yaml +29 -39
- package/package.json +48 -9
- package/dist/rail-CqUfuYXJ.d.cts +0 -615
- package/dist/rail-CqUfuYXJ.d.ts +0 -615
- package/dist/server.d.cts +0 -145
- package/dist/server.d.ts +0 -145
package/dist/bank.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// ../core/bytes.ts
|
|
2
|
+
function bytesToHex(bytes) {
|
|
3
|
+
let hex = "";
|
|
4
|
+
for (const byte of bytes) {
|
|
5
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
6
|
+
}
|
|
7
|
+
return hex;
|
|
8
|
+
}
|
|
9
|
+
function hexToBytes(hex) {
|
|
10
|
+
const bytes = new Uint8Array(hex.length >> 1);
|
|
11
|
+
for (let at = 0; at < bytes.length; at++) {
|
|
12
|
+
bytes[at] = parseInt(hex.slice(at * 2, at * 2 + 2), 16);
|
|
13
|
+
}
|
|
14
|
+
return bytes;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ../core/ed25519.ts
|
|
18
|
+
var PKCS8_HEADER = hexToBytes("302e020100300506032b657004220420");
|
|
19
|
+
var SMALL_ORDER = [
|
|
20
|
+
"00".repeat(32),
|
|
21
|
+
`01${"00".repeat(31)}`,
|
|
22
|
+
"26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
|
|
23
|
+
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
|
|
24
|
+
`ec${"ff".repeat(30)}7f`,
|
|
25
|
+
`ed${"ff".repeat(30)}7f`,
|
|
26
|
+
`ee${"ff".repeat(30)}7f`
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// ../core/hmac.ts
|
|
30
|
+
async function hmacHex(secret, payload) {
|
|
31
|
+
const raw = typeof secret === "string" ? new TextEncoder().encode(secret) : secret;
|
|
32
|
+
const key = await crypto.subtle.importKey(
|
|
33
|
+
"raw",
|
|
34
|
+
asBuffer(raw),
|
|
35
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
36
|
+
false,
|
|
37
|
+
["sign"]
|
|
38
|
+
);
|
|
39
|
+
const message = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
|
|
40
|
+
return bytesToHex(new Uint8Array(await crypto.subtle.sign("HMAC", key, asBuffer(message))));
|
|
41
|
+
}
|
|
42
|
+
function asBuffer(bytes) {
|
|
43
|
+
return bytes instanceof Uint8Array && bytes.buffer instanceof ArrayBuffer ? bytes : new Uint8Array(bytes);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ../core/sha256.ts
|
|
47
|
+
var ROUND_CONSTANTS = new Uint32Array([
|
|
48
|
+
1116352408,
|
|
49
|
+
1899447441,
|
|
50
|
+
3049323471,
|
|
51
|
+
3921009573,
|
|
52
|
+
961987163,
|
|
53
|
+
1508970993,
|
|
54
|
+
2453635748,
|
|
55
|
+
2870763221,
|
|
56
|
+
3624381080,
|
|
57
|
+
310598401,
|
|
58
|
+
607225278,
|
|
59
|
+
1426881987,
|
|
60
|
+
1925078388,
|
|
61
|
+
2162078206,
|
|
62
|
+
2614888103,
|
|
63
|
+
3248222580,
|
|
64
|
+
3835390401,
|
|
65
|
+
4022224774,
|
|
66
|
+
264347078,
|
|
67
|
+
604807628,
|
|
68
|
+
770255983,
|
|
69
|
+
1249150122,
|
|
70
|
+
1555081692,
|
|
71
|
+
1996064986,
|
|
72
|
+
2554220882,
|
|
73
|
+
2821834349,
|
|
74
|
+
2952996808,
|
|
75
|
+
3210313671,
|
|
76
|
+
3336571891,
|
|
77
|
+
3584528711,
|
|
78
|
+
113926993,
|
|
79
|
+
338241895,
|
|
80
|
+
666307205,
|
|
81
|
+
773529912,
|
|
82
|
+
1294757372,
|
|
83
|
+
1396182291,
|
|
84
|
+
1695183700,
|
|
85
|
+
1986661051,
|
|
86
|
+
2177026350,
|
|
87
|
+
2456956037,
|
|
88
|
+
2730485921,
|
|
89
|
+
2820302411,
|
|
90
|
+
3259730800,
|
|
91
|
+
3345764771,
|
|
92
|
+
3516065817,
|
|
93
|
+
3600352804,
|
|
94
|
+
4094571909,
|
|
95
|
+
275423344,
|
|
96
|
+
430227734,
|
|
97
|
+
506948616,
|
|
98
|
+
659060556,
|
|
99
|
+
883997877,
|
|
100
|
+
958139571,
|
|
101
|
+
1322822218,
|
|
102
|
+
1537002063,
|
|
103
|
+
1747873779,
|
|
104
|
+
1955562222,
|
|
105
|
+
2024104815,
|
|
106
|
+
2227730452,
|
|
107
|
+
2361852424,
|
|
108
|
+
2428436474,
|
|
109
|
+
2756734187,
|
|
110
|
+
3204031479,
|
|
111
|
+
3329325298
|
|
112
|
+
]);
|
|
113
|
+
var INITIAL_STATE = new Uint32Array([
|
|
114
|
+
1779033703,
|
|
115
|
+
3144134277,
|
|
116
|
+
1013904242,
|
|
117
|
+
2773480762,
|
|
118
|
+
1359893119,
|
|
119
|
+
2600822924,
|
|
120
|
+
528734635,
|
|
121
|
+
1541459225
|
|
122
|
+
]);
|
|
123
|
+
|
|
124
|
+
// ../core/sealed.ts
|
|
125
|
+
var MIN_SECRET_CHARS = 32;
|
|
126
|
+
var INFO = new TextEncoder().encode("thunder-bridge/sealed");
|
|
127
|
+
function refuseAWeakSecret(secret) {
|
|
128
|
+
if (secret.length < MIN_SECRET_CHARS) {
|
|
129
|
+
throw new Error(`the sealing secret needs ${MIN_SECRET_CHARS} characters of randomness`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/bank.ts
|
|
134
|
+
var DEFAULT_CURRENCY = "CZK";
|
|
135
|
+
var DEFAULT_LOOK_BACK_SECS = 7 * 24 * 60 * 60;
|
|
136
|
+
function bankAgent(config) {
|
|
137
|
+
refuseAWeakSecret(config.secret);
|
|
138
|
+
return config.gateway.attend({
|
|
139
|
+
onError: config.onError,
|
|
140
|
+
answer: async (paymentHash) => {
|
|
141
|
+
const order = await config.orders(paymentHash);
|
|
142
|
+
if (order === null) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const currency = order.currency ?? DEFAULT_CURRENCY;
|
|
146
|
+
return await creditedPreimage({
|
|
147
|
+
secret: config.secret,
|
|
148
|
+
asked: {
|
|
149
|
+
iban: accountOf(order.iban),
|
|
150
|
+
reference: order.reference,
|
|
151
|
+
amountMinor: order.amountMinor,
|
|
152
|
+
currency,
|
|
153
|
+
expiresAt: order.expiresAt
|
|
154
|
+
},
|
|
155
|
+
statement: order.statement,
|
|
156
|
+
lookBackSecs: config.lookBackSecs
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
async function creditedPreimage(params) {
|
|
162
|
+
const since = unixNow() - (params.lookBackSecs ?? DEFAULT_LOOK_BACK_SECS);
|
|
163
|
+
const landed = (await params.statement(since)).some((credit) => pays(credit, params.asked));
|
|
164
|
+
if (!landed) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return await hmacHex(params.secret, `preimage|${subjectOf(params.asked)}`);
|
|
168
|
+
}
|
|
169
|
+
function pays(credit, asked) {
|
|
170
|
+
return credit.amountMinor === asked.amountMinor && credit.currency.toUpperCase() === asked.currency.toUpperCase() && namesOnly(credit.reference, asked.reference);
|
|
171
|
+
}
|
|
172
|
+
function namesOnly(written, reference) {
|
|
173
|
+
const wanted = reference.toUpperCase();
|
|
174
|
+
const alike = [...written.toUpperCase().matchAll(wordsShapedLike(wanted))].map(([word]) => word);
|
|
175
|
+
return alike.includes(wanted) && (!/\d/.test(wanted) || alike.every((word) => word === wanted));
|
|
176
|
+
}
|
|
177
|
+
function wordsShapedLike(reference) {
|
|
178
|
+
const shape = [...reference].map((char) => {
|
|
179
|
+
if (/\d/.test(char)) {
|
|
180
|
+
return "\\d";
|
|
181
|
+
}
|
|
182
|
+
return /\p{L}/u.test(char) ? "\\p{L}" : char.replace(/[\^$\\.*+?()[\]{}|/]/g, "\\$&");
|
|
183
|
+
}).join("");
|
|
184
|
+
return new RegExp(`(?<![\\p{L}\\p{N}])${shape}(?![\\p{L}\\p{N}])`, "gu");
|
|
185
|
+
}
|
|
186
|
+
function subjectOf({ iban, amountMinor, currency, expiresAt, reference }) {
|
|
187
|
+
return `${iban}|${amountMinor}|${currency.toUpperCase()}|${expiresAt}|${reference}`;
|
|
188
|
+
}
|
|
189
|
+
function accountOf(iban) {
|
|
190
|
+
return iban.replace(/\s+/g, "").toUpperCase();
|
|
191
|
+
}
|
|
192
|
+
function unixNow() {
|
|
193
|
+
return Math.floor(Date.now() / 1e3);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/fio.ts
|
|
197
|
+
var BASE_URL = "https://fioapi.fio.cz/v1/rest";
|
|
198
|
+
var MINOR_UNITS = 100;
|
|
199
|
+
var MILLIS = 1e3;
|
|
200
|
+
var DATE_CHARS = "2026-08-04".length;
|
|
201
|
+
var DEFAULT_MIN_INTERVAL_SECS = 30;
|
|
202
|
+
var TOO_SOON = 409;
|
|
203
|
+
var BOOKED_AT = "column0";
|
|
204
|
+
var AMOUNT = "column1";
|
|
205
|
+
var CURRENCY = "column14";
|
|
206
|
+
var VARIABLE_SYMBOL = "column5";
|
|
207
|
+
var USER_IDENTIFICATION = "column7";
|
|
208
|
+
var MESSAGE_FOR_RECIPIENT = "column16";
|
|
209
|
+
var PAYER_REFERENCE = "column27";
|
|
210
|
+
var WHERE_A_PAYER_WRITES = [
|
|
211
|
+
MESSAGE_FOR_RECIPIENT,
|
|
212
|
+
VARIABLE_SYMBOL,
|
|
213
|
+
USER_IDENTIFICATION,
|
|
214
|
+
PAYER_REFERENCE
|
|
215
|
+
];
|
|
216
|
+
var UTC_OFFSET = /^[+-]\d{4}$/;
|
|
217
|
+
function fioStatement(config) {
|
|
218
|
+
const named = typeof config.token === "string" ? [config.token] : config.token;
|
|
219
|
+
if (named.length === 0) {
|
|
220
|
+
throw new Error("fioStatement needs at least one token to read with");
|
|
221
|
+
}
|
|
222
|
+
const usedAt = new Map(named.map((token) => [token, Number.NEGATIVE_INFINITY]));
|
|
223
|
+
const tokenWindowMs = (config.minIntervalSecs ?? DEFAULT_MIN_INTERVAL_SECS) * MILLIS;
|
|
224
|
+
const paceMs = tokenWindowMs / usedAt.size;
|
|
225
|
+
let lastRead = Number.NEGATIVE_INFINITY;
|
|
226
|
+
let credits = [];
|
|
227
|
+
return async (sinceUnix) => {
|
|
228
|
+
const now = Date.now();
|
|
229
|
+
if (now - lastRead < paceMs) {
|
|
230
|
+
return credits;
|
|
231
|
+
}
|
|
232
|
+
const idlest = longestUnused(usedAt);
|
|
233
|
+
if (now - idlest.usedAt < tokenWindowMs) {
|
|
234
|
+
return credits;
|
|
235
|
+
}
|
|
236
|
+
usedAt.set(idlest.token, now);
|
|
237
|
+
lastRead = now;
|
|
238
|
+
const url = [
|
|
239
|
+
config.baseUrl ?? BASE_URL,
|
|
240
|
+
"periods",
|
|
241
|
+
idlest.token,
|
|
242
|
+
asDate(sinceUnix),
|
|
243
|
+
asDate(Math.floor(now / MILLIS)),
|
|
244
|
+
"transactions.json"
|
|
245
|
+
].join("/");
|
|
246
|
+
const answer = await fetch(url, { headers: { accept: "application/json" } });
|
|
247
|
+
if (answer.status === TOO_SOON) {
|
|
248
|
+
throw new Error("fio refuses a second read of this token inside its 30 second window");
|
|
249
|
+
}
|
|
250
|
+
if (!answer.ok) {
|
|
251
|
+
throw new Error(`fio answered ${answer.status} reading the statement`);
|
|
252
|
+
}
|
|
253
|
+
const read = await answer.json();
|
|
254
|
+
credits = (read.accountStatement?.transactionList?.transaction ?? []).filter(isCredit).map(asCredit);
|
|
255
|
+
return credits;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function longestUnused(usedAt) {
|
|
259
|
+
let token = "";
|
|
260
|
+
let idleSince = Number.POSITIVE_INFINITY;
|
|
261
|
+
for (const [candidate, at] of usedAt) {
|
|
262
|
+
if (at < idleSince) {
|
|
263
|
+
token = candidate;
|
|
264
|
+
idleSince = at;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return { token, usedAt: idleSince };
|
|
268
|
+
}
|
|
269
|
+
function isCredit(transaction) {
|
|
270
|
+
return numberIn(transaction[AMOUNT]) > 0;
|
|
271
|
+
}
|
|
272
|
+
function asCredit(transaction) {
|
|
273
|
+
return {
|
|
274
|
+
amountMinor: Math.round(numberIn(transaction[AMOUNT]) * MINOR_UNITS),
|
|
275
|
+
currency: textIn(transaction[CURRENCY]),
|
|
276
|
+
reference: WHERE_A_PAYER_WRITES.map((column) => textIn(transaction[column])).filter((written) => written.length > 0).join(" "),
|
|
277
|
+
bookedAt: bookedAtIn(transaction[BOOKED_AT])
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function bookedAtIn(cell) {
|
|
281
|
+
const value = cell?.value;
|
|
282
|
+
if (typeof value === "number") {
|
|
283
|
+
return Math.floor(value / MILLIS);
|
|
284
|
+
}
|
|
285
|
+
const at = Date.parse(asIso8601(textIn(cell)));
|
|
286
|
+
return Number.isFinite(at) ? Math.floor(at / MILLIS) : 0;
|
|
287
|
+
}
|
|
288
|
+
function asIso8601(booked) {
|
|
289
|
+
const day = booked.slice(0, DATE_CHARS);
|
|
290
|
+
const zone = booked.slice(DATE_CHARS);
|
|
291
|
+
if (!UTC_OFFSET.test(zone)) {
|
|
292
|
+
return `${day}T00:00:00Z`;
|
|
293
|
+
}
|
|
294
|
+
return `${day}T00:00:00${zone.slice(0, 3)}:${zone.slice(3)}`;
|
|
295
|
+
}
|
|
296
|
+
function numberIn(cell) {
|
|
297
|
+
const value = Number(cell?.value ?? Number.NaN);
|
|
298
|
+
return Number.isFinite(value) ? value : 0;
|
|
299
|
+
}
|
|
300
|
+
function textIn(cell) {
|
|
301
|
+
const value = cell?.value;
|
|
302
|
+
return value === null || value === void 0 ? "" : String(value).trim();
|
|
303
|
+
}
|
|
304
|
+
function asDate(unix) {
|
|
305
|
+
return new Date(unix * MILLIS).toISOString().slice(0, DATE_CHARS);
|
|
306
|
+
}
|
|
307
|
+
export {
|
|
308
|
+
bankAgent,
|
|
309
|
+
fioStatement
|
|
310
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { W as WalletFailure } from './types-DYZ9EkmJ.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Why an amount was refused. A code rather than a message, because a caller can
|
|
5
|
+
* only recover from a failure it can name and a message is free to be reworded
|
|
6
|
+
*/
|
|
7
|
+
type AmountFault = "not-whole-satoshi" | "not-whole-millisatoshi" | "not-a-decimal" | "too-precise" | "unknown-currency";
|
|
8
|
+
/**
|
|
9
|
+
* Thrown when a price cannot be held exactly. Every constructor of an amount
|
|
10
|
+
* throws this rather than returning something approximate, because a payment
|
|
11
|
+
* library that rounds silently moves the wrong money
|
|
12
|
+
*/
|
|
13
|
+
declare class AmountError extends Error {
|
|
14
|
+
/**
|
|
15
|
+
* Whether a failure is one of these, without asking whether it is this exact
|
|
16
|
+
* class. Every entry point carries its own copy of the class, so a price
|
|
17
|
+
* refused inside `thunder-bridge/price` is not `instanceof` the `AmountError`
|
|
18
|
+
* imported from `thunder-bridge`. The name and the code are the same in every
|
|
19
|
+
* copy, so this holds where `instanceof` does not
|
|
20
|
+
*/
|
|
21
|
+
static is(failure: unknown): failure is AmountError;
|
|
22
|
+
readonly code: AmountFault;
|
|
23
|
+
constructor(code: AmountFault, detail: string);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The way a gateway was caught out, every code is a check that held against the
|
|
27
|
+
* recipient's own server and failed against what the gateway returned
|
|
28
|
+
*/
|
|
29
|
+
type GatewayCheatCode = "address_not_requested" | "hash_mismatch" | "amount_mismatch" | "description_hash_mismatch" | "verify_url_foreign" | "invoice_not_issued" | "preimage_mismatch" | "id_not_mine" | "invoice_settled" | "invoice_stale" | "invoice_reused";
|
|
30
|
+
/**
|
|
31
|
+
* Thrown when the gateway demonstrably misbehaved, the invoice it returned is
|
|
32
|
+
* not the one the address you asked for issued, or a settlement it reported
|
|
33
|
+
* carries a preimage that does not hash to the payment hash
|
|
34
|
+
*/
|
|
35
|
+
declare class GatewayCheatError extends Error {
|
|
36
|
+
readonly code: GatewayCheatCode;
|
|
37
|
+
readonly paymentId: string;
|
|
38
|
+
constructor(code: GatewayCheatCode, paymentId: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The way a wrapping operator was caught out. Every code is the wrapped invoice
|
|
42
|
+
* failing to bind to the recipient's own, which is the only thing that makes
|
|
43
|
+
* paying the wrap the same act as paying the recipient
|
|
44
|
+
*/
|
|
45
|
+
type WrapRefusalCode = "undecodable" | "hash_mismatch" | "amount_below_recipient" | "fee_above_allowance" | "recipient_expires_first";
|
|
46
|
+
/**
|
|
47
|
+
* Thrown when a wrapped invoice does not bind to the recipient's. Paying it
|
|
48
|
+
* would be paying the operator on its word rather than on the shared payment
|
|
49
|
+
* hash, which is the whole of what makes wrapping safe
|
|
50
|
+
*/
|
|
51
|
+
declare class WrapRefusedError extends Error {
|
|
52
|
+
readonly code: WrapRefusalCode;
|
|
53
|
+
constructor(code: WrapRefusalCode, detail: string);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Thrown when the recipient's own server could not be reached to check the
|
|
57
|
+
* invoice against, a CORS-blocked browser or a provider that is down, this is
|
|
58
|
+
* not proof the gateway cheated and it is not proof it did not
|
|
59
|
+
*/
|
|
60
|
+
declare class UnverifiedRecipientError extends Error {
|
|
61
|
+
readonly lnAddress: string;
|
|
62
|
+
readonly paymentId: string;
|
|
63
|
+
constructor(lnAddress: string, paymentId: string, cause: unknown);
|
|
64
|
+
}
|
|
65
|
+
/** An RFC 9457 problem document the gateway answered with */
|
|
66
|
+
declare class ProblemError extends Error {
|
|
67
|
+
static readonly NO_WALLET_AVAILABLE = "urn:problem-type:thunder-bridge:no-wallet-available";
|
|
68
|
+
static readonly REQUEST_IN_FLIGHT = "urn:problem-type:thunder-bridge:request-in-flight";
|
|
69
|
+
static readonly IDEMPOTENCY_KEY_REUSED = "urn:problem-type:thunder-bridge:idempotency-key-reused";
|
|
70
|
+
static readonly PAYMENT_ALREADY_WATCHED = "urn:problem-type:thunder-bridge:payment-already-watched";
|
|
71
|
+
static readonly INVALID_REQUEST = "urn:problem-type:thunder-bridge:invalid-request";
|
|
72
|
+
static readonly CALLER_UNKNOWN = "urn:problem-type:thunder-bridge:caller-unknown";
|
|
73
|
+
static readonly VERIFY_HOST_REFUSED = "urn:problem-type:thunder-bridge:verify-host-refused";
|
|
74
|
+
static readonly VERIFY_UNCONFIRMED = "urn:problem-type:thunder-bridge:verify-unconfirmed";
|
|
75
|
+
static readonly VERIFY_UNCONSENTED = "urn:problem-type:thunder-bridge:verify-unconsented";
|
|
76
|
+
static readonly WEBHOOK_UNCONFIRMED = "urn:problem-type:thunder-bridge:webhook-unconfirmed";
|
|
77
|
+
static readonly TOO_MANY_PENDING = "urn:problem-type:thunder-bridge:too-many-pending";
|
|
78
|
+
/**
|
|
79
|
+
* Whether a problem carries this type. Branch on the type, never on the prose,
|
|
80
|
+
* and reach for `instanceof` first: every type this SDK gives a class to has one
|
|
81
|
+
*/
|
|
82
|
+
static is(problem: {
|
|
83
|
+
type?: string;
|
|
84
|
+
}, type: string): boolean;
|
|
85
|
+
readonly type: string;
|
|
86
|
+
readonly title: string;
|
|
87
|
+
readonly status: number;
|
|
88
|
+
readonly detail: string | null;
|
|
89
|
+
constructor(problem: {
|
|
90
|
+
type?: string;
|
|
91
|
+
title?: string;
|
|
92
|
+
status?: number;
|
|
93
|
+
detail?: string;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Why an `Idempotency-Key` was refused, `request-in-flight` is the benign one and
|
|
98
|
+
* `key-reused` means the same key was sent for a different request
|
|
99
|
+
*/
|
|
100
|
+
type IdempotencyConflict = "request-in-flight" | "key-reused";
|
|
101
|
+
/**
|
|
102
|
+
* Thrown when an `Idempotency-Key` is held by another request. On
|
|
103
|
+
* `request-in-flight` the first attempt is still resolving, so wait and read the
|
|
104
|
+
* payment back rather than retrying. `key-reused` is a bug in the caller: the key
|
|
105
|
+
* is bound to the addresses, amount and webhook that claimed it
|
|
106
|
+
*/
|
|
107
|
+
declare class IdempotencyConflictError extends ProblemError {
|
|
108
|
+
readonly conflict: IdempotencyConflict;
|
|
109
|
+
constructor(problem: {
|
|
110
|
+
type?: string;
|
|
111
|
+
title?: string;
|
|
112
|
+
status?: number;
|
|
113
|
+
detail?: string;
|
|
114
|
+
}, conflict: IdempotencyConflict);
|
|
115
|
+
}
|
|
116
|
+
/** Thrown when no wallet on your list could issue a provable invoice, `wallets` says why each refused */
|
|
117
|
+
declare class NoWalletAvailableError extends ProblemError {
|
|
118
|
+
readonly wallets: WalletFailure[];
|
|
119
|
+
constructor(problem: {
|
|
120
|
+
title?: string;
|
|
121
|
+
status?: number;
|
|
122
|
+
detail?: string;
|
|
123
|
+
}, wallets: WalletFailure[]);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { AmountError as A, type GatewayCheatCode as G, type IdempotencyConflict as I, NoWalletAvailableError as N, ProblemError as P, UnverifiedRecipientError as U, type WrapRefusalCode as W, type AmountFault as a, GatewayCheatError as b, IdempotencyConflictError as c, WrapRefusedError as d };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { W as WalletFailure } from './types-DYZ9EkmJ.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Why an amount was refused. A code rather than a message, because a caller can
|
|
5
|
+
* only recover from a failure it can name and a message is free to be reworded
|
|
6
|
+
*/
|
|
7
|
+
type AmountFault = "not-whole-satoshi" | "not-whole-millisatoshi" | "not-a-decimal" | "too-precise" | "unknown-currency";
|
|
8
|
+
/**
|
|
9
|
+
* Thrown when a price cannot be held exactly. Every constructor of an amount
|
|
10
|
+
* throws this rather than returning something approximate, because a payment
|
|
11
|
+
* library that rounds silently moves the wrong money
|
|
12
|
+
*/
|
|
13
|
+
declare class AmountError extends Error {
|
|
14
|
+
/**
|
|
15
|
+
* Whether a failure is one of these, without asking whether it is this exact
|
|
16
|
+
* class. Every entry point carries its own copy of the class, so a price
|
|
17
|
+
* refused inside `thunder-bridge/price` is not `instanceof` the `AmountError`
|
|
18
|
+
* imported from `thunder-bridge`. The name and the code are the same in every
|
|
19
|
+
* copy, so this holds where `instanceof` does not
|
|
20
|
+
*/
|
|
21
|
+
static is(failure: unknown): failure is AmountError;
|
|
22
|
+
readonly code: AmountFault;
|
|
23
|
+
constructor(code: AmountFault, detail: string);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The way a gateway was caught out, every code is a check that held against the
|
|
27
|
+
* recipient's own server and failed against what the gateway returned
|
|
28
|
+
*/
|
|
29
|
+
type GatewayCheatCode = "address_not_requested" | "hash_mismatch" | "amount_mismatch" | "description_hash_mismatch" | "verify_url_foreign" | "invoice_not_issued" | "preimage_mismatch" | "id_not_mine" | "invoice_settled" | "invoice_stale" | "invoice_reused";
|
|
30
|
+
/**
|
|
31
|
+
* Thrown when the gateway demonstrably misbehaved, the invoice it returned is
|
|
32
|
+
* not the one the address you asked for issued, or a settlement it reported
|
|
33
|
+
* carries a preimage that does not hash to the payment hash
|
|
34
|
+
*/
|
|
35
|
+
declare class GatewayCheatError extends Error {
|
|
36
|
+
readonly code: GatewayCheatCode;
|
|
37
|
+
readonly paymentId: string;
|
|
38
|
+
constructor(code: GatewayCheatCode, paymentId: string);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The way a wrapping operator was caught out. Every code is the wrapped invoice
|
|
42
|
+
* failing to bind to the recipient's own, which is the only thing that makes
|
|
43
|
+
* paying the wrap the same act as paying the recipient
|
|
44
|
+
*/
|
|
45
|
+
type WrapRefusalCode = "undecodable" | "hash_mismatch" | "amount_below_recipient" | "fee_above_allowance" | "recipient_expires_first";
|
|
46
|
+
/**
|
|
47
|
+
* Thrown when a wrapped invoice does not bind to the recipient's. Paying it
|
|
48
|
+
* would be paying the operator on its word rather than on the shared payment
|
|
49
|
+
* hash, which is the whole of what makes wrapping safe
|
|
50
|
+
*/
|
|
51
|
+
declare class WrapRefusedError extends Error {
|
|
52
|
+
readonly code: WrapRefusalCode;
|
|
53
|
+
constructor(code: WrapRefusalCode, detail: string);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Thrown when the recipient's own server could not be reached to check the
|
|
57
|
+
* invoice against, a CORS-blocked browser or a provider that is down, this is
|
|
58
|
+
* not proof the gateway cheated and it is not proof it did not
|
|
59
|
+
*/
|
|
60
|
+
declare class UnverifiedRecipientError extends Error {
|
|
61
|
+
readonly lnAddress: string;
|
|
62
|
+
readonly paymentId: string;
|
|
63
|
+
constructor(lnAddress: string, paymentId: string, cause: unknown);
|
|
64
|
+
}
|
|
65
|
+
/** An RFC 9457 problem document the gateway answered with */
|
|
66
|
+
declare class ProblemError extends Error {
|
|
67
|
+
static readonly NO_WALLET_AVAILABLE = "urn:problem-type:thunder-bridge:no-wallet-available";
|
|
68
|
+
static readonly REQUEST_IN_FLIGHT = "urn:problem-type:thunder-bridge:request-in-flight";
|
|
69
|
+
static readonly IDEMPOTENCY_KEY_REUSED = "urn:problem-type:thunder-bridge:idempotency-key-reused";
|
|
70
|
+
static readonly PAYMENT_ALREADY_WATCHED = "urn:problem-type:thunder-bridge:payment-already-watched";
|
|
71
|
+
static readonly INVALID_REQUEST = "urn:problem-type:thunder-bridge:invalid-request";
|
|
72
|
+
static readonly CALLER_UNKNOWN = "urn:problem-type:thunder-bridge:caller-unknown";
|
|
73
|
+
static readonly VERIFY_HOST_REFUSED = "urn:problem-type:thunder-bridge:verify-host-refused";
|
|
74
|
+
static readonly VERIFY_UNCONFIRMED = "urn:problem-type:thunder-bridge:verify-unconfirmed";
|
|
75
|
+
static readonly VERIFY_UNCONSENTED = "urn:problem-type:thunder-bridge:verify-unconsented";
|
|
76
|
+
static readonly WEBHOOK_UNCONFIRMED = "urn:problem-type:thunder-bridge:webhook-unconfirmed";
|
|
77
|
+
static readonly TOO_MANY_PENDING = "urn:problem-type:thunder-bridge:too-many-pending";
|
|
78
|
+
/**
|
|
79
|
+
* Whether a problem carries this type. Branch on the type, never on the prose,
|
|
80
|
+
* and reach for `instanceof` first: every type this SDK gives a class to has one
|
|
81
|
+
*/
|
|
82
|
+
static is(problem: {
|
|
83
|
+
type?: string;
|
|
84
|
+
}, type: string): boolean;
|
|
85
|
+
readonly type: string;
|
|
86
|
+
readonly title: string;
|
|
87
|
+
readonly status: number;
|
|
88
|
+
readonly detail: string | null;
|
|
89
|
+
constructor(problem: {
|
|
90
|
+
type?: string;
|
|
91
|
+
title?: string;
|
|
92
|
+
status?: number;
|
|
93
|
+
detail?: string;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Why an `Idempotency-Key` was refused, `request-in-flight` is the benign one and
|
|
98
|
+
* `key-reused` means the same key was sent for a different request
|
|
99
|
+
*/
|
|
100
|
+
type IdempotencyConflict = "request-in-flight" | "key-reused";
|
|
101
|
+
/**
|
|
102
|
+
* Thrown when an `Idempotency-Key` is held by another request. On
|
|
103
|
+
* `request-in-flight` the first attempt is still resolving, so wait and read the
|
|
104
|
+
* payment back rather than retrying. `key-reused` is a bug in the caller: the key
|
|
105
|
+
* is bound to the addresses, amount and webhook that claimed it
|
|
106
|
+
*/
|
|
107
|
+
declare class IdempotencyConflictError extends ProblemError {
|
|
108
|
+
readonly conflict: IdempotencyConflict;
|
|
109
|
+
constructor(problem: {
|
|
110
|
+
type?: string;
|
|
111
|
+
title?: string;
|
|
112
|
+
status?: number;
|
|
113
|
+
detail?: string;
|
|
114
|
+
}, conflict: IdempotencyConflict);
|
|
115
|
+
}
|
|
116
|
+
/** Thrown when no wallet on your list could issue a provable invoice, `wallets` says why each refused */
|
|
117
|
+
declare class NoWalletAvailableError extends ProblemError {
|
|
118
|
+
readonly wallets: WalletFailure[];
|
|
119
|
+
constructor(problem: {
|
|
120
|
+
title?: string;
|
|
121
|
+
status?: number;
|
|
122
|
+
detail?: string;
|
|
123
|
+
}, wallets: WalletFailure[]);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { AmountError as A, type GatewayCheatCode as G, type IdempotencyConflict as I, NoWalletAvailableError as N, ProblemError as P, UnverifiedRecipientError as U, type WrapRefusalCode as W, type AmountFault as a, GatewayCheatError as b, IdempotencyConflictError as c, WrapRefusedError as d };
|