clean code, and use newly created PortalBtcWallet library

This commit is contained in:
Danny Morabito 2025-07-09 18:51:42 +02:00
parent 9825c53c1c
commit 45dfe1a1c7
Signed by: dannym
GPG key ID: 7CC8056A5A04557E
22 changed files with 503 additions and 1087 deletions

View file

@ -4,23 +4,19 @@ import {
generateSecretKey,
getPublicKey,
nip04,
nip19,
SimplePool,
verifyEvent,
} from "nostr-tools";
import type { Event as NostrEvent } from "nostr-tools";
import { derived, get, type Writable, writable } from "svelte/store";
import { derived, get } from "svelte/store";
import { browser } from "$app/environment";
import { getBreezSDK, refreshBalances } from "$lib/breez.svelte";
import type { BindingLiquidSdk } from "@breeztech/breez-sdk-liquid/web";
import {
bytesToHex,
hexToBytes,
type MetaEntry,
persistentDbWritable,
} from "./utils";
import { cashuDB, cashuState, getNpub } from "./cashu.svelte";
import { send } from "./payments";
} from "$lib";
import { walletState } from "$lib/wallet.svelte";
interface PaidInvoice {
paymentHash: string;
@ -81,8 +77,7 @@ interface JsonRpcReq {
| "get_balance"
| "list_transactions"
| "get_info"
| "make_invoice"
| "lookup_invoice";
| "make_invoice";
params: Record<string, unknown>;
}
@ -161,7 +156,6 @@ async function connect(): Promise<void> {
if (!maybePriv) throw new Error("NWC privkey missing");
const privkey = maybePriv;
const pubkey = getPublicKey(hexToBytes(privkey));
const breezSDK = await getBreezSDK();
pool = new SimplePool();
sub = pool.subscribeMany(relays, [
@ -171,7 +165,7 @@ async function connect(): Promise<void> {
},
], {
onevent: async (evt: NostrEvent) => {
await handleEvent(evt, privkey, pubkey, relays, breezSDK);
await handleEvent(evt, privkey, pubkey, relays);
},
});
}
@ -181,7 +175,6 @@ async function handleEvent(
privkey: string,
ourPubkey: string,
relays: string[],
breezSDK: BindingLiquidSdk,
): Promise<void> {
try {
if (!verifyEvent(evt)) return;
@ -231,6 +224,8 @@ async function handleEvent(
async function processRpc(
req: JsonRpcReq,
): Promise<unknown> {
const currentWalletState = get(walletState);
if (!currentWalletState.open) throw new Error("Wallet not open");
console.log("processRpc", req);
switch (req.method) {
case "pay_invoice": {
@ -239,102 +234,51 @@ async function processRpc(
throw { code: "INTERNAL", message: "missing_invoice" };
}
const breezSDK = await getBreezSDK();
const parsed = await breezSDK.parse(destination);
if (parsed.type !== "bolt11") {
throw { code: "INTERNAL", message: "not a bolt11 invoice" };
}
const paymentHash = (parsed as any).invoice.paymentHash;
const existing = await nwcDB.paidInvoices.get(paymentHash);
if (existing) {
throw { code: "OTHER", message: "invoice already paid" };
if (await nwcDB.paidInvoices.get(destination)) {
throw { code: "OTHER", message: "invoice_already_paid" };
}
const sender = send(destination, 0, parsed);
let res = await sender.next();
while (!res.done) {
const sender = currentWalletState.wallet.pay(destination, 0);
let res;
try {
res = await sender.next();
while (!res.done) {
res = await sender.next();
}
} catch (err) {
throw { code: "PAYMENT_FAILED", message: (err as Error).message };
}
if (res.value.error) {
throw { code: "PAYMENT_FAILED", message: res.value.error };
}
await nwcDB.paidInvoices.put({
paymentHash: destination,
timestamp: Math.floor(Date.now() / 1000),
}).catch((err) => {
console.error("Failed to save paid invoice", err);
});
if (res.value.paymentHash) {
await nwcDB.paidInvoices.put({
paymentHash: res.value.paymentHash,
timestamp: Math.floor(Date.now() / 1000),
});
}
await refreshBalances();
return {
preimage: res.value.preimage,
paymentHash: res.value.paymentHash,
};
return {};
// TODO: check if it works without the preimage and paymentHash
// return {
// preimage: res.value.preimage,
// paymentHash: res.value.paymentHash,
// };
}
case "get_balance": {
const breezSDK = await getBreezSDK();
const info = await breezSDK.getInfo();
return {
balance: info.walletInfo.balanceSat * 1000 + get(cashuState).balance *
1000,
balance: currentWalletState.balance * 1000,
};
}
case "list_transactions": {
const from_timestamp =
(req.params?.from_timestamp as number | undefined) ?? 0;
const to_timestamp = (req.params?.to_timestamp as number | undefined) ??
undefined;
const limit = (req.params?.limit as number | undefined) ?? 20;
const offset = (req.params?.offset as number | undefined) ?? undefined;
const offset = (req.params?.offset as number | undefined) ?? 0;
const breezSDK = await getBreezSDK();
const liquidPayments = (await breezSDK.listPayments({
fromTimestamp: from_timestamp,
toTimestamp: to_timestamp,
const transactions = await currentWalletState.wallet.listPayments(
limit,
offset,
})) as any[];
const cashuTxns = await cashuDB.txns.toArray();
const allTxns = [
...liquidPayments.map((p) => ({
type: p.paymentType,
invoice: p.bolt11,
description: p.description,
payment_hash: p.paymentHash,
preimage: p.paymentPreimage,
amount: p.amountSat * 1000,
fees_paid: p.feesSat * 1000,
created_at: Math.floor(p.paymentTime / 1000),
settled_at: p.status === "complete"
? Math.floor(p.paymentTime / 1000)
: undefined,
})),
...cashuTxns.map((p) => ({
type: p.paymentType,
invoice: "",
description: "Cashu",
payment_hash: p.txId,
preimage: "",
amount: p.amountSat * 1000,
fees_paid: 0,
created_at: p.timestamp,
settled_at: p.timestamp,
})),
].sort((a, b) => b.created_at - a.created_at);
return {
transactions: allTxns,
};
);
return { transactions };
}
case "get_info": {
const breezSDK = await getBreezSDK();
const info = await breezSDK.getInfo();
const privkey = get(nwcPrivkey);
if (!privkey) throw new Error("missing_privkey");
@ -349,7 +293,6 @@ async function processRpc(
"list_transactions",
"get_info",
"make_invoice",
"lookup_invoice",
],
notifications: [],
};
@ -361,7 +304,9 @@ async function processRpc(
throw { code: "INTERNAL", message: "missing_amount" };
}
const npub = await getNpub();
const npub = currentWalletState.wallet.npub;
if (!npub) throw new Error("Wallet not open");
const res = await fetch(
`/.well-known/lnurl-pay/callback/${npub}?amount=${amountMsat}`,
);
@ -385,35 +330,6 @@ async function processRpc(
invoice,
};
}
case "lookup_invoice": {
const paymentHash = req.params?.payment_hash as string | undefined;
if (!paymentHash) {
throw { code: "INTERNAL", message: "missing_payment_hash" };
}
const breezSDK = await getBreezSDK();
const payments = (await breezSDK.listPayments({})) as any[];
const payment = payments.find((p) => p.paymentHash === paymentHash);
if (!payment) {
throw { code: "NOT_FOUND", message: "invoice_not_found" };
}
return {
type: payment.paymentType,
invoice: payment.bolt11,
description: payment.description,
payment_hash: payment.paymentHash,
preimage: payment.paymentPreimage,
amount: payment.amountSat * 1000,
fees_paid: payment.feesSat * 1000,
created_at: Math.floor(payment.paymentTime / 1000),
settled_at: payment.status === "complete"
? Math.floor(payment.paymentTime / 1000)
: undefined,
};
}
default:
throw { code: "NOT_IMPLEMENTED", message: "unknown_method" };
}