mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 16:57:39 +00:00
fix(cashu): correct skip-on-decrypt-fail + auto-resume orphan mint quotes
Two small follow-ups to the audit refactor:
* recomputeUnspent: replace the broken `getOrPut { ... return@forEach }`
pattern (which short-circuited the outer loop on a single decryption
failure, skipping remaining tokens) with an explicit containsKey
guard. Decryption failures are now individually skipped without
affecting other tokens in the same pass.
* CashuWalletScreen: when the wallet opens and pendingQuotes (live
flow from CashuWalletState) is non-empty, automatically resume the
most recent kind:7374 by re-polling the mint and reopening the
receive dialog. Without this, a user who backgrounded the app
mid-mint would see no indication their pending invoice exists.
https://claude.ai/code/session_01MdWddiar819f8XYt5N8BjP
This commit is contained in:
+11
-5
@@ -324,12 +324,18 @@ class CashuWalletState(
|
||||
private suspend fun recomputeUnspent() {
|
||||
val all = tokenEvents.values.toList()
|
||||
// Decrypt anything we haven't seen before; reuse cached TokenContent
|
||||
// for events we've already decrypted.
|
||||
// for events we've already decrypted. Decryption failures are
|
||||
// skipped — the proof set rebuilds the next time a re-key happens.
|
||||
all.forEach { evt ->
|
||||
tokenContents.getOrPut(evt.id) {
|
||||
runCatching { evt.tokenContent(signer) }
|
||||
.onFailure { Log.w("CashuWallet") { "Failed to decrypt token ${evt.id.take(8)}: ${it.message}" } }
|
||||
.getOrNull() ?: return@getOrPut return@forEach
|
||||
if (!tokenContents.containsKey(evt.id)) {
|
||||
val content =
|
||||
runCatching { evt.tokenContent(signer) }
|
||||
.onFailure {
|
||||
Log.w("CashuWallet") {
|
||||
"Failed to decrypt token ${evt.id.take(8)}: ${it.message}"
|
||||
}
|
||||
}.getOrNull()
|
||||
if (content != null) tokenContents[evt.id] = content
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -97,12 +97,23 @@ fun CashuWalletScreen(
|
||||
val mints by viewModel.mints.collectAsState()
|
||||
val balanceSats by viewModel.balanceSats.collectAsState()
|
||||
val history by viewModel.history.collectAsState()
|
||||
val pendingQuotes by viewModel.pendingQuotes.collectAsState()
|
||||
|
||||
var receiveOpen by remember { mutableStateOf(false) }
|
||||
var sendLnOpen by remember { mutableStateOf(false) }
|
||||
var sendTokenOpen by remember { mutableStateOf(false) }
|
||||
var redeemOpen by remember { mutableStateOf(false) }
|
||||
|
||||
// If the user has an unfinished kind:7374 quote, surface it the next time
|
||||
// they open the wallet so the in-flight invoice isn't lost. Auto-resumes
|
||||
// the most recent quote on first composition.
|
||||
LaunchedEffect(pendingQuotes) {
|
||||
if (!receiveOpen && pendingQuotes.isNotEmpty()) {
|
||||
viewModel.resumeMintQuote(pendingQuotes.first())
|
||||
receiveOpen = true
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
|
||||
Reference in New Issue
Block a user