nip60: don't lose tokens when bolt11 payment fails.

This commit is contained in:
fiatjaf
2026-02-03 19:17:14 -03:00
parent e17995d427
commit beb8a72491
4 changed files with 48 additions and 8 deletions
+40 -8
View File
@@ -94,6 +94,11 @@ meltworked:
nil,
)
// mark tokens as reserved before attempting melt
for _, i := range chosen.tokenIndexes {
w.Tokens[i].reserved = true
}
// request from mint to _melt_ into paying the invoice
delay := 200 * time.Millisecond
// this request will block until the invoice is paid or it fails
@@ -103,17 +108,44 @@ meltworked:
Inputs: chosen.proofs,
Outputs: preChange.bm,
})
inspectmeltstatusresponse:
if err != nil || meltStatus.State == nut05.Unpaid {
return "", fmt.Errorf("error melting token: %w", err)
} else if meltStatus.State == nut05.Unknown {
return "", fmt.Errorf("we don't know what happened with the melt at %s: %v", chosen.mint, meltStatus)
} else if meltStatus.State == nut05.Pending {
for {
for {
if err != nil || meltStatus.State == nut05.Unpaid {
// unreserve tokens to available state on failure
for _, i := range chosen.tokenIndexes {
w.Tokens[i].reserved = false
}
return "", fmt.Errorf("error melting token: %w", err)
} else if meltStatus.State == nut05.Unknown {
// unreserve tokens to available state on failure
for _, i := range chosen.tokenIndexes {
w.Tokens[i].reserved = false
}
return "", fmt.Errorf("we don't know what happened with the melt at %s: %v", chosen.mint, meltStatus)
} else if meltStatus.State == nut05.Pending {
time.Sleep(delay)
delay *= 2
meltStatus, err = client.GetMeltQuoteState(ctx, chosen.mint, meltStatus.Quote)
goto inspectmeltstatusresponse
if err != nil {
// unreserve tokens to available state on failure
for _, i := range chosen.tokenIndexes {
w.Tokens[i].reserved = false
}
return "", fmt.Errorf("error checking melt status: %w", err)
}
if meltStatus.State == nut05.Unpaid || meltStatus.State == nut05.Unknown {
// unreserve tokens to available state on failure
for _, i := range chosen.tokenIndexes {
w.Tokens[i].reserved = false
}
return "", fmt.Errorf("melt failed with state %v", meltStatus.State)
} else if meltStatus.State == nut05.Paid {
// payment successful
break
}
// continue looping for pending state
continue
} else if meltStatus.State == nut05.Paid {
break
}
}
+3
View File
@@ -153,6 +153,9 @@ func (w *Wallet) getProofsForSending(
) (chosenTokens, uint64, error) {
byMint := make(map[string]chosenTokens)
for t, token := range w.Tokens {
if token.reserved {
continue
}
if fromMint != "" && token.Mint != fromMint {
continue
}
+1
View File
@@ -14,6 +14,7 @@ type Token struct {
Proofs cashu.Proofs `json:"proofs"`
Deleted []nostr.ID `json:"del,omitempty"`
reserved bool
mintedAt nostr.Timestamp
event *nostr.Event
}
+4
View File
@@ -249,6 +249,10 @@ func (w *Wallet) removeDeletedToken(eventId nostr.ID) {
func (w *Wallet) Balance() uint64 {
var sum uint64
for _, token := range w.Tokens {
if token.reserved {
continue
}
sum += token.Proofs.Amount()
}
return sum