Skip to content

Commit f61d02f

Browse files
committed
cmd/loop: warn user about low-confirmation deposits before loop-in
Display a warning when selected deposits have fewer than 6 confirmations, since the swap payment for those won't be received immediately. Works for both manually selected and auto-selected deposits by deriving confirmation count from the CSV expiry and blocks-until-expiry fields.
1 parent 87afcde commit f61d02f

File tree

2 files changed

+147
-0
lines changed

2 files changed

+147
-0
lines changed

cmd/loop/staticaddr.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"strings"
78

89
"github.com/lightninglabs/loop/labels"
910
"github.com/lightninglabs/loop/looprpc"
@@ -555,6 +556,13 @@ func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error {
555556
return errors.New("no deposited outputs available")
556557
}
557558

559+
summary, err := client.GetStaticAddressSummary(
560+
ctx, &looprpc.StaticAddressSummaryRequest{},
561+
)
562+
if err != nil {
563+
return err
564+
}
565+
558566
var depositOutpoints []string
559567
switch {
560568
case isAllSelected && isUtxoSelected:
@@ -609,6 +617,22 @@ func staticAddressLoopIn(ctx context.Context, cmd *cli.Command) error {
609617
return err
610618
}
611619

620+
// Warn the user if any selected deposits have fewer than 6
621+
// confirmations, as the swap payment won't be received immediately
622+
// for those.
623+
depositsToCheck := depositOutpoints
624+
if autoSelectDepositsForQuote {
625+
// When auto-selecting, any deposit could be chosen.
626+
depositsToCheck = depositsToOutpoints(allDeposits)
627+
}
628+
warning := lowConfDepositWarning(
629+
allDeposits, depositsToCheck,
630+
int64(summary.RelativeExpiryBlocks),
631+
)
632+
if warning != "" {
633+
fmt.Println(warning)
634+
}
635+
612636
if !(cmd.Bool("force") || cmd.Bool("f")) {
613637
err = displayInDetails(quoteReq, quote, cmd.Bool("verbose"))
614638
if err != nil {
@@ -664,6 +688,79 @@ func depositsToOutpoints(deposits []*looprpc.Deposit) []string {
664688
return outpoints
665689
}
666690

691+
// minImmediateConfs is the minimum number of confirmations a deposit needs
692+
// for the swap payment to be executed immediately.
693+
const minImmediateConfs = 6
694+
695+
// lowConfDepositWarning checks the selected deposits for fewer than 6
696+
// confirmations and returns a warning string if any are found. The swap
697+
// payment for such deposits won't be received immediately.
698+
func lowConfDepositWarning(allDeposits []*looprpc.Deposit,
699+
selectedOutpoints []string, csvExpiry int64) string {
700+
701+
depositMap := make(map[string]*looprpc.Deposit, len(allDeposits))
702+
for _, d := range allDeposits {
703+
depositMap[d.Outpoint] = d
704+
}
705+
706+
var lowConfEntries []string
707+
for _, op := range selectedOutpoints {
708+
d, ok := depositMap[op]
709+
if !ok {
710+
continue
711+
}
712+
713+
var confs int64
714+
switch {
715+
case d.ConfirmationHeight <= 0:
716+
confs = 0
717+
718+
case csvExpiry > 0:
719+
// For confirmed deposits we can compute
720+
// confirmations as CSVExpiry - BlocksUntilExpiry + 1.
721+
confs = csvExpiry - d.BlocksUntilExpiry + 1
722+
723+
default:
724+
// Can't determine confirmations without the CSV expiry.
725+
continue
726+
}
727+
728+
if confs >= minImmediateConfs {
729+
continue
730+
}
731+
732+
if confs == 0 {
733+
lowConfEntries = append(
734+
lowConfEntries,
735+
fmt.Sprintf(" - %s (unconfirmed)", op),
736+
)
737+
} else {
738+
lowConfEntries = append(
739+
lowConfEntries,
740+
fmt.Sprintf(
741+
" - %s (%d confirmations)", op,
742+
confs,
743+
),
744+
)
745+
}
746+
}
747+
748+
if len(lowConfEntries) == 0 {
749+
return ""
750+
}
751+
752+
return fmt.Sprintf(
753+
"\nWARNING: The following deposits have fewer than %d "+
754+
"confirmations:\n%s\n"+
755+
"The swap payment for these deposits may not be "+
756+
"received immediately.\nOnly deposits with %d or "+
757+
"more confirmations are executed immediately.\n",
758+
minImmediateConfs,
759+
strings.Join(lowConfEntries, "\n"),
760+
minImmediateConfs,
761+
)
762+
}
763+
667764
func displayNewAddressWarning() error {
668765
fmt.Printf("\nWARNING: Be aware that loosing your l402.token file in " +
669766
".loop under your home directory will take your ability to " +

cmd/loop/staticaddr_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package main
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/lightninglabs/loop/looprpc"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestLowConfDepositWarningConfirmedOnly(t *testing.T) {
12+
t.Parallel()
13+
14+
deposits := []*looprpc.Deposit{
15+
{
16+
Outpoint: "confirmed-low",
17+
ConfirmationHeight: 100,
18+
BlocksUntilExpiry: 140,
19+
},
20+
{
21+
Outpoint: "confirmed-high",
22+
ConfirmationHeight: 95,
23+
BlocksUntilExpiry: 139,
24+
},
25+
}
26+
27+
warning := lowConfDepositWarning(
28+
deposits, []string{"confirmed-low", "confirmed-high"}, 144,
29+
)
30+
31+
require.Contains(t, warning, "confirmed-low (5 confirmations)")
32+
require.NotContains(t, warning, "confirmed-high")
33+
}
34+
35+
func TestLowConfDepositWarningUnconfirmed(t *testing.T) {
36+
t.Parallel()
37+
38+
deposits := []*looprpc.Deposit{
39+
{
40+
Outpoint: "mempool",
41+
ConfirmationHeight: 0,
42+
BlocksUntilExpiry: 144,
43+
},
44+
}
45+
46+
warning := lowConfDepositWarning(deposits, []string{"mempool"}, 144)
47+
48+
require.Contains(t, warning, "mempool (unconfirmed)")
49+
require.True(t, strings.Contains(warning, "fewer than 6 confirmations"))
50+
}

0 commit comments

Comments
 (0)