nip60: just rely on nut08 overpaid fees to get change when melting, don't try to swap beforehand.

This commit is contained in:
fiatjaf
2025-01-30 09:45:00 -03:00
parent be65134354
commit f0054af4d8
5 changed files with 135 additions and 60 deletions

View File

@@ -26,18 +26,27 @@ func (w *Wallet) PayBolt11(ctx context.Context, invoice string, opts ...SendOpti
var chosen chosenTokens
var meltQuote string
var meltAmount uint64
var meltAmountWithoutFeeReserve uint64
feeReservePct := uint64(1)
feeReserveAbs := uint64(1)
excludeMints := make([]string, 0, 1)
for range 10 {
amount := invoiceAmount*(100+feeReservePct)/100 + feeReserveAbs
var fee uint64
chosen, fee, err = w.getProofsForSending(ctx, amount, ss.specificMint)
chosen, fee, err = w.getProofsForSending(ctx, amount, ss.specificMint, excludeMints)
if err != nil {
return "", err
}
// we will only do this in mints that support nut08
if info, _ := client.GetMintInfo(ctx, chosen.mint); info == nil || !info.Nuts.Nut08.Supported {
excludeMints = append(excludeMints, chosen.mint)
continue
}
// request _melt_ quote (ask the mint how much will it cost to pay a bolt11 invoice)
meltResp, err := client.PostMeltQuoteBolt11(ctx, chosen.mint, nut05.PostMeltQuoteBolt11Request{
Request: invoice,
@@ -49,36 +58,45 @@ func (w *Wallet) PayBolt11(ctx context.Context, invoice string, opts ...SendOpti
// if amount in proofs is not sufficient to pay for the melt request,
// increase the amount and get proofs again (because of lighting fees)
meltQuote = meltResp.Quote
meltAmount = meltResp.Amount + meltResp.FeeReserve + fee
if meltAmount > chosen.proofs.Amount() {
if meltResp.Amount+meltResp.FeeReserve+fee > chosen.proofs.Amount() {
feeReserveAbs++
} else {
meltQuote = meltResp.Quote
meltAmountWithoutFeeReserve = invoiceAmount + fee
goto meltworked
}
}
return "", fmt.Errorf("stop trying to do the melt because the invoice is too expensive")
return "", fmt.Errorf("stopped trying to do the melt because all the mints are charging way too much")
meltworked:
// swap our proofs so we get the exact amount for paying the invoice
principal, change, err := w.SwapProofs(ctx, chosen.mint, chosen.proofs, meltAmount)
activeKeyset, err := client.GetActiveKeyset(ctx, chosen.mint)
if err != nil {
return "", fmt.Errorf("failed to swap at %s into the exact melt amount: %w", chosen.mint, err)
return "", fmt.Errorf("failed to get active keyset for %s: %w", chosen.mint, err)
}
ksKeys, err := parseKeysetKeys(activeKeyset.Keys)
if err != nil {
return "", fmt.Errorf("failed to parse keys for %s: %w", chosen.mint, err)
}
if err := w.saveChangeAndDeleteUsedTokens(ctx, chosen.mint, change, chosen.tokenIndexes); err != nil {
return "", err
}
// since we rely on nut08 we will send all the proofs we've gathered and expect a change
// we do a split here and discard the principal, as we won't get it back from the mint
_, preChange, err := splitIntoPrincipalAndChange(
chosen.keysets,
chosen.proofs,
meltAmountWithoutFeeReserve,
activeKeyset.Id,
nil,
)
// 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
// (but the API also says it can return "pending" so we handle both)
meltStatus, err := client.PostMeltBolt11(ctx, chosen.mint, nut05.PostMeltBolt11Request{
Quote: meltQuote,
Inputs: principal,
Quote: meltQuote,
Inputs: chosen.proofs,
Outputs: preChange.bm,
})
inspectmeltstatusresponse:
if err != nil || meltStatus.State == nut05.Unpaid {
@@ -94,5 +112,15 @@ inspectmeltstatusresponse:
}
}
// the invoice has been paid, now we save the change we got
change, err := constructProofs(preChange, meltStatus.Change, ksKeys)
if err != nil {
return "", fmt.Errorf("failed to construct principal proofs: %w", err)
}
if err := w.saveChangeAndDeleteUsedTokens(ctx, chosen.mint, change, chosen.tokenIndexes); err != nil {
return "", err
}
return meltStatus.Preimage, nil
}