105 lines
3.2 KiB
TypeScript
105 lines
3.2 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 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) {
|
|
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?.();
|
|
}
|
|
}
|