Skip to content

Commit f54115f

Browse files
committed
loopd: fractional static address swap amount
1 parent a052f8a commit f54115f

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"
@@ -845,21 +847,30 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
845847
log.Infof("Loop in quote request received")
846848

847849
var (
848-
numDeposits = uint32(len(req.DepositOutpoints))
849-
err error
850+
selectedAmount = btcutil.Amount(req.Amt)
851+
selectedDepositAmount btcutil.Amount
852+
numDeposits = len(req.DepositOutpoints)
853+
err error
850854
)
851855

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

859868
// Retrieve deposits to calculate their total value.
860869
var depositList *looprpc.ListStaticAddressDepositsResponse
861-
amount := btcutil.Amount(req.Amt)
862-
if len(req.DepositOutpoints) > 0 {
870+
871+
// If deposits are selected, we need to retrieve them to calculate the
872+
// total value which we request a quote for. If a
873+
if numDeposits > 0 {
863874
depositList, err = s.ListStaticAddressDeposits(
864875
ctx, &looprpc.ListStaticAddressDepositsRequest{
865876
Outpoints: req.DepositOutpoints,
@@ -874,20 +885,34 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
874885
"deposit outpoints")
875886
}
876887

877-
// The requested amount should be 0 here if the request
878-
// contained deposit outpoints.
879-
if amount != 0 && len(depositList.FilteredDeposits) > 0 {
880-
return nil, fmt.Errorf("amount should be 0 for " +
881-
"deposit quotes")
888+
if numDeposits != len(depositList.FilteredDeposits) {
889+
return nil, fmt.Errorf("expected %d deposits, got %d",
890+
numDeposits, len(depositList.FilteredDeposits))
882891
}
883892

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

@@ -914,14 +939,14 @@ func (s *swapClientServer) GetLoopInQuote(ctx context.Context,
914939
}
915940

916941
quote, err := s.impl.LoopInQuote(ctx, &loop.LoopInQuoteRequest{
917-
Amount: amount,
942+
Amount: selectedAmount,
918943
HtlcConfTarget: htlcConfTarget,
919944
ExternalHtlc: req.ExternalHtlc,
920945
LastHop: lastHop,
921946
RouteHints: routeHints,
922947
Private: req.Private,
923948
Initiator: defaultLoopdInitiator,
924-
NumDeposits: numDeposits,
949+
NumDeposits: uint32(numDeposits),
925950
})
926951
if err != nil {
927952
return nil, err
@@ -1764,6 +1789,7 @@ func (s *swapClientServer) StaticAddressLoopIn(ctx context.Context,
17641789
}
17651790

17661791
req := &loop.StaticAddressLoopInRequest{
1792+
SelectedAmount: btcutil.Amount(in.Amount),
17671793
DepositOutpoints: in.Outpoints,
17681794
MaxSwapFee: btcutil.Amount(in.MaxSwapFeeSatoshis),
17691795
Label: in.Label,

0 commit comments

Comments
 (0)