@ambosstech/lightning-mpp-adapter-nwc 0.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/dist/index.cjs +322 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +77 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +288 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
NwcLightningProvider: () => NwcLightningProvider,
|
|
34
|
+
createNwcTransport: () => createNwcTransport,
|
|
35
|
+
mapNwcError: () => mapNwcError,
|
|
36
|
+
mapTransportError: () => mapTransportError,
|
|
37
|
+
parseConnectionString: () => parseConnectionString
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/connection-string.ts
|
|
42
|
+
var import_lightning_mpp_sdk = require("@ambosstech/lightning-mpp-sdk");
|
|
43
|
+
var HEX_64_RE = /^[0-9a-f]{64}$/;
|
|
44
|
+
function parseConnectionString(uri) {
|
|
45
|
+
if (!uri.startsWith("nostr+walletconnect://")) {
|
|
46
|
+
throw new import_lightning_mpp_sdk.ConnectionError(
|
|
47
|
+
"Invalid NWC connection string: must start with nostr+walletconnect://"
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const withoutScheme = uri.slice("nostr+walletconnect://".length);
|
|
51
|
+
const questionIdx = withoutScheme.indexOf("?");
|
|
52
|
+
if (questionIdx === -1) {
|
|
53
|
+
throw new import_lightning_mpp_sdk.ConnectionError(
|
|
54
|
+
"Invalid NWC connection string: missing query parameters"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
const walletPubkey = withoutScheme.slice(0, questionIdx);
|
|
58
|
+
if (!HEX_64_RE.test(walletPubkey)) {
|
|
59
|
+
throw new import_lightning_mpp_sdk.ConnectionError(
|
|
60
|
+
"Invalid NWC connection string: wallet pubkey must be 64-char hex"
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const params = new URLSearchParams(withoutScheme.slice(questionIdx + 1));
|
|
64
|
+
const relayUrl = params.get("relay");
|
|
65
|
+
if (!relayUrl || !relayUrl.startsWith("wss://")) {
|
|
66
|
+
throw new import_lightning_mpp_sdk.ConnectionError(
|
|
67
|
+
"Invalid NWC connection string: relay must be a wss:// URL"
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const secret = params.get("secret");
|
|
71
|
+
if (!secret || !HEX_64_RE.test(secret)) {
|
|
72
|
+
throw new import_lightning_mpp_sdk.ConnectionError(
|
|
73
|
+
"Invalid NWC connection string: secret must be 64-char hex"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const lud16 = params.get("lud16") || void 0;
|
|
77
|
+
return { walletPubkey, relayUrl, secret, lud16 };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/nwc-client.ts
|
|
81
|
+
var import_relay = require("nostr-tools/relay");
|
|
82
|
+
var import_pure = require("nostr-tools/pure");
|
|
83
|
+
var nip44 = __toESM(require("nostr-tools/nip44"), 1);
|
|
84
|
+
|
|
85
|
+
// src/error-mapper.ts
|
|
86
|
+
var import_lightning_mpp_sdk2 = require("@ambosstech/lightning-mpp-sdk");
|
|
87
|
+
function mapNwcError(response) {
|
|
88
|
+
const code = response.error?.code ?? "OTHER";
|
|
89
|
+
const message = (response.error?.message ?? "").toLowerCase();
|
|
90
|
+
switch (code) {
|
|
91
|
+
case "INSUFFICIENT_BALANCE":
|
|
92
|
+
return new import_lightning_mpp_sdk2.InsufficientBalanceError("Insufficient balance", {
|
|
93
|
+
cause: response.error
|
|
94
|
+
});
|
|
95
|
+
case "PAYMENT_FAILED":
|
|
96
|
+
if (message.includes("expired")) {
|
|
97
|
+
return new import_lightning_mpp_sdk2.InvoiceExpiredError("Invoice has expired", {
|
|
98
|
+
cause: response.error
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (message.includes("timeout") || message.includes("timed out")) {
|
|
102
|
+
return new import_lightning_mpp_sdk2.PaymentTimeoutError("Payment timed out", {
|
|
103
|
+
cause: response.error
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return new import_lightning_mpp_sdk2.RouteNotFoundError(
|
|
107
|
+
response.error?.message ?? "Payment failed",
|
|
108
|
+
{ cause: response.error }
|
|
109
|
+
);
|
|
110
|
+
case "UNAUTHORIZED":
|
|
111
|
+
case "RESTRICTED":
|
|
112
|
+
return new import_lightning_mpp_sdk2.AuthenticationError(
|
|
113
|
+
response.error?.message ?? "Unauthorized",
|
|
114
|
+
{ cause: response.error }
|
|
115
|
+
);
|
|
116
|
+
case "RATE_LIMITED":
|
|
117
|
+
case "QUOTA_EXCEEDED":
|
|
118
|
+
return new import_lightning_mpp_sdk2.ConnectionError(
|
|
119
|
+
response.error?.message ?? "Rate limited",
|
|
120
|
+
{ cause: response.error }
|
|
121
|
+
);
|
|
122
|
+
default:
|
|
123
|
+
return new import_lightning_mpp_sdk2.ConnectionError(
|
|
124
|
+
response.error?.message ?? `NWC error: ${code}`,
|
|
125
|
+
{ cause: response.error }
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function mapTransportError(error) {
|
|
130
|
+
const message = error instanceof Error ? error.message.toLowerCase() : "unknown";
|
|
131
|
+
if (message.includes("timeout") || message.includes("timed out")) {
|
|
132
|
+
return new import_lightning_mpp_sdk2.PaymentTimeoutError("NWC request timed out", { cause: error });
|
|
133
|
+
}
|
|
134
|
+
return new import_lightning_mpp_sdk2.ConnectionError(
|
|
135
|
+
`NWC transport error: ${error instanceof Error ? error.message : "unknown"}`,
|
|
136
|
+
{ cause: error }
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/nwc-client.ts
|
|
141
|
+
function hexToBytes(hex) {
|
|
142
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
143
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
144
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
145
|
+
}
|
|
146
|
+
return bytes;
|
|
147
|
+
}
|
|
148
|
+
var NWC_REQUEST_KIND = 23194;
|
|
149
|
+
var NWC_RESPONSE_KIND = 23195;
|
|
150
|
+
var DEFAULT_TIMEOUT_SECS = 60;
|
|
151
|
+
function createNwcTransport(info, opts = {}) {
|
|
152
|
+
const { walletPubkey, relayUrl, secret } = info;
|
|
153
|
+
const timeoutMs = (opts.timeoutSecs ?? DEFAULT_TIMEOUT_SECS) * 1e3;
|
|
154
|
+
const secretBytes = hexToBytes(secret);
|
|
155
|
+
const conversationKey = nip44.v2.utils.getConversationKey(
|
|
156
|
+
secretBytes,
|
|
157
|
+
walletPubkey
|
|
158
|
+
);
|
|
159
|
+
let relay = null;
|
|
160
|
+
async function ensureConnected() {
|
|
161
|
+
if (relay && relay.connected) return relay;
|
|
162
|
+
try {
|
|
163
|
+
relay = await import_relay.Relay.connect(relayUrl);
|
|
164
|
+
return relay;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw mapTransportError(error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function encrypt(content) {
|
|
170
|
+
return nip44.v2.encrypt(content, conversationKey);
|
|
171
|
+
}
|
|
172
|
+
function decrypt(content) {
|
|
173
|
+
return nip44.v2.decrypt(content, conversationKey);
|
|
174
|
+
}
|
|
175
|
+
async function sendRequest(method, params) {
|
|
176
|
+
const r = await ensureConnected();
|
|
177
|
+
const request = { method, params };
|
|
178
|
+
const encrypted = encrypt(JSON.stringify(request));
|
|
179
|
+
const event = (0, import_pure.finalizeEvent)(
|
|
180
|
+
{
|
|
181
|
+
kind: NWC_REQUEST_KIND,
|
|
182
|
+
content: encrypted,
|
|
183
|
+
tags: [["p", walletPubkey]],
|
|
184
|
+
created_at: Math.floor(Date.now() / 1e3)
|
|
185
|
+
},
|
|
186
|
+
secretBytes
|
|
187
|
+
);
|
|
188
|
+
return new Promise((resolve, reject) => {
|
|
189
|
+
const timer = setTimeout(() => {
|
|
190
|
+
sub.close();
|
|
191
|
+
reject(mapTransportError(new Error("NWC request timed out")));
|
|
192
|
+
}, timeoutMs);
|
|
193
|
+
const sub = r.subscribe(
|
|
194
|
+
[
|
|
195
|
+
{
|
|
196
|
+
kinds: [NWC_RESPONSE_KIND],
|
|
197
|
+
authors: [walletPubkey],
|
|
198
|
+
"#e": [event.id]
|
|
199
|
+
}
|
|
200
|
+
],
|
|
201
|
+
{
|
|
202
|
+
onevent(evt) {
|
|
203
|
+
clearTimeout(timer);
|
|
204
|
+
sub.close();
|
|
205
|
+
try {
|
|
206
|
+
const decrypted = decrypt(evt.content);
|
|
207
|
+
const response = JSON.parse(decrypted);
|
|
208
|
+
if (response.error) {
|
|
209
|
+
reject(mapNwcError(response));
|
|
210
|
+
} else {
|
|
211
|
+
resolve(response);
|
|
212
|
+
}
|
|
213
|
+
} catch (err) {
|
|
214
|
+
reject(mapTransportError(err));
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
oneose() {
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
r.publish(event).catch((err) => {
|
|
222
|
+
clearTimeout(timer);
|
|
223
|
+
sub.close();
|
|
224
|
+
reject(mapTransportError(err));
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
async makeInvoice(params) {
|
|
230
|
+
const response = await sendRequest("make_invoice", {
|
|
231
|
+
amount: params.amount,
|
|
232
|
+
description: params.description,
|
|
233
|
+
expiry: params.expiry
|
|
234
|
+
});
|
|
235
|
+
const result = response.result;
|
|
236
|
+
return { invoice: result.invoice, payment_hash: result.payment_hash };
|
|
237
|
+
},
|
|
238
|
+
async payInvoice(params) {
|
|
239
|
+
const reqParams = { invoice: params.invoice };
|
|
240
|
+
if (params.amount !== void 0) {
|
|
241
|
+
reqParams.amount = params.amount;
|
|
242
|
+
}
|
|
243
|
+
const response = await sendRequest("pay_invoice", reqParams);
|
|
244
|
+
const result = response.result;
|
|
245
|
+
return { preimage: result.preimage };
|
|
246
|
+
},
|
|
247
|
+
async lookupInvoice(params) {
|
|
248
|
+
const response = await sendRequest("lookup_invoice", {
|
|
249
|
+
payment_hash: params.payment_hash
|
|
250
|
+
});
|
|
251
|
+
const result = response.result;
|
|
252
|
+
return {
|
|
253
|
+
settled_at: result.settled_at,
|
|
254
|
+
preimage: result.preimage,
|
|
255
|
+
amount: result.amount
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
close() {
|
|
259
|
+
if (relay) {
|
|
260
|
+
relay.close();
|
|
261
|
+
relay = null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/nwc-provider.ts
|
|
268
|
+
var NwcLightningProvider = class {
|
|
269
|
+
transport;
|
|
270
|
+
constructor(config) {
|
|
271
|
+
const info = parseConnectionString(config.connectionString);
|
|
272
|
+
this.transport = createNwcTransport(info, {
|
|
273
|
+
timeoutSecs: config.timeoutSecs
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
async createInvoice(params) {
|
|
277
|
+
const response = await this.transport.makeInvoice({
|
|
278
|
+
amount: params.amountSats * 1e3,
|
|
279
|
+
// sats → msats
|
|
280
|
+
description: params.memo,
|
|
281
|
+
expiry: params.expirySecs
|
|
282
|
+
});
|
|
283
|
+
return {
|
|
284
|
+
bolt11: response.invoice,
|
|
285
|
+
paymentHash: response.payment_hash
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
async payInvoice(params) {
|
|
289
|
+
if (params.maxFeeSats !== void 0) {
|
|
290
|
+
console.warn(
|
|
291
|
+
"NWC does not support maxFeeSats \u2014 fee limits are controlled by the wallet"
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
const response = await this.transport.payInvoice({
|
|
295
|
+
invoice: params.bolt11,
|
|
296
|
+
amount: params.amountSats !== void 0 ? params.amountSats * 1e3 : void 0
|
|
297
|
+
});
|
|
298
|
+
return { preimage: response.preimage };
|
|
299
|
+
}
|
|
300
|
+
async lookupInvoice(params) {
|
|
301
|
+
const response = await this.transport.lookupInvoice({
|
|
302
|
+
payment_hash: params.paymentHash
|
|
303
|
+
});
|
|
304
|
+
return {
|
|
305
|
+
settled: response.settled_at !== void 0 && response.settled_at !== null,
|
|
306
|
+
preimage: response.preimage,
|
|
307
|
+
amountSats: response.amount !== void 0 ? Math.floor(response.amount / 1e3) : void 0
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
close() {
|
|
311
|
+
this.transport.close();
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
315
|
+
0 && (module.exports = {
|
|
316
|
+
NwcLightningProvider,
|
|
317
|
+
createNwcTransport,
|
|
318
|
+
mapNwcError,
|
|
319
|
+
mapTransportError,
|
|
320
|
+
parseConnectionString
|
|
321
|
+
});
|
|
322
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/connection-string.ts","../src/nwc-client.ts","../src/error-mapper.ts","../src/nwc-provider.ts"],"sourcesContent":["export { NwcLightningProvider } from \"./nwc-provider.js\";\nexport { createNwcTransport } from \"./nwc-client.js\";\nexport { parseConnectionString } from \"./connection-string.js\";\nexport { mapNwcError, mapTransportError } from \"./error-mapper.js\";\nexport type {\n NwcConfig,\n NwcConnectionInfo,\n NwcTransport,\n NwcRequest,\n NwcResponse,\n} from \"./types.js\";\n","import { ConnectionError } from \"@ambosstech/lightning-mpp-sdk\";\nimport type { NwcConnectionInfo } from \"./types.js\";\n\nconst HEX_64_RE = /^[0-9a-f]{64}$/;\n\nexport function parseConnectionString(uri: string): NwcConnectionInfo {\n if (!uri.startsWith(\"nostr+walletconnect://\")) {\n throw new ConnectionError(\n \"Invalid NWC connection string: must start with nostr+walletconnect://\",\n );\n }\n\n const withoutScheme = uri.slice(\"nostr+walletconnect://\".length);\n const questionIdx = withoutScheme.indexOf(\"?\");\n\n if (questionIdx === -1) {\n throw new ConnectionError(\n \"Invalid NWC connection string: missing query parameters\",\n );\n }\n\n const walletPubkey = withoutScheme.slice(0, questionIdx);\n\n if (!HEX_64_RE.test(walletPubkey)) {\n throw new ConnectionError(\n \"Invalid NWC connection string: wallet pubkey must be 64-char hex\",\n );\n }\n\n const params = new URLSearchParams(withoutScheme.slice(questionIdx + 1));\n\n const relayUrl = params.get(\"relay\");\n if (!relayUrl || !relayUrl.startsWith(\"wss://\")) {\n throw new ConnectionError(\n \"Invalid NWC connection string: relay must be a wss:// URL\",\n );\n }\n\n const secret = params.get(\"secret\");\n if (!secret || !HEX_64_RE.test(secret)) {\n throw new ConnectionError(\n \"Invalid NWC connection string: secret must be 64-char hex\",\n );\n }\n\n const lud16 = params.get(\"lud16\") || undefined;\n\n return { walletPubkey, relayUrl, secret, lud16 };\n}\n","import { Relay } from \"nostr-tools/relay\";\nimport { finalizeEvent } from \"nostr-tools/pure\";\nimport * as nip44 from \"nostr-tools/nip44\";\nimport { mapNwcError, mapTransportError } from \"./error-mapper.js\";\nimport type {\n NwcConnectionInfo,\n NwcRequest,\n NwcResponse,\n NwcTransport,\n} from \"./types.js\";\n\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\nconst NWC_REQUEST_KIND = 23194;\nconst NWC_RESPONSE_KIND = 23195;\nconst DEFAULT_TIMEOUT_SECS = 60;\n\ninterface NwcTransportOptions {\n timeoutSecs?: number;\n}\n\nexport function createNwcTransport(\n info: NwcConnectionInfo,\n opts: NwcTransportOptions = {},\n): NwcTransport {\n const { walletPubkey, relayUrl, secret } = info;\n const timeoutMs = (opts.timeoutSecs ?? DEFAULT_TIMEOUT_SECS) * 1000;\n const secretBytes = hexToBytes(secret);\n const conversationKey = nip44.v2.utils.getConversationKey(\n secretBytes,\n walletPubkey,\n );\n\n let relay: Relay | null = null;\n\n async function ensureConnected(): Promise<Relay> {\n if (relay && relay.connected) return relay;\n try {\n relay = await Relay.connect(relayUrl);\n return relay;\n } catch (error) {\n throw mapTransportError(error);\n }\n }\n\n function encrypt(content: string): string {\n return nip44.v2.encrypt(content, conversationKey);\n }\n\n function decrypt(content: string): string {\n return nip44.v2.decrypt(content, conversationKey);\n }\n\n async function sendRequest(\n method: string,\n params: Record<string, unknown>,\n ): Promise<NwcResponse> {\n const r = await ensureConnected();\n\n const request: NwcRequest = { method, params };\n const encrypted = encrypt(JSON.stringify(request));\n\n const event = finalizeEvent(\n {\n kind: NWC_REQUEST_KIND,\n content: encrypted,\n tags: [[\"p\", walletPubkey]],\n created_at: Math.floor(Date.now() / 1000),\n },\n secretBytes,\n );\n\n return new Promise<NwcResponse>((resolve, reject) => {\n const timer = setTimeout(() => {\n sub.close();\n reject(mapTransportError(new Error(\"NWC request timed out\")));\n }, timeoutMs);\n\n const sub = r.subscribe(\n [\n {\n kinds: [NWC_RESPONSE_KIND],\n authors: [walletPubkey],\n \"#e\": [event.id],\n },\n ],\n {\n onevent(evt) {\n clearTimeout(timer);\n sub.close();\n try {\n const decrypted = decrypt(evt.content);\n const response = JSON.parse(decrypted) as NwcResponse;\n\n if (response.error) {\n reject(mapNwcError(response));\n } else {\n resolve(response);\n }\n } catch (err) {\n reject(mapTransportError(err));\n }\n },\n oneose() {\n // Keep subscription open waiting for the response event\n },\n },\n );\n\n // Publish the request after subscribing\n r.publish(event).catch((err: unknown) => {\n clearTimeout(timer);\n sub.close();\n reject(mapTransportError(err));\n });\n });\n }\n\n return {\n async makeInvoice(params) {\n const response = await sendRequest(\"make_invoice\", {\n amount: params.amount,\n description: params.description,\n expiry: params.expiry,\n });\n const result = response.result as {\n invoice: string;\n payment_hash: string;\n };\n return { invoice: result.invoice, payment_hash: result.payment_hash };\n },\n\n async payInvoice(params) {\n const reqParams: Record<string, unknown> = { invoice: params.invoice };\n if (params.amount !== undefined) {\n reqParams.amount = params.amount;\n }\n const response = await sendRequest(\"pay_invoice\", reqParams);\n const result = response.result as { preimage: string };\n return { preimage: result.preimage };\n },\n\n async lookupInvoice(params) {\n const response = await sendRequest(\"lookup_invoice\", {\n payment_hash: params.payment_hash,\n });\n const result = response.result as {\n settled_at?: number;\n preimage?: string;\n amount?: number;\n };\n return {\n settled_at: result.settled_at,\n preimage: result.preimage,\n amount: result.amount,\n };\n },\n\n close() {\n if (relay) {\n relay.close();\n relay = null;\n }\n },\n };\n}\n","import {\n AuthenticationError,\n ConnectionError,\n InsufficientBalanceError,\n InvoiceExpiredError,\n type LightningError,\n PaymentTimeoutError,\n RouteNotFoundError,\n} from \"@ambosstech/lightning-mpp-sdk\";\nimport type { NwcResponse } from \"./types.js\";\n\nexport function mapNwcError(response: NwcResponse): LightningError {\n const code = response.error?.code ?? \"OTHER\";\n const message = (response.error?.message ?? \"\").toLowerCase();\n\n switch (code) {\n case \"INSUFFICIENT_BALANCE\":\n return new InsufficientBalanceError(\"Insufficient balance\", {\n cause: response.error,\n });\n\n case \"PAYMENT_FAILED\":\n if (message.includes(\"expired\")) {\n return new InvoiceExpiredError(\"Invoice has expired\", {\n cause: response.error,\n });\n }\n if (message.includes(\"timeout\") || message.includes(\"timed out\")) {\n return new PaymentTimeoutError(\"Payment timed out\", {\n cause: response.error,\n });\n }\n return new RouteNotFoundError(\n response.error?.message ?? \"Payment failed\",\n { cause: response.error },\n );\n\n case \"UNAUTHORIZED\":\n case \"RESTRICTED\":\n return new AuthenticationError(\n response.error?.message ?? \"Unauthorized\",\n { cause: response.error },\n );\n\n case \"RATE_LIMITED\":\n case \"QUOTA_EXCEEDED\":\n return new ConnectionError(\n response.error?.message ?? \"Rate limited\",\n { cause: response.error },\n );\n\n default:\n return new ConnectionError(\n response.error?.message ?? `NWC error: ${code}`,\n { cause: response.error },\n );\n }\n}\n\nexport function mapTransportError(error: unknown): LightningError {\n const message =\n error instanceof Error ? error.message.toLowerCase() : \"unknown\";\n\n if (message.includes(\"timeout\") || message.includes(\"timed out\")) {\n return new PaymentTimeoutError(\"NWC request timed out\", { cause: error });\n }\n\n return new ConnectionError(\n `NWC transport error: ${error instanceof Error ? error.message : \"unknown\"}`,\n { cause: error },\n );\n}\n","import type {\n CreateInvoiceParams,\n CreateInvoiceResult,\n LightningProvider,\n LookupInvoiceParams,\n LookupInvoiceResult,\n PayInvoiceParams,\n PayInvoiceResult,\n} from \"@ambosstech/lightning-mpp-sdk\";\nimport { parseConnectionString } from \"./connection-string.js\";\nimport { createNwcTransport } from \"./nwc-client.js\";\nimport type { NwcConfig, NwcTransport } from \"./types.js\";\n\nexport class NwcLightningProvider implements LightningProvider {\n private transport: NwcTransport;\n\n constructor(config: NwcConfig) {\n const info = parseConnectionString(config.connectionString);\n this.transport = createNwcTransport(info, {\n timeoutSecs: config.timeoutSecs,\n });\n }\n\n async createInvoice(\n params: CreateInvoiceParams,\n ): Promise<CreateInvoiceResult> {\n const response = await this.transport.makeInvoice({\n amount: params.amountSats * 1000, // sats → msats\n description: params.memo,\n expiry: params.expirySecs,\n });\n\n return {\n bolt11: response.invoice,\n paymentHash: response.payment_hash,\n };\n }\n\n async payInvoice(params: PayInvoiceParams): Promise<PayInvoiceResult> {\n if (params.maxFeeSats !== undefined) {\n console.warn(\n \"NWC does not support maxFeeSats — fee limits are controlled by the wallet\",\n );\n }\n\n const response = await this.transport.payInvoice({\n invoice: params.bolt11,\n amount:\n params.amountSats !== undefined\n ? params.amountSats * 1000 // sats → msats\n : undefined,\n });\n\n return { preimage: response.preimage };\n }\n\n async lookupInvoice(\n params: LookupInvoiceParams,\n ): Promise<LookupInvoiceResult> {\n const response = await this.transport.lookupInvoice({\n payment_hash: params.paymentHash,\n });\n\n return {\n settled: response.settled_at !== undefined && response.settled_at !== null,\n preimage: response.preimage,\n amountSats:\n response.amount !== undefined\n ? Math.floor(response.amount / 1000) // msats → sats\n : undefined,\n };\n }\n\n close(): void {\n this.transport.close();\n }\n}\n"],"mappings":"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","names":["import_lightning_mpp_sdk"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { LightningProvider, CreateInvoiceParams, CreateInvoiceResult, PayInvoiceParams, PayInvoiceResult, LookupInvoiceParams, LookupInvoiceResult, LightningError } from '@ambosstech/lightning-mpp-sdk';
|
|
2
|
+
|
|
3
|
+
interface NwcConfig {
|
|
4
|
+
/** NWC connection string: nostr+walletconnect://{pubkey}?relay={url}&secret={hex} */
|
|
5
|
+
connectionString: string;
|
|
6
|
+
/** Response timeout in seconds (default: 60) */
|
|
7
|
+
timeoutSecs?: number;
|
|
8
|
+
}
|
|
9
|
+
interface NwcConnectionInfo {
|
|
10
|
+
/** Wallet service pubkey (32-byte hex) */
|
|
11
|
+
walletPubkey: string;
|
|
12
|
+
/** Relay URL (wss://...) */
|
|
13
|
+
relayUrl: string;
|
|
14
|
+
/** Client secret / private key (32-byte hex) */
|
|
15
|
+
secret: string;
|
|
16
|
+
/** Optional lightning address */
|
|
17
|
+
lud16?: string;
|
|
18
|
+
}
|
|
19
|
+
interface NwcTransport {
|
|
20
|
+
makeInvoice(params: {
|
|
21
|
+
amount: number;
|
|
22
|
+
description?: string;
|
|
23
|
+
expiry?: number;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
invoice: string;
|
|
26
|
+
payment_hash: string;
|
|
27
|
+
}>;
|
|
28
|
+
payInvoice(params: {
|
|
29
|
+
invoice: string;
|
|
30
|
+
amount?: number;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
preimage: string;
|
|
33
|
+
}>;
|
|
34
|
+
lookupInvoice(params: {
|
|
35
|
+
payment_hash: string;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
settled_at?: number;
|
|
38
|
+
preimage?: string;
|
|
39
|
+
amount?: number;
|
|
40
|
+
}>;
|
|
41
|
+
close(): void;
|
|
42
|
+
}
|
|
43
|
+
/** NWC request event content (kind 23194) */
|
|
44
|
+
interface NwcRequest {
|
|
45
|
+
method: string;
|
|
46
|
+
params: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
/** NWC response event content (kind 23195) */
|
|
49
|
+
interface NwcResponse {
|
|
50
|
+
result_type: string;
|
|
51
|
+
error?: {
|
|
52
|
+
code: string;
|
|
53
|
+
message: string;
|
|
54
|
+
};
|
|
55
|
+
result?: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare class NwcLightningProvider implements LightningProvider {
|
|
59
|
+
private transport;
|
|
60
|
+
constructor(config: NwcConfig);
|
|
61
|
+
createInvoice(params: CreateInvoiceParams): Promise<CreateInvoiceResult>;
|
|
62
|
+
payInvoice(params: PayInvoiceParams): Promise<PayInvoiceResult>;
|
|
63
|
+
lookupInvoice(params: LookupInvoiceParams): Promise<LookupInvoiceResult>;
|
|
64
|
+
close(): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface NwcTransportOptions {
|
|
68
|
+
timeoutSecs?: number;
|
|
69
|
+
}
|
|
70
|
+
declare function createNwcTransport(info: NwcConnectionInfo, opts?: NwcTransportOptions): NwcTransport;
|
|
71
|
+
|
|
72
|
+
declare function parseConnectionString(uri: string): NwcConnectionInfo;
|
|
73
|
+
|
|
74
|
+
declare function mapNwcError(response: NwcResponse): LightningError;
|
|
75
|
+
declare function mapTransportError(error: unknown): LightningError;
|
|
76
|
+
|
|
77
|
+
export { type NwcConfig, type NwcConnectionInfo, NwcLightningProvider, type NwcRequest, type NwcResponse, type NwcTransport, createNwcTransport, mapNwcError, mapTransportError, parseConnectionString };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { LightningProvider, CreateInvoiceParams, CreateInvoiceResult, PayInvoiceParams, PayInvoiceResult, LookupInvoiceParams, LookupInvoiceResult, LightningError } from '@ambosstech/lightning-mpp-sdk';
|
|
2
|
+
|
|
3
|
+
interface NwcConfig {
|
|
4
|
+
/** NWC connection string: nostr+walletconnect://{pubkey}?relay={url}&secret={hex} */
|
|
5
|
+
connectionString: string;
|
|
6
|
+
/** Response timeout in seconds (default: 60) */
|
|
7
|
+
timeoutSecs?: number;
|
|
8
|
+
}
|
|
9
|
+
interface NwcConnectionInfo {
|
|
10
|
+
/** Wallet service pubkey (32-byte hex) */
|
|
11
|
+
walletPubkey: string;
|
|
12
|
+
/** Relay URL (wss://...) */
|
|
13
|
+
relayUrl: string;
|
|
14
|
+
/** Client secret / private key (32-byte hex) */
|
|
15
|
+
secret: string;
|
|
16
|
+
/** Optional lightning address */
|
|
17
|
+
lud16?: string;
|
|
18
|
+
}
|
|
19
|
+
interface NwcTransport {
|
|
20
|
+
makeInvoice(params: {
|
|
21
|
+
amount: number;
|
|
22
|
+
description?: string;
|
|
23
|
+
expiry?: number;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
invoice: string;
|
|
26
|
+
payment_hash: string;
|
|
27
|
+
}>;
|
|
28
|
+
payInvoice(params: {
|
|
29
|
+
invoice: string;
|
|
30
|
+
amount?: number;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
preimage: string;
|
|
33
|
+
}>;
|
|
34
|
+
lookupInvoice(params: {
|
|
35
|
+
payment_hash: string;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
settled_at?: number;
|
|
38
|
+
preimage?: string;
|
|
39
|
+
amount?: number;
|
|
40
|
+
}>;
|
|
41
|
+
close(): void;
|
|
42
|
+
}
|
|
43
|
+
/** NWC request event content (kind 23194) */
|
|
44
|
+
interface NwcRequest {
|
|
45
|
+
method: string;
|
|
46
|
+
params: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
/** NWC response event content (kind 23195) */
|
|
49
|
+
interface NwcResponse {
|
|
50
|
+
result_type: string;
|
|
51
|
+
error?: {
|
|
52
|
+
code: string;
|
|
53
|
+
message: string;
|
|
54
|
+
};
|
|
55
|
+
result?: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare class NwcLightningProvider implements LightningProvider {
|
|
59
|
+
private transport;
|
|
60
|
+
constructor(config: NwcConfig);
|
|
61
|
+
createInvoice(params: CreateInvoiceParams): Promise<CreateInvoiceResult>;
|
|
62
|
+
payInvoice(params: PayInvoiceParams): Promise<PayInvoiceResult>;
|
|
63
|
+
lookupInvoice(params: LookupInvoiceParams): Promise<LookupInvoiceResult>;
|
|
64
|
+
close(): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface NwcTransportOptions {
|
|
68
|
+
timeoutSecs?: number;
|
|
69
|
+
}
|
|
70
|
+
declare function createNwcTransport(info: NwcConnectionInfo, opts?: NwcTransportOptions): NwcTransport;
|
|
71
|
+
|
|
72
|
+
declare function parseConnectionString(uri: string): NwcConnectionInfo;
|
|
73
|
+
|
|
74
|
+
declare function mapNwcError(response: NwcResponse): LightningError;
|
|
75
|
+
declare function mapTransportError(error: unknown): LightningError;
|
|
76
|
+
|
|
77
|
+
export { type NwcConfig, type NwcConnectionInfo, NwcLightningProvider, type NwcRequest, type NwcResponse, type NwcTransport, createNwcTransport, mapNwcError, mapTransportError, parseConnectionString };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// src/connection-string.ts
|
|
2
|
+
import { ConnectionError } from "@ambosstech/lightning-mpp-sdk";
|
|
3
|
+
var HEX_64_RE = /^[0-9a-f]{64}$/;
|
|
4
|
+
function parseConnectionString(uri) {
|
|
5
|
+
if (!uri.startsWith("nostr+walletconnect://")) {
|
|
6
|
+
throw new ConnectionError(
|
|
7
|
+
"Invalid NWC connection string: must start with nostr+walletconnect://"
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
const withoutScheme = uri.slice("nostr+walletconnect://".length);
|
|
11
|
+
const questionIdx = withoutScheme.indexOf("?");
|
|
12
|
+
if (questionIdx === -1) {
|
|
13
|
+
throw new ConnectionError(
|
|
14
|
+
"Invalid NWC connection string: missing query parameters"
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const walletPubkey = withoutScheme.slice(0, questionIdx);
|
|
18
|
+
if (!HEX_64_RE.test(walletPubkey)) {
|
|
19
|
+
throw new ConnectionError(
|
|
20
|
+
"Invalid NWC connection string: wallet pubkey must be 64-char hex"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const params = new URLSearchParams(withoutScheme.slice(questionIdx + 1));
|
|
24
|
+
const relayUrl = params.get("relay");
|
|
25
|
+
if (!relayUrl || !relayUrl.startsWith("wss://")) {
|
|
26
|
+
throw new ConnectionError(
|
|
27
|
+
"Invalid NWC connection string: relay must be a wss:// URL"
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const secret = params.get("secret");
|
|
31
|
+
if (!secret || !HEX_64_RE.test(secret)) {
|
|
32
|
+
throw new ConnectionError(
|
|
33
|
+
"Invalid NWC connection string: secret must be 64-char hex"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const lud16 = params.get("lud16") || void 0;
|
|
37
|
+
return { walletPubkey, relayUrl, secret, lud16 };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/nwc-client.ts
|
|
41
|
+
import { Relay } from "nostr-tools/relay";
|
|
42
|
+
import { finalizeEvent } from "nostr-tools/pure";
|
|
43
|
+
import * as nip44 from "nostr-tools/nip44";
|
|
44
|
+
|
|
45
|
+
// src/error-mapper.ts
|
|
46
|
+
import {
|
|
47
|
+
AuthenticationError,
|
|
48
|
+
ConnectionError as ConnectionError2,
|
|
49
|
+
InsufficientBalanceError,
|
|
50
|
+
InvoiceExpiredError,
|
|
51
|
+
PaymentTimeoutError,
|
|
52
|
+
RouteNotFoundError
|
|
53
|
+
} from "@ambosstech/lightning-mpp-sdk";
|
|
54
|
+
function mapNwcError(response) {
|
|
55
|
+
const code = response.error?.code ?? "OTHER";
|
|
56
|
+
const message = (response.error?.message ?? "").toLowerCase();
|
|
57
|
+
switch (code) {
|
|
58
|
+
case "INSUFFICIENT_BALANCE":
|
|
59
|
+
return new InsufficientBalanceError("Insufficient balance", {
|
|
60
|
+
cause: response.error
|
|
61
|
+
});
|
|
62
|
+
case "PAYMENT_FAILED":
|
|
63
|
+
if (message.includes("expired")) {
|
|
64
|
+
return new InvoiceExpiredError("Invoice has expired", {
|
|
65
|
+
cause: response.error
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (message.includes("timeout") || message.includes("timed out")) {
|
|
69
|
+
return new PaymentTimeoutError("Payment timed out", {
|
|
70
|
+
cause: response.error
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return new RouteNotFoundError(
|
|
74
|
+
response.error?.message ?? "Payment failed",
|
|
75
|
+
{ cause: response.error }
|
|
76
|
+
);
|
|
77
|
+
case "UNAUTHORIZED":
|
|
78
|
+
case "RESTRICTED":
|
|
79
|
+
return new AuthenticationError(
|
|
80
|
+
response.error?.message ?? "Unauthorized",
|
|
81
|
+
{ cause: response.error }
|
|
82
|
+
);
|
|
83
|
+
case "RATE_LIMITED":
|
|
84
|
+
case "QUOTA_EXCEEDED":
|
|
85
|
+
return new ConnectionError2(
|
|
86
|
+
response.error?.message ?? "Rate limited",
|
|
87
|
+
{ cause: response.error }
|
|
88
|
+
);
|
|
89
|
+
default:
|
|
90
|
+
return new ConnectionError2(
|
|
91
|
+
response.error?.message ?? `NWC error: ${code}`,
|
|
92
|
+
{ cause: response.error }
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function mapTransportError(error) {
|
|
97
|
+
const message = error instanceof Error ? error.message.toLowerCase() : "unknown";
|
|
98
|
+
if (message.includes("timeout") || message.includes("timed out")) {
|
|
99
|
+
return new PaymentTimeoutError("NWC request timed out", { cause: error });
|
|
100
|
+
}
|
|
101
|
+
return new ConnectionError2(
|
|
102
|
+
`NWC transport error: ${error instanceof Error ? error.message : "unknown"}`,
|
|
103
|
+
{ cause: error }
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/nwc-client.ts
|
|
108
|
+
function hexToBytes(hex) {
|
|
109
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
110
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
111
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
112
|
+
}
|
|
113
|
+
return bytes;
|
|
114
|
+
}
|
|
115
|
+
var NWC_REQUEST_KIND = 23194;
|
|
116
|
+
var NWC_RESPONSE_KIND = 23195;
|
|
117
|
+
var DEFAULT_TIMEOUT_SECS = 60;
|
|
118
|
+
function createNwcTransport(info, opts = {}) {
|
|
119
|
+
const { walletPubkey, relayUrl, secret } = info;
|
|
120
|
+
const timeoutMs = (opts.timeoutSecs ?? DEFAULT_TIMEOUT_SECS) * 1e3;
|
|
121
|
+
const secretBytes = hexToBytes(secret);
|
|
122
|
+
const conversationKey = nip44.v2.utils.getConversationKey(
|
|
123
|
+
secretBytes,
|
|
124
|
+
walletPubkey
|
|
125
|
+
);
|
|
126
|
+
let relay = null;
|
|
127
|
+
async function ensureConnected() {
|
|
128
|
+
if (relay && relay.connected) return relay;
|
|
129
|
+
try {
|
|
130
|
+
relay = await Relay.connect(relayUrl);
|
|
131
|
+
return relay;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
throw mapTransportError(error);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function encrypt(content) {
|
|
137
|
+
return nip44.v2.encrypt(content, conversationKey);
|
|
138
|
+
}
|
|
139
|
+
function decrypt(content) {
|
|
140
|
+
return nip44.v2.decrypt(content, conversationKey);
|
|
141
|
+
}
|
|
142
|
+
async function sendRequest(method, params) {
|
|
143
|
+
const r = await ensureConnected();
|
|
144
|
+
const request = { method, params };
|
|
145
|
+
const encrypted = encrypt(JSON.stringify(request));
|
|
146
|
+
const event = finalizeEvent(
|
|
147
|
+
{
|
|
148
|
+
kind: NWC_REQUEST_KIND,
|
|
149
|
+
content: encrypted,
|
|
150
|
+
tags: [["p", walletPubkey]],
|
|
151
|
+
created_at: Math.floor(Date.now() / 1e3)
|
|
152
|
+
},
|
|
153
|
+
secretBytes
|
|
154
|
+
);
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
156
|
+
const timer = setTimeout(() => {
|
|
157
|
+
sub.close();
|
|
158
|
+
reject(mapTransportError(new Error("NWC request timed out")));
|
|
159
|
+
}, timeoutMs);
|
|
160
|
+
const sub = r.subscribe(
|
|
161
|
+
[
|
|
162
|
+
{
|
|
163
|
+
kinds: [NWC_RESPONSE_KIND],
|
|
164
|
+
authors: [walletPubkey],
|
|
165
|
+
"#e": [event.id]
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
{
|
|
169
|
+
onevent(evt) {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
sub.close();
|
|
172
|
+
try {
|
|
173
|
+
const decrypted = decrypt(evt.content);
|
|
174
|
+
const response = JSON.parse(decrypted);
|
|
175
|
+
if (response.error) {
|
|
176
|
+
reject(mapNwcError(response));
|
|
177
|
+
} else {
|
|
178
|
+
resolve(response);
|
|
179
|
+
}
|
|
180
|
+
} catch (err) {
|
|
181
|
+
reject(mapTransportError(err));
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
oneose() {
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
r.publish(event).catch((err) => {
|
|
189
|
+
clearTimeout(timer);
|
|
190
|
+
sub.close();
|
|
191
|
+
reject(mapTransportError(err));
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
async makeInvoice(params) {
|
|
197
|
+
const response = await sendRequest("make_invoice", {
|
|
198
|
+
amount: params.amount,
|
|
199
|
+
description: params.description,
|
|
200
|
+
expiry: params.expiry
|
|
201
|
+
});
|
|
202
|
+
const result = response.result;
|
|
203
|
+
return { invoice: result.invoice, payment_hash: result.payment_hash };
|
|
204
|
+
},
|
|
205
|
+
async payInvoice(params) {
|
|
206
|
+
const reqParams = { invoice: params.invoice };
|
|
207
|
+
if (params.amount !== void 0) {
|
|
208
|
+
reqParams.amount = params.amount;
|
|
209
|
+
}
|
|
210
|
+
const response = await sendRequest("pay_invoice", reqParams);
|
|
211
|
+
const result = response.result;
|
|
212
|
+
return { preimage: result.preimage };
|
|
213
|
+
},
|
|
214
|
+
async lookupInvoice(params) {
|
|
215
|
+
const response = await sendRequest("lookup_invoice", {
|
|
216
|
+
payment_hash: params.payment_hash
|
|
217
|
+
});
|
|
218
|
+
const result = response.result;
|
|
219
|
+
return {
|
|
220
|
+
settled_at: result.settled_at,
|
|
221
|
+
preimage: result.preimage,
|
|
222
|
+
amount: result.amount
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
close() {
|
|
226
|
+
if (relay) {
|
|
227
|
+
relay.close();
|
|
228
|
+
relay = null;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/nwc-provider.ts
|
|
235
|
+
var NwcLightningProvider = class {
|
|
236
|
+
transport;
|
|
237
|
+
constructor(config) {
|
|
238
|
+
const info = parseConnectionString(config.connectionString);
|
|
239
|
+
this.transport = createNwcTransport(info, {
|
|
240
|
+
timeoutSecs: config.timeoutSecs
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async createInvoice(params) {
|
|
244
|
+
const response = await this.transport.makeInvoice({
|
|
245
|
+
amount: params.amountSats * 1e3,
|
|
246
|
+
// sats → msats
|
|
247
|
+
description: params.memo,
|
|
248
|
+
expiry: params.expirySecs
|
|
249
|
+
});
|
|
250
|
+
return {
|
|
251
|
+
bolt11: response.invoice,
|
|
252
|
+
paymentHash: response.payment_hash
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
async payInvoice(params) {
|
|
256
|
+
if (params.maxFeeSats !== void 0) {
|
|
257
|
+
console.warn(
|
|
258
|
+
"NWC does not support maxFeeSats \u2014 fee limits are controlled by the wallet"
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
const response = await this.transport.payInvoice({
|
|
262
|
+
invoice: params.bolt11,
|
|
263
|
+
amount: params.amountSats !== void 0 ? params.amountSats * 1e3 : void 0
|
|
264
|
+
});
|
|
265
|
+
return { preimage: response.preimage };
|
|
266
|
+
}
|
|
267
|
+
async lookupInvoice(params) {
|
|
268
|
+
const response = await this.transport.lookupInvoice({
|
|
269
|
+
payment_hash: params.paymentHash
|
|
270
|
+
});
|
|
271
|
+
return {
|
|
272
|
+
settled: response.settled_at !== void 0 && response.settled_at !== null,
|
|
273
|
+
preimage: response.preimage,
|
|
274
|
+
amountSats: response.amount !== void 0 ? Math.floor(response.amount / 1e3) : void 0
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
close() {
|
|
278
|
+
this.transport.close();
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
export {
|
|
282
|
+
NwcLightningProvider,
|
|
283
|
+
createNwcTransport,
|
|
284
|
+
mapNwcError,
|
|
285
|
+
mapTransportError,
|
|
286
|
+
parseConnectionString
|
|
287
|
+
};
|
|
288
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connection-string.ts","../src/nwc-client.ts","../src/error-mapper.ts","../src/nwc-provider.ts"],"sourcesContent":["import { ConnectionError } from \"@ambosstech/lightning-mpp-sdk\";\nimport type { NwcConnectionInfo } from \"./types.js\";\n\nconst HEX_64_RE = /^[0-9a-f]{64}$/;\n\nexport function parseConnectionString(uri: string): NwcConnectionInfo {\n if (!uri.startsWith(\"nostr+walletconnect://\")) {\n throw new ConnectionError(\n \"Invalid NWC connection string: must start with nostr+walletconnect://\",\n );\n }\n\n const withoutScheme = uri.slice(\"nostr+walletconnect://\".length);\n const questionIdx = withoutScheme.indexOf(\"?\");\n\n if (questionIdx === -1) {\n throw new ConnectionError(\n \"Invalid NWC connection string: missing query parameters\",\n );\n }\n\n const walletPubkey = withoutScheme.slice(0, questionIdx);\n\n if (!HEX_64_RE.test(walletPubkey)) {\n throw new ConnectionError(\n \"Invalid NWC connection string: wallet pubkey must be 64-char hex\",\n );\n }\n\n const params = new URLSearchParams(withoutScheme.slice(questionIdx + 1));\n\n const relayUrl = params.get(\"relay\");\n if (!relayUrl || !relayUrl.startsWith(\"wss://\")) {\n throw new ConnectionError(\n \"Invalid NWC connection string: relay must be a wss:// URL\",\n );\n }\n\n const secret = params.get(\"secret\");\n if (!secret || !HEX_64_RE.test(secret)) {\n throw new ConnectionError(\n \"Invalid NWC connection string: secret must be 64-char hex\",\n );\n }\n\n const lud16 = params.get(\"lud16\") || undefined;\n\n return { walletPubkey, relayUrl, secret, lud16 };\n}\n","import { Relay } from \"nostr-tools/relay\";\nimport { finalizeEvent } from \"nostr-tools/pure\";\nimport * as nip44 from \"nostr-tools/nip44\";\nimport { mapNwcError, mapTransportError } from \"./error-mapper.js\";\nimport type {\n NwcConnectionInfo,\n NwcRequest,\n NwcResponse,\n NwcTransport,\n} from \"./types.js\";\n\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < hex.length; i += 2) {\n bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);\n }\n return bytes;\n}\n\nconst NWC_REQUEST_KIND = 23194;\nconst NWC_RESPONSE_KIND = 23195;\nconst DEFAULT_TIMEOUT_SECS = 60;\n\ninterface NwcTransportOptions {\n timeoutSecs?: number;\n}\n\nexport function createNwcTransport(\n info: NwcConnectionInfo,\n opts: NwcTransportOptions = {},\n): NwcTransport {\n const { walletPubkey, relayUrl, secret } = info;\n const timeoutMs = (opts.timeoutSecs ?? DEFAULT_TIMEOUT_SECS) * 1000;\n const secretBytes = hexToBytes(secret);\n const conversationKey = nip44.v2.utils.getConversationKey(\n secretBytes,\n walletPubkey,\n );\n\n let relay: Relay | null = null;\n\n async function ensureConnected(): Promise<Relay> {\n if (relay && relay.connected) return relay;\n try {\n relay = await Relay.connect(relayUrl);\n return relay;\n } catch (error) {\n throw mapTransportError(error);\n }\n }\n\n function encrypt(content: string): string {\n return nip44.v2.encrypt(content, conversationKey);\n }\n\n function decrypt(content: string): string {\n return nip44.v2.decrypt(content, conversationKey);\n }\n\n async function sendRequest(\n method: string,\n params: Record<string, unknown>,\n ): Promise<NwcResponse> {\n const r = await ensureConnected();\n\n const request: NwcRequest = { method, params };\n const encrypted = encrypt(JSON.stringify(request));\n\n const event = finalizeEvent(\n {\n kind: NWC_REQUEST_KIND,\n content: encrypted,\n tags: [[\"p\", walletPubkey]],\n created_at: Math.floor(Date.now() / 1000),\n },\n secretBytes,\n );\n\n return new Promise<NwcResponse>((resolve, reject) => {\n const timer = setTimeout(() => {\n sub.close();\n reject(mapTransportError(new Error(\"NWC request timed out\")));\n }, timeoutMs);\n\n const sub = r.subscribe(\n [\n {\n kinds: [NWC_RESPONSE_KIND],\n authors: [walletPubkey],\n \"#e\": [event.id],\n },\n ],\n {\n onevent(evt) {\n clearTimeout(timer);\n sub.close();\n try {\n const decrypted = decrypt(evt.content);\n const response = JSON.parse(decrypted) as NwcResponse;\n\n if (response.error) {\n reject(mapNwcError(response));\n } else {\n resolve(response);\n }\n } catch (err) {\n reject(mapTransportError(err));\n }\n },\n oneose() {\n // Keep subscription open waiting for the response event\n },\n },\n );\n\n // Publish the request after subscribing\n r.publish(event).catch((err: unknown) => {\n clearTimeout(timer);\n sub.close();\n reject(mapTransportError(err));\n });\n });\n }\n\n return {\n async makeInvoice(params) {\n const response = await sendRequest(\"make_invoice\", {\n amount: params.amount,\n description: params.description,\n expiry: params.expiry,\n });\n const result = response.result as {\n invoice: string;\n payment_hash: string;\n };\n return { invoice: result.invoice, payment_hash: result.payment_hash };\n },\n\n async payInvoice(params) {\n const reqParams: Record<string, unknown> = { invoice: params.invoice };\n if (params.amount !== undefined) {\n reqParams.amount = params.amount;\n }\n const response = await sendRequest(\"pay_invoice\", reqParams);\n const result = response.result as { preimage: string };\n return { preimage: result.preimage };\n },\n\n async lookupInvoice(params) {\n const response = await sendRequest(\"lookup_invoice\", {\n payment_hash: params.payment_hash,\n });\n const result = response.result as {\n settled_at?: number;\n preimage?: string;\n amount?: number;\n };\n return {\n settled_at: result.settled_at,\n preimage: result.preimage,\n amount: result.amount,\n };\n },\n\n close() {\n if (relay) {\n relay.close();\n relay = null;\n }\n },\n };\n}\n","import {\n AuthenticationError,\n ConnectionError,\n InsufficientBalanceError,\n InvoiceExpiredError,\n type LightningError,\n PaymentTimeoutError,\n RouteNotFoundError,\n} from \"@ambosstech/lightning-mpp-sdk\";\nimport type { NwcResponse } from \"./types.js\";\n\nexport function mapNwcError(response: NwcResponse): LightningError {\n const code = response.error?.code ?? \"OTHER\";\n const message = (response.error?.message ?? \"\").toLowerCase();\n\n switch (code) {\n case \"INSUFFICIENT_BALANCE\":\n return new InsufficientBalanceError(\"Insufficient balance\", {\n cause: response.error,\n });\n\n case \"PAYMENT_FAILED\":\n if (message.includes(\"expired\")) {\n return new InvoiceExpiredError(\"Invoice has expired\", {\n cause: response.error,\n });\n }\n if (message.includes(\"timeout\") || message.includes(\"timed out\")) {\n return new PaymentTimeoutError(\"Payment timed out\", {\n cause: response.error,\n });\n }\n return new RouteNotFoundError(\n response.error?.message ?? \"Payment failed\",\n { cause: response.error },\n );\n\n case \"UNAUTHORIZED\":\n case \"RESTRICTED\":\n return new AuthenticationError(\n response.error?.message ?? \"Unauthorized\",\n { cause: response.error },\n );\n\n case \"RATE_LIMITED\":\n case \"QUOTA_EXCEEDED\":\n return new ConnectionError(\n response.error?.message ?? \"Rate limited\",\n { cause: response.error },\n );\n\n default:\n return new ConnectionError(\n response.error?.message ?? `NWC error: ${code}`,\n { cause: response.error },\n );\n }\n}\n\nexport function mapTransportError(error: unknown): LightningError {\n const message =\n error instanceof Error ? error.message.toLowerCase() : \"unknown\";\n\n if (message.includes(\"timeout\") || message.includes(\"timed out\")) {\n return new PaymentTimeoutError(\"NWC request timed out\", { cause: error });\n }\n\n return new ConnectionError(\n `NWC transport error: ${error instanceof Error ? error.message : \"unknown\"}`,\n { cause: error },\n );\n}\n","import type {\n CreateInvoiceParams,\n CreateInvoiceResult,\n LightningProvider,\n LookupInvoiceParams,\n LookupInvoiceResult,\n PayInvoiceParams,\n PayInvoiceResult,\n} from \"@ambosstech/lightning-mpp-sdk\";\nimport { parseConnectionString } from \"./connection-string.js\";\nimport { createNwcTransport } from \"./nwc-client.js\";\nimport type { NwcConfig, NwcTransport } from \"./types.js\";\n\nexport class NwcLightningProvider implements LightningProvider {\n private transport: NwcTransport;\n\n constructor(config: NwcConfig) {\n const info = parseConnectionString(config.connectionString);\n this.transport = createNwcTransport(info, {\n timeoutSecs: config.timeoutSecs,\n });\n }\n\n async createInvoice(\n params: CreateInvoiceParams,\n ): Promise<CreateInvoiceResult> {\n const response = await this.transport.makeInvoice({\n amount: params.amountSats * 1000, // sats → msats\n description: params.memo,\n expiry: params.expirySecs,\n });\n\n return {\n bolt11: response.invoice,\n paymentHash: response.payment_hash,\n };\n }\n\n async payInvoice(params: PayInvoiceParams): Promise<PayInvoiceResult> {\n if (params.maxFeeSats !== undefined) {\n console.warn(\n \"NWC does not support maxFeeSats — fee limits are controlled by the wallet\",\n );\n }\n\n const response = await this.transport.payInvoice({\n invoice: params.bolt11,\n amount:\n params.amountSats !== undefined\n ? params.amountSats * 1000 // sats → msats\n : undefined,\n });\n\n return { preimage: response.preimage };\n }\n\n async lookupInvoice(\n params: LookupInvoiceParams,\n ): Promise<LookupInvoiceResult> {\n const response = await this.transport.lookupInvoice({\n payment_hash: params.paymentHash,\n });\n\n return {\n settled: response.settled_at !== undefined && response.settled_at !== null,\n preimage: response.preimage,\n amountSats:\n response.amount !== undefined\n ? Math.floor(response.amount / 1000) // msats → sats\n : undefined,\n };\n }\n\n close(): void {\n this.transport.close();\n }\n}\n"],"mappings":"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","names":["ConnectionError"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ambosstech/lightning-mpp-adapter-nwc",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"clean": "rm -rf dist"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@ambosstech/lightning-mpp-sdk": "workspace:*",
|
|
29
|
+
"nostr-tools": "^2.10.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsup": "^8.3.0",
|
|
33
|
+
"typescript": "~5.7.0",
|
|
34
|
+
"vitest": "^2.1.0"
|
|
35
|
+
}
|
|
36
|
+
}
|