Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFQ: add decimals cache #2502

Merged
merged 4 commits into from
May 8, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Feat: add decimalsCache, refactor decimal fetching
  • Loading branch information
dwasse committed Apr 15, 2024
commit 9f8b74d6974f93bd214407f0f6c9bbdd79e5d766
92 changes: 39 additions & 53 deletions services/rfq/relayer/service/chainindexer.go
Original file line number Diff line number Diff line change
@@ -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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not thread safe

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
})
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also not thread safe

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 {
6 changes: 3 additions & 3 deletions services/rfq/relayer/service/handlers.go
Original file line number Diff line number Diff line change
@@ -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,
2 changes: 2 additions & 0 deletions services/rfq/relayer/service/relayer.go
Original file line number Diff line number Diff line change
@@ -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,