contrib/promexporter/exporters/bridge.go
package exporters
import (
"context"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"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/services/explorer/contracts/bridge"
"github.com/synapsecns/sanguine/services/explorer/contracts/bridgeconfig"
"github.com/synapsecns/sanguine/services/explorer/contracts/swap"
)
func (e *exporter) getBridgeConfig(ctx context.Context) (*bridgeconfig.BridgeConfigRef, error) {
// client, err := e.omnirpcClient.GetClient(ctx, big.NewInt(int64(e.cfg.BridgeConfig.ChainID)))
client, err := e.omnirpcClient.GetConfirmationsClient(ctx, e.cfg.BridgeConfig.ChainID, 1)
if err != nil {
return nil, fmt.Errorf("could not get confirmations client: %w", err)
}
// note this will not update
configContract, err := bridgeconfig.NewBridgeConfigRef(common.HexToAddress(e.cfg.BridgeConfig.Address), client)
if err != nil {
return nil, fmt.Errorf("could not get bridge config contract: %w", err)
}
return configContract, nil
}
// Will be a lot faster w/: https://github.com/open-telemetry/opentelemetry-go/issues/3034
// nolint: cyclop
func (e *exporter) vpriceStats(ctx context.Context, chainID int, tokenID string) error {
client, err := e.omnirpcClient.GetConfirmationsClient(ctx, chainID, 1)
if err != nil {
return fmt.Errorf("could not get confirmations client: %w", err)
}
bridgeConfig, err := e.getBridgeConfig(ctx)
if err != nil {
return err
}
token, err := bridgeConfig.GetToken(&bind.CallOpts{Context: ctx}, tokenID, big.NewInt(int64(chainID)))
if err != nil {
return fmt.Errorf("could not get token: %w", err)
}
poolConfig, err := bridgeConfig.GetPoolConfig(
&bind.CallOpts{Context: ctx},
common.HexToAddress(token.TokenAddress),
big.NewInt(int64(chainID)),
)
if err != nil {
return errPoolNotExist
}
// pool doesn't exist, no metrics to record!
if poolConfig.PoolAddress == common.BigToAddress(big.NewInt(0)) {
return errPoolNotExist
}
tokenContract, err := bridge.NewERC20(common.HexToAddress(token.TokenAddress), client)
if err != nil {
return fmt.Errorf("could not get tokenID contract: %w", err)
}
decimals, err := tokenContract.Decimals(&bind.CallOpts{Context: ctx})
if err != nil {
return fmt.Errorf("could not get decimals: %w", err)
}
iswap, err := swap.NewISwap(poolConfig.PoolAddress, client)
if err != nil {
return fmt.Errorf("could not get iswap contract: %w", err)
}
realvPrice, err := iswap.GetVirtualPrice(&bind.CallOpts{Context: ctx})
if err != nil {
return fmt.Errorf("could not get virtual price: %w", err)
}
e.otelRecorder.RecordVPrice(chainID, core.BigToDecimals(realvPrice, decimals))
return nil
}
var errPoolNotExist = errors.New("pool does not exist")
// nolint: cyclop
func (e *exporter) getTokenBalancesStats(ctx context.Context) error {
allTokens, err := e.getAllTokens(ctx)
if err != nil {
return fmt.Errorf("could not get all tokens: %w", err)
}
for chainID, bridgeContract := range e.cfg.BridgeChecks {
client, err := e.omnirpcClient.GetConfirmationsClient(ctx, chainID, 1)
if err != nil {
return fmt.Errorf("could not get confirmations client: %w", err)
}
var realGasBalance big.Int
calls := []w3types.Caller{
eth.Balance(common.HexToAddress(bridgeContract), nil).Returns(&realGasBalance),
}
allTokenData := make([]tokenData, len(allTokens.GetForChainID(chainID)))
for i, tokenConfig := range allTokens.GetForChainID(chainID) {
// initialize empty struct
allTokenData[i] = tokenData{
metadata: tokenConfig,
contractBalance: new(big.Int),
totalSuppply: new(big.Int),
feeBalance: new(big.Int),
}
calls = append(calls,
eth.CallFunc(decoders.FuncBalanceOf(), tokenConfig.TokenAddress, common.HexToAddress(bridgeContract)).Returns(allTokenData[i].contractBalance),
eth.CallFunc(decoders.FuncTotalSupply(), tokenConfig.TokenAddress).Returns(allTokenData[i].totalSuppply),
eth.CallFunc(decoders.FuncFeeBalance(), common.HexToAddress(bridgeContract), tokenConfig.TokenAddress).Returns(allTokenData[i].feeBalance),
)
}
_ = e.batchCalls(ctx, client, calls)
e.otelRecorder.RecordBridgeGasBalance(chainID, core.BigToDecimals(&realGasBalance, 18)*params.Ether)
for _, td := range allTokenData {
e.otelRecorder.RecordTokenBalance(chainID, td)
}
}
return nil
}