import type { Proof } from "@cashu/cashu-ts"; import type NDK from "@nostr-dev-kit/ndk"; import { type CashuPaymentInfo, type LnPaymentInfo, NDKCashuWalletTx, type NDKNutzap, type NDKPaymentConfirmationLN, type NDKRelaySet, NDKUser, proofsTotalBalance, } from "@nostr-dev-kit/ndk"; import { getBolt11Amount, getBolt11Description } from "../../../utils/ln"; import type { MintUrl } from "../mint/utils"; import type { TokenCreationResult } from "../pay/nut"; import type { WalletOperation } from "./effect"; import type { PaymentWithOptionalZapInfo } from "./payment"; import type { UpdateStateResult } from "./state/update"; /** * Creates a tx event for outgoing payment, this means we are spending cashu proofs. * * Nutzaps are only here to indicate that we have redeemed nutzaps and spent them into a non-NIP-60 wallet, * like when redeeming nutzaps into an NWC wallet. */ export async function createOutTxEvent( ndk: NDK, paymentRequest: PaymentWithOptionalZapInfo, paymentResult: WalletOperation, relaySet?: NDKRelaySet, { nutzaps }: { nutzaps?: NDKNutzap[] } = {}, ): Promise { let description: string | undefined = paymentRequest.paymentDescription; let amount: number | undefined; if ((paymentRequest as LnPaymentInfo).pr) { amount = getBolt11Amount((paymentRequest as LnPaymentInfo).pr); description ??= getBolt11Description((paymentRequest as LnPaymentInfo).pr); if (amount) amount /= 1000; // convert to sats } else { amount = paymentRequest.amount; } if (!amount) { console.error("BUG: Unable to find amount for paymentRequest", paymentRequest); } const txEvent = new NDKCashuWalletTx(ndk); txEvent.direction = "out"; txEvent.amount = amount ?? 0; txEvent.mint = paymentResult.mint; txEvent.description = description; if (paymentResult.fee) txEvent.fee = paymentResult.fee; if (paymentRequest.target) { // tag the target if there is one txEvent.tags.push(paymentRequest.target.tagReference()); if (!(paymentRequest.target instanceof NDKUser)) { txEvent.tags.push(["p", paymentRequest.target.pubkey]); } } if (nutzaps) { txEvent.description ??= "nutzap redeem"; for (const nutzap of nutzaps) txEvent.addRedeemedNutzap(nutzap); } if (paymentResult.stateUpdate?.created) txEvent.createdTokens = [paymentResult.stateUpdate.created]; if (paymentResult.stateUpdate?.deleted) txEvent.destroyedTokenIds = paymentResult.stateUpdate.deleted; if (paymentResult.stateUpdate?.reserved) txEvent.reservedTokens = [paymentResult.stateUpdate.reserved]; await txEvent.sign(); txEvent.publish(relaySet); return txEvent; } export async function createInTxEvent( ndk: NDK, proofs: Proof[], mint: MintUrl, updateStateResult: UpdateStateResult, { nutzaps, fee, description }: { nutzaps?: NDKNutzap[]; fee?: number; description?: string }, relaySet?: NDKRelaySet, ): Promise { const txEvent = new NDKCashuWalletTx(ndk); const amount = proofsTotalBalance(proofs); txEvent.direction = "in"; txEvent.amount = amount; txEvent.mint = mint; txEvent.description = description; if (updateStateResult.created) txEvent.createdTokens = [updateStateResult.created]; if (updateStateResult.deleted) txEvent.destroyedTokenIds = updateStateResult.deleted; if (updateStateResult.reserved) txEvent.reservedTokens = [updateStateResult.reserved]; if (nutzaps) for (const nutzap of nutzaps) txEvent.addRedeemedNutzap(nutzap); if (fee) txEvent.fee = fee; await txEvent.sign(); txEvent.publish(relaySet); return txEvent; }