From 11e1ac417af62980c1f5898c7c43073317d46c6c Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 00:01:38 -0400 Subject: [PATCH 01/10] Feat: add MinRebalanceAmount with tests --- services/rfq/relayer/inventory/manager.go | 6 ++++ .../rfq/relayer/inventory/manager_test.go | 20 ++++++++++-- services/rfq/relayer/relconfig/config.go | 2 ++ services/rfq/relayer/relconfig/getters.go | 32 +++++++++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index 658d1a7220..e566e82515 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -472,6 +472,12 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm return nil, nil } + // clip the rebalance amount by the configured min + minAmount := cfg.GetMinRebalanceAmount(maxTokenData.ChainID, maxTokenData.Addr) + if amount.Cmp(minAmount) < 0 { + amount = minAmount + } + // clip the rebalance amount by the configured max maxAmount := cfg.GetMaxRebalanceAmount(maxTokenData.ChainID, maxTokenData.Addr) if amount.Cmp(maxAmount) > 0 { diff --git a/services/rfq/relayer/inventory/manager_test.go b/services/rfq/relayer/inventory/manager_test.go index 11d33709f6..0ff3cbee62 100644 --- a/services/rfq/relayer/inventory/manager_test.go +++ b/services/rfq/relayer/inventory/manager_test.go @@ -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: { @@ -98,6 +98,7 @@ func (i *InventoryTestSuite) TestGetRebalance() { Decimals: 6, MaintenanceBalancePct: 20, InitialBalancePct: 50, + MinRebalanceAmount: minRebalanceAmount, MaxRebalanceAmount: maxRebalanceAmount, }, }, @@ -109,6 +110,7 @@ func (i *InventoryTestSuite) TestGetRebalance() { Decimals: 6, MaintenanceBalancePct: 20, InitialBalancePct: 50, + MinRebalanceAmount: minRebalanceAmount, MaxRebalanceAmount: maxRebalanceAmount, }, }, @@ -120,6 +122,7 @@ func (i *InventoryTestSuite) TestGetRebalance() { Decimals: 6, MaintenanceBalancePct: 0, InitialBalancePct: 0, + MinRebalanceAmount: minRebalanceAmount, MaxRebalanceAmount: maxRebalanceAmount, }, }, @@ -129,7 +132,7 @@ 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) @@ -148,8 +151,19 @@ 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) + expected = &inventory.RebalanceData{ + OriginMetadata: &usdcDataOrigin, + DestMetadata: &usdcDataDest, + Amount: big.NewInt(1e7), + } + i.Equal(expected, 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{ diff --git a/services/rfq/relayer/relconfig/config.go b/services/rfq/relayer/relconfig/config.go index 12d3cb7ef6..a52e7b74f2 100644 --- a/services/rfq/relayer/relconfig/config.go +++ b/services/rfq/relayer/relconfig/config.go @@ -102,6 +102,8 @@ 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. + MinRebalanceAmount string `yaml:"min_rebalance_amount"` // MaxRebalanceAmount is the maximum amount to rebalance in human-readable units. MaxRebalanceAmount string `yaml:"max_rebalance_amount"` } diff --git a/services/rfq/relayer/relconfig/getters.go b/services/rfq/relayer/relconfig/getters.go index 2e0b5362de..6d7e87364e 100644 --- a/services/rfq/relayer/relconfig/getters.go +++ b/services/rfq/relayer/relconfig/getters.go @@ -547,6 +547,38 @@ func (c Config) GetMinQuoteAmount(chainID int, addr common.Address) *big.Int { return quoteAmountScaled } +var defaultMinRebalanceAmount = big.NewInt(1000) + +// 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) GetMinRebalanceAmount(chainID int, addr common.Address) *big.Int { + chainCfg, ok := c.Chains[chainID] + if !ok { + return defaultMinRebalanceAmount + } + + var tokenCfg *TokenConfig + for _, cfg := range chainCfg.Tokens { + if common.HexToAddress(cfg.Address).Hex() == addr.Hex() { + cfgCopy := cfg + tokenCfg = &cfgCopy + break + } + } + if tokenCfg == nil { + return defaultMinRebalanceAmount + } + rebalanceAmountFlt, ok := new(big.Float).SetString(tokenCfg.MinRebalanceAmount) + if !ok || rebalanceAmountFlt == nil { + return defaultMinRebalanceAmount + } + + // 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. From 7347ae612bf16b23d1046da9e9ee18bb87bcd0c8 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 00:01:43 -0400 Subject: [PATCH 02/10] [goreleaser] From e74b1939755af090ee4109d0bc8b4dee0badea88 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 00:06:06 -0400 Subject: [PATCH 03/10] Fix: handle zero case --- services/rfq/relayer/inventory/manager.go | 4 ++-- services/rfq/relayer/inventory/manager_test.go | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index e566e82515..4cfa74289c 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -466,8 +466,8 @@ 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 } diff --git a/services/rfq/relayer/inventory/manager_test.go b/services/rfq/relayer/inventory/manager_test.go index 0ff3cbee62..964a525418 100644 --- a/services/rfq/relayer/inventory/manager_test.go +++ b/services/rfq/relayer/inventory/manager_test.go @@ -139,6 +139,13 @@ func (i *InventoryTestSuite) TestGetRebalance() { 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) From 6f0e782c39c2fb5ba4c02eddc2273ccb12d6e2df Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 00:12:05 -0400 Subject: [PATCH 04/10] Feat: check for non positive amount outside of getRebalance() --- services/rfq/relayer/inventory/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index 4cfa74289c..6d8aa06bb3 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -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( From 3f55ebbb400652561baf123d4809603cb3655685 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 00:14:28 -0400 Subject: [PATCH 05/10] Cleanup: lint --- services/rfq/relayer/relconfig/getters.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/rfq/relayer/relconfig/getters.go b/services/rfq/relayer/relconfig/getters.go index 6d7e87364e..e0b892eb4b 100644 --- a/services/rfq/relayer/relconfig/getters.go +++ b/services/rfq/relayer/relconfig/getters.go @@ -551,6 +551,8 @@ var defaultMinRebalanceAmount = big.NewInt(1000) // GetMinRebalanceAmount returns the min rebalance amount for the given chain and address. // Note that this getter returns the value in native token decimals. +// +//nolint:dupl func (c Config) GetMinRebalanceAmount(chainID int, addr common.Address) *big.Int { chainCfg, ok := c.Chains[chainID] if !ok { @@ -583,6 +585,8 @@ 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 { chainCfg, ok := c.Chains[chainID] if !ok { From c751c46443322cc2c61fab11acfca8ddee09c47d Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 00:21:16 -0400 Subject: [PATCH 06/10] Cleanup: utilize getTokenConfig() helper --- services/rfq/relayer/relconfig/getters.go | 34 ++++------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/services/rfq/relayer/relconfig/getters.go b/services/rfq/relayer/relconfig/getters.go index e0b892eb4b..afc0c1d80c 100644 --- a/services/rfq/relayer/relconfig/getters.go +++ b/services/rfq/relayer/relconfig/getters.go @@ -554,21 +554,9 @@ var defaultMinRebalanceAmount = big.NewInt(1000) // //nolint:dupl func (c Config) GetMinRebalanceAmount(chainID int, addr common.Address) *big.Int { - chainCfg, ok := c.Chains[chainID] - if !ok { - return defaultMinRebalanceAmount - } - - var tokenCfg *TokenConfig - for _, cfg := range chainCfg.Tokens { - if common.HexToAddress(cfg.Address).Hex() == addr.Hex() { - cfgCopy := cfg - tokenCfg = &cfgCopy - break - } - } - if tokenCfg == nil { - return defaultMinRebalanceAmount + tokenCfg, _, err := c.getTokenConfigByAddr(chainID, addr.Hex()) + if err != nil { + return defaultMaxRebalanceAmount } rebalanceAmountFlt, ok := new(big.Float).SetString(tokenCfg.MinRebalanceAmount) if !ok || rebalanceAmountFlt == nil { @@ -588,20 +576,8 @@ var defaultMaxRebalanceAmount = abi.MaxInt256 // //nolint:dupl func (c Config) GetMaxRebalanceAmount(chainID int, addr common.Address) *big.Int { - chainCfg, ok := c.Chains[chainID] - if !ok { - return defaultMaxRebalanceAmount - } - - var tokenCfg *TokenConfig - for _, cfg := range chainCfg.Tokens { - if common.HexToAddress(cfg.Address).Hex() == addr.Hex() { - cfgCopy := cfg - tokenCfg = &cfgCopy - break - } - } - if tokenCfg == nil { + tokenCfg, _, err := c.getTokenConfigByAddr(chainID, addr.Hex()) + if err != nil { return defaultMaxRebalanceAmount } rebalanceAmountFlt, ok := new(big.Float).SetString(tokenCfg.MaxRebalanceAmount) From 5e752204128407f35d0438350992375782691780 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 00:21:20 -0400 Subject: [PATCH 07/10] [goreleaser] From a2423d9980c4bb47757a4e7d2abb1dda8b39df90 Mon Sep 17 00:00:00 2001 From: Trajan0x Date: Wed, 13 Mar 2024 04:23:30 +0000 Subject: [PATCH 08/10] update godoc [goreleaser] --- services/rfq/relayer/relconfig/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/services/rfq/relayer/relconfig/config.go b/services/rfq/relayer/relconfig/config.go index a52e7b74f2..1d29016909 100644 --- a/services/rfq/relayer/relconfig/config.go +++ b/services/rfq/relayer/relconfig/config.go @@ -95,6 +95,7 @@ type TokenConfig struct { // For now, specify the USD price of the token in the config. PriceUSD float64 `yaml:"price_usd"` // MinQuoteAmount is the minimum amount to quote for this token in human-readable units. + // for USDC-through-cctp pairs this defualts to $1,000. MinQuoteAmount string `yaml:"min_quote_amount"` // RebalanceMethod is the method to use for rebalancing. RebalanceMethod string `yaml:"rebalance_method"` From 112197b455c89ddaa43ec433a26eb25827c56c19 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 15:34:41 -0400 Subject: [PATCH 09/10] Cleanup: lint --- services/rfq/relayer/relconfig/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/rfq/relayer/relconfig/config.go b/services/rfq/relayer/relconfig/config.go index 1d29016909..864275e7bc 100644 --- a/services/rfq/relayer/relconfig/config.go +++ b/services/rfq/relayer/relconfig/config.go @@ -95,7 +95,6 @@ type TokenConfig struct { // For now, specify the USD price of the token in the config. PriceUSD float64 `yaml:"price_usd"` // MinQuoteAmount is the minimum amount to quote for this token in human-readable units. - // for USDC-through-cctp pairs this defualts to $1,000. MinQuoteAmount string `yaml:"min_quote_amount"` // RebalanceMethod is the method to use for rebalancing. RebalanceMethod string `yaml:"rebalance_method"` @@ -104,6 +103,7 @@ type TokenConfig struct { // 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"` From 43436e38f7ac8cdd74f57b459b44e0a91679c794 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Wed, 13 Mar 2024 15:42:21 -0400 Subject: [PATCH 10/10] Fix: don't rebalance if desired amount less than min --- services/rfq/relayer/inventory/manager.go | 6 ++++-- services/rfq/relayer/inventory/manager_test.go | 7 +------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index 6d8aa06bb3..abd131542a 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -472,10 +472,12 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm return nil, nil } - // clip the rebalance amount by the configured min + // filter the rebalance amount by the configured min minAmount := cfg.GetMinRebalanceAmount(maxTokenData.ChainID, maxTokenData.Addr) if amount.Cmp(minAmount) < 0 { - amount = minAmount + // no need to rebalance + //nolint:nilnil + return nil, nil } // clip the rebalance amount by the configured max diff --git a/services/rfq/relayer/inventory/manager_test.go b/services/rfq/relayer/inventory/manager_test.go index 964a525418..84444a2c5f 100644 --- a/services/rfq/relayer/inventory/manager_test.go +++ b/services/rfq/relayer/inventory/manager_test.go @@ -162,12 +162,7 @@ func (i *InventoryTestSuite) TestGetRebalance() { cfgWithMax := getConfig("10", "1000000000") rebalance, err = inventory.GetRebalance(cfgWithMax, tokens, origin, usdcDataOrigin.Addr) i.NoError(err) - expected = &inventory.RebalanceData{ - OriginMetadata: &usdcDataOrigin, - DestMetadata: &usdcDataDest, - Amount: big.NewInt(1e7), - } - i.Equal(expected, rebalance) + i.Nil(rebalance) // Set max rebalance amount cfgWithMax = getConfig("0", "1.1")