2023-07-13 16:09:56 -07:00
|
|
|
package clients
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-09-06 15:14:26 -07:00
|
|
|
"math/big"
|
2023-07-13 16:09:56 -07:00
|
|
|
"time"
|
|
|
|
|
2023-07-14 15:17:02 -07:00
|
|
|
"github.com/ethereum-optimism/optimism/op-ufm/pkg/metrics"
|
2023-07-13 16:09:56 -07:00
|
|
|
"github.com/ethereum/go-ethereum"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/core"
|
|
|
|
"github.com/ethereum/go-ethereum/core/txpool"
|
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
|
|
"github.com/ethereum/go-ethereum/ethclient"
|
|
|
|
)
|
|
|
|
|
|
|
|
type InstrumentedEthClient struct {
|
|
|
|
c *ethclient.Client
|
|
|
|
providerName string
|
|
|
|
}
|
|
|
|
|
|
|
|
func Dial(providerName string, url string) (*InstrumentedEthClient, error) {
|
|
|
|
start := time.Now()
|
|
|
|
c, err := ethclient.Dial(url)
|
|
|
|
if err != nil {
|
2023-09-06 15:09:03 -07:00
|
|
|
metrics.RecordErrorDetails(providerName, "ethclient.Dial", err)
|
2023-07-13 16:09:56 -07:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
metrics.RecordRPCLatency(providerName, "ethclient", "Dial", time.Since(start))
|
|
|
|
return &InstrumentedEthClient{c: c, providerName: providerName}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *InstrumentedEthClient) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) {
|
|
|
|
start := time.Now()
|
|
|
|
tx, isPending, err := i.c.TransactionByHash(ctx, hash)
|
|
|
|
if err != nil {
|
2023-07-14 14:08:02 -07:00
|
|
|
if !i.ignorableErrors(err) {
|
2023-09-06 15:09:03 -07:00
|
|
|
metrics.RecordErrorDetails(i.providerName, "ethclient.TransactionByHash", err)
|
2023-07-13 16:09:56 -07:00
|
|
|
}
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
metrics.RecordRPCLatency(i.providerName, "ethclient", "TransactionByHash", time.Since(start))
|
|
|
|
return tx, isPending, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *InstrumentedEthClient) PendingNonceAt(ctx context.Context, address string) (uint64, error) {
|
|
|
|
start := time.Now()
|
|
|
|
nonce, err := i.c.PendingNonceAt(ctx, common.HexToAddress(address))
|
|
|
|
if err != nil {
|
2023-09-06 15:09:03 -07:00
|
|
|
metrics.RecordErrorDetails(i.providerName, "ethclient.PendingNonceAt", err)
|
2023-07-13 16:09:56 -07:00
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
metrics.RecordRPCLatency(i.providerName, "ethclient", "PendingNonceAt", time.Since(start))
|
|
|
|
return nonce, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *InstrumentedEthClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
|
|
|
start := time.Now()
|
|
|
|
receipt, err := i.c.TransactionReceipt(ctx, txHash)
|
|
|
|
if err != nil {
|
2023-07-14 14:08:02 -07:00
|
|
|
if !i.ignorableErrors(err) {
|
2023-09-06 15:09:03 -07:00
|
|
|
metrics.RecordErrorDetails(i.providerName, "ethclient.TransactionReceipt", err)
|
2023-07-13 16:09:56 -07:00
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
metrics.RecordRPCLatency(i.providerName, "ethclient", "TransactionReceipt", time.Since(start))
|
|
|
|
return receipt, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *InstrumentedEthClient) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
|
|
|
start := time.Now()
|
|
|
|
err := i.c.SendTransaction(ctx, tx)
|
|
|
|
if err != nil {
|
2023-07-14 14:08:02 -07:00
|
|
|
if !i.ignorableErrors(err) {
|
2023-09-06 15:09:03 -07:00
|
|
|
metrics.RecordErrorDetails(i.providerName, "ethclient.SendTransaction", err)
|
2023-07-13 16:09:56 -07:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
metrics.RecordRPCLatency(i.providerName, "ethclient", "SendTransaction", time.Since(start))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-09-06 15:14:26 -07:00
|
|
|
func (i *InstrumentedEthClient) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
|
|
|
|
start := time.Now()
|
|
|
|
gas, err := i.c.EstimateGas(ctx, msg)
|
|
|
|
if err != nil {
|
|
|
|
metrics.RecordErrorDetails(i.providerName, "ethclient.EstimateGas", err)
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
metrics.RecordRPCLatency(i.providerName, "ethclient", "EstimateGas", time.Since(start))
|
|
|
|
return gas, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *InstrumentedEthClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
|
|
|
start := time.Now()
|
|
|
|
gasTipCap, err := i.c.SuggestGasTipCap(ctx)
|
|
|
|
if err != nil {
|
|
|
|
metrics.RecordErrorDetails(i.providerName, "ethclient.SuggestGasTipCap", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
metrics.RecordRPCLatency(i.providerName, "ethclient", "SuggestGasTipCap", time.Since(start))
|
|
|
|
return gasTipCap, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *InstrumentedEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
|
|
|
start := time.Now()
|
|
|
|
header, err := i.c.HeaderByNumber(ctx, number)
|
|
|
|
if err != nil {
|
|
|
|
metrics.RecordErrorDetails(i.providerName, "ethclient.HeaderByNumber", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
metrics.RecordRPCLatency(i.providerName, "ethclient", "HeaderByNumber", time.Since(start))
|
|
|
|
return header, err
|
|
|
|
}
|
|
|
|
|
2023-07-14 14:08:02 -07:00
|
|
|
func (i *InstrumentedEthClient) ignorableErrors(err error) bool {
|
2023-07-13 16:09:56 -07:00
|
|
|
msg := err.Error()
|
|
|
|
// we dont use errors.Is because eth client actually uses errors.New,
|
|
|
|
// therefore creating an incomparable instance :(
|
|
|
|
return msg == ethereum.NotFound.Error() ||
|
|
|
|
msg == txpool.ErrAlreadyKnown.Error() ||
|
|
|
|
msg == core.ErrNonceTooLow.Error()
|
|
|
|
}
|