mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-14 12:43:03 +02:00
liquidity: generalize threshold rule variable names
The current wording in this function is very specific to loop out. In this commit, we refactor to use `target` and `reserve` rather than inbound and outbound so that it can be used more generically.
This commit is contained in:
parent
06c4c347fd
commit
00c2d4e5f0
2 changed files with 67 additions and 40 deletions
|
|
@ -65,73 +65,99 @@ func (r *ThresholdRule) validate() error {
|
|||
// swapAmount suggests a swap based on the liquidity thresholds configured,
|
||||
// returning zero if no swap is recommended.
|
||||
func (r *ThresholdRule) swapAmount(channel *balances,
|
||||
outRestrictions *Restrictions) btcutil.Amount {
|
||||
restrictions *Restrictions) btcutil.Amount {
|
||||
|
||||
var (
|
||||
// For loop out swaps, we want to adjust our incoming liquidity
|
||||
// so the channel's incoming balance is our target.
|
||||
targetBalance = channel.incoming
|
||||
|
||||
// For loop out swaps, we target a minimum amount of incoming
|
||||
// liquidity, so the minimum incoming threshold is our target
|
||||
// percentage.
|
||||
targetPercentage = uint64(r.MinimumIncoming)
|
||||
|
||||
// For loop out swaps, we may want to preserve some of our
|
||||
// outgoing balance, so the channel's outgoing balance is our
|
||||
// reserve.
|
||||
reserveBalance = channel.outgoing
|
||||
|
||||
// For loop out swaps, we may want to preserve some percentage
|
||||
// of our outgoing balance, so the minimum outgoing threshold
|
||||
// is our reserve percentage.
|
||||
reservePercentage = uint64(r.MinimumOutgoing)
|
||||
)
|
||||
|
||||
// Examine our total balance and required ratios to decide whether we
|
||||
// need to swap.
|
||||
amount := loopOutSwapAmount(
|
||||
channel, r.MinimumIncoming, r.MinimumOutgoing,
|
||||
amount := calculateSwapAmount(
|
||||
targetBalance, reserveBalance, channel.capacity,
|
||||
targetPercentage, reservePercentage,
|
||||
)
|
||||
|
||||
// Limit our swap amount by the minimum/maximum thresholds set.
|
||||
switch {
|
||||
case amount < outRestrictions.Minimum:
|
||||
case amount < restrictions.Minimum:
|
||||
return 0
|
||||
|
||||
case amount > outRestrictions.Maximum:
|
||||
return outRestrictions.Maximum
|
||||
case amount > restrictions.Maximum:
|
||||
return restrictions.Maximum
|
||||
|
||||
default:
|
||||
return amount
|
||||
}
|
||||
}
|
||||
|
||||
// loopOutSwapAmount determines whether we can perform a loop out swap, and
|
||||
// returns the amount we need to swap to reach the desired liquidity balance
|
||||
// specified by the incoming and outgoing thresholds.
|
||||
func loopOutSwapAmount(balances *balances, incomingThresholdPercent,
|
||||
outgoingThresholdPercent int) btcutil.Amount {
|
||||
// calculateSwapAmount calculates amount for a swap based on thresholds.
|
||||
// This function can be used for loop out or loop in, but the concept is the
|
||||
// same - we want liquidity in one (target) direction, while preserving some
|
||||
// minimum in the other (reserve) direction.
|
||||
// * target: this is the side of the channel(s) where we want to acquire some
|
||||
// liquidity. We aim for this liquidity to reach the threshold amount set.
|
||||
// * reserve: this is the side of the channel(s) that we will move liquidity
|
||||
// away from. This may not drop below a certain reserve threshold.
|
||||
func calculateSwapAmount(targetAmount, reserveAmount,
|
||||
capacity btcutil.Amount, targetThresholdPercentage,
|
||||
reserveThresholdPercentage uint64) btcutil.Amount {
|
||||
|
||||
minimumIncoming := btcutil.Amount(uint64(
|
||||
balances.capacity) *
|
||||
uint64(incomingThresholdPercent) / 100,
|
||||
targetGoal := btcutil.Amount(
|
||||
uint64(capacity) * targetThresholdPercentage / 100,
|
||||
)
|
||||
|
||||
minimumOutgoing := btcutil.Amount(
|
||||
uint64(balances.capacity) *
|
||||
uint64(outgoingThresholdPercent) / 100,
|
||||
reserveMinimum := btcutil.Amount(
|
||||
uint64(capacity) * reserveThresholdPercentage / 100,
|
||||
)
|
||||
|
||||
switch {
|
||||
// If we have sufficient incoming capacity, we do not need to loop out.
|
||||
case balances.incoming >= minimumIncoming:
|
||||
// If we have sufficient target capacity, we do not need to swap.
|
||||
case targetAmount >= targetGoal:
|
||||
return 0
|
||||
|
||||
// If we are already below the threshold set for outgoing capacity, we
|
||||
// If we are already below the threshold set for reserve capacity, we
|
||||
// cannot take any further action.
|
||||
case balances.outgoing <= minimumOutgoing:
|
||||
case reserveAmount <= reserveMinimum:
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
// Express our minimum outgoing amount as a maximum incoming amount.
|
||||
// Express our minimum reserve amount as a maximum target amount.
|
||||
// We will use this value to limit the amount that we swap, so that we
|
||||
// do not dip below our outgoing threshold.
|
||||
maximumIncoming := balances.capacity - minimumOutgoing
|
||||
// do not dip below our reserve threshold.
|
||||
maximumTarget := capacity - reserveMinimum
|
||||
|
||||
// Calculate the midpoint between our minimum and maximum incoming
|
||||
// values. We will aim to swap this amount so that we do not tip our
|
||||
// outgoing balance beneath the desired level.
|
||||
midpoint := (minimumIncoming + maximumIncoming) / 2
|
||||
// Calculate the midpoint between our minimum and maximum target values.
|
||||
// We will aim to swap this amount so that we do not tip our reserve
|
||||
// balance beneath the desired level.
|
||||
midpoint := (targetGoal + maximumTarget) / 2
|
||||
|
||||
// Calculate the amount of incoming balance we need to shift to reach
|
||||
// Calculate the amount of target balance we need to shift to reach
|
||||
// this desired midpoint.
|
||||
required := midpoint - balances.incoming
|
||||
required := midpoint - targetAmount
|
||||
|
||||
// Since we can have pending htlcs on our channel, we check the amount
|
||||
// of outbound capacity that we can shift before we fall below our
|
||||
// of reserve capacity that we can shift before we fall below our
|
||||
// threshold.
|
||||
available := balances.outgoing - minimumOutgoing
|
||||
available := reserveAmount - reserveMinimum
|
||||
|
||||
// If we do not have enough balance available to reach our midpoint, we
|
||||
// take no action. This is the case when we have a large portion of
|
||||
|
|
|
|||
|
|
@ -93,18 +93,18 @@ func TestValidateThreshold(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestLoopOutAmount tests assessing of a set of balances to determine whether
|
||||
// we should perform a loop out.
|
||||
func TestLoopOutAmount(t *testing.T) {
|
||||
// TestCalculateAmount tests calculation of the amount we recommend for a given
|
||||
// set of balances and threshold rule.
|
||||
func TestCalculateAmount(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
minIncoming int
|
||||
minOutgoing int
|
||||
minIncoming uint64
|
||||
minOutgoing uint64
|
||||
balances *balances
|
||||
amt btcutil.Amount
|
||||
}{
|
||||
{
|
||||
name: "insufficient surplus",
|
||||
name: "insufficient outgoing",
|
||||
balances: &balances{
|
||||
capacity: 100,
|
||||
incoming: 20,
|
||||
|
|
@ -166,8 +166,9 @@ func TestLoopOutAmount(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
amt := loopOutSwapAmount(
|
||||
test.balances, test.minIncoming,
|
||||
amt := calculateSwapAmount(
|
||||
test.balances.incoming, test.balances.outgoing,
|
||||
test.balances.capacity, test.minIncoming,
|
||||
test.minOutgoing,
|
||||
)
|
||||
require.Equal(t, test.amt, amt)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue