mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
liquidity: add calculations for threshold rule
This commit is contained in:
parent
7a3dcc3a89
commit
cd8a7704af
3 changed files with 160 additions and 0 deletions
18
liquidity/balances.go
Normal file
18
liquidity/balances.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package liquidity
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// balances summarizes the state of the balances of a channel. Channel reserve,
|
||||
// fees and pending htlc balances are not included in these balances.
|
||||
type balances struct {
|
||||
// capacity is the total capacity of the channel.
|
||||
capacity btcutil.Amount
|
||||
|
||||
// incoming is the remote balance of the channel.
|
||||
incoming btcutil.Amount
|
||||
|
||||
// outgoing is the local balance of the channel.
|
||||
outgoing btcutil.Amount
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package liquidity
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -59,3 +61,60 @@ func (r *ThresholdRule) validate() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
minimumIncoming := btcutil.Amount(uint64(
|
||||
balances.capacity) *
|
||||
uint64(incomingThresholdPercent) / 100,
|
||||
)
|
||||
|
||||
minimumOutgoing := btcutil.Amount(
|
||||
uint64(balances.capacity) *
|
||||
uint64(outgoingThresholdPercent) / 100,
|
||||
)
|
||||
|
||||
switch {
|
||||
// If we have sufficient incoming capacity, we do not need to loop out.
|
||||
case balances.incoming >= minimumIncoming:
|
||||
return 0
|
||||
|
||||
// If we are already below the threshold set for outgoing capacity, we
|
||||
// cannot take any further action.
|
||||
case balances.outgoing <= minimumOutgoing:
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
// Express our minimum outgoing amount as a maximum incoming 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
|
||||
|
||||
// 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 amount of incoming balance we need to shift to reach
|
||||
// this desired midpoint.
|
||||
required := midpoint - balances.incoming
|
||||
|
||||
// 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
|
||||
// threshold.
|
||||
available := balances.outgoing - minimumOutgoing
|
||||
|
||||
// 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
|
||||
// pending htlcs.
|
||||
if available < required {
|
||||
return 0
|
||||
}
|
||||
|
||||
return required
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package liquidity
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -91,3 +92,85 @@ 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) {
|
||||
tests := []struct {
|
||||
name string
|
||||
minIncoming int
|
||||
minOutgoing int
|
||||
balances *balances
|
||||
amt btcutil.Amount
|
||||
}{
|
||||
{
|
||||
name: "insufficient surplus",
|
||||
balances: &balances{
|
||||
capacity: 100,
|
||||
incoming: 20,
|
||||
outgoing: 20,
|
||||
},
|
||||
minOutgoing: 40,
|
||||
minIncoming: 40,
|
||||
amt: 0,
|
||||
},
|
||||
{
|
||||
name: "loop out",
|
||||
balances: &balances{
|
||||
capacity: 100,
|
||||
incoming: 20,
|
||||
outgoing: 80,
|
||||
},
|
||||
minOutgoing: 20,
|
||||
minIncoming: 60,
|
||||
amt: 50,
|
||||
},
|
||||
{
|
||||
name: "pending htlcs",
|
||||
balances: &balances{
|
||||
capacity: 100,
|
||||
incoming: 20,
|
||||
outgoing: 30,
|
||||
},
|
||||
minOutgoing: 20,
|
||||
minIncoming: 60,
|
||||
amt: 0,
|
||||
},
|
||||
{
|
||||
name: "loop in",
|
||||
balances: &balances{
|
||||
capacity: 100,
|
||||
incoming: 50,
|
||||
outgoing: 50,
|
||||
},
|
||||
minOutgoing: 60,
|
||||
minIncoming: 30,
|
||||
amt: 0,
|
||||
},
|
||||
{
|
||||
name: "liquidity ok",
|
||||
balances: &balances{
|
||||
capacity: 100,
|
||||
incoming: 50,
|
||||
outgoing: 50,
|
||||
},
|
||||
minOutgoing: 40,
|
||||
minIncoming: 40,
|
||||
amt: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
amt := loopOutSwapAmount(
|
||||
test.balances, test.minIncoming,
|
||||
test.minOutgoing,
|
||||
)
|
||||
require.Equal(t, test.amt, amt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue