@garydevenay/emporion 0.0.2 → 0.0.3
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 +213 -5
- package/dist/src/cli.js +1195 -182
- package/dist/src/cli.js.map +1 -1
- package/dist/src/context-store.d.ts +22 -0
- package/dist/src/context-store.js +133 -0
- package/dist/src/context-store.js.map +1 -0
- package/dist/src/daemon.d.ts +2 -0
- package/dist/src/daemon.js.map +1 -1
- package/dist/src/errors.d.ts +8 -0
- package/dist/src/errors.js +8 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/experience/deals-store.d.ts +37 -0
- package/dist/src/experience/deals-store.js +96 -0
- package/dist/src/experience/deals-store.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/wallet/config-store.d.ts +16 -0
- package/dist/src/wallet/config-store.js +180 -0
- package/dist/src/wallet/config-store.js.map +1 -0
- package/dist/src/wallet/index.d.ts +3 -0
- package/dist/src/wallet/index.js +4 -0
- package/dist/src/wallet/index.js.map +1 -0
- package/dist/src/wallet/ledger.d.ts +50 -0
- package/dist/src/wallet/ledger.js +340 -0
- package/dist/src/wallet/ledger.js.map +1 -0
- package/dist/src/wallet/nostr-nwc-adapter.d.ts +40 -0
- package/dist/src/wallet/nostr-nwc-adapter.js +506 -0
- package/dist/src/wallet/nostr-nwc-adapter.js.map +1 -0
- package/dist/src/wallet/nwc-adapter.d.ts +20 -0
- package/dist/src/wallet/nwc-adapter.js +233 -0
- package/dist/src/wallet/nwc-adapter.js.map +1 -0
- package/dist/src/wallet/service.d.ts +42 -0
- package/dist/src/wallet/service.js +390 -0
- package/dist/src/wallet/service.js.map +1 -0
- package/dist/src/wallet/types.d.ts +140 -0
- package/dist/src/wallet/types.js +2 -0
- package/dist/src/wallet/types.js.map +1 -0
- package/dist/test/experience.test.d.ts +1 -0
- package/dist/test/experience.test.js +232 -0
- package/dist/test/experience.test.js.map +1 -0
- package/dist/test/wallet.test.d.ts +1 -0
- package/dist/test/wallet.test.js +853 -0
- package/dist/test/wallet.test.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import http from "node:http";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { runCli } from "../src/cli.js";
|
|
8
|
+
import { WalletAuthError, WalletUnavailableError } from "../src/errors.js";
|
|
9
|
+
import { WalletConfigStore } from "../src/wallet/config-store.js";
|
|
10
|
+
import { NwcWalletAdapter } from "../src/wallet/nwc-adapter.js";
|
|
11
|
+
import { NostrWalletConnectAdapter, parseNostrWalletConnectMetadata } from "../src/wallet/nostr-nwc-adapter.js";
|
|
12
|
+
import { WalletService } from "../src/wallet/service.js";
|
|
13
|
+
import { createBootstrapNode, createTempDir, removeTempDir, waitFor } from "./helpers.js";
|
|
14
|
+
function createCaptureIo() {
|
|
15
|
+
const stdout = [];
|
|
16
|
+
const stderr = [];
|
|
17
|
+
return {
|
|
18
|
+
stdout,
|
|
19
|
+
stderr,
|
|
20
|
+
io: {
|
|
21
|
+
stdout(message) {
|
|
22
|
+
stdout.push(message);
|
|
23
|
+
},
|
|
24
|
+
stderr(message) {
|
|
25
|
+
stderr.push(message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function forceStopDaemon(dataDir) {
|
|
31
|
+
const capture = createCaptureIo();
|
|
32
|
+
await runCli(["daemon", "stop", "--data-dir", dataDir], capture.io);
|
|
33
|
+
const killedPids = new Set();
|
|
34
|
+
const pidPath = path.join(dataDir, "runtime", "daemon.pid");
|
|
35
|
+
try {
|
|
36
|
+
const pid = Number.parseInt((await readFile(pidPath, "utf8")).trim(), 10);
|
|
37
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
38
|
+
throw new Error("pid-not-valid");
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
process.kill(pid, "SIGTERM");
|
|
42
|
+
killedPids.add(pid);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// continue with process table fallback
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// pid file not present; nothing else to stop.
|
|
50
|
+
}
|
|
51
|
+
const processTable = execFileSync("ps", ["-axo", "pid=,command="], { encoding: "utf8" });
|
|
52
|
+
for (const line of processTable.split("\n")) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (trimmed.length === 0) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (!trimmed.includes("src/cli.ts daemon run") || !trimmed.includes(`--data-dir ${dataDir}`)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const firstSpace = trimmed.indexOf(" ");
|
|
61
|
+
if (firstSpace === -1) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const pid = Number.parseInt(trimmed.slice(0, firstSpace).trim(), 10);
|
|
65
|
+
if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
process.kill(pid, "SIGTERM");
|
|
70
|
+
killedPids.add(pid);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Ignore already-dead processes.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const pid of killedPids) {
|
|
77
|
+
await waitFor(() => {
|
|
78
|
+
try {
|
|
79
|
+
process.kill(pid, 0);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
}, { timeoutMs: 5_000, intervalMs: 100 });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function createMockNwcServer(options) {
|
|
89
|
+
const state = {
|
|
90
|
+
invoiceStatusByRef: new Map(),
|
|
91
|
+
paymentStatusByRef: new Map(),
|
|
92
|
+
createInvoiceCalls: 0,
|
|
93
|
+
payInvoiceCalls: 0
|
|
94
|
+
};
|
|
95
|
+
const server = http.createServer((req, res) => {
|
|
96
|
+
const expectedToken = options?.requireToken;
|
|
97
|
+
if (expectedToken) {
|
|
98
|
+
const authorization = req.headers.authorization;
|
|
99
|
+
if (authorization !== `Bearer ${expectedToken}`) {
|
|
100
|
+
res.statusCode = 401;
|
|
101
|
+
res.end("unauthorized");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const bodyChunks = [];
|
|
106
|
+
req.on("data", (chunk) => bodyChunks.push(chunk));
|
|
107
|
+
req.on("end", () => {
|
|
108
|
+
const payload = JSON.parse(Buffer.concat(bodyChunks).toString("utf8"));
|
|
109
|
+
if (payload.method === "create_invoice") {
|
|
110
|
+
state.createInvoiceCalls += 1;
|
|
111
|
+
const createInvoiceDelayMs = options?.createInvoiceDelayMs ?? (options?.hangCreateInvoice ? 2_000 : 0);
|
|
112
|
+
if (createInvoiceDelayMs > 0) {
|
|
113
|
+
const delayed = setTimeout(() => {
|
|
114
|
+
if (!res.writableEnded) {
|
|
115
|
+
res.setHeader("content-type", "application/json");
|
|
116
|
+
res.end(JSON.stringify({
|
|
117
|
+
jsonrpc: "2.0",
|
|
118
|
+
id: payload.id,
|
|
119
|
+
result: {
|
|
120
|
+
bolt11: "lnbc1delayed",
|
|
121
|
+
external_ref: "inv-delayed",
|
|
122
|
+
status: "created"
|
|
123
|
+
}
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
}, createInvoiceDelayMs);
|
|
127
|
+
delayed.unref();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const invoiceId = `inv-${state.createInvoiceCalls}`;
|
|
131
|
+
state.invoiceStatusByRef.set(invoiceId, "created");
|
|
132
|
+
res.setHeader("content-type", "application/json");
|
|
133
|
+
res.end(JSON.stringify({
|
|
134
|
+
jsonrpc: "2.0",
|
|
135
|
+
id: payload.id,
|
|
136
|
+
result: {
|
|
137
|
+
bolt11: `lnbc1${invoiceId}`,
|
|
138
|
+
external_ref: invoiceId,
|
|
139
|
+
status: "created"
|
|
140
|
+
}
|
|
141
|
+
}));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (payload.method === "pay_invoice") {
|
|
145
|
+
state.payInvoiceCalls += 1;
|
|
146
|
+
const paymentId = `pay-${state.payInvoiceCalls}`;
|
|
147
|
+
state.paymentStatusByRef.set(paymentId, "pending");
|
|
148
|
+
res.setHeader("content-type", "application/json");
|
|
149
|
+
res.end(JSON.stringify({
|
|
150
|
+
jsonrpc: "2.0",
|
|
151
|
+
id: payload.id,
|
|
152
|
+
result: {
|
|
153
|
+
external_ref: paymentId,
|
|
154
|
+
amount_sats: 1000,
|
|
155
|
+
fee_sats: 2,
|
|
156
|
+
status: "pending"
|
|
157
|
+
}
|
|
158
|
+
}));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (payload.method === "get_invoice") {
|
|
162
|
+
const params = payload.params ?? {};
|
|
163
|
+
const externalRef = String(params.external_ref ?? "");
|
|
164
|
+
const status = state.invoiceStatusByRef.get(externalRef) ?? "created";
|
|
165
|
+
res.setHeader("content-type", "application/json");
|
|
166
|
+
res.end(JSON.stringify({
|
|
167
|
+
jsonrpc: "2.0",
|
|
168
|
+
id: payload.id,
|
|
169
|
+
result: {
|
|
170
|
+
status
|
|
171
|
+
}
|
|
172
|
+
}));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (payload.method === "get_payment") {
|
|
176
|
+
const params = payload.params ?? {};
|
|
177
|
+
const externalRef = String(params.external_ref ?? "");
|
|
178
|
+
const status = state.paymentStatusByRef.get(externalRef) ?? "pending";
|
|
179
|
+
res.setHeader("content-type", "application/json");
|
|
180
|
+
res.end(JSON.stringify({
|
|
181
|
+
jsonrpc: "2.0",
|
|
182
|
+
id: payload.id,
|
|
183
|
+
result: {
|
|
184
|
+
status,
|
|
185
|
+
fee_sats: 2
|
|
186
|
+
}
|
|
187
|
+
}));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
res.setHeader("content-type", "application/json");
|
|
191
|
+
res.end(JSON.stringify({
|
|
192
|
+
jsonrpc: "2.0",
|
|
193
|
+
id: payload.id,
|
|
194
|
+
error: {
|
|
195
|
+
code: -32601,
|
|
196
|
+
message: `Unsupported method: ${payload.method}`
|
|
197
|
+
}
|
|
198
|
+
}));
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
await new Promise((resolve, reject) => {
|
|
202
|
+
server.once("error", reject);
|
|
203
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
204
|
+
});
|
|
205
|
+
const address = server.address();
|
|
206
|
+
const token = options?.requireToken ?? "test-token";
|
|
207
|
+
const connectionUri = `nwc+http://127.0.0.1:${address.port}/rpc?token=${encodeURIComponent(token)}`;
|
|
208
|
+
return {
|
|
209
|
+
connectionUri,
|
|
210
|
+
state,
|
|
211
|
+
async close() {
|
|
212
|
+
server.closeAllConnections();
|
|
213
|
+
await new Promise((resolve, reject) => {
|
|
214
|
+
server.close((error) => {
|
|
215
|
+
if (error) {
|
|
216
|
+
reject(error);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
resolve();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
test("wallet config store encrypts secrets and supports key rotation", async () => {
|
|
226
|
+
const dataDir = await createTempDir("emporion-wallet-config-");
|
|
227
|
+
try {
|
|
228
|
+
const store = new WalletConfigStore(dataDir);
|
|
229
|
+
await store.writeConnection({
|
|
230
|
+
backend: "nwc",
|
|
231
|
+
network: "bitcoin",
|
|
232
|
+
connectionUri: "nwc+https://wallet.example/rpc?token=abc",
|
|
233
|
+
endpoint: "https://wallet.example/rpc",
|
|
234
|
+
connectedAt: new Date().toISOString()
|
|
235
|
+
}, "first-key");
|
|
236
|
+
const initial = await store.readConnection("first-key");
|
|
237
|
+
assert.equal(initial?.connectionUri.includes("token=abc"), true);
|
|
238
|
+
await assert.rejects(() => store.readConnection("wrong-key"), WalletAuthError);
|
|
239
|
+
await store.rotateKey("first-key", "second-key");
|
|
240
|
+
await assert.rejects(() => store.readConnection("first-key"), WalletAuthError);
|
|
241
|
+
const rotated = await store.readConnection("second-key");
|
|
242
|
+
assert.equal(rotated?.endpoint, "https://wallet.example/rpc");
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
await removeTempDir(dataDir);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
test("nwc adapter maps invoice and payment calls and handles timeout/auth errors", async () => {
|
|
249
|
+
const goodServer = await createMockNwcServer({ requireToken: "good-token" });
|
|
250
|
+
const timeoutServer = await createMockNwcServer({ requireToken: "slow-token", hangCreateInvoice: true });
|
|
251
|
+
try {
|
|
252
|
+
const adapter = new NwcWalletAdapter(goodServer.connectionUri.replace("test-token", "good-token"));
|
|
253
|
+
const invoice = await adapter.createInvoice({ amountSats: 1200, memo: "build" });
|
|
254
|
+
assert.equal(invoice.status, "created");
|
|
255
|
+
const payment = await adapter.payInvoice({ invoice: invoice.bolt11 });
|
|
256
|
+
assert.equal(payment.status, "pending");
|
|
257
|
+
const invoiceStatus = await adapter.getInvoiceStatus(invoice.externalRef);
|
|
258
|
+
assert.equal(invoiceStatus, "created");
|
|
259
|
+
const paymentStatus = await adapter.getPaymentStatus(payment.externalRef);
|
|
260
|
+
assert.equal(paymentStatus.status, "pending");
|
|
261
|
+
const unauthorizedAdapter = new NwcWalletAdapter(goodServer.connectionUri.replace("good-token", "wrong-token"));
|
|
262
|
+
await assert.rejects(() => unauthorizedAdapter.createInvoice({ amountSats: 500 }), WalletAuthError);
|
|
263
|
+
const timeoutAdapter = new NwcWalletAdapter(timeoutServer.connectionUri.replace("test-token", "slow-token"), 50);
|
|
264
|
+
await assert.rejects(() => timeoutAdapter.createInvoice({ amountSats: 700 }), WalletUnavailableError);
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
await Promise.all([goodServer.close(), timeoutServer.close()]);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
test("nostr+walletconnect adapter parses metadata and maps NIP-47 responses", async () => {
|
|
271
|
+
const walletPubkey = "a".repeat(64);
|
|
272
|
+
const secret = "b".repeat(64);
|
|
273
|
+
const connectionUri = `nostr+walletconnect://${walletPubkey}?relay=wss://relay.one&relay=wss://relay.two&secret=${secret}`;
|
|
274
|
+
const metadata = parseNostrWalletConnectMetadata(connectionUri);
|
|
275
|
+
assert.equal(metadata.endpoint, `nostr+walletconnect://${walletPubkey}?relay=${encodeURIComponent("wss://relay.one")}`);
|
|
276
|
+
const calls = [];
|
|
277
|
+
const client = {
|
|
278
|
+
async request(method, params) {
|
|
279
|
+
calls.push({ method, params });
|
|
280
|
+
if (method === "make_invoice") {
|
|
281
|
+
return {
|
|
282
|
+
result_type: "make_invoice",
|
|
283
|
+
result: {
|
|
284
|
+
type: "incoming",
|
|
285
|
+
invoice: "lnbc1nostr",
|
|
286
|
+
payment_hash: "hash-invoice-1",
|
|
287
|
+
expires_at: Math.floor(Date.now() / 1000) + 120
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (method === "pay_invoice") {
|
|
292
|
+
return {
|
|
293
|
+
result_type: "pay_invoice",
|
|
294
|
+
result: {
|
|
295
|
+
payment_hash: "hash-payment-1",
|
|
296
|
+
preimage: "preimage-1",
|
|
297
|
+
amount: 1_000_000,
|
|
298
|
+
fees_paid: 5_000
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
if (method === "lookup_invoice") {
|
|
303
|
+
return {
|
|
304
|
+
result_type: "lookup_invoice",
|
|
305
|
+
result: {
|
|
306
|
+
payment_hash: String(params.payment_hash ?? ""),
|
|
307
|
+
settled_at: Math.floor(Date.now() / 1000),
|
|
308
|
+
fees_paid: 3_000
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
error: {
|
|
314
|
+
code: "NOT_IMPLEMENTED",
|
|
315
|
+
message: `Unsupported method ${method}`
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
},
|
|
319
|
+
close() {
|
|
320
|
+
// no-op
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
const adapter = new NostrWalletConnectAdapter(connectionUri, { client });
|
|
324
|
+
const invoice = await adapter.createInvoice({ amountSats: 2500, memo: "nostr invoice" });
|
|
325
|
+
assert.equal(invoice.bolt11, "lnbc1nostr");
|
|
326
|
+
assert.equal(invoice.externalRef, "hash-invoice-1");
|
|
327
|
+
assert.equal(invoice.status, "created");
|
|
328
|
+
const payment = await adapter.payInvoice({ invoice: "lnbc1to-pay" });
|
|
329
|
+
assert.equal(payment.externalRef, "hash-payment-1");
|
|
330
|
+
assert.equal(payment.status, "succeeded");
|
|
331
|
+
assert.equal(payment.feeSats, 5);
|
|
332
|
+
const invoiceStatus = await adapter.getInvoiceStatus("hash-invoice-1");
|
|
333
|
+
assert.equal(invoiceStatus, "paid");
|
|
334
|
+
const paymentStatus = await adapter.getPaymentStatus("hash-payment-1");
|
|
335
|
+
assert.equal(paymentStatus.status, "succeeded");
|
|
336
|
+
assert.equal(paymentStatus.feeSats, 3);
|
|
337
|
+
assert.equal(calls.some((call) => call.method === "make_invoice"), true);
|
|
338
|
+
const makeInvoiceCall = calls.find((call) => call.method === "make_invoice");
|
|
339
|
+
assert.ok(makeInvoiceCall);
|
|
340
|
+
assert.equal(makeInvoiceCall.params.amount, 2_500_000);
|
|
341
|
+
assert.equal(calls.some((call) => call.method === "pay_invoice"), true);
|
|
342
|
+
assert.equal(calls.filter((call) => call.method === "lookup_invoice").length >= 2, true);
|
|
343
|
+
const unauthorizedClient = {
|
|
344
|
+
async request() {
|
|
345
|
+
return {
|
|
346
|
+
error: {
|
|
347
|
+
code: "UNAUTHORIZED",
|
|
348
|
+
message: "wallet rejected request"
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
},
|
|
352
|
+
close() {
|
|
353
|
+
// no-op
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
const unauthorizedAdapter = new NostrWalletConnectAdapter(connectionUri, { client: unauthorizedClient });
|
|
357
|
+
await assert.rejects(() => unauthorizedAdapter.createInvoice({ amountSats: 1000 }), WalletAuthError);
|
|
358
|
+
});
|
|
359
|
+
test("nostr+walletconnect adapter accepts provider response variants", async () => {
|
|
360
|
+
const walletPubkey = "c".repeat(64);
|
|
361
|
+
const secret = "d".repeat(64);
|
|
362
|
+
const connectionUri = `nostr+walletconnect://${walletPubkey}?relay=wss://relay.one&secret=${secret}`;
|
|
363
|
+
const expectedHashHex = "e".repeat(64);
|
|
364
|
+
const expectedHashBase64 = Buffer.from(expectedHashHex, "hex").toString("base64");
|
|
365
|
+
const calls = [];
|
|
366
|
+
const client = {
|
|
367
|
+
async request(method, params) {
|
|
368
|
+
calls.push({ method, params });
|
|
369
|
+
if (method === "make_invoice") {
|
|
370
|
+
return {
|
|
371
|
+
result_type: "make_invoice",
|
|
372
|
+
result: {
|
|
373
|
+
pr: "lnbc1variantinvoice",
|
|
374
|
+
r_hash: expectedHashBase64
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
if (method === "pay_invoice") {
|
|
379
|
+
return {
|
|
380
|
+
result_type: "pay_invoice",
|
|
381
|
+
result: {
|
|
382
|
+
preimage: "preimage-variant",
|
|
383
|
+
amount: 1_200_000,
|
|
384
|
+
fees_paid: 4_000
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
if (method === "lookup_invoice") {
|
|
389
|
+
return {
|
|
390
|
+
result_type: "lookup_invoice",
|
|
391
|
+
result: {
|
|
392
|
+
payment_hash: String(params.payment_hash ?? ""),
|
|
393
|
+
invoice: String(params.invoice ?? ""),
|
|
394
|
+
settled_at: Math.floor(Date.now() / 1000)
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
error: {
|
|
400
|
+
code: "NOT_IMPLEMENTED",
|
|
401
|
+
message: `Unsupported method ${method}`
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
},
|
|
405
|
+
close() {
|
|
406
|
+
// no-op
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
const adapter = new NostrWalletConnectAdapter(connectionUri, { client });
|
|
410
|
+
const created = await adapter.createInvoice({ amountSats: 500 });
|
|
411
|
+
assert.equal(created.bolt11, "lnbc1variantinvoice");
|
|
412
|
+
assert.equal(created.externalRef, expectedHashHex);
|
|
413
|
+
const invoiceStatus = await adapter.getInvoiceStatus(created.externalRef);
|
|
414
|
+
assert.equal(invoiceStatus, "paid");
|
|
415
|
+
const paid = await adapter.payInvoice({ invoice: "lnbc1outgoingvariant" });
|
|
416
|
+
assert.equal(paid.externalRef, "lnbc1outgoingvariant");
|
|
417
|
+
assert.equal(paid.status, "succeeded");
|
|
418
|
+
const paymentStatus = await adapter.getPaymentStatus(paid.externalRef);
|
|
419
|
+
assert.equal(paymentStatus.status, "succeeded");
|
|
420
|
+
const lookupCalls = calls.filter((entry) => entry.method === "lookup_invoice");
|
|
421
|
+
const invoiceLookupByHash = lookupCalls.find((entry) => entry.params.payment_hash === expectedHashHex);
|
|
422
|
+
assert.ok(invoiceLookupByHash);
|
|
423
|
+
const outgoingLookupByInvoice = lookupCalls.find((entry) => entry.params.invoice === "lnbc1outgoingvariant");
|
|
424
|
+
assert.ok(outgoingLookupByInvoice);
|
|
425
|
+
});
|
|
426
|
+
test("nostr+walletconnect adapter accepts result string invoice fallback", async () => {
|
|
427
|
+
const walletPubkey = "1".repeat(64);
|
|
428
|
+
const secret = "2".repeat(64);
|
|
429
|
+
const connectionUri = `nostr+walletconnect://${walletPubkey}?relay=wss://relay.one&secret=${secret}`;
|
|
430
|
+
const calls = [];
|
|
431
|
+
const client = {
|
|
432
|
+
async request(method, params) {
|
|
433
|
+
calls.push({ method, params });
|
|
434
|
+
if (method === "make_invoice") {
|
|
435
|
+
return {
|
|
436
|
+
result_type: "make_invoice",
|
|
437
|
+
result: "lnbc1stringresultinvoice"
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
if (method === "lookup_invoice") {
|
|
441
|
+
return {
|
|
442
|
+
result_type: "lookup_invoice",
|
|
443
|
+
result: {
|
|
444
|
+
invoice: String(params.invoice ?? ""),
|
|
445
|
+
settled_at: Math.floor(Date.now() / 1000)
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
result: {
|
|
451
|
+
preimage: "ok"
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
},
|
|
455
|
+
close() {
|
|
456
|
+
// no-op
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
const adapter = new NostrWalletConnectAdapter(connectionUri, { client });
|
|
460
|
+
const created = await adapter.createInvoice({ amountSats: 1000 });
|
|
461
|
+
assert.equal(created.bolt11, "lnbc1stringresultinvoice");
|
|
462
|
+
assert.equal(created.externalRef, "lnbc1stringresultinvoice");
|
|
463
|
+
const status = await adapter.getInvoiceStatus(created.externalRef);
|
|
464
|
+
assert.equal(status, "paid");
|
|
465
|
+
const lookupCall = calls.find((entry) => entry.method === "lookup_invoice" && entry.params.invoice === "lnbc1stringresultinvoice");
|
|
466
|
+
assert.ok(lookupCall);
|
|
467
|
+
});
|
|
468
|
+
test("wallet service auto-settle is idempotent per event and lightning reference", async () => {
|
|
469
|
+
const dataDir = await createTempDir("emporion-wallet-auto-settle-");
|
|
470
|
+
const originalKey = process.env.EMPORION_WALLET_KEY;
|
|
471
|
+
process.env.EMPORION_WALLET_KEY = "auto-settle-key";
|
|
472
|
+
let payCalls = 0;
|
|
473
|
+
const stubAdapter = {
|
|
474
|
+
async createInvoice() {
|
|
475
|
+
return {
|
|
476
|
+
bolt11: "lnbc1stub",
|
|
477
|
+
externalRef: "inv-stub",
|
|
478
|
+
status: "created"
|
|
479
|
+
};
|
|
480
|
+
},
|
|
481
|
+
async payInvoice() {
|
|
482
|
+
payCalls += 1;
|
|
483
|
+
return {
|
|
484
|
+
externalRef: `pay-${payCalls}`,
|
|
485
|
+
amountSats: 100,
|
|
486
|
+
feeSats: 1,
|
|
487
|
+
status: "succeeded"
|
|
488
|
+
};
|
|
489
|
+
},
|
|
490
|
+
async getInvoiceStatus() {
|
|
491
|
+
return "created";
|
|
492
|
+
},
|
|
493
|
+
async getPaymentStatus() {
|
|
494
|
+
return { status: "succeeded" };
|
|
495
|
+
},
|
|
496
|
+
async disconnect() {
|
|
497
|
+
// no-op
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
try {
|
|
501
|
+
const service = await WalletService.create({
|
|
502
|
+
dataDir,
|
|
503
|
+
env: process.env,
|
|
504
|
+
adapterFactory: () => stubAdapter
|
|
505
|
+
});
|
|
506
|
+
await service.connect("nwc+http://127.0.0.1:7777/rpc?token=stub");
|
|
507
|
+
const first = await service.attemptAutoSettle({
|
|
508
|
+
triggerObjectKind: "offer",
|
|
509
|
+
triggerObjectId: "offer-1",
|
|
510
|
+
eventId: "event-1",
|
|
511
|
+
lightningRef: {
|
|
512
|
+
type: "bolt11",
|
|
513
|
+
network: "bitcoin",
|
|
514
|
+
reference: "lnbc1example"
|
|
515
|
+
},
|
|
516
|
+
amountSats: 100
|
|
517
|
+
});
|
|
518
|
+
assert.equal(first.executed, true);
|
|
519
|
+
assert.equal(first.state, "succeeded");
|
|
520
|
+
const second = await service.attemptAutoSettle({
|
|
521
|
+
triggerObjectKind: "offer",
|
|
522
|
+
triggerObjectId: "offer-1",
|
|
523
|
+
eventId: "event-1",
|
|
524
|
+
lightningRef: {
|
|
525
|
+
type: "bolt11",
|
|
526
|
+
network: "bitcoin",
|
|
527
|
+
reference: "lnbc1example"
|
|
528
|
+
},
|
|
529
|
+
amountSats: 100
|
|
530
|
+
});
|
|
531
|
+
assert.equal(second.executed, false);
|
|
532
|
+
assert.equal(second.deduped, true);
|
|
533
|
+
assert.equal(payCalls, 1);
|
|
534
|
+
const payments = await service.listLedger({ kind: "payment" });
|
|
535
|
+
assert.equal(payments.length, 1);
|
|
536
|
+
await service.close();
|
|
537
|
+
}
|
|
538
|
+
finally {
|
|
539
|
+
if (originalKey === undefined) {
|
|
540
|
+
delete process.env.EMPORION_WALLET_KEY;
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
process.env.EMPORION_WALLET_KEY = originalKey;
|
|
544
|
+
}
|
|
545
|
+
await removeTempDir(dataDir);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
test("wallet CLI commands validate options and usage output includes wallet family", async () => {
|
|
549
|
+
const dataDir = await createTempDir("emporion-wallet-cli-");
|
|
550
|
+
const previousKey = process.env.EMPORION_WALLET_KEY;
|
|
551
|
+
const walletPubkey = "c".repeat(64);
|
|
552
|
+
const secret = "d".repeat(64);
|
|
553
|
+
try {
|
|
554
|
+
delete process.env.EMPORION_WALLET_KEY;
|
|
555
|
+
const helpCapture = createCaptureIo();
|
|
556
|
+
assert.equal(await runCli(["wallet", "status", "--help"], helpCapture.io), 0);
|
|
557
|
+
assert.equal(helpCapture.stdout.join("").includes("emporion wallet connect nwc"), true);
|
|
558
|
+
const invalidKindCapture = createCaptureIo();
|
|
559
|
+
assert.equal(await runCli(["wallet", "ledger", "list", "--data-dir", dataDir, "--kind", "bad-kind"], invalidKindCapture.io), 1);
|
|
560
|
+
assert.equal(invalidKindCapture.stderr.join("").includes("--kind must be one of"), true);
|
|
561
|
+
const connectCapture = createCaptureIo();
|
|
562
|
+
assert.equal(await runCli(["wallet", "connect", "nwc", "--data-dir", dataDir, "--connection-uri", "nwc+https://wallet.example/rpc?token=a"], connectCapture.io), 1);
|
|
563
|
+
assert.equal(connectCapture.stderr.join("").includes("Wallet key is required in EMPORION_WALLET_KEY"), true);
|
|
564
|
+
process.env.EMPORION_WALLET_KEY = "cli-nostr-key";
|
|
565
|
+
const nostrConnectCapture = createCaptureIo();
|
|
566
|
+
assert.equal(await runCli([
|
|
567
|
+
"wallet",
|
|
568
|
+
"connect",
|
|
569
|
+
"nwc",
|
|
570
|
+
"--data-dir",
|
|
571
|
+
dataDir,
|
|
572
|
+
"--connection-uri",
|
|
573
|
+
`nostr+walletconnect://${walletPubkey}?relay=wss://relay.example&secret=${secret}`
|
|
574
|
+
], nostrConnectCapture.io), 0);
|
|
575
|
+
const nostrPayload = JSON.parse(nostrConnectCapture.stdout.join(""));
|
|
576
|
+
assert.equal(nostrPayload.endpoint.startsWith(`nostr+walletconnect://${walletPubkey}?relay=`), true);
|
|
577
|
+
}
|
|
578
|
+
finally {
|
|
579
|
+
if (previousKey === undefined) {
|
|
580
|
+
delete process.env.EMPORION_WALLET_KEY;
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
process.env.EMPORION_WALLET_KEY = previousKey;
|
|
584
|
+
}
|
|
585
|
+
await removeTempDir(dataDir);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
test("wallet connect can be executed through a running daemon without daemon restart", async () => {
|
|
589
|
+
const dataDir = await createTempDir("emp-w-proxy-");
|
|
590
|
+
const bootstrap = await createBootstrapNode();
|
|
591
|
+
const nwcServer = await createMockNwcServer({ requireToken: "proxy-token" });
|
|
592
|
+
const previousKey = process.env.EMPORION_WALLET_KEY;
|
|
593
|
+
process.env.EMPORION_WALLET_KEY = "proxy-wallet-key";
|
|
594
|
+
try {
|
|
595
|
+
const daemonStartCapture = createCaptureIo();
|
|
596
|
+
assert.equal(await runCli([
|
|
597
|
+
"daemon",
|
|
598
|
+
"start",
|
|
599
|
+
"--data-dir",
|
|
600
|
+
dataDir,
|
|
601
|
+
"--bootstrap",
|
|
602
|
+
bootstrap.bootstrap[0],
|
|
603
|
+
"--log-level",
|
|
604
|
+
"error"
|
|
605
|
+
], daemonStartCapture.io), 0);
|
|
606
|
+
const connectCapture = createCaptureIo();
|
|
607
|
+
assert.equal(await runCli([
|
|
608
|
+
"wallet",
|
|
609
|
+
"connect",
|
|
610
|
+
"nwc",
|
|
611
|
+
"--data-dir",
|
|
612
|
+
dataDir,
|
|
613
|
+
"--connection-uri",
|
|
614
|
+
nwcServer.connectionUri.replace("test-token", "proxy-token")
|
|
615
|
+
], connectCapture.io), 0);
|
|
616
|
+
const connectPayload = JSON.parse(connectCapture.stdout.join(""));
|
|
617
|
+
assert.equal(connectPayload.wallet.connected, true);
|
|
618
|
+
assert.equal(connectPayload.wallet.autoSettleEnabled, true);
|
|
619
|
+
const statusCapture = createCaptureIo();
|
|
620
|
+
assert.equal(await runCli(["daemon", "status", "--data-dir", dataDir], statusCapture.io), 0);
|
|
621
|
+
const statusPayload = JSON.parse(statusCapture.stdout.join(""));
|
|
622
|
+
assert.equal(statusPayload.status.wallet.connected, true);
|
|
623
|
+
assert.equal(statusPayload.status.wallet.autoSettleEnabled, true);
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
await Promise.allSettled([forceStopDaemon(dataDir), bootstrap.destroy(), nwcServer.close(), removeTempDir(dataDir)]);
|
|
627
|
+
if (previousKey === undefined) {
|
|
628
|
+
delete process.env.EMPORION_WALLET_KEY;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
process.env.EMPORION_WALLET_KEY = previousKey;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
test("wallet invoice create succeeds through daemon proxy when backend exceeds default IPC timeout", async () => {
|
|
636
|
+
const dataDir = await createTempDir("emp-w-proxy-invoice-");
|
|
637
|
+
const bootstrap = await createBootstrapNode();
|
|
638
|
+
const nwcServer = await createMockNwcServer({
|
|
639
|
+
requireToken: "proxy-slow-token",
|
|
640
|
+
createInvoiceDelayMs: 6_000
|
|
641
|
+
});
|
|
642
|
+
const previousKey = process.env.EMPORION_WALLET_KEY;
|
|
643
|
+
process.env.EMPORION_WALLET_KEY = "proxy-wallet-key";
|
|
644
|
+
try {
|
|
645
|
+
const daemonStartCapture = createCaptureIo();
|
|
646
|
+
assert.equal(await runCli([
|
|
647
|
+
"daemon",
|
|
648
|
+
"start",
|
|
649
|
+
"--data-dir",
|
|
650
|
+
dataDir,
|
|
651
|
+
"--bootstrap",
|
|
652
|
+
bootstrap.bootstrap[0],
|
|
653
|
+
"--log-level",
|
|
654
|
+
"error"
|
|
655
|
+
], daemonStartCapture.io), 0);
|
|
656
|
+
const connectCapture = createCaptureIo();
|
|
657
|
+
assert.equal(await runCli([
|
|
658
|
+
"wallet",
|
|
659
|
+
"connect",
|
|
660
|
+
"nwc",
|
|
661
|
+
"--data-dir",
|
|
662
|
+
dataDir,
|
|
663
|
+
"--connection-uri",
|
|
664
|
+
nwcServer.connectionUri.replace("test-token", "proxy-slow-token")
|
|
665
|
+
], connectCapture.io), 0);
|
|
666
|
+
const invoiceCapture = createCaptureIo();
|
|
667
|
+
assert.equal(await runCli([
|
|
668
|
+
"wallet",
|
|
669
|
+
"invoice",
|
|
670
|
+
"create",
|
|
671
|
+
"--data-dir",
|
|
672
|
+
dataDir,
|
|
673
|
+
"--amount-sats",
|
|
674
|
+
"1000",
|
|
675
|
+
"--memo",
|
|
676
|
+
"slow invoice"
|
|
677
|
+
], invoiceCapture.io), 0);
|
|
678
|
+
const invoicePayload = JSON.parse(invoiceCapture.stdout.join(""));
|
|
679
|
+
assert.equal(invoicePayload.command, "wallet.invoice.create");
|
|
680
|
+
assert.equal(invoicePayload.bolt11, "lnbc1delayed");
|
|
681
|
+
}
|
|
682
|
+
finally {
|
|
683
|
+
await Promise.allSettled([forceStopDaemon(dataDir), bootstrap.destroy(), nwcServer.close(), removeTempDir(dataDir)]);
|
|
684
|
+
if (previousKey === undefined) {
|
|
685
|
+
delete process.env.EMPORION_WALLET_KEY;
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
process.env.EMPORION_WALLET_KEY = previousKey;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
test("daemon wallet runtime auto-settles accepted offers and recovers pending payments after restart", async () => {
|
|
693
|
+
const dataDir = await createTempDir("emporion-wallet-daemon-");
|
|
694
|
+
const bootstrap = await createBootstrapNode();
|
|
695
|
+
const nwcServer = await createMockNwcServer({ requireToken: "daemon-token" });
|
|
696
|
+
const previousKey = process.env.EMPORION_WALLET_KEY;
|
|
697
|
+
process.env.EMPORION_WALLET_KEY = "daemon-wallet-key";
|
|
698
|
+
try {
|
|
699
|
+
const connectCapture = createCaptureIo();
|
|
700
|
+
assert.equal(await runCli([
|
|
701
|
+
"wallet",
|
|
702
|
+
"connect",
|
|
703
|
+
"nwc",
|
|
704
|
+
"--data-dir",
|
|
705
|
+
dataDir,
|
|
706
|
+
"--connection-uri",
|
|
707
|
+
nwcServer.connectionUri.replace("test-token", "daemon-token")
|
|
708
|
+
], connectCapture.io), 0);
|
|
709
|
+
const startCapture = createCaptureIo();
|
|
710
|
+
assert.equal(await runCli([
|
|
711
|
+
"daemon",
|
|
712
|
+
"start",
|
|
713
|
+
"--data-dir",
|
|
714
|
+
dataDir,
|
|
715
|
+
"--bootstrap",
|
|
716
|
+
bootstrap.bootstrap[0],
|
|
717
|
+
"--marketplace",
|
|
718
|
+
"coding",
|
|
719
|
+
"--agent-topic",
|
|
720
|
+
"--log-level",
|
|
721
|
+
"error"
|
|
722
|
+
], startCapture.io), 0);
|
|
723
|
+
const offerCapture = createCaptureIo();
|
|
724
|
+
assert.equal(await runCli([
|
|
725
|
+
"market",
|
|
726
|
+
"offer",
|
|
727
|
+
"submit",
|
|
728
|
+
"--data-dir",
|
|
729
|
+
dataDir,
|
|
730
|
+
"--marketplace",
|
|
731
|
+
"coding",
|
|
732
|
+
"--amount-sats",
|
|
733
|
+
"1000",
|
|
734
|
+
"--lightning-ref",
|
|
735
|
+
"bolt11:bitcoin:lnbc1auto"
|
|
736
|
+
], offerCapture.io), 0);
|
|
737
|
+
const offerPayload = JSON.parse(offerCapture.stdout.join(""));
|
|
738
|
+
const acceptCapture = createCaptureIo();
|
|
739
|
+
assert.equal(await runCli(["market", "offer", "accept", "--data-dir", dataDir, "--id", offerPayload.objectId], acceptCapture.io), 0);
|
|
740
|
+
await waitFor(async () => nwcServer.state.payInvoiceCalls === 1, {
|
|
741
|
+
timeoutMs: 12_000,
|
|
742
|
+
message: "Daemon auto-settle did not call pay_invoice"
|
|
743
|
+
});
|
|
744
|
+
await waitFor(async () => {
|
|
745
|
+
const ledgerCapture = createCaptureIo();
|
|
746
|
+
const exitCode = await runCli(["wallet", "ledger", "list", "--data-dir", dataDir, "--kind", "payment", "--status", "pending"], ledgerCapture.io);
|
|
747
|
+
if (exitCode !== 0) {
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
const payload = JSON.parse(ledgerCapture.stdout.join(""));
|
|
751
|
+
return payload.entries.length === 1;
|
|
752
|
+
}, {
|
|
753
|
+
timeoutMs: 12_000,
|
|
754
|
+
message: "Pending payment was not persisted"
|
|
755
|
+
});
|
|
756
|
+
await forceStopDaemon(dataDir);
|
|
757
|
+
// Simulate settlement while daemon is offline, then verify poll recovery after restart.
|
|
758
|
+
nwcServer.state.paymentStatusByRef.set("pay-1", "succeeded");
|
|
759
|
+
const restartCapture = createCaptureIo();
|
|
760
|
+
assert.equal(await runCli([
|
|
761
|
+
"daemon",
|
|
762
|
+
"start",
|
|
763
|
+
"--data-dir",
|
|
764
|
+
dataDir,
|
|
765
|
+
"--bootstrap",
|
|
766
|
+
bootstrap.bootstrap[0],
|
|
767
|
+
"--marketplace",
|
|
768
|
+
"coding",
|
|
769
|
+
"--agent-topic",
|
|
770
|
+
"--log-level",
|
|
771
|
+
"error"
|
|
772
|
+
], restartCapture.io), 0);
|
|
773
|
+
await waitFor(async () => {
|
|
774
|
+
const ledgerCapture = createCaptureIo();
|
|
775
|
+
const exitCode = await runCli(["wallet", "ledger", "list", "--data-dir", dataDir, "--kind", "payment", "--status", "succeeded"], ledgerCapture.io);
|
|
776
|
+
if (exitCode !== 0) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
const payload = JSON.parse(ledgerCapture.stdout.join(""));
|
|
780
|
+
return payload.entries.length === 1;
|
|
781
|
+
}, {
|
|
782
|
+
timeoutMs: 12_000,
|
|
783
|
+
message: "Daemon restart did not recover pending payment state"
|
|
784
|
+
});
|
|
785
|
+
const statusCapture = createCaptureIo();
|
|
786
|
+
assert.equal(await runCli(["daemon", "status", "--data-dir", dataDir], statusCapture.io), 0);
|
|
787
|
+
const statusPayload = JSON.parse(statusCapture.stdout.join(""));
|
|
788
|
+
assert.equal(statusPayload.status.wallet.connected, true);
|
|
789
|
+
assert.equal(statusPayload.status.wallet.backend, "nwc");
|
|
790
|
+
assert.equal(statusPayload.status.wallet.network, "bitcoin");
|
|
791
|
+
assert.equal(statusPayload.status.wallet.autoSettleEnabled, true);
|
|
792
|
+
}
|
|
793
|
+
finally {
|
|
794
|
+
await Promise.allSettled([
|
|
795
|
+
forceStopDaemon(dataDir),
|
|
796
|
+
bootstrap.destroy(),
|
|
797
|
+
nwcServer.close(),
|
|
798
|
+
removeTempDir(dataDir)
|
|
799
|
+
]);
|
|
800
|
+
if (previousKey === undefined) {
|
|
801
|
+
delete process.env.EMPORION_WALLET_KEY;
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
process.env.EMPORION_WALLET_KEY = previousKey;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
test("wallet daemon starts with locked wallet when encrypted config exists without EMPORION_WALLET_KEY", async () => {
|
|
809
|
+
const dataDir = await createTempDir("emporion-wallet-daemon-key-");
|
|
810
|
+
const bootstrap = await createBootstrapNode();
|
|
811
|
+
const nwcServer = await createMockNwcServer({ requireToken: "key-test-token" });
|
|
812
|
+
const previousKey = process.env.EMPORION_WALLET_KEY;
|
|
813
|
+
try {
|
|
814
|
+
process.env.EMPORION_WALLET_KEY = "initial-key";
|
|
815
|
+
const connectCapture = createCaptureIo();
|
|
816
|
+
assert.equal(await runCli([
|
|
817
|
+
"wallet",
|
|
818
|
+
"connect",
|
|
819
|
+
"nwc",
|
|
820
|
+
"--data-dir",
|
|
821
|
+
dataDir,
|
|
822
|
+
"--connection-uri",
|
|
823
|
+
nwcServer.connectionUri.replace("test-token", "key-test-token")
|
|
824
|
+
], connectCapture.io), 0);
|
|
825
|
+
delete process.env.EMPORION_WALLET_KEY;
|
|
826
|
+
const capture = createCaptureIo();
|
|
827
|
+
assert.equal(await runCli([
|
|
828
|
+
"daemon",
|
|
829
|
+
"start",
|
|
830
|
+
"--data-dir",
|
|
831
|
+
dataDir,
|
|
832
|
+
"--bootstrap",
|
|
833
|
+
bootstrap.bootstrap[0],
|
|
834
|
+
"--log-level",
|
|
835
|
+
"error"
|
|
836
|
+
], capture.io), 0);
|
|
837
|
+
const statusCapture = createCaptureIo();
|
|
838
|
+
assert.equal(await runCli(["daemon", "status", "--data-dir", dataDir], statusCapture.io), 0);
|
|
839
|
+
const statusPayload = JSON.parse(statusCapture.stdout.join(""));
|
|
840
|
+
assert.equal(statusPayload.status.wallet.connected, true);
|
|
841
|
+
assert.equal(statusPayload.status.wallet.autoSettleEnabled, false);
|
|
842
|
+
}
|
|
843
|
+
finally {
|
|
844
|
+
await Promise.allSettled([forceStopDaemon(dataDir), bootstrap.destroy(), nwcServer.close(), removeTempDir(dataDir)]);
|
|
845
|
+
if (previousKey === undefined) {
|
|
846
|
+
delete process.env.EMPORION_WALLET_KEY;
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
process.env.EMPORION_WALLET_KEY = previousKey;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
//# sourceMappingURL=wallet.test.js.map
|