diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt index 568a427d14..93e89ca49e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt @@ -942,6 +942,14 @@ class AccountSettings( fun updateNutzapInfo(newNutzapInfo: NutzapInfoEvent?) { if (newNutzapInfo == null || newNutzapInfo.tags.isEmpty()) return + // A mints-less kind:10019 is the "stop receiving nutzaps" tombstone + // (an empty replacement carrying only an `alt` tag). Don't restore it + // on next launch — backing it up would undo clearNutzapInfo() once the + // empty event round-trips back through LocalCache. + if (newNutzapInfo.mints().isEmpty()) { + clearNutzapInfo() + return + } if (backupNutzapInfo?.id != newNutzapInfo.id) { backupNutzapInfo = newNutzapInfo saveAccountSettings() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletState.kt index d560f0e368..6388a2e071 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/nip60Cashu/CashuWalletState.kt @@ -563,6 +563,11 @@ class CashuWalletState( // Any wallet event resolves the "discovering" state — whether it // came from cache backfill or a fresh relay delivery. _discovering.value = false + // The NUT-13 seed is derived from the wallet's P2PK key. A new + // kind:17375 may carry a rotated key (our own recreateNutzapKey, or + // a rotation from another client), so drop the cached seed and let + // ensureSeed re-derive from whatever key the live event now holds. + cachedSeed = null walletEventInternal?.let { evt -> _mints.value = runCatching { evt.mints(signer) } @@ -838,10 +843,9 @@ class CashuWalletState( p2pkPrivkeyHex = manualPrivkeyHex?.takeIf { it.isNotBlank() }, nutzapRelays = outboxRelaysFlow.value.toList(), ) - // The NUT-13 seed is derived from the P2PK key, which just changed — - // drop the cache so the next mint op re-derives from the new key - // (re-populated lazily by ensureSeed once the new kind:17375 lands). - cachedSeed = null + // The NUT-13 seed (derived from the P2PK key) is invalidated by + // applyEvents when the new kind:17375 round-trips in, so it re-derives + // from the rotated key. No need to reset it here. } // ============================================================ diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/AddCashuWalletScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/AddCashuWalletScreen.kt index 5f70e64f5c..db7ca683ed 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/AddCashuWalletScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/AddCashuWalletScreen.kt @@ -99,7 +99,11 @@ fun AddCashuWalletScreen( // suggestions on first open instead of waiting for the next relay // round-trip. Cheap (one sweep over the existing cache); idempotent. LaunchedEffect(Unit) { LocalCache.ensureMintDirectoryBackfilled() } - var keyMode by remember { + // Keyed on isEditMode so that if the wallet is delivered AFTER this screen + // first composes (existingWallet null → non-null), keyMode flips to + // KeepCurrent. Otherwise a cold open with an undelivered wallet would keep + // AutoGenerate and silently rotate the key on save, orphaning nutzaps. + var keyMode by remember(isEditMode) { mutableStateOf( if (isEditMode) CashuWalletViewModel.P2pkKeyMode.KeepCurrent else CashuWalletViewModel.P2pkKeyMode.AutoGenerate, )