Skip to content

staticaddr: fractional swap amount #887

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 32 additions & 42 deletions cmd/loop/staticaddr.go
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,13 @@ var staticAddressLoopInCommand = cli.Command{
"The client can retry the swap with adjusted " +
"parameters after the payment timed out.",
},
cli.IntFlag{
Name: "amount",
Usage: "the number of satoshis that should be " +
"swapped from the selected deposits. If there" +
"is change it is sent back to the static " +
"address.",
},
lastHopFlag,
labelFlag,
routeHintsFlag,
Expand All @@ -499,13 +506,15 @@ func staticAddressLoopIn(ctx *cli.Context) error {
defer cleanup()

var (
ctxb = context.Background()
isAllSelected = ctx.IsSet("all")
isUtxoSelected = ctx.IsSet("utxo")
label = ctx.String("static-loop-in")
hints []*swapserverrpc.RouteHint
lastHop []byte
paymentTimeoutSeconds = uint32(loopin.DefaultPaymentTimeoutSeconds)
ctxb = context.Background()
isAllSelected = ctx.IsSet("all")
isUtxoSelected = ctx.IsSet("utxo")
selectedAmount = ctx.Int64("amount")
selectDepositsForQuote bool
label = ctx.String("static-loop-in")
hints []*swapserverrpc.RouteHint
lastHop []byte
paymentTimeoutSeconds = uint32(loopin.DefaultPaymentTimeoutSeconds)
)

// Validate our label early so that we can fail before getting a quote.
Expand Down Expand Up @@ -541,7 +550,9 @@ func staticAddressLoopIn(ctx *cli.Context) error {
return err
}

if len(depositList.FilteredDeposits) == 0 {
allDeposits := depositList.FilteredDeposits

if len(allDeposits) == 0 {
errString := fmt.Sprintf("no confirmed deposits available, "+
"deposits need at least %v confirmations",
deposit.MinConfs)
Expand All @@ -551,17 +562,18 @@ func staticAddressLoopIn(ctx *cli.Context) error {

var depositOutpoints []string
switch {
case isAllSelected == isUtxoSelected:
return errors.New("must select either all or some utxos")
case isAllSelected && isUtxoSelected:
return errors.New("cannot select all and specific utxos")

case isAllSelected:
depositOutpoints = depositsToOutpoints(
depositList.FilteredDeposits,
)
depositOutpoints = depositsToOutpoints(allDeposits)

case isUtxoSelected:
depositOutpoints = ctx.StringSlice("utxo")

case selectedAmount > 0:
// If only an amount is selected we will trigger coin selection.

default:
return fmt.Errorf("unknown quote request")
}
Expand All @@ -570,11 +582,17 @@ func staticAddressLoopIn(ctx *cli.Context) error {
return errors.New("duplicate outpoints detected")
}

if len(depositOutpoints) == 0 && selectedAmount > 0 {
selectDepositsForQuote = true
}

quoteReq := &looprpc.QuoteRequest{
Amt: selectedAmount,
LoopInRouteHints: hints,
LoopInLastHop: lastHop,
Private: ctx.Bool(privateFlag.Name),
DepositOutpoints: depositOutpoints,
SelectDeposits: selectDepositsForQuote,
}
quote, err := client.GetLoopInQuote(ctxb, quoteReq)
if err != nil {
Expand All @@ -583,15 +601,6 @@ func staticAddressLoopIn(ctx *cli.Context) error {

limits := getInLimits(quote)

// populate the quote request with the sum of selected deposits and
// prompt the user for acceptance.
quoteReq.Amt, err = sumDeposits(
depositOutpoints, depositList.FilteredDeposits,
)
if err != nil {
return err
}

if !(ctx.Bool("force") || ctx.Bool("f")) {
err = displayInDetails(quoteReq, quote, ctx.Bool("verbose"))
if err != nil {
Expand All @@ -604,6 +613,7 @@ func staticAddressLoopIn(ctx *cli.Context) error {
}

req := &looprpc.StaticAddressLoopInRequest{
Amount: quoteReq.Amt,
Outpoints: depositOutpoints,
MaxSwapFeeSatoshis: int64(limits.maxSwapFee),
LastHop: lastHop,
Expand Down Expand Up @@ -636,26 +646,6 @@ func containsDuplicates(outpoints []string) bool {
return false
}

func sumDeposits(outpoints []string, deposits []*looprpc.Deposit) (int64,
error) {

var sum int64
depositMap := make(map[string]*looprpc.Deposit)
for _, deposit := range deposits {
depositMap[deposit.Outpoint] = deposit
}

for _, outpoint := range outpoints {
if _, ok := depositMap[outpoint]; !ok {
return 0, fmt.Errorf("deposit %v not found", outpoint)
}

sum += depositMap[outpoint].Value
}

return sum, nil
}

func depositsToOutpoints(deposits []*looprpc.Deposit) []string {
outpoints := make([]string, 0, len(deposits))
for _, deposit := range deposits {
Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0
github.com/jackc/pgconn v1.14.3
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
github.com/jackc/pgx/v5 v5.6.0
github.com/jessevdk/go-flags v1.4.0
github.com/lib/pq v1.10.9
github.com/lightninglabs/aperture v0.3.13-beta
github.com/lightninglabs/lndclient v0.19.0-12
github.com/lightninglabs/loop/looprpc v1.0.7
github.com/lightninglabs/loop/swapserverrpc v1.0.14
github.com/lightninglabs/taproot-assets v0.6.1
github.com/lightninglabs/taproot-assets/taprpc v1.0.8-0.20250617163017-cf2a5e5bb47c
github.com/lightninglabs/taproot-assets v0.6.1-0.20250729190616-3f323918a96e
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250729190616-3f323918a96e
github.com/lightningnetwork/lnd v0.19.2-beta
github.com/lightningnetwork/lnd/cert v1.2.2
github.com/lightningnetwork/lnd/clock v1.1.1
github.com/lightningnetwork/lnd/queue v1.1.1
github.com/lightningnetwork/lnd/ticker v1.1.1
github.com/lightningnetwork/lnd/tlv v1.3.1
github.com/lightningnetwork/lnd/tlv v1.3.2
github.com/lightningnetwork/lnd/tor v1.1.6
github.com/ory/dockertest/v3 v3.10.0
github.com/stretchr/testify v1.10.0
Expand Down Expand Up @@ -104,7 +105,6 @@ require (
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgtype v1.14.0 // indirect
github.com/jackc/pgx/v4 v4.18.2 // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/jackc/puddle v1.3.0 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jackpal/gateway v1.0.5 // indirect
Expand All @@ -124,7 +124,7 @@ require (
github.com/lightningnetwork/lnd/fn/v2 v2.0.8 // indirect
github.com/lightningnetwork/lnd/healthcheck v1.2.6 // indirect
github.com/lightningnetwork/lnd/kvdb v1.4.16 // indirect
github.com/lightningnetwork/lnd/sqldb v1.0.9 // indirect
github.com/lightningnetwork/lnd/sqldb v1.0.10 // indirect
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
Expand Down
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1116,10 +1116,10 @@ github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3
github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo=
github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display h1:w7FM5LH9Z6CpKxl13mS48idsu6F+cEZf0lkyiV+Dq9g=
github.com/lightninglabs/protobuf-go-hex-display v1.34.2-hex-display/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
github.com/lightninglabs/taproot-assets v0.6.1 h1:98XCk7nvAridyE67uct0NDVpyY1evpIdvPQpeNElskM=
github.com/lightninglabs/taproot-assets v0.6.1/go.mod h1:rF+GwuUVuDVUejAHsUCml4Nru9xnl7A4YZQfR4qLMzY=
github.com/lightninglabs/taproot-assets/taprpc v1.0.8-0.20250617163017-cf2a5e5bb47c h1:Fzob+kYq68uPuaEd78rVa/Jvjn/Rp4rLcG7xRTOxK7Y=
github.com/lightninglabs/taproot-assets/taprpc v1.0.8-0.20250617163017-cf2a5e5bb47c/go.mod h1:vOM2Ap2wYhEZjiJU7bNNg+e5tDxkvRAuyXwf/KQ4tgo=
github.com/lightninglabs/taproot-assets v0.6.1-0.20250729190616-3f323918a96e h1:wlaM8dTlpCQ0uNj0TBskBDeNTTDessxiXiakYDB4RFo=
github.com/lightninglabs/taproot-assets v0.6.1-0.20250729190616-3f323918a96e/go.mod h1:mIgx0p/GkMZeEjEm91vYQsH41YQBAgJl7TP6JcT+wgs=
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250729190616-3f323918a96e h1:MnXspinwkd6VhV8G9I+TdSak05UitfYyNBCQhTIIr0g=
github.com/lightninglabs/taproot-assets/taprpc v1.0.10-0.20250729190616-3f323918a96e/go.mod h1:c8gTEcKEUoUPVChgZNwqTL1hss7UWa5FDeObr8WBzQk=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb h1:yfM05S8DXKhuCBp5qSMZdtSwvJ+GFzl94KbXMNB1JDY=
github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb/go.mod h1:c0kvRShutpj3l6B9WtTsNTBUtjSmjZXbJd9ZBRQOSKI=
github.com/lightningnetwork/lnd v0.19.2-beta h1:3SKVrKYFY4IJLlrMf7cDzZcBeT+MxjI9Xy6YpY+EEX4=
Expand All @@ -1136,12 +1136,12 @@ github.com/lightningnetwork/lnd/kvdb v1.4.16 h1:9BZgWdDfjmHRHLS97cz39bVuBAqMc4/p
github.com/lightningnetwork/lnd/kvdb v1.4.16/go.mod h1:HW+bvwkxNaopkz3oIgBV6NEnV4jCEZCACFUcNg4xSjM=
github.com/lightningnetwork/lnd/queue v1.1.1 h1:99ovBlpM9B0FRCGYJo6RSFDlt8/vOkQQZznVb18iNMI=
github.com/lightningnetwork/lnd/queue v1.1.1/go.mod h1:7A6nC1Qrm32FHuhx/mi1cieAiBZo5O6l8IBIoQxvkz4=
github.com/lightningnetwork/lnd/sqldb v1.0.9 h1:7OHi+Hui823mB/U9NzCdlZTAGSVdDCbjp33+6d/Q+G0=
github.com/lightningnetwork/lnd/sqldb v1.0.9/go.mod h1:OG09zL/PHPaBJefp4HsPz2YLUJ+zIQHbpgCtLnOx8I4=
github.com/lightningnetwork/lnd/sqldb v1.0.10 h1:ZLV7TGwjnKupVfCd+DJ43MAc9BKVSFCnvhpSPGKdN3M=
github.com/lightningnetwork/lnd/sqldb v1.0.10/go.mod h1:c/vWoQfcxu6FAfHzGajkIQi7CEIeIZFhhH4DYh1BJpc=
github.com/lightningnetwork/lnd/ticker v1.1.1 h1:J/b6N2hibFtC7JLV77ULQp++QLtCwT6ijJlbdiZFbSM=
github.com/lightningnetwork/lnd/ticker v1.1.1/go.mod h1:waPTRAAcwtu7Ji3+3k+u/xH5GHovTsCoSVpho0KDvdA=
github.com/lightningnetwork/lnd/tlv v1.3.1 h1:o7CZg06y+rJZfUMAo0WzBLr0pgBWCzrt0f9gpujYUzk=
github.com/lightningnetwork/lnd/tlv v1.3.1/go.mod h1:pJuiBj1ecr1WWLOtcZ+2+hu9Ey25aJWFIsjmAoPPnmc=
github.com/lightningnetwork/lnd/tlv v1.3.2 h1:MO4FCk7F4k5xPMqVZF6Nb/kOpxlwPrUQpYjmyKny5s0=
github.com/lightningnetwork/lnd/tlv v1.3.2/go.mod h1:pJuiBj1ecr1WWLOtcZ+2+hu9Ey25aJWFIsjmAoPPnmc=
github.com/lightningnetwork/lnd/tor v1.1.6 h1:WHUumk7WgU6BUFsqHuqszI9P6nfhMeIG+rjJBlVE6OE=
github.com/lightningnetwork/lnd/tor v1.1.6/go.mod h1:qSRB8llhAK+a6kaTPWOLLXSZc6Hg8ZC0mq1sUQ/8JfI=
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
Expand Down
5 changes: 5 additions & 0 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@ type StaticAddressLoopInRequest struct {
// swap payment. If the timeout is reached the swap will be aborted and
// the client can retry the swap if desired with different parameters.
PaymentTimeoutSeconds uint32

// SelectedAmount is the amount that the user selected for the swap. If
// the user did not select an amount, the amount of the selected
// deposits is used.
SelectedAmount btcutil.Amount
}

// LoopInTerms are the server terms on which it executes loop in swaps.
Expand Down
20 changes: 20 additions & 0 deletions loopd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,26 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
clock.NewDefaultClock(), d.lnd.ChainParams,
)

// Run the deposit swap hash migration.
err = loopin.MigrateDepositSwapHash(
d.mainCtx, swapDb, depositStore, staticAddressLoopInStore,
)
if err != nil {
errorf("Deposit swap hash migration failed: %v", err)

return err
}

// Run the selected amount migration.
err = loopin.MigrateSelectedSwapAmount(
d.mainCtx, swapDb, depositStore, staticAddressLoopInStore,
)
if err != nil {
errorf("Selected amount migration failed: %v", err)

return err
}

staticLoopInManager = loopin.NewManager(&loopin.Config{
Server: staticAddressClient,
QuoteGetter: swapClient.Server,
Expand Down
Loading
Loading