PortalBTCLib/cashu.ts

104 lines
3.1 KiB
TypeScript

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;
private mint = new CashuMint(PortalBtcWallet.MINT_URL);
private cashuWallet: CashuWallet = new CashuWallet(this.mint);
get txns() {
return this.cashuTxns;
}
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 { proofs, quotes } = await this.npubCash
.tryRedeemUnredeemedCashuQuotes(
await this.cashuStore.getLastRedeemedCashuQuoteTimestamp(),
);
for (const quote of quotes) {
this.cashuTxns.push({
txId: `cashu-quote-${quote.quoteId}`,
paymentType: "receive",
amountSat: quote.amountSat,
timestamp: quote.timestamp,
status: "complete",
});
}
this.proofs.push(...proofs);
await this.persistState();
this.onBalanceUpdated?.();
return proofs;
}
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) {
await this.cashuWallet.loadMint();
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?.();
}
}