Skip to content

Commit 358ab53

Browse files
committed
loopd: fractional static address swap amount
1 parent 874d55d commit 358ab53

File tree

2 files changed

+53
-47
lines changed

2 files changed

+53
-47
lines changed

cmd/loop/staticaddr.go

+9-29
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,13 @@ var staticAddressLoopInCommand = cli.Command{
458458
"The client can retry the swap with adjusted " +
459459
"parameters after the payment timed out.",
460460
},
461+
cli.IntFlag{
462+
Name: "amount",
463+
Usage: "the number of satoshis that should be " +
464+
"swapped from the selected deposits. If there" +
465+
"is change it is sent back to the static " +
466+
"address.",
467+
},
461468
lastHopFlag,
462469
labelFlag,
463470
routeHintsFlag,
@@ -552,6 +559,7 @@ func staticAddressLoopIn(ctx *cli.Context) error {
552559
}
553560

554561
quoteReq := &looprpc.QuoteRequest{
562+
Amt: ctx.Int64("amount"),
555563
LoopInRouteHints: hints,
556564
LoopInLastHop: lastHop,
557565
Private: ctx.Bool(privateFlag.Name),
@@ -564,15 +572,6 @@ func staticAddressLoopIn(ctx *cli.Context) error {
564572

565573
limits := getInLimits(quote)
566574

567-
// populate the quote request with the sum of selected deposits and
568-
// prompt the user for acceptance.
569-
quoteReq.Amt, err = sumDeposits(
570-
depositOutpoints, depositList.FilteredDeposits,
571-
)
572-
if err != nil {
573-
return err
574-
}
575-
576575
if !(ctx.Bool("force") || ctx.Bool("f")) {
577576
err = displayInDetails(quoteReq, quote, ctx.Bool("verbose"))
578577
if err != nil {
@@ -585,6 +584,7 @@ func staticAddressLoopIn(ctx *cli.Context) error {
585584
}
586585

587586
req := &looprpc.StaticAddressLoopInRequest{
587+
Amount: quoteReq.Amt,
588588
Outpoints: depositOutpoints,
589589
MaxSwapFeeSatoshis: int64(limits.maxSwapFee),
590590
LastHop: lastHop,
@@ -617,26 +617,6 @@ func containsDuplicates(outpoints []string) bool {
617617
return false
618618
}
619619

620-
func sumDeposits(outpoints []string, deposits []*looprpc.Deposit) (int64,
621-
error) {
622-
623-
var sum int64
624-
depositMap := make(map[string]*looprpc.Deposit)
625-
for _, deposit := range deposits {
626-
depositMap[deposit.Outpoint] = deposit
627-
}
628-
629-
for _, outpoint := range outpoints {
630-
if _, ok := depositMap[outpoint]; !ok {
631-
return 0, fmt.Errorf("deposit %v not found", outpoint)
632-
}
633-
634-
sum += depositMap[outpoint].Value
635-
}
636-
637-
return sum, nil
638-
}
639-
640620
func depositsToOutpoints(deposits []*looprpc.Deposit) []string {
641621
outpoints := make([]string, 0, len(deposits))
642622
for _, deposit := range deposits {

loopd/swapclient_server.go

+44-18
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ import (
3434
"github.com/lightninglabs/loop/swap"
3535
"github.com/lightninglabs/loop/swapserverrpc"
3636
"github.com/lightninglabs/taproot-assets/rfqmath"
37+
"github.com/lightningnetwork/lnd/input"
3738
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
3839
"github.com/lightningnetwork/lnd/lntypes"
40+
"github.com/lightningnetwork/lnd/lnwallet"
3941
"github.com/lightningnetwork/lnd/queue"
4042
"github.com/lightningnetwork/lnd/routing/route"
4143
"github.com/lightningnetwork/lnd/zpay32"
@@ -843,21 +845,30 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
843845
log.Infof("Loop in quote request received")
844846

845847
var (
846-
numDeposits = uint32(len(req.DepositOutpoints))
847-
err error
848+
selectedAmount = btcutil.Amount(req.Amt)
849+
selectedDepositAmount btcutil.Amount
850+
numDeposits = len(req.DepositOutpoints)
851+
err error
848852
)
849853

854+
if selectedAmount == 0 && numDeposits == 0 {
855+
return nil, fmt.Errorf("amount and deposit outpoints " +
856+
"cannot both be zero")
857+
}
858+
850859
htlcConfTarget, err := validateLoopInRequest(
851-
req.ConfTarget, req.ExternalHtlc, numDeposits, req.Amt,
860+
req.ConfTarget, req.ExternalHtlc, uint32(numDeposits), req.Amt,
852861
)
853862
if err != nil {
854863
return nil, err
855864
}
856865

857866
// Retrieve deposits to calculate their total value.
858867
var depositList *looprpc.ListStaticAddressDepositsResponse
859-
amount := btcutil.Amount(req.Amt)
860-
if len(req.DepositOutpoints) > 0 {
868+
869+
// If deposits are selected, we need to retrieve them to calculate the
870+
// total value which we request a quote for. If a
871+
if numDeposits > 0 {
861872
depositList, err = s.ListStaticAddressDeposits(
862873
ctx, &looprpc.ListStaticAddressDepositsRequest{
863874
Outpoints: req.DepositOutpoints,
@@ -872,20 +883,34 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
872883
"deposit outpoints")
873884
}
874885

875-
// The requested amount should be 0 here if the request
876-
// contained deposit outpoints.
877-
if amount != 0 && len(depositList.FilteredDeposits) > 0 {
878-
return nil, fmt.Errorf("amount should be 0 for " +
879-
"deposit quotes")
886+
if numDeposits != len(depositList.FilteredDeposits) {
887+
return nil, fmt.Errorf("expected %d deposits, got %d",
888+
numDeposits, len(depositList.FilteredDeposits))
880889
}
881890

882891
// In case we quote for deposits we send the server both the
883-
// total value and the number of deposits. This is so the server
884-
// can probe the total amount and calculate the per input fee.
885-
if amount == 0 && len(depositList.FilteredDeposits) > 0 {
886-
for _, deposit := range depositList.FilteredDeposits {
887-
amount += btcutil.Amount(deposit.Value)
888-
}
892+
// selected value and the number of deposits. This is so the
893+
// server can probe the selected value and calculate the per
894+
// input fee.
895+
for _, deposit := range depositList.FilteredDeposits {
896+
selectedDepositAmount += btcutil.Amount(
897+
deposit.Value,
898+
)
899+
}
900+
901+
// If the selected amount would leave a dust change output or
902+
// exceeds the total deposits value, we return an error.
903+
dustLimit := lnwallet.DustLimitForSize(input.P2TRSize)
904+
if selectedDepositAmount-selectedAmount < dustLimit {
905+
return nil, fmt.Errorf("selected amount %v leaves "+
906+
"dust or exceeds total deposit value %v",
907+
selectedAmount, selectedDepositAmount)
908+
}
909+
910+
// If the client didn't select an amount we quote for the total
911+
// deposits value.
912+
if selectedAmount == 0 {
913+
selectedAmount = selectedDepositAmount
889914
}
890915
}
891916

@@ -912,14 +937,14 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
912937
}
913938

914939
quote, err := s.impl.LoopInQuote(ctx, &loop.LoopInQuoteRequest{
915-
Amount: amount,
940+
Amount: selectedAmount,
916941
HtlcConfTarget: htlcConfTarget,
917942
ExternalHtlc: req.ExternalHtlc,
918943
LastHop: lastHop,
919944
RouteHints: routeHints,
920945
Private: req.Private,
921946
Initiator: defaultLoopdInitiator,
922-
NumDeposits: numDeposits,
947+
NumDeposits: uint32(numDeposits),
923948
})
924949
if err != nil {
925950
return nil, err
@@ -1762,6 +1787,7 @@ func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context,
17621787
}
17631788

17641789
req := &loop.StaticAddressLoopInRequest{
1790+
SelectedAmount: btcutil.Amount(in.Amount),
17651791
DepositOutpoints: in.Outpoints,
17661792
MaxSwapFee: btcutil.Amount(in.MaxSwapFeeSatoshis),
17671793
Label: in.Label,

0 commit comments

Comments
 (0)