@nostr-dev-kit/ndk-wallet
Version:
99 lines (86 loc) • 3.23 kB
text/typescript
import type { Proof } from "@cashu/cashu-ts";
import type { CashuWallet } from "@cashu/cashu-ts";
import type { MintUrl } from "../mint/utils";
import { calculateFee } from "./fee";
import type { NDKCashuWallet } from "./index.js";
import type { WalletProofChange } from "./state/index";
import type { UpdateStateResult } from "./state/update";
type WithProofReserveCb<T> = {
result: T;
change: Proof[];
};
/**
* The result of performing an operation with the wallet state.
*
* This is a wrapper around the result of the operation that might include
* results of a payment (`result`).
*
* The rest of the fields are used to update the wallet state and generate
* tx events.
*/
export type WalletOperation<T> = {
result?: T;
proofsChange: WalletProofChange | null;
stateUpdate: UpdateStateResult | null;
mint: MintUrl;
fee: number;
};
/**
* This function is used to reserve proofs for a given mint.
* It will reserve the proofs for the given mint, and then call the callback function.
* If the callback function returns a result, the wallet state will be updated based on the result.
* If the callback function returns null, the proofs will be unreserved.
*
* Use this function to wrap any function that might affect the wallet state.
*
* @param wallet
* @param cashuWallet
* @param mint
* @param amountWithFees - The amount of proofs to gather including fees (include fees)
* @param intendedAmount - The amount of proofs the payment originally was intended to gather (exclude fees)
* @param cb
* @returns
*/
export async function withProofReserve<T>(
wallet: NDKCashuWallet,
cashuWallet: CashuWallet | undefined,
mint: MintUrl,
amountWithFees: number,
amountWithoutFees: number,
cb: (proofsToUse: Proof[], allOurProofs: Proof[]) => Promise<WithProofReserveCb<T> | null>,
): Promise<WalletOperation<T> | null> {
cashuWallet ??= await wallet.getCashuWallet(mint);
const availableMintProofs = wallet.state.getProofs({ mint, onlyAvailable: true });
const proofs = cashuWallet.selectProofsToSend(availableMintProofs, amountWithFees);
const fetchedAmount = proofs.send.reduce((a, b) => a + b.amount, 0);
if (fetchedAmount < amountWithFees) return null;
// reserve the proofs we'll be using
wallet.state.reserveProofs(proofs.send, amountWithFees);
let cbResult: WithProofReserveCb<T> | null = null;
let proofsChange: WalletProofChange | null = null;
let updateRes: UpdateStateResult | null = null;
try {
cbResult = await cb(proofs.send, availableMintProofs);
if (!cbResult) return null;
proofsChange = {
mint,
store: cbResult.change,
destroy: proofs.send,
};
/**
* Update the wallet state.
*/
updateRes = await wallet.state.update(proofsChange);
} catch (e: any) {
wallet.state.unreserveProofs(proofs.send, amountWithFees, "available");
throw e;
}
if (!cbResult) return null;
return {
result: cbResult.result,
proofsChange,
stateUpdate: updateRes,
mint,
fee: calculateFee(amountWithoutFees, proofs.send, cbResult.change),
};
}