wormhole-explorer/contract-watcher/internal/evm/client.go

127 lines
3.0 KiB
Go

package evm
import (
"context"
"fmt"
"net/http"
"github.com/go-resty/resty/v2"
"github.com/wormhole-foundation/wormhole-explorer/common/utils"
"github.com/wormhole-foundation/wormhole-explorer/contract-watcher/internal/metrics"
"go.uber.org/ratelimit"
)
var ErrTooManyRequests = fmt.Errorf("too many requests")
const clientName = "evm"
type EvmSDK struct {
client *resty.Client
rl ratelimit.Limiter
metrics metrics.Metrics
}
func NewEvmSDK(url string, rl ratelimit.Limiter, metrics metrics.Metrics) *EvmSDK {
return &EvmSDK{
rl: rl,
client: resty.New().SetBaseURL(url),
metrics: metrics,
}
}
func (s *EvmSDK) GetLatestBlock(ctx context.Context) (uint64, error) {
s.rl.Take()
req := newEvmRequest("eth_blockNumber")
resp, err := s.client.R().
SetContext(ctx).
SetBody(req).
SetResult(&getLatestBlockResponse{}).
Post("")
if err != nil {
return 0, err
}
s.metrics.IncRpcRequest(clientName, "get-latest-block", resp.StatusCode())
if resp.IsError() {
if resp.StatusCode() == http.StatusTooManyRequests {
return 0, ErrTooManyRequests
}
return 0, fmt.Errorf("status code: %s. %s", resp.Status(), string(resp.Body()))
}
result := resp.Result().(*getLatestBlockResponse)
if result == nil {
return 0, fmt.Errorf("empty response")
}
return utils.DecodeUint64(result.Result)
}
func (s *EvmSDK) GetBlock(ctx context.Context, block uint64) (*GetBlockResult, error) {
s.rl.Take()
req := newEvmRequest("eth_getBlockByNumber", utils.EncodeHex(block), true)
resp, err := s.client.R().
SetContext(ctx).
SetBody(req).
SetResult(&getBlockResponse{}).
Post("")
if err != nil {
return nil, err
}
s.metrics.IncRpcRequest(clientName, "get-block", resp.StatusCode())
if resp.IsError() {
if resp.StatusCode() == http.StatusTooManyRequests {
return nil, ErrTooManyRequests
}
return nil, fmt.Errorf("status code: %s. %s", resp.Status(), string(resp.Body()))
}
result := resp.Result().(*getBlockResponse)
if result == nil {
return nil, fmt.Errorf("empty response")
}
return &result.Result, nil
}
func (s *EvmSDK) GetTransactionReceipt(ctx context.Context, txHash string) (*TransactionReceiptResult, error) {
s.rl.Take()
req := newEvmRequest("eth_getTransactionReceipt", txHash)
resp, err := s.client.R().
SetContext(ctx).
SetBody(req).
SetResult(&getTransactionReceiptResponse{}).
Post("")
if err != nil {
return nil, err
}
s.metrics.IncRpcRequest(clientName, "get-transaction-receipt", resp.StatusCode())
if resp.IsError() {
if resp.StatusCode() == http.StatusTooManyRequests {
return nil, ErrTooManyRequests
}
return nil, fmt.Errorf("status code: %s. %s", resp.Status(), string(resp.Body()))
}
result := resp.Result().(*getTransactionReceiptResponse)
if result == nil {
return nil, fmt.Errorf("empty response")
}
return &result.Result, nil
}
func newEvmRequest(method string, params ...any) EvmRequest {
return EvmRequest{
Jsonrpc: "2.0",
Method: method,
Params: params,
ID: 1,
}
}