Initial Commit

This commit is contained in:
Danny Morabito 2025-07-09 12:45:59 +02:00
commit 2d858727b0
Signed by: dannym
GPG key ID: 7CC8056A5A04557E
10 changed files with 1128 additions and 0 deletions

107
cashu.ts Normal file
View file

@ -0,0 +1,107 @@
import { CashuMint, type Proof } from "@cashu/cashu-ts";
import { CashuWallet } from "@cashu/cashu-ts";
import NpubCash from "./npubCash";
import type { CashuStore, CashuTxn } from "./paymentStatus";
import PortalBtcWallet from ".";
export default class PortalBtcWalletCashu {
private npubCash: NpubCash;
private cashuTxns: CashuTxn[] = [];
private proofs: Proof[] = [];
private onBalanceUpdated: (() => void) | null = null;
get txns() {
return this.cashuTxns;
}
get cashuWallet() {
const mint = new CashuMint(PortalBtcWallet.MINT_URL);
return new CashuWallet(mint);
}
get balance() {
return this.proofs.reduce((sum, p) => sum + p.amount, 0);
}
constructor(
mnemonic: string,
private cashuStore: CashuStore,
) {
this.npubCash = new NpubCash(mnemonic);
}
async init(onBalanceUpdated: () => void) {
this.cashuTxns = await this.cashuStore.getTxns();
this.proofs = await this.cashuStore.getProofs();
this.onBalanceUpdated = onBalanceUpdated;
}
async redeemCashuQuotes() {
const attempt = this.npubCash.tryRedeemUnredeemedCashuQuotes(
await this.cashuStore.getLastRedeemedCashuQuoteTimestamp(),
);
for await (const quote of attempt) {
this.cashuTxns.push({
txId: `cashu-quote-${quote.quoteId}`,
paymentType: "receive",
amountSat: quote.amountSat,
timestamp: quote.timestamp,
status: "complete",
});
}
const proofs = await attempt.next();
if (!proofs.done || typeof proofs.value === "undefined") return [];
this.proofs.push(...proofs.value);
await this.persistState();
this.onBalanceUpdated?.();
return proofs.value;
}
async persistState() {
await this.cashuStore.persistProofs(this.proofs);
await this.cashuStore.persistTxns(this.cashuTxns);
await this.cashuStore.persistLastRedeemedCashuQuoteTimestamp(
this.cashuTxns[this.cashuTxns.length - 1]?.timestamp ?? 0,
);
}
async meltProofsToPayInvoice(invoice: string) {
const meltQuote = await this.cashuWallet.createMeltQuote(invoice);
const amountToMelt = meltQuote.amount + meltQuote.fee_reserve;
const { keep, send } = await this.cashuWallet.send(
amountToMelt,
this.proofs,
{
includeFees: true,
},
);
const { change } = await this.cashuWallet.meltProofs(meltQuote, send);
this.proofs = [...keep, ...change];
this.cashuTxns.push({
txId: `cashu-send-${Date.now()}`,
paymentType: "send",
amountSat: amountToMelt,
timestamp: Math.floor(Date.now() / 1000),
status: "complete",
});
await this.persistState();
this.onBalanceUpdated?.();
}
async redeemToken(token: string): Promise<void> {
if (!token.trim()) throw new Error("Token is empty");
const received = await this.cashuWallet.receive(token.trim());
this.proofs.push(...received);
const amountReceived = received.reduce((sum, p) => sum + p.amount, 0);
this.cashuTxns.push({
txId: `cashu-receive-${Date.now()}`,
paymentType: "receive",
amountSat: amountReceived,
timestamp: Math.floor(Date.now() / 1000),
status: "complete",
});
await this.persistState();
this.onBalanceUpdated?.();
}
}