Skip to content

Commit

Permalink
Add MinRebalanceAmount to RFQ relayer (#2263)
Browse files Browse the repository at this point in the history
* Feat: add MinRebalanceAmount with tests

* [goreleaser]

* Fix: handle zero case

* Feat: check for non positive amount outside of getRebalance()

* Cleanup: lint

* Cleanup: utilize getTokenConfig() helper

* [goreleaser]

* update godoc [goreleaser]

* Cleanup: lint

* Fix: don't rebalance if desired amount less than min

---------

Co-authored-by: Trajan0x <[email protected]>
  • Loading branch information
dwasse and trajan0x authored Mar 13, 2024
1 parent 0bad3fa commit 60ca5cd
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 20 deletions.
14 changes: 11 additions & 3 deletions services/rfq/relayer/inventory/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int,
if err != nil {
return fmt.Errorf("could not get rebalance: %w", err)
}
if rebalance == nil {
if rebalance == nil || rebalance.Amount.Cmp(big.NewInt(0)) <= 0 {
return nil
}
span.SetAttributes(
Expand Down Expand Up @@ -466,8 +466,16 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm
initialThresh, _ := new(big.Float).Mul(new(big.Float).SetInt(totalBalance), big.NewFloat(initialPct/100)).Int(nil)
amount := new(big.Int).Sub(maxTokenData.Balance, initialThresh)

// no need to rebalance since amount would be negative
if amount.Cmp(big.NewInt(0)) < 0 {
// no need to rebalance since amount would not be positive
if amount.Cmp(big.NewInt(0)) <= 0 {
//nolint:nilnil
return nil, nil
}

// filter the rebalance amount by the configured min
minAmount := cfg.GetMinRebalanceAmount(maxTokenData.ChainID, maxTokenData.Addr)
if amount.Cmp(minAmount) < 0 {
// no need to rebalance
//nolint:nilnil
return nil, nil
}
Expand Down
22 changes: 19 additions & 3 deletions services/rfq/relayer/inventory/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (i *InventoryTestSuite) TestGetRebalance() {
usdcDataDest.Addr: &usdcDataDest,
},
}
getConfig := func(maxRebalanceAmount string) relconfig.Config {
getConfig := func(minRebalanceAmount, maxRebalanceAmount string) relconfig.Config {
return relconfig.Config{
Chains: map[int]relconfig.ChainConfig{
origin: {
Expand All @@ -98,6 +98,7 @@ func (i *InventoryTestSuite) TestGetRebalance() {
Decimals: 6,
MaintenanceBalancePct: 20,
InitialBalancePct: 50,
MinRebalanceAmount: minRebalanceAmount,
MaxRebalanceAmount: maxRebalanceAmount,
},
},
Expand All @@ -109,6 +110,7 @@ func (i *InventoryTestSuite) TestGetRebalance() {
Decimals: 6,
MaintenanceBalancePct: 20,
InitialBalancePct: 50,
MinRebalanceAmount: minRebalanceAmount,
MaxRebalanceAmount: maxRebalanceAmount,
},
},
Expand All @@ -120,6 +122,7 @@ func (i *InventoryTestSuite) TestGetRebalance() {
Decimals: 6,
MaintenanceBalancePct: 0,
InitialBalancePct: 0,
MinRebalanceAmount: minRebalanceAmount,
MaxRebalanceAmount: maxRebalanceAmount,
},
},
Expand All @@ -129,13 +132,20 @@ func (i *InventoryTestSuite) TestGetRebalance() {
}

// 10 USDC on both chains; no rebalance needed
cfg := getConfig("")
cfg := getConfig("", "")
usdcDataOrigin.Balance = big.NewInt(1e7)
usdcDataDest.Balance = big.NewInt(1e7)
rebalance, err := inventory.GetRebalance(cfg, tokens, origin, usdcDataOrigin.Addr)
i.NoError(err)
i.Nil(rebalance)

// Set balances to zero
usdcDataOrigin.Balance = big.NewInt(0)
usdcDataDest.Balance = big.NewInt(0)
rebalance, err = inventory.GetRebalance(cfg, tokens, origin, usdcDataOrigin.Addr)
i.NoError(err)
i.Nil(rebalance)

// Set origin balance below maintenance threshold; need rebalance
usdcDataOrigin.Balance = big.NewInt(9e6)
usdcDataDest.Balance = big.NewInt(1e6)
Expand All @@ -148,8 +158,14 @@ func (i *InventoryTestSuite) TestGetRebalance() {
}
i.Equal(expected, rebalance)

// Set min rebalance amount
cfgWithMax := getConfig("10", "1000000000")
rebalance, err = inventory.GetRebalance(cfgWithMax, tokens, origin, usdcDataOrigin.Addr)
i.NoError(err)
i.Nil(rebalance)

// Set max rebalance amount
cfgWithMax := getConfig("1.1")
cfgWithMax = getConfig("0", "1.1")
rebalance, err = inventory.GetRebalance(cfgWithMax, tokens, origin, usdcDataOrigin.Addr)
i.NoError(err)
expected = &inventory.RebalanceData{
Expand Down
3 changes: 3 additions & 0 deletions services/rfq/relayer/relconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ type TokenConfig struct {
MaintenanceBalancePct float64 `yaml:"maintenance_balance_pct"`
// InitialBalancePct is the percentage of the total balance to retain when triggering a rebalance.
InitialBalancePct float64 `yaml:"initial_balance_pct"`
// MinRebalanceAmount is the minimum amount to rebalance in human-readable units.
// For USDC-through-cctp pairs this defaults to $1,000.
MinRebalanceAmount string `yaml:"min_rebalance_amount"`
// MaxRebalanceAmount is the maximum amount to rebalance in human-readable units.
MaxRebalanceAmount string `yaml:"max_rebalance_amount"`
}
Expand Down
40 changes: 26 additions & 14 deletions services/rfq/relayer/relconfig/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,25 +547,37 @@ func (c Config) GetMinQuoteAmount(chainID int, addr common.Address) *big.Int {
return quoteAmountScaled
}

var defaultMaxRebalanceAmount = abi.MaxInt256
var defaultMinRebalanceAmount = big.NewInt(1000)

// GetMaxRebalanceAmount returns the max rebalance amount for the given chain and address.
// GetMinRebalanceAmount returns the min rebalance amount for the given chain and address.
// Note that this getter returns the value in native token decimals.
func (c Config) GetMaxRebalanceAmount(chainID int, addr common.Address) *big.Int {
chainCfg, ok := c.Chains[chainID]
if !ok {
//
//nolint:dupl
func (c Config) GetMinRebalanceAmount(chainID int, addr common.Address) *big.Int {
tokenCfg, _, err := c.getTokenConfigByAddr(chainID, addr.Hex())
if err != nil {
return defaultMaxRebalanceAmount
}

var tokenCfg *TokenConfig
for _, cfg := range chainCfg.Tokens {
if common.HexToAddress(cfg.Address).Hex() == addr.Hex() {
cfgCopy := cfg
tokenCfg = &cfgCopy
break
}
rebalanceAmountFlt, ok := new(big.Float).SetString(tokenCfg.MinRebalanceAmount)
if !ok || rebalanceAmountFlt == nil {
return defaultMinRebalanceAmount
}
if tokenCfg == nil {

// Scale by the token decimals.
denomDecimalsFactor := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(tokenCfg.Decimals)), nil)
minRebalanceAmountScaled, _ := new(big.Float).Mul(rebalanceAmountFlt, new(big.Float).SetInt(denomDecimalsFactor)).Int(nil)
return minRebalanceAmountScaled
}

var defaultMaxRebalanceAmount = abi.MaxInt256

// GetMaxRebalanceAmount returns the max rebalance amount for the given chain and address.
// Note that this getter returns the value in native token decimals.
//
//nolint:dupl
func (c Config) GetMaxRebalanceAmount(chainID int, addr common.Address) *big.Int {
tokenCfg, _, err := c.getTokenConfigByAddr(chainID, addr.Hex())
if err != nil {
return defaultMaxRebalanceAmount
}
rebalanceAmountFlt, ok := new(big.Float).SetString(tokenCfg.MaxRebalanceAmount)
Expand Down

0 comments on commit 60ca5cd

Please sign in to comment.