From 9f8b74d6974f93bd214407f0f6c9bbdd79e5d766 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Mon, 15 Apr 2024 11:01:49 -0500 Subject: [PATCH 1/4] Feat: add decimalsCache, refactor decimal fetching --- services/rfq/relayer/service/chainindexer.go | 92 +++++++++----------- services/rfq/relayer/service/handlers.go | 6 +- services/rfq/relayer/service/relayer.go | 2 + 3 files changed, 44 insertions(+), 56 deletions(-) diff --git a/services/rfq/relayer/service/chainindexer.go b/services/rfq/relayer/service/chainindexer.go index f900964eee..9ab1e830a5 100644 --- a/services/rfq/relayer/service/chainindexer.go +++ b/services/rfq/relayer/service/chainindexer.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/services/rfq/contracts/fastbridge" @@ -119,10 +120,10 @@ func (r *Relayer) runChainIndexer(ctx context.Context, chainID int) (err error) return nil } -const ethDecimals = 18 +var ethDecimals uint8 = 18 // getDecimals gets the decimals for the origin and dest tokens. -func (r *Relayer) getDecimals(parentCtx context.Context, bridgeTx fastbridge.IFastBridgeBridgeTransaction) (_ *decimalsRes, err error) { +func (r *Relayer) getDecimalsFromBridgeTx(parentCtx context.Context, bridgeTx fastbridge.IFastBridgeBridgeTransaction) (originDecimals *uint8, destDecimals *uint8, err error) { ctx, span := r.metrics.Tracer().Start(parentCtx, "getDecimals", trace.WithAttributes( attribute.String("sender", bridgeTx.OriginSender.String()), )) @@ -131,75 +132,60 @@ func (r *Relayer) getDecimals(parentCtx context.Context, bridgeTx fastbridge.IFa metrics.EndSpanWithErr(span, err) }() - // TODO: add a cache for decimals. - var originERC20, destERC20 *ierc20.IERC20 - res := decimalsRes{} - - if bridgeTx.OriginToken == chain.EthAddress { - res.originDecimals = ethDecimals - } else { - // TODO: cleanup duplication, but keep paralellism. - // this is a bit of a pain since it deals w/ 3 different fields, but shouldn't take too long - originClient, err := r.client.GetChainClient(ctx, int(bridgeTx.OriginChainId)) + // do rpc calls to fetch the erc20 decimals. + g, ctx := errgroup.WithContext(ctx) + g.Go(func() error { + originDecimals, err = r.getDecimals(ctx, bridgeTx.OriginToken, bridgeTx.OriginChainId) if err != nil { - return nil, fmt.Errorf("could not get origin client: %w", err) + return fmt.Errorf("could not get origin decimals: %w", err) } - originERC20, err = ierc20.NewIERC20(bridgeTx.OriginToken, originClient) + return nil + }) + g.Go(func() error { + destDecimals, err = r.getDecimals(ctx, bridgeTx.DestToken, bridgeTx.DestChainId) if err != nil { - return nil, fmt.Errorf("could not get origin token") + return fmt.Errorf("could not get dest decimals: %w", err) } + return nil + }) + err = g.Wait() + if err != nil { + return nil, nil, fmt.Errorf("could not get decimals: %w", err) } - if bridgeTx.DestToken == chain.EthAddress { - res.destDecimals = ethDecimals - } else { - destClient, err := r.client.GetChainClient(ctx, int(bridgeTx.DestChainId)) - if err != nil { - return nil, fmt.Errorf("could not get dest client: %w", err) - } - destERC20, err = ierc20.NewIERC20(bridgeTx.DestToken, destClient) - if err != nil { - return nil, fmt.Errorf("could not get dest token") - } - } + return originDecimals, destDecimals, nil +} - // return early if both dest and origin are ETH. - if originERC20 == nil && destERC20 == nil { - return &res, nil +func (r *Relayer) getDecimals(ctx context.Context, addr common.Address, chainID uint32) (decimals *uint8, err error) { + key := getDecimalsKey(addr, chainID) + decimals, ok := r.decimalsCache[key] + if ok { + return decimals, nil } - // do rpc calls to fetch the erc20 decimals. - g, ctx := errgroup.WithContext(ctx) - if originERC20 != nil { - g.Go(func() error { - res.originDecimals, err = originERC20.Decimals(&bind.CallOpts{Context: ctx}) - if err != nil { - return fmt.Errorf("could not get dest decimals: %w", err) - } - return nil - }) + if addr == chain.EthAddress { + return ðDecimals, nil } - if destERC20 != nil { - g.Go(func() error { - res.destDecimals, err = destERC20.Decimals(&bind.CallOpts{Context: ctx}) - if err != nil { - return fmt.Errorf("could not get origin decimals: %w", err) - } - return nil - }) + client, err := r.client.GetChainClient(ctx, int(chainID)) + if err != nil { + return nil, fmt.Errorf("could not get client for chain %d: %w", chainID, err) + } + erc20, err := ierc20.NewIERC20(addr, client) + if err != nil { + return nil, fmt.Errorf("could not get token at %s: %w", addr.String(), err) } - err = g.Wait() + dec, err := erc20.Decimals(&bind.CallOpts{Context: ctx}) if err != nil { return nil, fmt.Errorf("could not get decimals: %w", err) } - - return &res, nil + r.decimalsCache[key] = &dec + return &dec, nil } -type decimalsRes struct { - originDecimals, destDecimals uint8 +func getDecimalsKey(addr common.Address, chainID uint32) string { + return fmt.Sprintf("%s-%d", addr.Hex(), chainID) } func (r *Relayer) handleDepositClaimed(ctx context.Context, event *fastbridge.FastBridgeBridgeDepositClaimed, chainID int) error { diff --git a/services/rfq/relayer/service/handlers.go b/services/rfq/relayer/service/handlers.go index 21b18b7d0f..903b341c64 100644 --- a/services/rfq/relayer/service/handlers.go +++ b/services/rfq/relayer/service/handlers.go @@ -58,7 +58,7 @@ func (r *Relayer) handleBridgeRequestedLog(parentCtx context.Context, req *fastb } // TODO: you can just pull these out of inventory. If they don't exist mark as invalid. - decimals, err := r.getDecimals(ctx, bridgeTx) + originDecimals, destDecimals, err := r.getDecimalsFromBridgeTx(ctx, bridgeTx) // can't use errors.is here if err != nil && strings.Contains(err.Error(), "no contract code at given address") { logger.Warnf("invalid token, skipping") @@ -72,8 +72,8 @@ func (r *Relayer) handleBridgeRequestedLog(parentCtx context.Context, req *fastb err = r.db.StoreQuoteRequest(ctx, reldb.QuoteRequest{ BlockNumber: req.Raw.BlockNumber, RawRequest: req.Request, - OriginTokenDecimals: decimals.originDecimals, - DestTokenDecimals: decimals.destDecimals, + OriginTokenDecimals: originDecimals, + DestTokenDecimals: destDecimals, TransactionID: req.TransactionId, Sender: req.Sender, Transaction: bridgeTx, diff --git a/services/rfq/relayer/service/relayer.go b/services/rfq/relayer/service/relayer.go index fb6b25652c..d42bfd36c4 100644 --- a/services/rfq/relayer/service/relayer.go +++ b/services/rfq/relayer/service/relayer.go @@ -45,6 +45,7 @@ type Relayer struct { submitter submitter.TransactionSubmitter signer signer.Signer claimCache *ttlcache.Cache[common.Hash, bool] + decimalsCache map[string]*uint8 } var logger = log.Logger("relayer") @@ -126,6 +127,7 @@ func NewRelayer(ctx context.Context, metricHandler metrics.Handler, cfg relconfi quoter: q, metrics: metricHandler, claimCache: cache, + decimalsCache: make(map[string]*uint8), cfg: cfg, inventory: im, submitter: sm, From bcf75484d4b18e74b604d22eb48bf949d95adfa3 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Mon, 15 Apr 2024 11:04:13 -0500 Subject: [PATCH 2/4] Cleanup: comments --- services/rfq/relayer/service/chainindexer.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/services/rfq/relayer/service/chainindexer.go b/services/rfq/relayer/service/chainindexer.go index 9ab1e830a5..7c337cc963 100644 --- a/services/rfq/relayer/service/chainindexer.go +++ b/services/rfq/relayer/service/chainindexer.go @@ -132,7 +132,7 @@ func (r *Relayer) getDecimalsFromBridgeTx(parentCtx context.Context, bridgeTx fa metrics.EndSpanWithErr(span, err) }() - // do rpc calls to fetch the erc20 decimals. + // fetch the token decimals in parallel g, ctx := errgroup.WithContext(ctx) g.Go(func() error { originDecimals, err = r.getDecimals(ctx, bridgeTx.OriginToken, bridgeTx.OriginChainId) @@ -156,7 +156,11 @@ func (r *Relayer) getDecimalsFromBridgeTx(parentCtx context.Context, bridgeTx fa return originDecimals, destDecimals, nil } +// getDecimals gets the decimals for a token on a chain. +// It will attempt to load a result from the cache first. +// The cache will be updated if the result is fetched from RPC. func (r *Relayer) getDecimals(ctx context.Context, addr common.Address, chainID uint32) (decimals *uint8, err error) { + // attempt to load decimal from cache key := getDecimalsKey(addr, chainID) decimals, ok := r.decimalsCache[key] if ok { @@ -167,6 +171,7 @@ func (r *Relayer) getDecimals(ctx context.Context, addr common.Address, chainID return ðDecimals, nil } + // fetch decimals from RPC client, err := r.client.GetChainClient(ctx, int(chainID)) if err != nil { return nil, fmt.Errorf("could not get client for chain %d: %w", chainID, err) @@ -175,11 +180,12 @@ func (r *Relayer) getDecimals(ctx context.Context, addr common.Address, chainID if err != nil { return nil, fmt.Errorf("could not get token at %s: %w", addr.String(), err) } - dec, err := erc20.Decimals(&bind.CallOpts{Context: ctx}) if err != nil { return nil, fmt.Errorf("could not get decimals: %w", err) } + + // update the cache r.decimalsCache[key] = &dec return &dec, nil } From df40e5045463ca0be4373a1acb9b7b55d5e4b2d5 Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Mon, 15 Apr 2024 11:05:57 -0500 Subject: [PATCH 3/4] Fix: build --- services/rfq/relayer/service/handlers.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/rfq/relayer/service/handlers.go b/services/rfq/relayer/service/handlers.go index 903b341c64..88acbadbab 100644 --- a/services/rfq/relayer/service/handlers.go +++ b/services/rfq/relayer/service/handlers.go @@ -65,15 +65,15 @@ func (r *Relayer) handleBridgeRequestedLog(parentCtx context.Context, req *fastb return nil } - if err != nil { + if err != nil || originDecimals == nil || destDecimals == nil { return fmt.Errorf("could not get decimals: %w", err) } err = r.db.StoreQuoteRequest(ctx, reldb.QuoteRequest{ BlockNumber: req.Raw.BlockNumber, RawRequest: req.Request, - OriginTokenDecimals: originDecimals, - DestTokenDecimals: destDecimals, + OriginTokenDecimals: *originDecimals, + DestTokenDecimals: *destDecimals, TransactionID: req.TransactionId, Sender: req.Sender, Transaction: bridgeTx, From 371f546dd23019efae39b18cc6f84af314b4c6dc Mon Sep 17 00:00:00 2001 From: Daniel Wasserman Date: Mon, 29 Apr 2024 15:37:34 -0500 Subject: [PATCH 4/4] Fix: use concurrent map --- services/rfq/relayer/service/chainindexer.go | 4 ++-- services/rfq/relayer/service/relayer.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/services/rfq/relayer/service/chainindexer.go b/services/rfq/relayer/service/chainindexer.go index 7c337cc963..3b1e7b7acb 100644 --- a/services/rfq/relayer/service/chainindexer.go +++ b/services/rfq/relayer/service/chainindexer.go @@ -162,7 +162,7 @@ func (r *Relayer) getDecimalsFromBridgeTx(parentCtx context.Context, bridgeTx fa func (r *Relayer) getDecimals(ctx context.Context, addr common.Address, chainID uint32) (decimals *uint8, err error) { // attempt to load decimal from cache key := getDecimalsKey(addr, chainID) - decimals, ok := r.decimalsCache[key] + decimals, ok := r.decimalsCache.Load(key) if ok { return decimals, nil } @@ -186,7 +186,7 @@ func (r *Relayer) getDecimals(ctx context.Context, addr common.Address, chainID } // update the cache - r.decimalsCache[key] = &dec + r.decimalsCache.Store(key, &dec) return &dec, nil } diff --git a/services/rfq/relayer/service/relayer.go b/services/rfq/relayer/service/relayer.go index d42bfd36c4..340b0b15b0 100644 --- a/services/rfq/relayer/service/relayer.go +++ b/services/rfq/relayer/service/relayer.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ipfs/go-log" "github.com/jellydator/ttlcache/v3" + "github.com/puzpuzpuz/xsync/v2" "github.com/synapsecns/sanguine/core/dbcommon" "github.com/synapsecns/sanguine/core/metrics" "github.com/synapsecns/sanguine/ethergo/listener" @@ -45,7 +46,7 @@ type Relayer struct { submitter submitter.TransactionSubmitter signer signer.Signer claimCache *ttlcache.Cache[common.Hash, bool] - decimalsCache map[string]*uint8 + decimalsCache *xsync.MapOf[string, *uint8] } var logger = log.Logger("relayer") @@ -127,7 +128,7 @@ func NewRelayer(ctx context.Context, metricHandler metrics.Handler, cfg relconfi quoter: q, metrics: metricHandler, claimCache: cache, - decimalsCache: make(map[string]*uint8), + decimalsCache: xsync.NewMapOf[*uint8](), cfg: cfg, inventory: im, submitter: sm,