synapsecns/sanguine

View on GitHub
contrib/promexporter/exporters/util.go

Summary

Maintainability
A
35 mins
Test Coverage
package exporters

import (
    "context"
    "fmt"
    "math/big"

    "github.com/ethereum/go-ethereum/accounts/abi/bind"
    "github.com/ethereum/go-ethereum/common"
    "github.com/lmittmann/w3/module/eth"
    "github.com/lmittmann/w3/w3types"
    "github.com/synapsecns/sanguine/contrib/promexporter/internal/decoders"
    "github.com/synapsecns/sanguine/core"
    "github.com/synapsecns/sanguine/core/metrics"
    ethergoClient "github.com/synapsecns/sanguine/ethergo/client"
    "github.com/synapsecns/sanguine/services/explorer/contracts/bridgeconfig"
    "golang.org/x/sync/errgroup"
)

func (e *exporter) getAllTokens(parentCtx context.Context) (allTokens Tokens, err error) {
    allTokens = []TokenConfig{}

    ctx, span := e.metrics.Tracer().Start(parentCtx, "get_all_tokens")

    defer func() {
        metrics.EndSpanWithErr(span, err)
    }()

    bridgeConfig, err := e.getBridgeConfig(ctx)
    if err != nil {
        return nil, fmt.Errorf("could not get bridge config: %w", err)
    }

    // TODO: multicall is preferable here, but I ain't got time for that
    tokenIDs, err := bridgeConfig.GetAllTokenIDs(&bind.CallOpts{Context: ctx})
    if err != nil {
        return nil, fmt.Errorf("could not get all token ids: %w", err)
    }

    bridgeConfigClient, err := e.omnirpcClient.GetConfirmationsClient(ctx, e.cfg.BridgeConfig.ChainID, 1)
    if err != nil {
        return nil, fmt.Errorf("could not get confirmations client: %w", err)
    }

    bridgeTokens := make([]*bridgeconfig.BridgeConfigV3Token, len(tokenIDs)*len(e.cfg.BridgeChecks))

    //nolint: revive
    tokenIDS := make([]string, len(tokenIDs)*len(e.cfg.BridgeChecks))

    var calls []w3types.Caller

    i := 0
    for _, tokenID := range tokenIDs {
        for chainID := range e.cfg.BridgeChecks {
            token := &bridgeconfig.BridgeConfigV3Token{}
            calls = append(calls, eth.CallFunc(decoders.TokenConfigGetToken(), bridgeConfig.Address(), tokenID, big.NewInt(int64(chainID))).Returns(token))
            bridgeTokens[i] = token
            tokenIDS[i] = tokenID
            i++
        }
    }

    // TODO: once go 1.21 is introduced do min(cfg.BatchCallLimit, 2)
    err = e.batchCalls(ctx, bridgeConfigClient, calls)
    if err != nil {
        return nil, fmt.Errorf("could not get token balances: %w", err)
    }

    for i, token := range bridgeTokens {
        tokenID := tokenIDS[i]

        if token.TokenAddress == "" {
            continue
        }

        allTokens = append(allTokens, TokenConfig{
            TokenID:       tokenID,
            ChainID:       int(token.ChainId.Int64()),
            TokenAddress:  common.HexToAddress(token.TokenAddress),
            TokenDecimals: token.TokenDecimals,
            HasUnderlying: token.HasUnderlying,
            IsUnderlying:  token.IsUnderlying,
        })
    }

    return allTokens, nil
}

func (e *exporter) batchCalls(ctx context.Context, evmClient ethergoClient.EVM, calls []w3types.Caller) (err error) {
    tasks := core.ChunkSlice(calls, e.cfg.BatchCallLimit)

    g, ctx := errgroup.WithContext(ctx)
    for _, task := range tasks {
        task := task // capture func literal
        g.Go(func() error {
            err = evmClient.BatchWithContext(ctx, task...)
            if err != nil {
                return fmt.Errorf("could not batch calls: %w", err)
            }

            return nil
        })
    }

    err = g.Wait()
    if err != nil {
        return fmt.Errorf("could not get token balances: %w", err)
    }

    return nil
}

// Tokens is a list of token configs.
type Tokens []TokenConfig

// GetForChainID returns all tokens for a given chainID.
func (t Tokens) GetForChainID(chainID int) Tokens {
    var chainTokens []TokenConfig
    for _, token := range t {
        if token.ChainID == chainID {
            chainTokens = append(chainTokens, token)
        }
    }

    return chainTokens
}

// TokenConfig is a cleaned up token config.
type TokenConfig struct {
    TokenID       string
    ChainID       int
    TokenAddress  common.Address
    TokenDecimals uint8
    HasUnderlying bool
    IsUnderlying  bool
}