From 7d7f2f275f154e3e276fbbfb78b09c9e794f01b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 11 Jun 2026 19:48:56 +0000 Subject: [PATCH] refactor(quartz): dedicated NutzapEvent.buildToUser for profile nutzaps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restores the non-null zappedEvent on NutzapEvent.build and adds a separate buildToUser builder (p tag only, no e/k tags) for nutzaps that target a profile instead of an event — mirroring NIP-57's profile zap convention. CashuWalletOps.sendNutzap dispatches between the two. https://claude.ai/code/session_01UERRsbDoRPz46Qx5HCXgAa --- .../model/nip60Cashu/CashuWalletOps.kt | 33 ++++++++++++------- .../quartz/nip61Nutzaps/nutzap/NutzapEvent.kt | 33 ++++++++++++++----- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletOps.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletOps.kt index be93f51034..ee9e9f50ff 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletOps.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletOps.kt @@ -602,9 +602,10 @@ class CashuWalletOps( * * Spends [available] proofs at [mintUrl] to produce P2PK-locked outputs * worth [amountSats] for [recipientPubKey]'s [recipientP2pkPubkeyHex]. - * Publishes a kind:9321 with the locked proofs + message, rolls leftover - * change into a new kind:7375, NIP-09-deletes the source token events, - * and records the spend in kind:7376. + * Publishes a kind:9321 with the locked proofs + message (referencing + * [zappedEvent], or p-tag-only when null for a profile nutzap), rolls + * leftover change into a new kind:7375, NIP-09-deletes the source token + * events, and records the spend in kind:7376. * * Throws if `available` doesn't sum to at least [amountSats]. */ @@ -637,14 +638,24 @@ class CashuWalletOps( // Build the kind:9321 first so we have its id to reference from history. val proofJsons = swap.send.map { nutzapProofJson.encodeToString(NutzapProofJson.serializer(), it.toNutzapJson()) } val nutzapTemplate = - NutzapEvent.build( - message = message, - proofs = proofJsons, - mintUrl = mintUrl, - unit = "sat", - zappedEvent = zappedEvent, - recipientPubKey = recipientPubKey, - ) + if (zappedEvent != null) { + NutzapEvent.build( + message = message, + proofs = proofJsons, + mintUrl = mintUrl, + unit = "sat", + zappedEvent = zappedEvent, + recipientPubKey = recipientPubKey, + ) + } else { + NutzapEvent.buildToUser( + message = message, + proofs = proofJsons, + mintUrl = mintUrl, + unit = "sat", + recipientPubKey = recipientPubKey, + ) + } val nutzapEvent = signer.sign(nutzapTemplate) publish(nutzapEvent) // kind:9321 is out — recipient's auto-redeem can fire from here. diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip61Nutzaps/nutzap/NutzapEvent.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip61Nutzaps/nutzap/NutzapEvent.kt index 2ad47ddc9d..e33c5d5d14 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip61Nutzaps/nutzap/NutzapEvent.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip61Nutzaps/nutzap/NutzapEvent.kt @@ -62,16 +62,35 @@ class NutzapEvent( const val KIND = 9321 const val ALT_DESCRIPTION = "Nutzap" - /** - * [zappedEvent] is optional: a nutzap aimed at a profile rather than a - * specific event carries only the `p` tag (the `e`/`k` tags are omitted). - */ fun build( message: String, proofs: List, mintUrl: String, unit: String, - zappedEvent: EventHintBundle?, + zappedEvent: EventHintBundle, + recipientPubKey: HexKey, + createdAt: Long = TimeUtils.now(), + initializer: TagArrayBuilder.() -> Unit = {}, + ) = eventTemplate(KIND, message, createdAt) { + alt(ALT_DESCRIPTION) + proofs(proofs) + mintUrl(mintUrl) + unit(unit) + zappedEvent(zappedEvent) + zappedEventKind(zappedEvent.event.kind) + recipient(recipientPubKey) + initializer() + } + + /** + * Nutzap aimed at a profile rather than a specific event: carries only + * the `p` tag (no `e`/`k` tags), mirroring NIP-57's profile zaps. + */ + fun buildToUser( + message: String, + proofs: List, + mintUrl: String, + unit: String, recipientPubKey: HexKey, createdAt: Long = TimeUtils.now(), initializer: TagArrayBuilder.() -> Unit = {}, @@ -80,10 +99,6 @@ class NutzapEvent( proofs(proofs) mintUrl(mintUrl) unit(unit) - if (zappedEvent != null) { - zappedEvent(zappedEvent) - zappedEventKind(zappedEvent.event.kind) - } recipient(recipientPubKey) initializer() }