Skip to content

Commit

Permalink
RFQ: add decimals cache (#2502)
Browse files Browse the repository at this point in the history
* Feat: add decimalsCache, refactor decimal fetching

* Cleanup: comments

* Fix: build

* Fix: use concurrent map
  • Loading branch information
dwasse authored May 8, 2024
1 parent b9652fb commit e6573c6
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 57 deletions.
98 changes: 45 additions & 53 deletions services/rfq/relayer/service/chainindexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()),
))
Expand All @@ -131,75 +132,66 @@ 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))
// fetch the token decimals in parallel
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
// 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.Load(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 &ethDecimals, 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
})
// 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)
}

err = g.Wait()
erc20, err := ierc20.NewIERC20(addr, client)
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)
}

return &res, nil
// update the cache
r.decimalsCache.Store(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 {
Expand Down
8 changes: 4 additions & 4 deletions services/rfq/relayer/service/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,22 @@ 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")
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: decimals.originDecimals,
DestTokenDecimals: decimals.destDecimals,
OriginTokenDecimals: *originDecimals,
DestTokenDecimals: *destDecimals,
TransactionID: req.TransactionId,
Sender: req.Sender,
Transaction: bridgeTx,
Expand Down
3 changes: 3 additions & 0 deletions services/rfq/relayer/service/relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -45,6 +46,7 @@ type Relayer struct {
submitter submitter.TransactionSubmitter
signer signer.Signer
claimCache *ttlcache.Cache[common.Hash, bool]
decimalsCache *xsync.MapOf[string, *uint8]
}

var logger = log.Logger("relayer")
Expand Down Expand Up @@ -126,6 +128,7 @@ func NewRelayer(ctx context.Context, metricHandler metrics.Handler, cfg relconfi
quoter: q,
metrics: metricHandler,
claimCache: cache,
decimalsCache: xsync.NewMapOf[*uint8](),
cfg: cfg,
inventory: im,
submitter: sm,
Expand Down

0 comments on commit e6573c6

Please sign in to comment.