github.com/status-im/status-go@v1.1.0/rpc/chain/client.go (about)

     1  package chain
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math/big"
     8  	"strings"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/ethereum/go-ethereum"
    13  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/core"
    16  	"github.com/ethereum/go-ethereum/core/types"
    17  	"github.com/ethereum/go-ethereum/core/vm"
    18  	"github.com/ethereum/go-ethereum/ethclient"
    19  	"github.com/ethereum/go-ethereum/log"
    20  	"github.com/ethereum/go-ethereum/rpc"
    21  	"github.com/status-im/status-go/circuitbreaker"
    22  	"github.com/status-im/status-go/services/rpcstats"
    23  	"github.com/status-im/status-go/services/wallet/connection"
    24  )
    25  
    26  type BatchCallClient interface {
    27  	BatchCallContext(ctx context.Context, b []rpc.BatchElem) error
    28  }
    29  
    30  type ChainInterface interface {
    31  	BatchCallClient
    32  	HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
    33  	BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
    34  	BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error)
    35  	NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
    36  	FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error)
    37  	BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
    38  	HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
    39  	CallBlockHashByTransaction(ctx context.Context, blockNumber *big.Int, index uint) (common.Hash, error)
    40  	GetBaseFeeFromBlock(ctx context.Context, blockNumber *big.Int) (string, error)
    41  	NetworkID() uint64
    42  	ToBigInt() *big.Int
    43  	CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
    44  	CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
    45  	CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error
    46  	TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error)
    47  	TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
    48  	BlockNumber(ctx context.Context) (uint64, error)
    49  }
    50  
    51  type ClientInterface interface {
    52  	ChainInterface
    53  	GetWalletNotifier() func(chainId uint64, message string)
    54  	SetWalletNotifier(notifier func(chainId uint64, message string))
    55  	connection.Connectable
    56  	bind.ContractCaller
    57  	bind.ContractTransactor
    58  	bind.ContractFilterer
    59  	GetLimiter() RequestLimiter
    60  	SetLimiter(RequestLimiter)
    61  }
    62  
    63  type Tagger interface {
    64  	Tag() string
    65  	SetTag(tag string)
    66  	GroupTag() string
    67  	SetGroupTag(tag string)
    68  	DeepCopyTag() Tagger
    69  }
    70  
    71  func DeepCopyTagger(t Tagger) Tagger {
    72  	return t.DeepCopyTag()
    73  }
    74  
    75  type HealthMonitor interface {
    76  	GetCircuitBreaker() *circuitbreaker.CircuitBreaker
    77  	SetCircuitBreaker(cb *circuitbreaker.CircuitBreaker)
    78  }
    79  
    80  type Copyable interface {
    81  	Copy() interface{}
    82  }
    83  
    84  // Shallow copy of the client with a deep copy of tag and group tag
    85  // To avoid passing tags as parameter to every chain call, it is sufficient for now
    86  // to set the tag and group tag once on the client
    87  func ClientWithTag(chainClient ClientInterface, tag, groupTag string) ClientInterface {
    88  	newClient := chainClient
    89  	if tagIface, ok := chainClient.(Tagger); ok {
    90  		tagIface = DeepCopyTagger(tagIface)
    91  		tagIface.SetTag(tag)
    92  		tagIface.SetGroupTag(groupTag)
    93  		newClient = tagIface.(ClientInterface)
    94  	}
    95  
    96  	return newClient
    97  }
    98  
    99  type EthClient struct {
   100  	ethClient *ethclient.Client
   101  	limiter   *RPCRpsLimiter
   102  	rpcClient *rpc.Client
   103  	name      string
   104  }
   105  
   106  func NewEthClient(ethClient *ethclient.Client, limiter *RPCRpsLimiter, rpcClient *rpc.Client, name string) *EthClient {
   107  	return &EthClient{
   108  		ethClient: ethClient,
   109  		limiter:   limiter,
   110  		rpcClient: rpcClient,
   111  		name:      name,
   112  	}
   113  }
   114  
   115  type ClientWithFallback struct {
   116  	ChainID        uint64
   117  	ethClients     []*EthClient
   118  	commonLimiter  RequestLimiter
   119  	circuitbreaker *circuitbreaker.CircuitBreaker
   120  
   121  	WalletNotifier func(chainId uint64, message string)
   122  
   123  	isConnected   *atomic.Bool
   124  	LastCheckedAt int64
   125  
   126  	tag      string // tag for the limiter
   127  	groupTag string // tag for the limiter group
   128  }
   129  
   130  func (c *ClientWithFallback) Copy() interface{} {
   131  	return &ClientWithFallback{
   132  		ChainID:        c.ChainID,
   133  		ethClients:     c.ethClients,
   134  		commonLimiter:  c.commonLimiter,
   135  		circuitbreaker: c.circuitbreaker,
   136  		WalletNotifier: c.WalletNotifier,
   137  		isConnected:    c.isConnected,
   138  		LastCheckedAt:  c.LastCheckedAt,
   139  		tag:            c.tag,
   140  		groupTag:       c.groupTag,
   141  	}
   142  }
   143  
   144  // Don't mark connection as failed if we get one of these errors
   145  var propagateErrors = []error{
   146  	vm.ErrOutOfGas,
   147  	vm.ErrCodeStoreOutOfGas,
   148  	vm.ErrDepth,
   149  	vm.ErrInsufficientBalance,
   150  	vm.ErrContractAddressCollision,
   151  	vm.ErrExecutionReverted,
   152  	vm.ErrMaxCodeSizeExceeded,
   153  	vm.ErrInvalidJump,
   154  	vm.ErrWriteProtection,
   155  	vm.ErrReturnDataOutOfBounds,
   156  	vm.ErrGasUintOverflow,
   157  	vm.ErrInvalidCode,
   158  	vm.ErrNonceUintOverflow,
   159  
   160  	// Used by balance history to check state
   161  	bind.ErrNoCode,
   162  }
   163  
   164  func NewSimpleClient(ethClient EthClient, chainID uint64) *ClientWithFallback {
   165  	cbConfig := circuitbreaker.Config{
   166  		Timeout:               20000,
   167  		MaxConcurrentRequests: 100,
   168  		SleepWindow:           300000,
   169  		ErrorPercentThreshold: 25,
   170  	}
   171  
   172  	isConnected := &atomic.Bool{}
   173  	isConnected.Store(true)
   174  	return &ClientWithFallback{
   175  		ChainID:        chainID,
   176  		ethClients:     []*EthClient{&ethClient},
   177  		isConnected:    isConnected,
   178  		LastCheckedAt:  time.Now().Unix(),
   179  		circuitbreaker: circuitbreaker.NewCircuitBreaker(cbConfig),
   180  	}
   181  }
   182  
   183  func NewClient(ethClients []*EthClient, chainID uint64) *ClientWithFallback {
   184  	cbConfig := circuitbreaker.Config{
   185  		Timeout:               20000,
   186  		MaxConcurrentRequests: 100,
   187  		SleepWindow:           300000,
   188  		ErrorPercentThreshold: 25,
   189  	}
   190  
   191  	isConnected := &atomic.Bool{}
   192  	isConnected.Store(true)
   193  
   194  	return &ClientWithFallback{
   195  		ChainID:        chainID,
   196  		ethClients:     ethClients,
   197  		isConnected:    isConnected,
   198  		LastCheckedAt:  time.Now().Unix(),
   199  		circuitbreaker: circuitbreaker.NewCircuitBreaker(cbConfig),
   200  	}
   201  }
   202  
   203  func (c *ClientWithFallback) Close() {
   204  	for _, client := range c.ethClients {
   205  		client.ethClient.Close()
   206  	}
   207  }
   208  
   209  // Not found should not be cancelling the requests, as that's returned
   210  // when we are hitting a non archival node for example, it should continue the
   211  // chain as the next provider might have archival support.
   212  func isNotFoundError(err error) bool {
   213  	return strings.Contains(err.Error(), ethereum.NotFound.Error())
   214  }
   215  
   216  func isVMError(err error) bool {
   217  	if strings.Contains(err.Error(), core.ErrInsufficientFunds.Error()) {
   218  		return true
   219  	}
   220  	for _, vmError := range propagateErrors {
   221  		if strings.Contains(err.Error(), vmError.Error()) {
   222  			return true
   223  		}
   224  	}
   225  	return false
   226  }
   227  
   228  func isRPSLimitError(err error) bool {
   229  	return strings.Contains(err.Error(), "backoff_seconds") ||
   230  		strings.Contains(err.Error(), "has exceeded its throughput limit") ||
   231  		strings.Contains(err.Error(), "request rate exceeded")
   232  }
   233  
   234  func (c *ClientWithFallback) SetIsConnected(value bool) {
   235  	c.LastCheckedAt = time.Now().Unix()
   236  	if !value {
   237  		if c.isConnected.Load() {
   238  			if c.WalletNotifier != nil {
   239  				c.WalletNotifier(c.ChainID, "down")
   240  			}
   241  			c.isConnected.Store(false)
   242  		}
   243  
   244  	} else {
   245  		if !c.isConnected.Load() {
   246  			c.isConnected.Store(true)
   247  			if c.WalletNotifier != nil {
   248  				c.WalletNotifier(c.ChainID, "up")
   249  			}
   250  		}
   251  	}
   252  }
   253  
   254  func (c *ClientWithFallback) IsConnected() bool {
   255  	return c.isConnected.Load()
   256  }
   257  
   258  func (c *ClientWithFallback) makeCall(ctx context.Context, ethClients []*EthClient, f func(client *EthClient) (interface{}, error)) (interface{}, error) {
   259  	if c.commonLimiter != nil {
   260  		if allow, err := c.commonLimiter.Allow(c.tag); !allow {
   261  			return nil, fmt.Errorf("tag=%s, %w", c.tag, err)
   262  		}
   263  
   264  		if allow, err := c.commonLimiter.Allow(c.groupTag); !allow {
   265  			return nil, fmt.Errorf("groupTag=%s, %w", c.groupTag, err)
   266  		}
   267  	}
   268  
   269  	c.LastCheckedAt = time.Now().Unix()
   270  
   271  	cmd := circuitbreaker.NewCommand(ctx, nil)
   272  	for _, provider := range ethClients {
   273  		provider := provider
   274  		cmd.Add(circuitbreaker.NewFunctor(func() ([]interface{}, error) {
   275  			err := provider.limiter.WaitForRequestsAvailability(1)
   276  			if err != nil {
   277  				return nil, err
   278  			}
   279  
   280  			res, err := f(provider)
   281  			if err != nil {
   282  				if isRPSLimitError(err) {
   283  					provider.limiter.ReduceLimit()
   284  
   285  					err = provider.limiter.WaitForRequestsAvailability(1)
   286  					if err != nil {
   287  						return nil, err
   288  					}
   289  
   290  					res, err = f(provider)
   291  					if err == nil {
   292  						return []interface{}{res}, err
   293  					}
   294  				}
   295  
   296  				if isVMError(err) || errors.Is(err, context.Canceled) {
   297  					cmd.Cancel()
   298  				}
   299  
   300  				return nil, err
   301  			}
   302  			return []interface{}{res}, err
   303  		}, provider.name))
   304  	}
   305  
   306  	result := c.circuitbreaker.Execute(cmd)
   307  	if result.Error() != nil {
   308  		return nil, result.Error()
   309  	}
   310  
   311  	return result.Result()[0], nil
   312  }
   313  
   314  func (c *ClientWithFallback) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
   315  	rpcstats.CountCallWithTag("eth_BlockByHash", c.tag)
   316  
   317  	res, err := c.makeCall(
   318  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   319  			return client.ethClient.BlockByHash(ctx, hash)
   320  		},
   321  	)
   322  
   323  	c.toggleConnectionState(err)
   324  
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	return res.(*types.Block), nil
   330  }
   331  
   332  func (c *ClientWithFallback) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
   333  	rpcstats.CountCallWithTag("eth_BlockByNumber", c.tag)
   334  	res, err := c.makeCall(
   335  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   336  			return client.ethClient.BlockByNumber(ctx, number)
   337  		},
   338  	)
   339  
   340  	c.toggleConnectionState(err)
   341  
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  
   346  	return res.(*types.Block), nil
   347  }
   348  
   349  func (c *ClientWithFallback) BlockNumber(ctx context.Context) (uint64, error) {
   350  	rpcstats.CountCallWithTag("eth_BlockNumber", c.tag)
   351  
   352  	res, err := c.makeCall(
   353  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   354  			return client.ethClient.BlockNumber(ctx)
   355  		},
   356  	)
   357  
   358  	c.toggleConnectionState(err)
   359  
   360  	if err != nil {
   361  		return 0, err
   362  	}
   363  
   364  	return res.(uint64), nil
   365  }
   366  
   367  func (c *ClientWithFallback) PeerCount(ctx context.Context) (uint64, error) {
   368  	rpcstats.CountCallWithTag("eth_PeerCount", c.tag)
   369  
   370  	res, err := c.makeCall(
   371  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   372  			return client.ethClient.PeerCount(ctx)
   373  		},
   374  	)
   375  
   376  	c.toggleConnectionState(err)
   377  
   378  	if err != nil {
   379  		return 0, err
   380  	}
   381  
   382  	return res.(uint64), nil
   383  }
   384  
   385  func (c *ClientWithFallback) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
   386  	rpcstats.CountCallWithTag("eth_HeaderByHash", c.tag)
   387  	res, err := c.makeCall(
   388  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   389  			return client.ethClient.HeaderByHash(ctx, hash)
   390  		},
   391  	)
   392  
   393  	c.toggleConnectionState(err)
   394  
   395  	if err != nil {
   396  		return nil, err
   397  	}
   398  
   399  	return res.(*types.Header), nil
   400  }
   401  
   402  func (c *ClientWithFallback) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
   403  	rpcstats.CountCallWithTag("eth_HeaderByNumber", c.tag)
   404  	res, err := c.makeCall(
   405  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   406  			return client.ethClient.HeaderByNumber(ctx, number)
   407  		},
   408  	)
   409  
   410  	c.toggleConnectionState(err)
   411  
   412  	if err != nil {
   413  		return nil, err
   414  	}
   415  
   416  	return res.(*types.Header), nil
   417  }
   418  
   419  func (c *ClientWithFallback) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) {
   420  	rpcstats.CountCallWithTag("eth_TransactionByHash", c.tag)
   421  
   422  	res, err := c.makeCall(
   423  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   424  			tx, isPending, err := client.ethClient.TransactionByHash(ctx, hash)
   425  			return []any{tx, isPending}, err
   426  		},
   427  	)
   428  
   429  	c.toggleConnectionState(err)
   430  
   431  	if err != nil {
   432  		return nil, false, err
   433  	}
   434  
   435  	resArr := res.([]any)
   436  	return resArr[0].(*types.Transaction), resArr[1].(bool), nil
   437  }
   438  
   439  func (c *ClientWithFallback) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) {
   440  	rpcstats.CountCall("eth_TransactionSender")
   441  
   442  	res, err := c.makeCall(
   443  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   444  			return client.ethClient.TransactionSender(ctx, tx, block, index)
   445  		},
   446  	)
   447  
   448  	c.toggleConnectionState(err)
   449  
   450  	return res.(common.Address), err
   451  }
   452  
   453  func (c *ClientWithFallback) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
   454  	rpcstats.CountCall("eth_TransactionCount")
   455  
   456  	res, err := c.makeCall(
   457  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   458  			return client.ethClient.TransactionCount(ctx, blockHash)
   459  		},
   460  	)
   461  
   462  	c.toggleConnectionState(err)
   463  
   464  	if err != nil {
   465  		return 0, err
   466  	}
   467  
   468  	return res.(uint), nil
   469  }
   470  
   471  func (c *ClientWithFallback) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
   472  	rpcstats.CountCall("eth_TransactionInBlock")
   473  
   474  	res, err := c.makeCall(
   475  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   476  			return client.ethClient.TransactionInBlock(ctx, blockHash, index)
   477  		},
   478  	)
   479  
   480  	c.toggleConnectionState(err)
   481  
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	return res.(*types.Transaction), nil
   487  }
   488  
   489  func (c *ClientWithFallback) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
   490  	rpcstats.CountCall("eth_TransactionReceipt")
   491  
   492  	res, err := c.makeCall(
   493  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   494  			return client.ethClient.TransactionReceipt(ctx, txHash)
   495  		},
   496  	)
   497  
   498  	c.toggleConnectionState(err)
   499  
   500  	if err != nil {
   501  		return nil, err
   502  	}
   503  
   504  	return res.(*types.Receipt), nil
   505  }
   506  
   507  func (c *ClientWithFallback) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
   508  	rpcstats.CountCall("eth_SyncProgress")
   509  
   510  	res, err := c.makeCall(
   511  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   512  			return client.ethClient.SyncProgress(ctx)
   513  		},
   514  	)
   515  
   516  	c.toggleConnectionState(err)
   517  
   518  	if err != nil {
   519  		return nil, err
   520  	}
   521  
   522  	return res.(*ethereum.SyncProgress), nil
   523  }
   524  
   525  func (c *ClientWithFallback) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
   526  	rpcstats.CountCall("eth_SubscribeNewHead")
   527  
   528  	res, err := c.makeCall(
   529  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   530  			return client.ethClient.SubscribeNewHead(ctx, ch)
   531  		},
   532  	)
   533  
   534  	c.toggleConnectionState(err)
   535  
   536  	if err != nil {
   537  		return nil, err
   538  	}
   539  
   540  	return res.(ethereum.Subscription), nil
   541  }
   542  
   543  func (c *ClientWithFallback) NetworkID() uint64 {
   544  	return c.ChainID
   545  }
   546  
   547  func (c *ClientWithFallback) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
   548  	rpcstats.CountCallWithTag("eth_BalanceAt", c.tag)
   549  
   550  	res, err := c.makeCall(
   551  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   552  			return client.ethClient.BalanceAt(ctx, account, blockNumber)
   553  		},
   554  	)
   555  
   556  	c.toggleConnectionState(err)
   557  
   558  	if err != nil {
   559  		return nil, err
   560  	}
   561  
   562  	return res.(*big.Int), nil
   563  }
   564  
   565  func (c *ClientWithFallback) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
   566  	rpcstats.CountCall("eth_StorageAt")
   567  
   568  	res, err := c.makeCall(
   569  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   570  			return client.ethClient.StorageAt(ctx, account, key, blockNumber)
   571  		},
   572  	)
   573  
   574  	c.toggleConnectionState(err)
   575  
   576  	if err != nil {
   577  		return nil, err
   578  	}
   579  
   580  	return res.([]byte), nil
   581  }
   582  
   583  func (c *ClientWithFallback) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
   584  	rpcstats.CountCall("eth_CodeAt")
   585  
   586  	res, err := c.makeCall(
   587  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   588  			return client.ethClient.CodeAt(ctx, account, blockNumber)
   589  		},
   590  	)
   591  
   592  	c.toggleConnectionState(err)
   593  
   594  	if err != nil {
   595  		return nil, err
   596  	}
   597  
   598  	return res.([]byte), nil
   599  }
   600  
   601  func (c *ClientWithFallback) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
   602  	rpcstats.CountCallWithTag("eth_NonceAt", c.tag)
   603  
   604  	res, err := c.makeCall(
   605  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   606  			return client.ethClient.NonceAt(ctx, account, blockNumber)
   607  		},
   608  	)
   609  
   610  	c.toggleConnectionState(err)
   611  
   612  	if err != nil {
   613  		return 0, err
   614  	}
   615  
   616  	return res.(uint64), nil
   617  }
   618  
   619  func (c *ClientWithFallback) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
   620  	rpcstats.CountCallWithTag("eth_FilterLogs", c.tag)
   621  
   622  	// Override providers name to use a separate circuit for this command as it more often fails due to rate limiting
   623  	ethClients := make([]*EthClient, len(c.ethClients))
   624  	for i, client := range c.ethClients {
   625  		ethClients[i] = &EthClient{
   626  			ethClient: client.ethClient,
   627  			limiter:   client.limiter,
   628  			rpcClient: client.rpcClient,
   629  			name:      client.name + "_FilterLogs",
   630  		}
   631  	}
   632  
   633  	res, err := c.makeCall(
   634  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   635  			return client.ethClient.FilterLogs(ctx, q)
   636  		},
   637  	)
   638  
   639  	// No connection state toggling here, as it often mail fail due to archive node rate limiting
   640  	// which does not impact other calls
   641  
   642  	if err != nil {
   643  		return nil, err
   644  	}
   645  
   646  	return res.([]types.Log), nil
   647  }
   648  
   649  func (c *ClientWithFallback) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
   650  	rpcstats.CountCall("eth_SubscribeFilterLogs")
   651  
   652  	res, err := c.makeCall(
   653  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   654  			return client.ethClient.SubscribeFilterLogs(ctx, q, ch)
   655  		},
   656  	)
   657  
   658  	c.toggleConnectionState(err)
   659  
   660  	if err != nil {
   661  		return nil, err
   662  	}
   663  
   664  	return res.(ethereum.Subscription), nil
   665  }
   666  
   667  func (c *ClientWithFallback) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) {
   668  	rpcstats.CountCall("eth_PendingBalanceAt")
   669  
   670  	res, err := c.makeCall(
   671  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   672  			return client.ethClient.PendingBalanceAt(ctx, account)
   673  		},
   674  	)
   675  
   676  	c.toggleConnectionState(err)
   677  
   678  	if err != nil {
   679  		return nil, err
   680  	}
   681  
   682  	return res.(*big.Int), nil
   683  }
   684  
   685  func (c *ClientWithFallback) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) {
   686  	rpcstats.CountCall("eth_PendingStorageAt")
   687  
   688  	res, err := c.makeCall(
   689  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   690  			return client.ethClient.PendingStorageAt(ctx, account, key)
   691  		},
   692  	)
   693  
   694  	c.toggleConnectionState(err)
   695  
   696  	if err != nil {
   697  		return nil, err
   698  	}
   699  
   700  	return res.([]byte), nil
   701  }
   702  
   703  func (c *ClientWithFallback) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
   704  	rpcstats.CountCall("eth_PendingCodeAt")
   705  
   706  	res, err := c.makeCall(
   707  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   708  			return client.ethClient.PendingCodeAt(ctx, account)
   709  		},
   710  	)
   711  
   712  	c.toggleConnectionState(err)
   713  
   714  	if err != nil {
   715  		return nil, err
   716  	}
   717  
   718  	return res.([]byte), nil
   719  }
   720  
   721  func (c *ClientWithFallback) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
   722  	rpcstats.CountCall("eth_PendingNonceAt")
   723  
   724  	res, err := c.makeCall(
   725  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   726  			return client.ethClient.PendingNonceAt(ctx, account)
   727  		},
   728  	)
   729  
   730  	c.toggleConnectionState(err)
   731  
   732  	if err != nil {
   733  		return 0, err
   734  	}
   735  
   736  	return res.(uint64), nil
   737  }
   738  
   739  func (c *ClientWithFallback) PendingTransactionCount(ctx context.Context) (uint, error) {
   740  	rpcstats.CountCall("eth_PendingTransactionCount")
   741  
   742  	res, err := c.makeCall(
   743  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   744  			return client.ethClient.PendingTransactionCount(ctx)
   745  		},
   746  	)
   747  
   748  	c.toggleConnectionState(err)
   749  
   750  	if err != nil {
   751  		return 0, err
   752  	}
   753  
   754  	return res.(uint), nil
   755  }
   756  
   757  func (c *ClientWithFallback) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
   758  	rpcstats.CountCall("eth_CallContract_" + msg.To.String())
   759  
   760  	res, err := c.makeCall(
   761  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   762  			return client.ethClient.CallContract(ctx, msg, blockNumber)
   763  		},
   764  	)
   765  
   766  	c.toggleConnectionState(err)
   767  
   768  	if err != nil {
   769  		return nil, err
   770  	}
   771  
   772  	return res.([]byte), nil
   773  }
   774  
   775  func (c *ClientWithFallback) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) {
   776  	rpcstats.CountCall("eth_CallContractAtHash")
   777  
   778  	res, err := c.makeCall(
   779  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   780  			return client.ethClient.CallContractAtHash(ctx, msg, blockHash)
   781  		},
   782  	)
   783  
   784  	c.toggleConnectionState(err)
   785  
   786  	if err != nil {
   787  		return nil, err
   788  	}
   789  
   790  	return res.([]byte), nil
   791  }
   792  
   793  func (c *ClientWithFallback) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
   794  	rpcstats.CountCall("eth_PendingCallContract")
   795  
   796  	res, err := c.makeCall(
   797  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   798  			return client.ethClient.PendingCallContract(ctx, msg)
   799  		},
   800  	)
   801  
   802  	c.toggleConnectionState(err)
   803  
   804  	if err != nil {
   805  		return nil, err
   806  	}
   807  
   808  	return res.([]byte), nil
   809  }
   810  
   811  func (c *ClientWithFallback) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
   812  	rpcstats.CountCall("eth_SuggestGasPrice")
   813  
   814  	res, err := c.makeCall(
   815  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   816  			return client.ethClient.SuggestGasPrice(ctx)
   817  		},
   818  	)
   819  
   820  	c.toggleConnectionState(err)
   821  
   822  	if err != nil {
   823  		return nil, err
   824  	}
   825  
   826  	return res.(*big.Int), nil
   827  }
   828  
   829  func (c *ClientWithFallback) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
   830  	rpcstats.CountCall("eth_SuggestGasTipCap")
   831  
   832  	res, err := c.makeCall(
   833  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   834  			return client.ethClient.SuggestGasTipCap(ctx)
   835  		},
   836  	)
   837  
   838  	c.toggleConnectionState(err)
   839  
   840  	if err != nil {
   841  		return nil, err
   842  	}
   843  
   844  	return res.(*big.Int), nil
   845  }
   846  
   847  func (c *ClientWithFallback) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) {
   848  	rpcstats.CountCall("eth_FeeHistory")
   849  
   850  	res, err := c.makeCall(
   851  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   852  			return client.ethClient.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
   853  		},
   854  	)
   855  
   856  	c.toggleConnectionState(err)
   857  
   858  	if err != nil {
   859  		return nil, err
   860  	}
   861  
   862  	return res.(*ethereum.FeeHistory), nil
   863  }
   864  
   865  func (c *ClientWithFallback) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
   866  	rpcstats.CountCall("eth_EstimateGas")
   867  
   868  	res, err := c.makeCall(
   869  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   870  			return client.ethClient.EstimateGas(ctx, msg)
   871  		},
   872  	)
   873  
   874  	c.toggleConnectionState(err)
   875  
   876  	if err != nil {
   877  		return 0, err
   878  	}
   879  
   880  	return res.(uint64), nil
   881  }
   882  
   883  func (c *ClientWithFallback) SendTransaction(ctx context.Context, tx *types.Transaction) error {
   884  	rpcstats.CountCall("eth_SendTransaction")
   885  
   886  	_, err := c.makeCall(
   887  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   888  			return nil, client.ethClient.SendTransaction(ctx, tx)
   889  		},
   890  	)
   891  
   892  	c.toggleConnectionState(err)
   893  
   894  	return err
   895  }
   896  
   897  func (c *ClientWithFallback) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
   898  	rpcstats.CountCall("eth_CallContext")
   899  
   900  	_, err := c.makeCall(
   901  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   902  			return nil, client.rpcClient.CallContext(ctx, result, method, args...)
   903  		},
   904  	)
   905  
   906  	c.toggleConnectionState(err)
   907  
   908  	return err
   909  }
   910  
   911  func (c *ClientWithFallback) BatchCallContext(ctx context.Context, b []rpc.BatchElem) error {
   912  	rpcstats.CountCall("eth_BatchCallContext")
   913  
   914  	_, err := c.makeCall(
   915  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   916  			return nil, client.rpcClient.BatchCallContext(ctx, b)
   917  		},
   918  	)
   919  
   920  	c.toggleConnectionState(err)
   921  
   922  	return err
   923  }
   924  
   925  func (c *ClientWithFallback) ToBigInt() *big.Int {
   926  	return big.NewInt(int64(c.ChainID))
   927  }
   928  
   929  func (c *ClientWithFallback) GetBaseFeeFromBlock(ctx context.Context, blockNumber *big.Int) (string, error) {
   930  	rpcstats.CountCall("eth_GetBaseFeeFromBlock")
   931  
   932  	feeHistory, err := c.FeeHistory(ctx, 1, blockNumber, nil)
   933  
   934  	if err != nil {
   935  		if err.Error() == "the method eth_feeHistory does not exist/is not available" {
   936  			return "", nil
   937  		}
   938  		return "", err
   939  	}
   940  
   941  	var baseGasFee string = ""
   942  	if len(feeHistory.BaseFee) > 0 {
   943  		baseGasFee = feeHistory.BaseFee[0].String()
   944  	}
   945  
   946  	return baseGasFee, err
   947  }
   948  
   949  // go-ethereum's `Transaction` items drop the blkHash obtained during the RPC call.
   950  // This function preserves the additional data. This is the cheapest way to obtain
   951  // the block hash for a given block number.
   952  func (c *ClientWithFallback) CallBlockHashByTransaction(ctx context.Context, blockNumber *big.Int, index uint) (common.Hash, error) {
   953  	rpcstats.CountCallWithTag("eth_FullTransactionByBlockNumberAndIndex", c.tag)
   954  
   955  	res, err := c.makeCall(
   956  		ctx, c.ethClients, func(client *EthClient) (interface{}, error) {
   957  			return callBlockHashByTransaction(ctx, client.rpcClient, blockNumber, index)
   958  		},
   959  	)
   960  
   961  	c.toggleConnectionState(err)
   962  
   963  	if err != nil {
   964  		return common.HexToHash(""), err
   965  	}
   966  
   967  	return res.(common.Hash), nil
   968  }
   969  
   970  func (c *ClientWithFallback) GetWalletNotifier() func(chainId uint64, message string) {
   971  	return c.WalletNotifier
   972  }
   973  
   974  func (c *ClientWithFallback) SetWalletNotifier(notifier func(chainId uint64, message string)) {
   975  	c.WalletNotifier = notifier
   976  }
   977  
   978  func (c *ClientWithFallback) toggleConnectionState(err error) {
   979  	connected := true
   980  	if err != nil {
   981  		if !isNotFoundError(err) && !isVMError(err) && !errors.Is(err, ErrRequestsOverLimit) && !errors.Is(err, context.Canceled) {
   982  			log.Warn("Error not in chain call", "error", err, "chain", c.ChainID)
   983  			connected = false
   984  		} else {
   985  			log.Warn("Error in chain call", "error", err)
   986  		}
   987  	}
   988  	c.SetIsConnected(connected)
   989  }
   990  
   991  func (c *ClientWithFallback) Tag() string {
   992  	return c.tag
   993  }
   994  
   995  func (c *ClientWithFallback) SetTag(tag string) {
   996  	c.tag = tag
   997  }
   998  
   999  func (c *ClientWithFallback) GroupTag() string {
  1000  	return c.groupTag
  1001  }
  1002  
  1003  func (c *ClientWithFallback) SetGroupTag(tag string) {
  1004  	c.groupTag = tag
  1005  }
  1006  
  1007  func (c *ClientWithFallback) DeepCopyTag() Tagger {
  1008  	copy := *c
  1009  	return &copy
  1010  }
  1011  
  1012  func (c *ClientWithFallback) GetLimiter() RequestLimiter {
  1013  	return c.commonLimiter
  1014  }
  1015  
  1016  func (c *ClientWithFallback) SetLimiter(limiter RequestLimiter) {
  1017  	c.commonLimiter = limiter
  1018  }
  1019  
  1020  func (c *ClientWithFallback) GetCircuitBreaker() *circuitbreaker.CircuitBreaker {
  1021  	return c.circuitbreaker
  1022  }
  1023  
  1024  func (c *ClientWithFallback) SetCircuitBreaker(cb *circuitbreaker.CircuitBreaker) {
  1025  	c.circuitbreaker = cb
  1026  }