Skip to content

Commit 2c87921

Browse files
committed
feat: add feature to change btc/rbtc liquidity ratio
1 parent 8db9e07 commit 2c87921

File tree

16 files changed

+567
-41
lines changed

16 files changed

+567
-41
lines changed

docker-compose/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ services:
154154
- COLD_WALLET_FORCE_TRANSFER_AFTER_SECONDS
155155
- HOT_WALLET_LOW_LIQUIDITY_WARNING
156156
- HOT_WALLET_LOW_LIQUIDITY_CRITICAL
157+
- BTC_LIQUIDITY_TARGET_PERCENTAGE
157158
ports:
158159
- "8080:8080"
159160
volumes:

docker-compose/local/docker-compose.lps.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ services:
9292
- COLD_WALLET_FORCE_TRANSFER_AFTER_SECONDS
9393
- HOT_WALLET_LOW_LIQUIDITY_WARNING
9494
- HOT_WALLET_LOW_LIQUIDITY_CRITICAL
95+
- BTC_LIQUIDITY_TARGET_PERCENTAGE
9596
ports:
9697
- "8080:8080"
9798
volumes:

docker-compose/mainnet/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ services:
106106
- COLD_WALLET_FORCE_TRANSFER_AFTER_SECONDS
107107
- HOT_WALLET_LOW_LIQUIDITY_WARNING
108108
- HOT_WALLET_LOW_LIQUIDITY_CRITICAL
109+
- BTC_LIQUIDITY_TARGET_PERCENTAGE
109110
ports:
110111
- "8080:8080"
111112
volumes:

