From 226b5368206893c63183c983bc7a19fb27b88e49 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Thu, 2 May 2024 11:54:16 -0500 Subject: [PATCH 01/19] WIP: refactor GetRebalanceMethod() --- services/rfq/relayer/inventory/manager.go | 21 +++++------ services/rfq/relayer/relconfig/enum.go | 18 ++++++++++ services/rfq/relayer/relconfig/getters.go | 44 ++++++++++++----------- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index d12c5849c3..e234a81992 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -425,19 +425,10 @@ func (i *inventoryManagerImpl) HasSufficientGas(parentCtx context.Context, chain // will be rebalanced. // //nolint:cyclop -func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, token common.Address) error { - // evaluate the rebalance method - method, err := i.cfg.GetRebalanceMethod(chainID, token.Hex()) - if err != nil { - return fmt.Errorf("could not get rebalance method: %w", err) - } - if method == relconfig.RebalanceMethodNone { - return nil - } +func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, token common.Address) (err error) { ctx, span := i.handler.Tracer().Start(parentCtx, "Rebalance", trace.WithAttributes( attribute.Int(metrics.ChainID, chainID), attribute.String("token", token.Hex()), - attribute.String("rebalance_method", method.String()), )) defer func(err error) { metrics.EndSpanWithErr(span, err) @@ -457,6 +448,16 @@ func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, attribute.String("rebalance_amount", rebalance.Amount.String()), ) + // evaluate the rebalance method + method, err := i.cfg.GetRebalanceMethod(chainID, rebalance.DestMetadata.ChainID, token.Hex()) + if err != nil { + return fmt.Errorf("could not get rebalance method: %w", err) + } + span.SetAttributes(attribute.String("rebalance_method", method.String())) + if method == relconfig.RebalanceMethodNone { + return nil + } + // make sure there are no pending rebalances that touch the given path pendingRebalances, err := i.db.GetPendingRebalances(ctx, uint64(rebalance.OriginMetadata.ChainID), uint64(rebalance.DestMetadata.ChainID)) if err != nil { diff --git a/services/rfq/relayer/relconfig/enum.go b/services/rfq/relayer/relconfig/enum.go index 8a8665aba6..439c75715e 100644 --- a/services/rfq/relayer/relconfig/enum.go +++ b/services/rfq/relayer/relconfig/enum.go @@ -1,5 +1,7 @@ package relconfig +import "fmt" + // RebalanceMethod is the method to rebalance. // //go:generate go run golang.org/x/tools/cmd/stringer -type=RebalanceMethod @@ -15,3 +17,19 @@ const ( // RebalanceMethodNative is the rebalance method for native bridge. RebalanceMethodNative ) + +// RebalanceMethodFromString converts a string to a RebalanceMethod. +func RebalanceMethodFromString(str string) (RebalanceMethod, error) { + switch str { + case "synapsecctp": + return RebalanceMethodSynapseCCTP, nil + case "circlecctp": + return RebalanceMethodCircleCCTP, nil + case "native": + return RebalanceMethodNative, nil + case "": + return RebalanceMethodNone, nil + default: + return RebalanceMethodNone, fmt.Errorf("invalid rebalance method: %s", str) + } +} diff --git a/services/rfq/relayer/relconfig/getters.go b/services/rfq/relayer/relconfig/getters.go index 57d03ac48e..45377e7d70 100644 --- a/services/rfq/relayer/relconfig/getters.go +++ b/services/rfq/relayer/relconfig/getters.go @@ -402,32 +402,36 @@ func (c Config) getTokenConfigByAddr(chainID int, tokenAddr string) (cfg TokenCo return cfg, name, fmt.Errorf("no token config for chain %d and address %s", chainID, tokenAddr) } -// GetRebalanceMethod returns the rebalance method for the given chain and token address. -func (c Config) GetRebalanceMethod(chainID int, tokenAddr string) (method RebalanceMethod, err error) { - tokenConfig, tokenName, err := c.getTokenConfigByAddr(chainID, tokenAddr) +// GetRebalanceMethod returns the rebalance method for the given chain path and token address. +// This method will error if there is a rebalance method mismatch, and neither methods correspond to +// RebalanceMethodNone. +func (c Config) GetRebalanceMethod(origin, dest int, originAddr string) (method RebalanceMethod, err error) { + originTokenCfg, tokenName, err := c.getTokenConfigByAddr(origin, originAddr) if err != nil { return 0, err } - if tokenConfig.RebalanceMethod == "" { - return RebalanceMethodNone, nil + destTokenCfg, ok := c.Chains[dest].Tokens[tokenName] + if !ok { + return 0, fmt.Errorf("could not get dest token config with chain %d and token name %s", dest, tokenName) } - for cid, chainCfg := range c.Chains { - tokenCfg, ok := chainCfg.Tokens[tokenName] - if ok { - if tokenConfig.RebalanceMethod != tokenCfg.RebalanceMethod { - return RebalanceMethodNone, fmt.Errorf("rebalance method mismatch for token %s on chains %d and %d", tokenName, chainID, cid) - } - } + + originMethod, err := RebalanceMethodFromString(originTokenCfg.RebalanceMethod) + if err != nil { + return 0, err } - switch tokenConfig.RebalanceMethod { - case "synapsecctp": - return RebalanceMethodSynapseCCTP, nil - case "circlecctp": - return RebalanceMethodCircleCCTP, nil - case "native": - return RebalanceMethodNative, nil + destMethod, err := RebalanceMethodFromString(destTokenCfg.RebalanceMethod) + if err != nil { + return 0, err + } + + if originMethod == destMethod { + return originMethod, nil } - return RebalanceMethodNone, nil + if originMethod == RebalanceMethodNone || destMethod == RebalanceMethodNone { + return RebalanceMethodNone, nil + } + + return RebalanceMethodNone, fmt.Errorf("rebalance method mismatch for token %s on chains %d and %d", tokenName, origin, dest) } // GetRebalanceMethods returns all rebalance methods present in the config. From 478d9ce08d5b8d94c0b28fe735eabcf292f8956c Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Thu, 2 May 2024 12:04:26 -0500 Subject: [PATCH 02/19] Feat: implement more robust rebalance method handling --- services/rfq/relayer/inventory/manager.go | 28 +++++++++++++++++------ services/rfq/relayer/relconfig/enum.go | 8 +++++++ services/rfq/relayer/relconfig/getters.go | 24 ++++--------------- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index e234a81992..5e3841efae 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -426,6 +426,15 @@ func (i *inventoryManagerImpl) HasSufficientGas(parentCtx context.Context, chain // //nolint:cyclop func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, token common.Address) (err error) { + // evaluate the rebalance method + methodOrigin, err := i.cfg.GetRebalanceMethod(chainID, token.Hex()) + if err != nil { + return fmt.Errorf("could not get origin rebalance method: %w", err) + } + if methodOrigin == relconfig.RebalanceMethodNone { + return nil + } + ctx, span := i.handler.Tracer().Start(parentCtx, "Rebalance", trace.WithAttributes( attribute.Int(metrics.ChainID, chainID), attribute.String("token", token.Hex()), @@ -448,13 +457,18 @@ func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, attribute.String("rebalance_amount", rebalance.Amount.String()), ) - // evaluate the rebalance method - method, err := i.cfg.GetRebalanceMethod(chainID, rebalance.DestMetadata.ChainID, token.Hex()) + // validate the rebalance method pair + methodDest, err := i.cfg.GetRebalanceMethod(int(rebalance.DestMetadata.ChainID), rebalance.DestMetadata.Addr.Hex()) if err != nil { - return fmt.Errorf("could not get rebalance method: %w", err) + return fmt.Errorf("could not get dest rebalance method: %w", err) } - span.SetAttributes(attribute.String("rebalance_method", method.String())) - if method == relconfig.RebalanceMethodNone { + mismatch := relconfig.IsRebalanceMethodMismatch(methodOrigin, methodDest) + defer func() { + span.SetAttributes(attribute.Bool("rebalance_method_mismatch", mismatch)) + span.SetAttributes(attribute.Int("origin_rebalance_method", int(methodOrigin))) + span.SetAttributes(attribute.Int("dest_rebalance_method", int(methodDest))) + }() + if mismatch { return nil } @@ -476,9 +490,9 @@ func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, } // execute the rebalance - manager, ok := i.rebalanceManagers[method] + manager, ok := i.rebalanceManagers[methodOrigin] if !ok { - return fmt.Errorf("no rebalance manager for method: %s", method) + return fmt.Errorf("no rebalance manager for method: %s", methodOrigin) } err = manager.Execute(ctx, rebalance) if err != nil { diff --git a/services/rfq/relayer/relconfig/enum.go b/services/rfq/relayer/relconfig/enum.go index 439c75715e..0e227e48d4 100644 --- a/services/rfq/relayer/relconfig/enum.go +++ b/services/rfq/relayer/relconfig/enum.go @@ -33,3 +33,11 @@ func RebalanceMethodFromString(str string) (RebalanceMethod, error) { return RebalanceMethodNone, fmt.Errorf("invalid rebalance method: %s", str) } } + +// IsRebalanceMethodMismatch returns true if the two rebalance methods are different and both are not None. +func IsRebalanceMethodMismatch(a, b RebalanceMethod) bool { + if a == RebalanceMethodNone || b == RebalanceMethodNone { + return false + } + return a != b +} diff --git a/services/rfq/relayer/relconfig/getters.go b/services/rfq/relayer/relconfig/getters.go index 45377e7d70..d844850762 100644 --- a/services/rfq/relayer/relconfig/getters.go +++ b/services/rfq/relayer/relconfig/getters.go @@ -405,33 +405,17 @@ func (c Config) getTokenConfigByAddr(chainID int, tokenAddr string) (cfg TokenCo // GetRebalanceMethod returns the rebalance method for the given chain path and token address. // This method will error if there is a rebalance method mismatch, and neither methods correspond to // RebalanceMethodNone. -func (c Config) GetRebalanceMethod(origin, dest int, originAddr string) (method RebalanceMethod, err error) { - originTokenCfg, tokenName, err := c.getTokenConfigByAddr(origin, originAddr) +func (c Config) GetRebalanceMethod(chainID int, tokenAddr string) (method RebalanceMethod, err error) { + tokenCfg, _, err := c.getTokenConfigByAddr(chainID, tokenAddr) if err != nil { return 0, err } - destTokenCfg, ok := c.Chains[dest].Tokens[tokenName] - if !ok { - return 0, fmt.Errorf("could not get dest token config with chain %d and token name %s", dest, tokenName) - } - originMethod, err := RebalanceMethodFromString(originTokenCfg.RebalanceMethod) - if err != nil { - return 0, err - } - destMethod, err := RebalanceMethodFromString(destTokenCfg.RebalanceMethod) + method, err = RebalanceMethodFromString(tokenCfg.RebalanceMethod) if err != nil { return 0, err } - - if originMethod == destMethod { - return originMethod, nil - } - if originMethod == RebalanceMethodNone || destMethod == RebalanceMethodNone { - return RebalanceMethodNone, nil - } - - return RebalanceMethodNone, fmt.Errorf("rebalance method mismatch for token %s on chains %d and %d", tokenName, origin, dest) + return method, nil } // GetRebalanceMethods returns all rebalance methods present in the config. From 8e331bdfdd55bfc12cd6fe4c530c0fdacd73fa2a Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Thu, 2 May 2024 12:26:14 -0500 Subject: [PATCH 03/19] Feat: move method validation to getRebalance() func --- services/rfq/relayer/inventory/manager.go | 101 ++++++++++++-------- services/rfq/relayer/inventory/rebalance.go | 3 + services/rfq/relayer/relconfig/enum.go | 10 +- 3 files changed, 69 insertions(+), 45 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index 5e3841efae..693c6d9c0a 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -426,7 +426,7 @@ func (i *inventoryManagerImpl) HasSufficientGas(parentCtx context.Context, chain // //nolint:cyclop func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, token common.Address) (err error) { - // evaluate the rebalance method + // short circuit if origin does not specify a rebalance method methodOrigin, err := i.cfg.GetRebalanceMethod(chainID, token.Hex()) if err != nil { return fmt.Errorf("could not get origin rebalance method: %w", err) @@ -457,21 +457,6 @@ func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, attribute.String("rebalance_amount", rebalance.Amount.String()), ) - // validate the rebalance method pair - methodDest, err := i.cfg.GetRebalanceMethod(int(rebalance.DestMetadata.ChainID), rebalance.DestMetadata.Addr.Hex()) - if err != nil { - return fmt.Errorf("could not get dest rebalance method: %w", err) - } - mismatch := relconfig.IsRebalanceMethodMismatch(methodOrigin, methodDest) - defer func() { - span.SetAttributes(attribute.Bool("rebalance_method_mismatch", mismatch)) - span.SetAttributes(attribute.Int("origin_rebalance_method", int(methodOrigin))) - span.SetAttributes(attribute.Int("dest_rebalance_method", int(methodDest))) - }() - if mismatch { - return nil - } - // make sure there are no pending rebalances that touch the given path pendingRebalances, err := i.db.GetPendingRebalances(ctx, uint64(rebalance.OriginMetadata.ChainID), uint64(rebalance.DestMetadata.ChainID)) if err != nil { @@ -537,54 +522,89 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm } } - // get total balance for given token across all chains - totalBalance := big.NewInt(0) + // evaluate the origin and dest of the rebalance based on min/max token balances + var destTokenData, originTokenData *TokenMetadata for _, tokenMap := range tokens { for _, tokenData := range tokenMap { if tokenData.Name == rebalanceTokenData.Name { - totalBalance.Add(totalBalance, tokenData.Balance) + if destTokenData == nil || tokenData.Balance.Cmp(destTokenData.Balance) < 0 { + destTokenData = tokenData + } + if originTokenData == nil || tokenData.Balance.Cmp(originTokenData.Balance) > 0 { + originTokenData = tokenData + } } } } - // check if any balances are below maintenance threshold - var minTokenData, maxTokenData *TokenMetadata - for _, tokenMap := range tokens { - for _, tokenData := range tokenMap { - if tokenData.Name == rebalanceTokenData.Name { - if minTokenData == nil || tokenData.Balance.Cmp(minTokenData.Balance) < 0 { - minTokenData = tokenData - } - if maxTokenData == nil || tokenData.Balance.Cmp(maxTokenData.Balance) > 0 { - maxTokenData = tokenData - } - } + // if the given chain is not the origin of the rebalance, no need to do anything + defer func() { + if span != nil { + span.SetAttributes( + attribute.Int("rebalance_chain_id", chainID), + attribute.Int("rebalance_origin", originTokenData.ChainID), + attribute.Int("rebalance_dest", destTokenData.ChainID), + ) } + }() + if originTokenData.ChainID != chainID { + return nil, nil + } + + // validate the rebalance method pair + methodOrigin, err := cfg.GetRebalanceMethod(int(rebalance.OriginMetadata.ChainID), rebalance.OriginMetadata.Addr.Hex()) + if err != nil { + return nil, fmt.Errorf("could not get origin rebalance method: %w", err) + } + methodDest, err := cfg.GetRebalanceMethod(int(rebalance.DestMetadata.ChainID), rebalance.DestMetadata.Addr.Hex()) + if err != nil { + return nil, fmt.Errorf("could not get dest rebalance method: %w", err) + } + rebalanceMethod := relconfig.CoalesceRebalanceMethods(methodOrigin, methodDest) + defer func() { + if span != nil { + span.SetAttributes(attribute.Int("rebalance_method", int(rebalanceMethod))) + span.SetAttributes(attribute.Int("origin_rebalance_method", int(methodOrigin))) + span.SetAttributes(attribute.Int("dest_rebalance_method", int(methodDest))) + } + }() + if rebalanceMethod == relconfig.RebalanceMethodNone { + return nil, nil } // get the initialPct for the origin chain - initialPct, err := cfg.GetInitialBalancePct(maxTokenData.ChainID, maxTokenData.Addr.Hex()) + initialPct, err := cfg.GetInitialBalancePct(originTokenData.ChainID, originTokenData.Addr.Hex()) if err != nil { return nil, fmt.Errorf("could not get initial pct: %w", err) } + + // calculate maintenance threshold relative to total balance + totalBalance := big.NewInt(0) + for _, tokenMap := range tokens { + for _, tokenData := range tokenMap { + if tokenData.Name == rebalanceTokenData.Name { + totalBalance.Add(totalBalance, tokenData.Balance) + } + } + } maintenanceThresh, _ := new(big.Float).Mul(new(big.Float).SetInt(totalBalance), big.NewFloat(maintenancePct/100)).Int(nil) if span != nil { span.SetAttributes(attribute.Float64("maintenance_pct", maintenancePct)) span.SetAttributes(attribute.Float64("initial_pct", initialPct)) - span.SetAttributes(attribute.String("max_token_balance", maxTokenData.Balance.String())) - span.SetAttributes(attribute.String("min_token_balance", minTokenData.Balance.String())) + span.SetAttributes(attribute.String("max_token_balance", originTokenData.Balance.String())) + span.SetAttributes(attribute.String("min_token_balance", destTokenData.Balance.String())) span.SetAttributes(attribute.String("total_balance", totalBalance.String())) span.SetAttributes(attribute.String("maintenance_thresh", maintenanceThresh.String())) } // check if the minimum balance is below the threshold and trigger rebalance - if minTokenData.Balance.Cmp(maintenanceThresh) > 0 { + if destTokenData.Balance.Cmp(maintenanceThresh) > 0 { return rebalance, nil } // calculate the amount to rebalance vs the initial threshold on origin initialThresh, _ := new(big.Float).Mul(new(big.Float).SetInt(totalBalance), big.NewFloat(initialPct/100)).Int(nil) - amount := new(big.Int).Sub(maxTokenData.Balance, initialThresh) + amount := new(big.Int).Sub(originTokenData.Balance, initialThresh) // no need to rebalance since amount would not be positive if amount.Cmp(big.NewInt(0)) <= 0 { @@ -593,7 +613,7 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm } // filter the rebalance amount by the configured min - minAmount := cfg.GetMinRebalanceAmount(maxTokenData.ChainID, maxTokenData.Addr) + minAmount := cfg.GetMinRebalanceAmount(originTokenData.ChainID, originTokenData.Addr) if amount.Cmp(minAmount) < 0 { // no need to rebalance //nolint:nilnil @@ -601,7 +621,7 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm } // clip the rebalance amount by the configured max - maxAmount := cfg.GetMaxRebalanceAmount(maxTokenData.ChainID, maxTokenData.Addr) + maxAmount := cfg.GetMaxRebalanceAmount(originTokenData.ChainID, originTokenData.Addr) if amount.Cmp(maxAmount) > 0 { amount = maxAmount } @@ -614,9 +634,10 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm } rebalance = &RebalanceData{ - OriginMetadata: maxTokenData, - DestMetadata: minTokenData, + OriginMetadata: originTokenData, + DestMetadata: destTokenData, Amount: amount, + Method: rebalanceMethod, } return rebalance, nil } diff --git a/services/rfq/relayer/inventory/rebalance.go b/services/rfq/relayer/inventory/rebalance.go index 40119f9555..a0d5bd9874 100644 --- a/services/rfq/relayer/inventory/rebalance.go +++ b/services/rfq/relayer/inventory/rebalance.go @@ -3,6 +3,8 @@ package inventory import ( "context" "math/big" + + "github.com/synapsecns/sanguine/services/rfq/relayer/relconfig" ) // RebalanceData contains metadata for a rebalance action. @@ -10,6 +12,7 @@ type RebalanceData struct { OriginMetadata *TokenMetadata DestMetadata *TokenMetadata Amount *big.Int + Method relconfig.RebalanceMethod } // RebalanceManager is the interface for the rebalance manager. diff --git a/services/rfq/relayer/relconfig/enum.go b/services/rfq/relayer/relconfig/enum.go index 0e227e48d4..66f6f3949f 100644 --- a/services/rfq/relayer/relconfig/enum.go +++ b/services/rfq/relayer/relconfig/enum.go @@ -34,10 +34,10 @@ func RebalanceMethodFromString(str string) (RebalanceMethod, error) { } } -// IsRebalanceMethodMismatch returns true if the two rebalance methods are different and both are not None. -func IsRebalanceMethodMismatch(a, b RebalanceMethod) bool { - if a == RebalanceMethodNone || b == RebalanceMethodNone { - return false +// CoalesceRebalanceMethods coalesces two rebalance methods. +func CoalesceRebalanceMethods(a, b RebalanceMethod) RebalanceMethod { + if a == b { + return a } - return a != b + return RebalanceMethodNone } From 5c8a13a6d59aa80b448ce502ed7665ec78b05c4f Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Thu, 2 May 2024 12:30:11 -0500 Subject: [PATCH 04/19] Feat: manual impl for String() on RebalanceMethod --- services/rfq/relayer/relconfig/enum.go | 17 ++++++++++-- .../relconfig/rebalancemethod_string.go | 26 ------------------- 2 files changed, 15 insertions(+), 28 deletions(-) delete mode 100644 services/rfq/relayer/relconfig/rebalancemethod_string.go diff --git a/services/rfq/relayer/relconfig/enum.go b/services/rfq/relayer/relconfig/enum.go index 66f6f3949f..9e90c1b545 100644 --- a/services/rfq/relayer/relconfig/enum.go +++ b/services/rfq/relayer/relconfig/enum.go @@ -3,8 +3,6 @@ package relconfig import "fmt" // RebalanceMethod is the method to rebalance. -// -//go:generate go run golang.org/x/tools/cmd/stringer -type=RebalanceMethod type RebalanceMethod uint8 const ( @@ -34,6 +32,21 @@ func RebalanceMethodFromString(str string) (RebalanceMethod, error) { } } +func (i RebalanceMethod) String() string { + switch i { + case RebalanceMethodNone: + return "" + case RebalanceMethodSynapseCCTP: + return "synapsecctp" + case RebalanceMethodCircleCCTP: + return "circlecctp" + case RebalanceMethodNative: + return "native" + default: + return "" + } +} + // CoalesceRebalanceMethods coalesces two rebalance methods. func CoalesceRebalanceMethods(a, b RebalanceMethod) RebalanceMethod { if a == b { diff --git a/services/rfq/relayer/relconfig/rebalancemethod_string.go b/services/rfq/relayer/relconfig/rebalancemethod_string.go deleted file mode 100644 index 44dae853ef..0000000000 --- a/services/rfq/relayer/relconfig/rebalancemethod_string.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by "stringer -type=RebalanceMethod"; DO NOT EDIT. - -package relconfig - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[RebalanceMethodNone-0] - _ = x[RebalanceMethodSynapseCCTP-1] - _ = x[RebalanceMethodCircleCCTP-2] - _ = x[RebalanceMethodNative-3] -} - -const _RebalanceMethod_name = "RebalanceMethodNoneRebalanceMethodSynapseCCTPRebalanceMethodCircleCCTPRebalanceMethodNative" - -var _RebalanceMethod_index = [...]uint8{0, 19, 45, 70, 91} - -func (i RebalanceMethod) String() string { - if i >= RebalanceMethod(len(_RebalanceMethod_index)-1) { - return "RebalanceMethod(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _RebalanceMethod_name[_RebalanceMethod_index[i]:_RebalanceMethod_index[i+1]] -} From ce61396f7cf8f422ea2c75b8765da71887d24ac2 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Thu, 2 May 2024 12:34:37 -0500 Subject: [PATCH 05/19] Fix: tests --- services/rfq/relayer/inventory/manager.go | 4 ++-- services/rfq/relayer/inventory/manager_test.go | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index 693c6d9c0a..fd5b143454 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -552,11 +552,11 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm } // validate the rebalance method pair - methodOrigin, err := cfg.GetRebalanceMethod(int(rebalance.OriginMetadata.ChainID), rebalance.OriginMetadata.Addr.Hex()) + methodOrigin, err := cfg.GetRebalanceMethod(int(originTokenData.ChainID), originTokenData.Addr.Hex()) if err != nil { return nil, fmt.Errorf("could not get origin rebalance method: %w", err) } - methodDest, err := cfg.GetRebalanceMethod(int(rebalance.DestMetadata.ChainID), rebalance.DestMetadata.Addr.Hex()) + methodDest, err := cfg.GetRebalanceMethod(int(destTokenData.ChainID), destTokenData.Addr.Hex()) if err != nil { return nil, fmt.Errorf("could not get dest rebalance method: %w", err) } diff --git a/services/rfq/relayer/inventory/manager_test.go b/services/rfq/relayer/inventory/manager_test.go index 1f66159f54..80e6b80212 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(minRebalanceAmount, maxRebalanceAmount string) relconfig.Config { + getConfig := func(minRebalanceAmount, maxRebalanceAmount string, originMethod, destMethod relconfig.RebalanceMethod) relconfig.Config { return relconfig.Config{ Chains: map[int]relconfig.ChainConfig{ origin: { @@ -100,6 +100,7 @@ func (i *InventoryTestSuite) TestGetRebalance() { InitialBalancePct: 50, MinRebalanceAmount: minRebalanceAmount, MaxRebalanceAmount: maxRebalanceAmount, + RebalanceMethod: originMethod.String(), }, }, }, @@ -112,6 +113,7 @@ func (i *InventoryTestSuite) TestGetRebalance() { InitialBalancePct: 50, MinRebalanceAmount: minRebalanceAmount, MaxRebalanceAmount: maxRebalanceAmount, + RebalanceMethod: destMethod.String(), }, }, }, @@ -124,6 +126,7 @@ func (i *InventoryTestSuite) TestGetRebalance() { InitialBalancePct: 0, MinRebalanceAmount: minRebalanceAmount, MaxRebalanceAmount: maxRebalanceAmount, + RebalanceMethod: destMethod.String(), }, }, }, @@ -132,7 +135,7 @@ func (i *InventoryTestSuite) TestGetRebalance() { } // 10 USDC on both chains; no rebalance needed - cfg := getConfig("", "") + cfg := getConfig("", "", relconfig.RebalanceMethodSynapseCCTP, relconfig.RebalanceMethodSynapseCCTP) usdcDataOrigin.Balance = big.NewInt(1e7) usdcDataDest.Balance = big.NewInt(1e7) rebalance, err := inventory.GetRebalance(cfg, tokens, origin, usdcDataOrigin.Addr) @@ -155,23 +158,25 @@ func (i *InventoryTestSuite) TestGetRebalance() { OriginMetadata: &usdcDataOrigin, DestMetadata: &usdcDataDest, Amount: big.NewInt(4e6), + Method: relconfig.RebalanceMethodSynapseCCTP, } i.Equal(expected, rebalance) // Set min rebalance amount - cfgWithMax := getConfig("10", "1000000000") + cfgWithMax := getConfig("10", "1000000000", relconfig.RebalanceMethodSynapseCCTP, relconfig.RebalanceMethodSynapseCCTP) rebalance, err = inventory.GetRebalance(cfgWithMax, tokens, origin, usdcDataOrigin.Addr) i.NoError(err) i.Nil(rebalance) // Set max rebalance amount - cfgWithMax = getConfig("0", "1.1") + cfgWithMax = getConfig("0", "1.1", relconfig.RebalanceMethodSynapseCCTP, relconfig.RebalanceMethodSynapseCCTP) rebalance, err = inventory.GetRebalance(cfgWithMax, tokens, origin, usdcDataOrigin.Addr) i.NoError(err) expected = &inventory.RebalanceData{ OriginMetadata: &usdcDataOrigin, DestMetadata: &usdcDataDest, Amount: big.NewInt(1.1e6), + Method: relconfig.RebalanceMethodSynapseCCTP, } i.Equal(expected, rebalance) From 893776b476576fe28b008fdd2188d2629f534f0f Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Thu, 2 May 2024 12:37:09 -0500 Subject: [PATCH 06/19] Feat: add rebalance method clauses to TestGetRebalance --- services/rfq/relayer/inventory/manager_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/services/rfq/relayer/inventory/manager_test.go b/services/rfq/relayer/inventory/manager_test.go index 80e6b80212..741b9e180e 100644 --- a/services/rfq/relayer/inventory/manager_test.go +++ b/services/rfq/relayer/inventory/manager_test.go @@ -162,6 +162,18 @@ func (i *InventoryTestSuite) TestGetRebalance() { } i.Equal(expected, rebalance) + // Set rebalance methods to mismatch + cfg = getConfig("", "", relconfig.RebalanceMethodCircleCCTP, relconfig.RebalanceMethodSynapseCCTP) + rebalance, err = inventory.GetRebalance(cfg, tokens, origin, usdcDataOrigin.Addr) + i.NoError(err) + i.Nil(rebalance) + + // Set one rebalance method to None + cfg = getConfig("", "", relconfig.RebalanceMethodNone, relconfig.RebalanceMethodSynapseCCTP) + rebalance, err = inventory.GetRebalance(cfg, tokens, origin, usdcDataOrigin.Addr) + i.NoError(err) + i.Nil(rebalance) + // Set min rebalance amount cfgWithMax := getConfig("10", "1000000000", relconfig.RebalanceMethodSynapseCCTP, relconfig.RebalanceMethodSynapseCCTP) rebalance, err = inventory.GetRebalance(cfgWithMax, tokens, origin, usdcDataOrigin.Addr) From e844c9794a6379132649ff1d7246c6133d5af2b6 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Thu, 2 May 2024 12:39:19 -0500 Subject: [PATCH 07/19] Feat: use rebalance.Method to choose executor --- 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 fd5b143454..4d39cd8141 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -475,7 +475,7 @@ func (i *inventoryManagerImpl) Rebalance(parentCtx context.Context, chainID int, } // execute the rebalance - manager, ok := i.rebalanceManagers[methodOrigin] + manager, ok := i.rebalanceManagers[rebalance.Method] if !ok { return fmt.Errorf("no rebalance manager for method: %s", methodOrigin) } From d7dd21492279f5f98b17136b2332c53e3842c253 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 16:30:22 -0500 Subject: [PATCH 08/19] Cleanup: lint --- services/rfq/relayer/inventory/manager.go | 6 +++--- services/rfq/relayer/relconfig/getters.go | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index 4d39cd8141..66e0d88073 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -506,7 +506,7 @@ func (i *inventoryManagerImpl) registerPendingRebalance(ctx context.Context, reb return nil } -//nolint:cyclop,gocognit +//nolint:cyclop,gocognit,nilnil func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[common.Address]*TokenMetadata, chainID int, token common.Address) (rebalance *RebalanceData, err error) { maintenancePct, err := cfg.GetMaintenanceBalancePct(chainID, token.Hex()) if err != nil { @@ -552,11 +552,11 @@ func getRebalance(span trace.Span, cfg relconfig.Config, tokens map[int]map[comm } // validate the rebalance method pair - methodOrigin, err := cfg.GetRebalanceMethod(int(originTokenData.ChainID), originTokenData.Addr.Hex()) + methodOrigin, err := cfg.GetRebalanceMethod(originTokenData.ChainID, originTokenData.Addr.Hex()) if err != nil { return nil, fmt.Errorf("could not get origin rebalance method: %w", err) } - methodDest, err := cfg.GetRebalanceMethod(int(destTokenData.ChainID), destTokenData.Addr.Hex()) + methodDest, err := cfg.GetRebalanceMethod(destTokenData.ChainID, destTokenData.Addr.Hex()) if err != nil { return nil, fmt.Errorf("could not get dest rebalance method: %w", err) } diff --git a/services/rfq/relayer/relconfig/getters.go b/services/rfq/relayer/relconfig/getters.go index d844850762..c1f2599cf1 100644 --- a/services/rfq/relayer/relconfig/getters.go +++ b/services/rfq/relayer/relconfig/getters.go @@ -389,24 +389,24 @@ func (c Config) GetHTTPTimeout() time.Duration { return time.Duration(timeoutMs) * time.Millisecond } -func (c Config) getTokenConfigByAddr(chainID int, tokenAddr string) (cfg TokenConfig, name string, err error) { +func (c Config) getTokenConfigByAddr(chainID int, tokenAddr string) (cfg TokenConfig, err error) { chainConfig, ok := c.Chains[chainID] if !ok { - return cfg, name, fmt.Errorf("no chain config for chain %d", chainID) + return cfg, fmt.Errorf("no chain config for chain %d", chainID) } - for tokenName, tokenConfig := range chainConfig.Tokens { + for _, tokenConfig := range chainConfig.Tokens { if common.HexToAddress(tokenConfig.Address).Hex() == common.HexToAddress(tokenAddr).Hex() { - return tokenConfig, tokenName, nil + return tokenConfig, nil } } - return cfg, name, fmt.Errorf("no token config for chain %d and address %s", chainID, tokenAddr) + return cfg, fmt.Errorf("no token config for chain %d and address %s", chainID, tokenAddr) } // GetRebalanceMethod returns the rebalance method for the given chain path and token address. // This method will error if there is a rebalance method mismatch, and neither methods correspond to // RebalanceMethodNone. func (c Config) GetRebalanceMethod(chainID int, tokenAddr string) (method RebalanceMethod, err error) { - tokenCfg, _, err := c.getTokenConfigByAddr(chainID, tokenAddr) + tokenCfg, err := c.getTokenConfigByAddr(chainID, tokenAddr) if err != nil { return 0, err } @@ -437,7 +437,7 @@ func (c Config) GetRebalanceMethods() (methods map[RebalanceMethod]bool, err err // GetMaintenanceBalancePct returns the maintenance balance percentage for the given chain and token address. func (c Config) GetMaintenanceBalancePct(chainID int, tokenAddr string) (float64, error) { - tokenConfig, _, err := c.getTokenConfigByAddr(chainID, tokenAddr) + tokenConfig, err := c.getTokenConfigByAddr(chainID, tokenAddr) if err != nil { return 0, err } @@ -449,7 +449,7 @@ func (c Config) GetMaintenanceBalancePct(chainID int, tokenAddr string) (float64 // GetInitialBalancePct returns the initial balance percentage for the given chain and token address. func (c Config) GetInitialBalancePct(chainID int, tokenAddr string) (float64, error) { - tokenConfig, _, err := c.getTokenConfigByAddr(chainID, tokenAddr) + tokenConfig, err := c.getTokenConfigByAddr(chainID, tokenAddr) if err != nil { return 0, err } @@ -561,7 +561,7 @@ var defaultMinRebalanceAmount = big.NewInt(1000) // //nolint:dupl func (c Config) GetMinRebalanceAmount(chainID int, addr common.Address) *big.Int { - tokenCfg, _, err := c.getTokenConfigByAddr(chainID, addr.Hex()) + tokenCfg, err := c.getTokenConfigByAddr(chainID, addr.Hex()) if err != nil { return defaultMaxRebalanceAmount } @@ -583,7 +583,7 @@ var defaultMaxRebalanceAmount = abi.MaxInt256 // //nolint:dupl func (c Config) GetMaxRebalanceAmount(chainID int, addr common.Address) *big.Int { - tokenCfg, _, err := c.getTokenConfigByAddr(chainID, addr.Hex()) + tokenCfg, err := c.getTokenConfigByAddr(chainID, addr.Hex()) if err != nil { return defaultMaxRebalanceAmount } From 928f85fb9a52649315afdd69fa89054b7e37146e Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 16:30:28 -0500 Subject: [PATCH 09/19] [goreleaser] From a089e1a68f5ea151a3ef078431ad7011abdd4ca7 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 16:50:54 -0500 Subject: [PATCH 10/19] [goreleaser] From f08f8a91b237c6caa5330163759357513c529b3a Mon Sep 17 00:00:00 2001 From: dwasse Date: Fri, 3 May 2024 18:00:06 -0500 Subject: [PATCH 11/19] RFQ: drop requests for unsupported chains (#2563) * Feat: check for unsupported chain in inventory manager * Cleanup: lint --- services/rfq/relayer/inventory/manager.go | 6 +++++- services/rfq/relayer/service/handlers.go | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/services/rfq/relayer/inventory/manager.go b/services/rfq/relayer/inventory/manager.go index a47545388f..d92e836b0f 100644 --- a/services/rfq/relayer/inventory/manager.go +++ b/services/rfq/relayer/inventory/manager.go @@ -2,6 +2,7 @@ package inventory import ( "context" + "errors" "fmt" "math/big" "strconv" @@ -82,6 +83,9 @@ type inventoryManagerImpl struct { pendingHist metric.Float64Histogram } +// ErrUnsupportedChain is the error for an unsupported chain. +var ErrUnsupportedChain = errors.New("could not get gas balance for unsupported chain") + // GetCommittableBalance gets the committable balances. func (i *inventoryManagerImpl) GetCommittableBalance(ctx context.Context, chainID int, token common.Address, options ...BalanceFetchArgOption) (*big.Int, error) { committableBalances, err := i.GetCommittableBalances(ctx, options...) @@ -94,7 +98,7 @@ func (i *inventoryManagerImpl) GetCommittableBalance(ctx context.Context, chainI if balance == nil && token == chain.EthAddress { gasBalance, ok := i.gasBalances[chainID] if !ok || gasBalance == nil { - return nil, fmt.Errorf("could not get gas balance for chain %d", chainID) + return nil, ErrUnsupportedChain } balance = i.gasBalances[chainID] } diff --git a/services/rfq/relayer/service/handlers.go b/services/rfq/relayer/service/handlers.go index 21b18b7d0f..cda68a0ae0 100644 --- a/services/rfq/relayer/service/handlers.go +++ b/services/rfq/relayer/service/handlers.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge" + "github.com/synapsecns/sanguine/services/rfq/relayer/inventory" "github.com/synapsecns/sanguine/services/rfq/relayer/reldb" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" @@ -93,6 +94,8 @@ func (r *Relayer) handleBridgeRequestedLog(parentCtx context.Context, req *fastb // // This is the second step in the bridge process. It is emitted when the relayer sees the request. // We check if we have enough inventory to process the request and mark it as committed pending. +// +//nolint:cyclop func (q *QuoteRequestHandler) handleSeen(ctx context.Context, span trace.Span, request reldb.QuoteRequest) (err error) { shouldProcess, err := q.Quoter.ShouldProcess(ctx, request) if err != nil { @@ -120,11 +123,21 @@ func (q *QuoteRequestHandler) handleSeen(ctx context.Context, span trace.Span, r return nil } - // get destination committable balancs + // get destination committable balance committableBalance, err := q.Inventory.GetCommittableBalance(ctx, int(q.Dest.ChainID), request.Transaction.DestToken) + if errors.Is(err, inventory.ErrUnsupportedChain) { + // don't process request if chain is currently unsupported + span.AddEvent("dropping unsupported chain") + err = q.db.UpdateQuoteRequestStatus(ctx, request.TransactionID, reldb.WillNotProcess) + if err != nil { + return fmt.Errorf("could not update request status: %w", err) + } + return nil + } if err != nil { return fmt.Errorf("could not get committable balance: %w", err) } + // if committableBalance > destAmount if committableBalance.Cmp(request.Transaction.DestAmount) < 0 { err = q.db.UpdateQuoteRequestStatus(ctx, request.TransactionID, reldb.NotEnoughInventory) From 8132d950fcc319ee5548f25af74a9460398220e8 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 18:00:31 -0500 Subject: [PATCH 12/19] [goreleaser] From c137a488a39a18f6bf52667d761568d51df364ac Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 18:35:28 -0500 Subject: [PATCH 13/19] Fix: check for nil base fee --- services/rfq/relayer/pricer/fee_pricer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/rfq/relayer/pricer/fee_pricer.go b/services/rfq/relayer/pricer/fee_pricer.go index c17921d421..a87a8fa8f9 100644 --- a/services/rfq/relayer/pricer/fee_pricer.go +++ b/services/rfq/relayer/pricer/fee_pricer.go @@ -280,6 +280,9 @@ func (f *feePricer) GetGasPrice(ctx context.Context, chainID uint32) (*big.Int, if err != nil { return nil, err } + if header.BaseFee == nil { + return nil, fmt.Errorf("base fee is nil on chain %d", chainID) + } gasPrice = header.BaseFee f.gasPriceCache.Set(chainID, gasPrice, 0) } else { From 37c128cd9beef2154105a2ae13310d396f63a186 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 18:35:29 -0500 Subject: [PATCH 14/19] [goreleaser] From d7abcdb5525b50d347e2caf3f749f6d4a1707060 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 18:40:36 -0500 Subject: [PATCH 15/19] Feat: fee pricer gets gas price from SuggestGasPrice() --- services/rfq/relayer/pricer/fee_pricer.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/services/rfq/relayer/pricer/fee_pricer.go b/services/rfq/relayer/pricer/fee_pricer.go index a87a8fa8f9..1eeb6eff25 100644 --- a/services/rfq/relayer/pricer/fee_pricer.go +++ b/services/rfq/relayer/pricer/fee_pricer.go @@ -276,14 +276,10 @@ func (f *feePricer) GetGasPrice(ctx context.Context, chainID uint32) (*big.Int, if err != nil { return nil, err } - header, err := client.HeaderByNumber(ctx, nil) - if err != nil { - return nil, err - } - if header.BaseFee == nil { - return nil, fmt.Errorf("base fee is nil on chain %d", chainID) + gasPrice, err = client.SuggestGasPrice(ctx) + if gasPrice == nil { + return nil, fmt.Errorf("gas price is nil on chain %d", chainID) } - gasPrice = header.BaseFee f.gasPriceCache.Set(chainID, gasPrice, 0) } else { gasPrice = gasPriceItem.Value() From 72df0b1e0cec52ab083e8052db23b205ee4fb840 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 18:40:37 -0500 Subject: [PATCH 16/19] [goreleaser] From 1108475948492cf1eb0eb6cfc66745f28b9d5a20 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Fri, 3 May 2024 18:53:58 -0500 Subject: [PATCH 17/19] Fix: tests --- .../rfq/relayer/pricer/fee_pricer_test.go | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/services/rfq/relayer/pricer/fee_pricer_test.go b/services/rfq/relayer/pricer/fee_pricer_test.go index eae93bce86..aae9e7614c 100644 --- a/services/rfq/relayer/pricer/fee_pricer_test.go +++ b/services/rfq/relayer/pricer/fee_pricer_test.go @@ -4,7 +4,6 @@ import ( "fmt" "math/big" - "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/mock" "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/core/testsuite" @@ -34,8 +33,8 @@ func (s *PricerSuite) TestGetOriginFee() { // Build a new FeePricer with a mocked client for fetching gas price and token price. clientFetcher := new(fetcherMocks.ClientFetcher) client := new(clientMocks.EVM) - currentHeader := &types.Header{BaseFee: big.NewInt(100_000_000_000)} // 100 gwei - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(currentHeader, nil) + currentHeader := big.NewInt(100_000_000_000) // 100 gwei + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(currentHeader, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, mock.Anything).Twice().Return(client, nil) priceFetcher := getPriceFetcher(nil) feePricer := pricer.NewFeePricer(s.config, clientFetcher, priceFetcher, metrics.NewNullHandler()) @@ -60,7 +59,7 @@ func (s *PricerSuite) TestGetOriginFee() { s.Equal(expectedFee, fee) // Ensure that the fee has been cached. - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(nil, fmt.Errorf("could not fetch header")) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(nil, fmt.Errorf("could not fetch header")) fee, err = feePricer.GetOriginFee(s.GetTestContext(), s.origin, s.destination, "USDC", true) s.NoError(err) s.Equal(expectedFee, fee) @@ -78,8 +77,8 @@ func (s *PricerSuite) TestGetOriginFeeWithOverrides() { // Build a new FeePricer with a mocked client for fetching gas price. clientFetcher := new(fetcherMocks.ClientFetcher) client := new(clientMocks.EVM) - currentHeader := &types.Header{BaseFee: big.NewInt(100_000_000_000)} // 100 gwei - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Return(currentHeader, nil) + currentHeader := big.NewInt(100_000_000_000) // 100 gwei + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Return(currentHeader, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, mock.Anything).Return(client, nil) priceFetcher := getPriceFetcher(map[string]float64{"ETH": 1000}) feePricer := pricer.NewFeePricer(s.config, clientFetcher, priceFetcher, metrics.NewNullHandler()) @@ -109,7 +108,7 @@ func (s *PricerSuite) TestGetOriginFeeWithOverrides() { s.Equal(expectedFee, fee) // Ensure that the fee has been cached. - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(nil, fmt.Errorf("could not fetch header")) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(nil, fmt.Errorf("could not fetch header")) fee, err = feePricer.GetOriginFee(s.GetTestContext(), s.origin, s.destination, "USDC", true) s.NoError(err) s.Equal(expectedFee, fee) @@ -119,8 +118,8 @@ func (s *PricerSuite) TestGetDestinationFee() { // Build a new FeePricer with a mocked client for fetching gas price. clientFetcher := new(fetcherMocks.ClientFetcher) client := new(clientMocks.EVM) - currentHeader := &types.Header{BaseFee: big.NewInt(500_000_000_000)} // 500 gwei - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(currentHeader, nil) + currentHeader := big.NewInt(500_000_000_000) // 500 gwei + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(currentHeader, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, mock.Anything).Twice().Return(client, nil) priceFetcher := getPriceFetcher(nil) feePricer := pricer.NewFeePricer(s.config, clientFetcher, priceFetcher, metrics.NewNullHandler()) @@ -145,7 +144,7 @@ func (s *PricerSuite) TestGetDestinationFee() { s.Equal(expectedFee, fee) // Ensure that the fee has been cached. - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(nil, fmt.Errorf("could not fetch header")) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(nil, fmt.Errorf("could not fetch header")) fee, err = feePricer.GetDestinationFee(s.GetTestContext(), s.origin, s.destination, "USDC", true) s.NoError(err) s.Equal(expectedFee, fee) @@ -163,8 +162,8 @@ func (s *PricerSuite) TestGetDestinationFeeWithOverrides() { // Build a new FeePricer with a mocked client for fetching gas price. clientFetcher := new(fetcherMocks.ClientFetcher) client := new(clientMocks.EVM) - currentHeader := &types.Header{BaseFee: big.NewInt(500_000_000_000)} // 500 gwei - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Return(currentHeader, nil) + currentHeader := big.NewInt(500_000_000_000) // 500 gwei + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Return(currentHeader, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, mock.Anything).Return(client, nil) priceFetcher := getPriceFetcher(nil) feePricer := pricer.NewFeePricer(s.config, clientFetcher, priceFetcher, metrics.NewNullHandler()) @@ -194,7 +193,7 @@ func (s *PricerSuite) TestGetDestinationFeeWithOverrides() { s.Equal(expectedFee, fee) // Ensure that the fee has been cached. - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(nil, fmt.Errorf("could not fetch header")) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(nil, fmt.Errorf("could not fetch header")) fee, err = feePricer.GetDestinationFee(s.GetTestContext(), s.origin, s.destination, "USDC", true) s.NoError(err) s.Equal(expectedFee, fee) @@ -205,10 +204,10 @@ func (s *PricerSuite) TestGetTotalFee() { clientFetcher := new(fetcherMocks.ClientFetcher) clientOrigin := new(clientMocks.EVM) clientDestination := new(clientMocks.EVM) - headerOrigin := &types.Header{BaseFee: big.NewInt(100_000_000_000)} // 100 gwei - headerDestination := &types.Header{BaseFee: big.NewInt(500_000_000_000)} // 500 gwei - clientOrigin.On(testsuite.GetFunctionName(clientOrigin.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(headerOrigin, nil) - clientDestination.On(testsuite.GetFunctionName(clientDestination.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(headerDestination, nil) + headerOrigin := big.NewInt(100_000_000_000) // 100 gwei + headerDestination := big.NewInt(500_000_000_000) // 500 gwei + clientOrigin.On(testsuite.GetFunctionName(clientOrigin.SuggestGasPrice), mock.Anything).Once().Return(headerOrigin, nil) + clientDestination.On(testsuite.GetFunctionName(clientDestination.SuggestGasPrice), mock.Anything).Once().Return(headerDestination, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, big.NewInt(int64(s.origin))).Once().Return(clientOrigin, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, big.NewInt(int64(s.destination))).Once().Return(clientDestination, nil) priceFetcher := getPriceFetcher(nil) @@ -228,8 +227,8 @@ func (s *PricerSuite) TestGetGasPrice() { // Build a new FeePricer with a mocked client for fetching gas price. clientFetcher := new(fetcherMocks.ClientFetcher) client := new(clientMocks.EVM) - currentHeader := &types.Header{BaseFee: big.NewInt(100_000_000_000)} // 100 gwei - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(currentHeader, nil) + currentHeader := big.NewInt(100_000_000_000) // 100 gwei + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(currentHeader, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, mock.Anything).Twice().Return(client, nil) // Override the gas price cache TTL to 1 second. s.config.FeePricer.GasPriceCacheTTLSeconds = 1 @@ -250,8 +249,8 @@ func (s *PricerSuite) TestGetGasPrice() { // Check that the mocked gas price is eventually evicted from the cache, // and an updated gas price is fetched. - currentHeader = &types.Header{BaseFee: big.NewInt(200_000_000_000)} // 200 gwei - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Return(currentHeader, nil) + currentHeader = big.NewInt(200_000_000_000) // 200 gwei + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Return(currentHeader, nil) s.Eventually(func() bool { gasPrice, err = feePricer.GetGasPrice(s.GetTestContext(), s.origin) s.NoError(err) @@ -268,10 +267,10 @@ func (s *PricerSuite) TestGetTotalFeeWithMultiplier() { clientFetcher := new(fetcherMocks.ClientFetcher) clientOrigin := new(clientMocks.EVM) clientDestination := new(clientMocks.EVM) - headerOrigin := &types.Header{BaseFee: big.NewInt(100_000_000_000)} // 100 gwei - headerDestination := &types.Header{BaseFee: big.NewInt(500_000_000_000)} // 500 gwei - clientOrigin.On(testsuite.GetFunctionName(clientOrigin.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(headerOrigin, nil) - clientDestination.On(testsuite.GetFunctionName(clientDestination.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(headerDestination, nil) + headerOrigin := big.NewInt(100_000_000_000) // 100 gwei + headerDestination := big.NewInt(500_000_000_000) // 500 gwei + clientOrigin.On(testsuite.GetFunctionName(clientOrigin.SuggestGasPrice), mock.Anything).Once().Return(headerOrigin, nil) + clientDestination.On(testsuite.GetFunctionName(clientDestination.SuggestGasPrice), mock.Anything).Once().Return(headerDestination, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, big.NewInt(int64(s.origin))).Once().Return(clientOrigin, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, big.NewInt(int64(s.destination))).Once().Return(clientDestination, nil) priceFetcher := getPriceFetcher(nil) @@ -290,8 +289,8 @@ func (s *PricerSuite) TestGetTotalFeeWithMultiplier() { s.config.BaseChainConfig.FixedFeeMultiplier = -1 // Build a new FeePricer with a mocked client for fetching gas price. - clientOrigin.On(testsuite.GetFunctionName(clientOrigin.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(headerOrigin, nil) - clientDestination.On(testsuite.GetFunctionName(clientDestination.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(headerDestination, nil) + clientOrigin.On(testsuite.GetFunctionName(clientOrigin.SuggestGasPrice), mock.Anything).Once().Return(headerOrigin, nil) + clientDestination.On(testsuite.GetFunctionName(clientDestination.SuggestGasPrice), mock.Anything).Once().Return(headerDestination, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, big.NewInt(int64(s.origin))).Once().Return(clientOrigin, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, big.NewInt(int64(s.destination))).Once().Return(clientDestination, nil) feePricer = pricer.NewFeePricer(s.config, clientFetcher, priceFetcher, metrics.NewNullHandler()) @@ -309,8 +308,8 @@ func (s *PricerSuite) TestGetTotalFeeWithMultiplier() { s.config.BaseChainConfig.FixedFeeMultiplier = 0 // Build a new FeePricer with a mocked client for fetching gas price. - clientOrigin.On(testsuite.GetFunctionName(clientOrigin.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(headerOrigin, nil) - clientDestination.On(testsuite.GetFunctionName(clientDestination.HeaderByNumber), mock.Anything, mock.Anything).Once().Return(headerDestination, nil) + clientOrigin.On(testsuite.GetFunctionName(clientOrigin.SuggestGasPrice), mock.Anything).Once().Return(headerOrigin, nil) + clientDestination.On(testsuite.GetFunctionName(clientDestination.SuggestGasPrice), mock.Anything).Once().Return(headerDestination, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, big.NewInt(int64(s.origin))).Once().Return(clientOrigin, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, big.NewInt(int64(s.destination))).Once().Return(clientDestination, nil) feePricer = pricer.NewFeePricer(s.config, clientFetcher, priceFetcher, metrics.NewNullHandler()) From d34ae45c084990820e852a9222ff05cf81742784 Mon Sep 17 00:00:00 2001 From: aureliusbtc <82057759+aureliusbtc@users.noreply.github.com> Date: Sat, 4 May 2024 20:16:45 -0400 Subject: [PATCH 18/19] lint, add err around clientSuggestGasPrice --- services/rfq/relayer/pricer/fee_pricer.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/rfq/relayer/pricer/fee_pricer.go b/services/rfq/relayer/pricer/fee_pricer.go index 1eeb6eff25..8faf91599f 100644 --- a/services/rfq/relayer/pricer/fee_pricer.go +++ b/services/rfq/relayer/pricer/fee_pricer.go @@ -277,6 +277,9 @@ func (f *feePricer) GetGasPrice(ctx context.Context, chainID uint32) (*big.Int, return nil, err } gasPrice, err = client.SuggestGasPrice(ctx) + if err != nil { + return nil, fmt.Errorf("failed to suggest gas price on chain %d: %w", chainID, err) + } if gasPrice == nil { return nil, fmt.Errorf("gas price is nil on chain %d", chainID) } From 93d1a3e4ceb1d7a54a068475fdc98d48f7409928 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Mon, 6 May 2024 10:54:40 -0500 Subject: [PATCH 19/19] Fix: tests --- services/rfq/relayer/quoter/suite_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/services/rfq/relayer/quoter/suite_test.go b/services/rfq/relayer/quoter/suite_test.go index c0b5b8e53c..119b7cf6fc 100644 --- a/services/rfq/relayer/quoter/suite_test.go +++ b/services/rfq/relayer/quoter/suite_test.go @@ -5,7 +5,6 @@ import ( "math/big" "testing" - "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" "github.com/synapsecns/sanguine/core/metrics" @@ -108,8 +107,8 @@ func (s *QuoterSuite) SetupTest() { clientFetcher := new(fetcherMocks.ClientFetcher) client := new(clientMocks.EVM) priceFetcher := new(priceMocks.CoingeckoPriceFetcher) - currentHeader := &types.Header{BaseFee: big.NewInt(100_000_000_000)} // 100 gwei - client.On(testsuite.GetFunctionName(client.HeaderByNumber), mock.Anything, mock.Anything).Return(currentHeader, nil) + gasPrice := big.NewInt(100_000_000_000) // 100 gwei + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Return(gasPrice, nil) clientFetcher.On(testsuite.GetFunctionName(clientFetcher.GetClient), mock.Anything, mock.Anything).Twice().Return(client, nil) priceFetcher.On(testsuite.GetFunctionName(priceFetcher.GetPrice), mock.Anything, mock.Anything).Return(0., fmt.Errorf("not using mocked price")) feePricer := pricer.NewFeePricer(s.config, clientFetcher, priceFetcher, metrics.NewNullHandler())