@@ -3,6 +3,7 @@ package pegout
33import (
44 "context"
55 "errors"
6+ "fmt"
67 "sync"
78
89 "github.com/rsksmart/liquidity-provider-server/internal/entities"
@@ -20,12 +21,31 @@ const (
2021 BridgeConversionGasPrice = 60000000
2122)
2223
24+ type RebalanceStrategy string
25+
26+ const (
27+ AllAtOnce RebalanceStrategy = "ALL_AT_ONCE"
28+ UtxoSplit RebalanceStrategy = "UTXO_SPLIT"
29+ )
30+
31+ func ParseRebalanceStrategy (s string ) (RebalanceStrategy , error ) {
32+ switch s {
33+ case string (AllAtOnce ):
34+ return AllAtOnce , nil
35+ case string (UtxoSplit ):
36+ return UtxoSplit , nil
37+ default :
38+ return "" , fmt .Errorf ("unknown rebalance strategy: %q" , s )
39+ }
40+ }
41+
2342type BridgePegoutUseCase struct {
2443 quoteRepository quote.PegoutQuoteRepository
2544 pegoutProvider liquidity_provider.PegoutLiquidityProvider
2645 rskWallet blockchain.RootstockWallet
2746 contracts blockchain.RskContracts
2847 rskWalletMutex sync.Locker
48+ strategy RebalanceStrategy
2949}
3050
3151func NewBridgePegoutUseCase (
@@ -34,19 +54,21 @@ func NewBridgePegoutUseCase(
3454 rskWallet blockchain.RootstockWallet ,
3555 contracts blockchain.RskContracts ,
3656 rskWalletMutex sync.Locker ,
57+ strategy RebalanceStrategy ,
3758) * BridgePegoutUseCase {
3859 return & BridgePegoutUseCase {
3960 quoteRepository : quoteRepository ,
4061 pegoutProvider : pegoutProvider ,
4162 rskWallet : rskWallet ,
4263 contracts : contracts ,
4364 rskWalletMutex : rskWalletMutex ,
65+ strategy : strategy ,
4466 }
4567}
4668
4769func (useCase * BridgePegoutUseCase ) Run (ctx context.Context , watchedQuotes ... quote.WatchedPegoutQuote ) error {
4870 var err error
49- var balance , totalValue * entities.Wei
71+ var totalValue * entities.Wei
5072
5173 totalValue , err = useCase .calculateTotalToPegout (watchedQuotes )
5274 if err != nil {
@@ -66,6 +88,18 @@ func (useCase *BridgePegoutUseCase) Run(ctx context.Context, watchedQuotes ...qu
6688 useCase .rskWalletMutex .Lock ()
6789 defer useCase .rskWalletMutex .Unlock ()
6890
91+ switch useCase .strategy {
92+ case UtxoSplit :
93+ return useCase .runUtxoSplit (ctx , totalValue , pegoutConfig , watchedQuotes )
94+ default :
95+ return useCase .runAllAtOnce (ctx , totalValue , watchedQuotes )
96+ }
97+ }
98+
99+ func (useCase * BridgePegoutUseCase ) runAllAtOnce (ctx context.Context , totalValue * entities.Wei , watchedQuotes []quote.WatchedPegoutQuote ) error {
100+ var balance * entities.Wei
101+ var err error
102+
69103 requiredBalance := new (entities.Wei ).Add (totalValue , entities .NewWei (BridgeConversionGasLimit * BridgeConversionGasPrice ))
70104 if balance , err = useCase .rskWallet .GetBalance (ctx ); err != nil {
71105 return usecases .WrapUseCaseError (usecases .BridgePegoutId , err )
@@ -86,6 +120,77 @@ func (useCase *BridgePegoutUseCase) Run(ctx context.Context, watchedQuotes ...qu
86120 return nil
87121}
88122
123+ func (useCase * BridgePegoutUseCase ) runUtxoSplit (
124+ ctx context.Context ,
125+ totalValue * entities.Wei ,
126+ pegoutConfig liquidity_provider.PegoutConfiguration ,
127+ watchedQuotes []quote.WatchedPegoutQuote ,
128+ ) error {
129+ var balance * entities.Wei
130+ var err error
131+
132+ bridgeMin := pegoutConfig .BridgeTransactionMin
133+ numTxs , _ := new (entities.Wei ).Div (totalValue , bridgeMin )
134+ remainder := new (entities.Wei ).Sub (totalValue , new (entities.Wei ).Mul (numTxs , bridgeMin ))
135+
136+ gasPerTx := entities .NewWei (BridgeConversionGasLimit * BridgeConversionGasPrice )
137+ requiredBalance := new (entities.Wei ).Add (totalValue , new (entities.Wei ).Mul (numTxs , gasPerTx ))
138+ if balance , err = useCase .rskWallet .GetBalance (ctx ); err != nil {
139+ return usecases .WrapUseCaseError (usecases .BridgePegoutId , err )
140+ } else if balance .Cmp (requiredBalance ) < 0 {
141+ return usecases .WrapUseCaseError (usecases .BridgePegoutId , usecases .InsufficientAmountError )
142+ }
143+
144+ bridgeAddress := useCase .contracts .Bridge .GetAddress ()
145+ n := numTxs .Uint64 ()
146+
147+ if n == 1 {
148+ config := blockchain .NewTransactionConfig (totalValue , BridgeConversionGasLimit , entities .NewWei (BridgeConversionGasPrice ))
149+ receipt , txErr := useCase .rskWallet .SendRbtc (ctx , config , bridgeAddress )
150+ if txErr == nil {
151+ log .Debugf ("%s: transaction sent to the bridge successfully (%s)" , usecases .BridgePegoutId , receipt .TransactionHash )
152+ }
153+ err = useCase .updateQuotes (ctx , receipt , txErr , watchedQuotes )
154+ if err != nil {
155+ return usecases .WrapUseCaseError (usecases .BridgePegoutId , err )
156+ }
157+ return nil
158+ }
159+
160+ // N >= 2: first chunk absorbs the remainder
161+ firstChunk := new (entities.Wei ).Add (bridgeMin .Copy (), remainder )
162+ var receipt blockchain.TransactionReceipt
163+ var txErr error
164+
165+ config := blockchain .NewTransactionConfig (firstChunk , BridgeConversionGasLimit , entities .NewWei (BridgeConversionGasPrice ))
166+ receipt , txErr = useCase .rskWallet .SendRbtc (ctx , config , bridgeAddress )
167+ if txErr == nil {
168+ log .Debugf ("%s: split tx 1/%d sent to the bridge successfully (%s)" , usecases .BridgePegoutId , n , receipt .TransactionHash )
169+ } else {
170+ err = useCase .updateQuotes (ctx , receipt , txErr , watchedQuotes )
171+ if err != nil {
172+ return usecases .WrapUseCaseError (usecases .BridgePegoutId , err )
173+ }
174+ return usecases .WrapUseCaseError (usecases .BridgePegoutId , txErr )
175+ }
176+
177+ for i := uint64 (1 ); i < n ; i ++ {
178+ config = blockchain .NewTransactionConfig (bridgeMin .Copy (), BridgeConversionGasLimit , entities .NewWei (BridgeConversionGasPrice ))
179+ receipt , txErr = useCase .rskWallet .SendRbtc (ctx , config , bridgeAddress )
180+ if txErr == nil {
181+ log .Debugf ("%s: split tx %d/%d sent to the bridge successfully (%s)" , usecases .BridgePegoutId , i + 1 , n , receipt .TransactionHash )
182+ } else {
183+ break
184+ }
185+ }
186+
187+ err = useCase .updateQuotes (ctx , receipt , txErr , watchedQuotes )
188+ if err != nil {
189+ return usecases .WrapUseCaseError (usecases .BridgePegoutId , err )
190+ }
191+ return nil
192+ }
193+
89194func (useCase * BridgePegoutUseCase ) updateQuotes (
90195 ctx context.Context ,
91196 receipt blockchain.TransactionReceipt ,
0 commit comments