docs/Environment.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ These are the environment variables required by the liquidity provider server (L
7474
| `COLD_WALLET_FORCE_TRANSFER_AFTER_SECONDS` | Number of seconds after which excess liquidity will be transferred to cold wallet even if below threshold | `1209600` (2 weeks) | No |
7575
| `HOT_WALLET_LOW_LIQUIDITY_WARNING` | Hot wallet liquidity threshold in whole coins (BTC/RBTC) below which a warning alert is emitted every check cycle | `3` | No |
7676
| `HOT_WALLET_LOW_LIQUIDITY_CRITICAL` | Hot wallet liquidity threshold in whole coins (BTC/RBTC) below which a critical alert is emitted every check cycle. Must be less than `HOT_WALLET_LOW_LIQUIDITY_WARNING` | `1` | No |
77+
| `BTC_LIQUIDITY_TARGET_PERCENTAGE` | Percentage of MaxLiquidity allocated to BTC in the hot wallet (RBTC gets the remainder). Must be between 10 and 90 | `50` | No |
7778

7879
## AWS variables
7980
You may notice that in [`sample-config.env`](https://github.com/rsksmart/liquidity-provider-server/blob/master/sample-config.env) there are some environment variables that are related to AWS. These variables are required to use AWS services, however, they are not listed in the table as the AWS SDK has the functionality to load them from multiple sources. For that reason, they are not accessed directly from the code and are not listed in the table above.

internal/adapters/entrypoints/watcher/liquidity_check_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/rsksmart/liquidity-provider-server/internal/adapters/entrypoints/watcher"
1010
"github.com/rsksmart/liquidity-provider-server/internal/entities"
1111
"github.com/rsksmart/liquidity-provider-server/internal/entities/blockchain"
12+
lpEntity "github.com/rsksmart/liquidity-provider-server/internal/entities/liquidity_provider"
1213
"github.com/rsksmart/liquidity-provider-server/internal/entities/utils"
1314
"github.com/rsksmart/liquidity-provider-server/internal/usecases/liquidity_provider"
1415
"github.com/rsksmart/liquidity-provider-server/test"
@@ -28,7 +29,7 @@ func TestNewLiquidityCheckWatcher(t *testing.T) {
2829
ticker := &mocks.TickerMock{}
2930
providerMock := &mocks.ProviderMock{}
3031
checkLiquidityUseCase := liquidity_provider.NewCheckLiquidityUseCase(providerMock, providerMock, blockchain.RskContracts{}, &mocks.AlertSenderMock{}, test.AnyString)
31-
lowLiquidityUseCase := liquidity_provider.NewLowLiquidityAlertUseCase(providerMock, providerMock, &mocks.AlertSenderMock{}, test.AnyString, 3, 1)
32+
lowLiquidityUseCase := liquidity_provider.NewLowLiquidityAlertUseCase(providerMock, providerMock, providerMock, &mocks.AlertSenderMock{}, test.AnyString, 3, 1)
3233
test.AssertNonZeroValues(t, watcher.NewLiquidityCheckWatcher(checkLiquidityUseCase, lowLiquidityUseCase, ticker, time.Duration(1)))
3334
}
3435

@@ -46,8 +47,9 @@ func TestLiquidityCheckWatcher_Start(t *testing.T) {
4647
bridgeMock.On("GetMinimumLockTxValue").Return(entities.NewWei(5), nil)
4748
alertSenderMock := &mocks.AlertSenderMock{}
4849
alertSenderMock.On("SendAlert", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
50+
providerMock.On("StateConfiguration", mock.Anything).Return(lpEntity.StateConfiguration{}, nil)
4951
checkLiquidityUseCase := liquidity_provider.NewCheckLiquidityUseCase(providerMock, providerMock, blockchain.RskContracts{Bridge: bridgeMock}, alertSenderMock, test.AnyString)
50-
lowLiquidityUseCase := liquidity_provider.NewLowLiquidityAlertUseCase(providerMock, providerMock, alertSenderMock, test.AnyString, 3, 1)
52+
lowLiquidityUseCase := liquidity_provider.NewLowLiquidityAlertUseCase(providerMock, providerMock, providerMock, alertSenderMock, test.AnyString, 3, 1)
5153
w := watcher.NewLiquidityCheckWatcher(checkLiquidityUseCase, lowLiquidityUseCase, ticker, time.Duration(1))
5254
wg := sync.WaitGroup{}
5355
wg.Add(2)
@@ -81,8 +83,9 @@ func TestLiquidityCheckWatcher_Start_ErrorHandling(t *testing.T) {
8183
bridgeMock.On("GetMinimumLockTxValue").Return(nil, assert.AnError)
8284
alertSenderMock := &mocks.AlertSenderMock{}
8385
alertSenderMock.On("SendAlert", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
86+
providerMock.On("StateConfiguration", mock.Anything).Return(lpEntity.StateConfiguration{}, nil)
8487
checkLiquidityUseCase := liquidity_provider.NewCheckLiquidityUseCase(providerMock, providerMock, blockchain.RskContracts{Bridge: bridgeMock}, alertSenderMock, test.AnyString)
85-
lowLiquidityUseCase := liquidity_provider.NewLowLiquidityAlertUseCase(providerMock, providerMock, alertSenderMock, test.AnyString, 3, 1)
88+
lowLiquidityUseCase := liquidity_provider.NewLowLiquidityAlertUseCase(providerMock, providerMock, providerMock, alertSenderMock, test.AnyString, 3, 1)
8689
w := watcher.NewLiquidityCheckWatcher(checkLiquidityUseCase, lowLiquidityUseCase, ticker, time.Duration(1))
8790
wg := sync.WaitGroup{}
8891
wg.Add(2)

internal/configuration/environment/environment.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ type ColdWalletEnv struct {
195195
ForceTransferAfterSeconds uint64 `env:"COLD_WALLET_FORCE_TRANSFER_AFTER_SECONDS"`
196196
HotWalletLowLiquidityWarning uint64 `env:"HOT_WALLET_LOW_LIQUIDITY_WARNING"`
197197
HotWalletLowLiquidityCritical uint64 `env:"HOT_WALLET_LOW_LIQUIDITY_CRITICAL"`
198+
BtcLiquidityTargetPercentage uint64 `env:"BTC_LIQUIDITY_TARGET_PERCENTAGE"`
198199
}
199200

200201
func (env *ColdWalletEnv) FillWithDefaults() *ColdWalletEnv {
@@ -204,15 +205,20 @@ func (env *ColdWalletEnv) FillWithDefaults() *ColdWalletEnv {
204205
ForceTransferAfterSeconds: 1209600, // 2 weeks (14 days * 24 hours * 60 minutes * 60 seconds)
205206
HotWalletLowLiquidityWarning: 3,
206207
HotWalletLowLiquidityCritical: 1,
208+
BtcLiquidityTargetPercentage: 50,
207209
}
208210
env.BtcMinTransferFeeMultiplier = utils.FirstNonZero(env.BtcMinTransferFeeMultiplier, defaults.BtcMinTransferFeeMultiplier)
209211
env.RbtcMinTransferFeeMultiplier = utils.FirstNonZero(env.RbtcMinTransferFeeMultiplier, defaults.RbtcMinTransferFeeMultiplier)
210212
env.ForceTransferAfterSeconds = utils.FirstNonZero(env.ForceTransferAfterSeconds, defaults.ForceTransferAfterSeconds)
211213
env.HotWalletLowLiquidityWarning = utils.FirstNonZero(env.HotWalletLowLiquidityWarning, defaults.HotWalletLowLiquidityWarning)
212214
env.HotWalletLowLiquidityCritical = utils.FirstNonZero(env.HotWalletLowLiquidityCritical, defaults.HotWalletLowLiquidityCritical)
215+
env.BtcLiquidityTargetPercentage = utils.FirstNonZero(env.BtcLiquidityTargetPercentage, defaults.BtcLiquidityTargetPercentage)
213216
if env.HotWalletLowLiquidityCritical >= env.HotWalletLowLiquidityWarning {
214217
log.Fatal("HOT_WALLET_LOW_LIQUIDITY_CRITICAL must be less than HOT_WALLET_LOW_LIQUIDITY_WARNING")
215218
}
219+
if env.BtcLiquidityTargetPercentage < 10 || env.BtcLiquidityTargetPercentage > 90 {
220+
log.Fatal("BTC_LIQUIDITY_TARGET_PERCENTAGE must be between 10 and 90")
221+
}
216222
return env
217223
}
218224

internal/configuration/registry/usecase.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ func NewUseCaseRegistry(
262262
lpRegistry.ColdWallet,
263263
rskRegistry.Wallet,
264264
signingHashFunction,
265+
env.ColdWallet.BtcLiquidityTargetPercentage,
265266
),
266267
getManagementUiDataUseCase: liquidity_provider.NewGetManagementUiDataUseCase(
267268
databaseRegistry.LiquidityProviderRepository,
@@ -393,6 +394,7 @@ func NewUseCaseRegistry(
393394
env.Provider.AlertRecipientEmail,
394395
),
395396
lowLiquidityAlertUseCase: liquidity_provider.NewLowLiquidityAlertUseCase(
397+
lpRegistry.LiquidityProvider,
396398
lpRegistry.LiquidityProvider,
397399
lpRegistry.LiquidityProvider,
398400
messaging.AlertSender,

internal/entities/liquidity_provider/configuration.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ type StateConfiguration struct {
110110
LastRbtcToColdWalletTransfer int64 `json:"lastRbtcToColdWalletTransfer" bson:"last_rbtc_to_cold_wallet_transfer"`
111111
BtcColdWalletAddressHash string `json:"btcColdWalletAddressHash" bson:"btc_cold_wallet_address_hash"`
112112
RskColdWalletAddressHash string `json:"rskColdWalletAddressHash" bson:"rsk_cold_wallet_address_hash"`
113+
BtcLiquidityTargetPercentage uint64 `json:"btcLiquidityTargetPercentage" bson:"btc_liquidity_target_percentage"`
114+
RatioCooldownEndTimestamp int64 `json:"ratioCooldownEndTimestamp" bson:"ratio_cooldown_end_timestamp"`
113115
}
114116

115117
type ConfigurationType interface {

internal/usecases/liquidity_provider/initialize_state_configuration.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ import (
1313
log "github.com/sirupsen/logrus"
1414
)
1515

16+
const CoolDownAfterRatioChange int64 = 10800 // 3 hours
17+
1618
type InitializeStateConfigurationUseCase struct {
17-
provider liquidity_provider.LiquidityProvider
18-
lpRepository liquidity_provider.LiquidityProviderRepository
19-
coldWallet cold_wallet.ColdWallet
20-
signer entities.Signer
21-
hashFunc entities.HashFunction
19+
provider liquidity_provider.LiquidityProvider
20+
lpRepository liquidity_provider.LiquidityProviderRepository
21+
coldWallet cold_wallet.ColdWallet
22+
signer entities.Signer
23+
hashFunc entities.HashFunction
24+
btcLiquidityTargetPercentage uint64
2225
}
2326

2427
func NewInitializeStateConfigurationUseCase(
@@ -27,13 +30,15 @@ func NewInitializeStateConfigurationUseCase(
2730
coldWallet cold_wallet.ColdWallet,
2831
signer entities.Signer,
2932
hashFunc entities.HashFunction,
33+
btcLiquidityTargetPercentage uint64,
3034
) *InitializeStateConfigurationUseCase {
3135
return &InitializeStateConfigurationUseCase{
32-
provider: provider,
33-
lpRepository: lpRepository,
34-
coldWallet: coldWallet,
35-
signer: signer,
36-
hashFunc: hashFunc,
36+
provider: provider,
37+
lpRepository: lpRepository,
38+
coldWallet: coldWallet,
39+
signer: signer,
40+
hashFunc: hashFunc,
41+
btcLiquidityTargetPercentage: btcLiquidityTargetPercentage,
3742
}
3843
}
3944

@@ -80,6 +85,11 @@ func (useCase *InitializeStateConfigurationUseCase) Run(ctx context.Context) err
8085
modified = true
8186
}
8287

88+
stateConfig, ratioUpdated := useCase.applyRatioUpdate(stateConfig, now)
89+
if ratioUpdated {
90+
modified = true
91+
}
92+
8393
if !modified {
8494
log.Debug("State configuration already fully initialized")
8595
return nil
@@ -88,6 +98,22 @@ func (useCase *InitializeStateConfigurationUseCase) Run(ctx context.Context) err
8898
return useCase.signAndPersist(ctx, stateConfig)
8999
}
90100

101+
func (useCase *InitializeStateConfigurationUseCase) applyRatioUpdate(stateConfig liquidity_provider.StateConfiguration, now int64) (liquidity_provider.StateConfiguration, bool) {
102+
if stateConfig.BtcLiquidityTargetPercentage == 0 {
103+
log.Infof("Initializing BtcLiquidityTargetPercentage to %d", useCase.btcLiquidityTargetPercentage)
104+
stateConfig.BtcLiquidityTargetPercentage = useCase.btcLiquidityTargetPercentage
105+
return stateConfig, true
106+
}
107+
if stateConfig.BtcLiquidityTargetPercentage != useCase.btcLiquidityTargetPercentage {
108+
log.Infof("BtcLiquidityTargetPercentage changed from %d to %d, activating cooldown",
109+
stateConfig.BtcLiquidityTargetPercentage, useCase.btcLiquidityTargetPercentage)
110+
stateConfig.BtcLiquidityTargetPercentage = useCase.btcLiquidityTargetPercentage
111+
stateConfig.RatioCooldownEndTimestamp = now + CoolDownAfterRatioChange
112+
return stateConfig, true
113+
}
114+
return stateConfig, false
115+
}
116+
91117
func (useCase *InitializeStateConfigurationUseCase) validateColdWalletAddresses() (coldWalletAddresses, error) {
92118
btcAddr := useCase.coldWallet.GetBtcAddress()
93119
rskAddr := useCase.coldWallet.GetRskAddress()

0 commit comments

Comments
 (0)