2026-04-13 00:11:52 -05:00
|
|
|
package loopin
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
|
|
|
"github.com/lightninglabs/loop"
|
|
|
|
|
"github.com/lightninglabs/loop/staticaddr/deposit"
|
|
|
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
// ErrNoAutoloopCandidate is returned when the static-address side
|
|
|
|
|
// cannot build a full-deposit, no-change loop-in candidate that fits
|
|
|
|
|
// the planner's requested amount bounds.
|
|
|
|
|
ErrNoAutoloopCandidate = errors.New("no autoloop candidate")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// PrepareAutoloopLoopIn builds a static-address loop-in request for autoloop
|
|
|
|
|
// without dispatching it. The returned request always uses full deposits,
|
|
|
|
|
// explicit outpoints, and an explicit selected amount, so the caller can
|
|
|
|
|
// account for the suggestion without depending on static-address internals.
|
|
|
|
|
func (m *Manager) PrepareAutoloopLoopIn(ctx context.Context,
|
|
|
|
|
lastHop route.Vertex, minAmount, maxAmount btcutil.Amount, label,
|
|
|
|
|
initiator string, excludedOutpoints []string) (
|
|
|
|
|
*loop.StaticAddressLoopInRequest, int, bool, error) {
|
|
|
|
|
|
|
|
|
|
if minAmount <= 0 || maxAmount < minAmount {
|
|
|
|
|
return nil, 0, false, ErrNoAutoloopCandidate
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-08 13:55:04 +02:00
|
|
|
err := m.cfg.DepositManager.EnsureDepositsFresh(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, false, err
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-13 00:11:52 -05:00
|
|
|
allDeposits, err := m.cfg.DepositManager.GetActiveDepositsInState(
|
|
|
|
|
deposit.Deposited,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
params, err := m.cfg.AddressManager.GetStaticAddressParameters(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
excluded := make(map[string]struct{}, len(excludedOutpoints))
|
|
|
|
|
for _, outpoint := range excludedOutpoints {
|
|
|
|
|
excluded[outpoint] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selectedDeposits, err := selectNoChangeDeposits(
|
|
|
|
|
maxAmount, minAmount, allDeposits, params.Expiry,
|
|
|
|
|
m.currentHeight.Load(), excluded,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selectedAmount := sumOfDeposits(selectedDeposits)
|
|
|
|
|
quote, err := m.cfg.QuoteGetter.GetLoopInQuote(
|
|
|
|
|
ctx, selectedAmount, m.cfg.NodePubkey, &lastHop, nil,
|
|
|
|
|
initiator, uint32(len(selectedDeposits)), false,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, 0, false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outpoints := make([]string, 0, len(selectedDeposits))
|
|
|
|
|
for _, selectedDeposit := range selectedDeposits {
|
|
|
|
|
outpoints = append(outpoints, selectedDeposit.OutPoint.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
request := &loop.StaticAddressLoopInRequest{
|
|
|
|
|
DepositOutpoints: outpoints,
|
|
|
|
|
SelectedAmount: selectedAmount,
|
|
|
|
|
MaxSwapFee: quote.SwapFee,
|
|
|
|
|
LastHop: &lastHop,
|
|
|
|
|
Label: label,
|
|
|
|
|
Initiator: initiator,
|
|
|
|
|
Fast: false,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return request, len(selectedDeposits), false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// selectNoChangeDeposits chooses the highest-value swappable deposit set whose
|
|
|
|
|
// full value stays within the requested range. The selector never creates
|
|
|
|
|
// change, so the returned set's total is the actual swap amount.
|
|
|
|
|
func selectNoChangeDeposits(maxAmount, minAmount btcutil.Amount,
|
|
|
|
|
unfilteredDeposits []*deposit.Deposit, csvExpiry, blockHeight uint32,
|
|
|
|
|
excludedOutpoints map[string]struct{}) ([]*deposit.Deposit, error) {
|
|
|
|
|
|
2026-05-14 01:20:54 -05:00
|
|
|
return selectNoChangeDepositsWithMemoryBudget(
|
|
|
|
|
maxAmount, minAmount, unfilteredDeposits, csvExpiry,
|
|
|
|
|
blockHeight, excludedOutpoints, autoloopDPMaxMemoryBytes,
|
2026-04-13 00:11:52 -05:00
|
|
|
)
|
|
|
|
|
}
|