github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/rpc/web3/ethclient/client.go (about)

     1  package ethclient
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/hyperledger/burrow/crypto"
     9  	"github.com/hyperledger/burrow/encoding/web3hex"
    10  	"github.com/hyperledger/burrow/rpc"
    11  	"github.com/hyperledger/burrow/rpc/rpcevents"
    12  	"github.com/hyperledger/burrow/rpc/web3"
    13  )
    14  
    15  const (
    16  	EthGetLogsMethod               = "eth_getLogs"
    17  	EthSyncingMethod               = "eth_syncing"
    18  	EthBlockNumberMethod           = "eth_blockNumber"
    19  	EthSendTransactionMethod       = "eth_sendTransaction"
    20  	EthSendRawTransactionMethod    = "eth_sendRawTransaction"
    21  	EthGetTransactionCountMethod   = "eth_getTransactionCount"
    22  	EthAccountsMethod              = "eth_accounts"
    23  	EthGetBlockByNumberMethod      = "eth_getBlockByNumber"
    24  	EthGetTransactionByHashMethod  = "eth_getTransactionByHash"
    25  	EthGetTransactionReceiptMethod = "eth_getTransactionReceipt"
    26  	EthGasPriceMethod              = "eth_gasPrice"
    27  	NetVersionMethod               = "net_version"
    28  	Web3ClientVersionMethod        = "web3_clientVersion"
    29  )
    30  
    31  // Adjust the polling frequency of AwaitTransaction
    32  const awaitTransactionSleep = 200 * time.Millisecond
    33  
    34  type EthClient struct {
    35  	rpc.Client
    36  }
    37  
    38  func NewEthClient(cli rpc.Client) *EthClient {
    39  	return &EthClient{Client: cli}
    40  }
    41  
    42  func (c *EthClient) SendTransaction(tx *EthSendTransactionParam) (string, error) {
    43  	hash := new(string)
    44  	err := c.Call(EthSendTransactionMethod, []*EthSendTransactionParam{tx}, &hash)
    45  	if err != nil {
    46  		return "", err
    47  	}
    48  	return *hash, nil
    49  }
    50  
    51  func (c *EthClient) SendRawTransaction(txHex string) (string, error) {
    52  	hash := new(string)
    53  	err := c.Call(EthSendRawTransactionMethod, []string{txHex}, &hash)
    54  	if err != nil {
    55  		return "", err
    56  	}
    57  	return *hash, nil
    58  }
    59  
    60  func (c *EthClient) GetTransactionCount(address crypto.Address) (string, error) {
    61  	var count string
    62  	err := c.Call(EthGetTransactionCountMethod, []string{web3hex.Encoder.Address(address), "latest"}, &count)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  	return count, nil
    67  }
    68  
    69  func (c *EthClient) GetLogs(filter *Filter) ([]*EthLog, error) {
    70  	var logs []*EthLog
    71  	err := c.Call(EthGetLogsMethod, []*EthFilter{filter.EthFilter()}, &logs)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	return logs, nil
    76  }
    77  
    78  func (c *EthClient) Accounts() ([]string, error) {
    79  	var accounts []string
    80  	err := c.Call(EthAccountsMethod, nil, &accounts)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	return accounts, nil
    85  }
    86  
    87  func (c *EthClient) GetBlockByNumber(height string) (*Block, error) {
    88  	block := new(Block)
    89  	err := c.Call(EthGetBlockByNumberMethod, []interface{}{height, false}, block)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	return block, nil
    94  }
    95  
    96  func (c *EthClient) GetTransactionByHash(txHash string) (*web3.Transaction, error) {
    97  	tx := new(web3.Transaction)
    98  	err := c.Call(EthGetTransactionByHashMethod, []string{txHash}, tx)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return tx, nil
   103  }
   104  
   105  func (c *EthClient) GetTransactionReceipt(txHash string) (*Receipt, error) {
   106  	tx := new(Receipt)
   107  	err := c.Call(EthGetTransactionReceiptMethod, []string{txHash}, tx)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	return tx, nil
   112  }
   113  
   114  func (c *EthClient) Syncing() (bool, error) {
   115  	syncing := new(bool)
   116  	err := c.Call(EthSyncingMethod, nil, syncing)
   117  	if err != nil {
   118  		return false, err
   119  	}
   120  	return *syncing, nil
   121  }
   122  
   123  func (c *EthClient) BlockNumber() (uint64, error) {
   124  	latestBlock := new(string)
   125  	err := c.Call(EthBlockNumberMethod, nil, latestBlock)
   126  	if err != nil {
   127  		return 0, err
   128  	}
   129  	d := new(web3hex.Decoder)
   130  	return d.Uint64(*latestBlock), d.Err()
   131  }
   132  
   133  func (c *EthClient) GasPrice() (string, error) {
   134  	gasPrice := new(string)
   135  	err := c.Call(EthGasPriceMethod, nil, gasPrice)
   136  	if err != nil {
   137  		return "", err
   138  	}
   139  	return *gasPrice, nil
   140  }
   141  
   142  // AKA ChainID
   143  func (c *EthClient) NetVersion() (string, error) {
   144  	version := new(string)
   145  	err := c.Call(NetVersionMethod, nil, version)
   146  	if err != nil {
   147  		return "", err
   148  	}
   149  	return *version, nil
   150  }
   151  
   152  func (c *EthClient) Web3ClientVersion() (string, error) {
   153  	version := new(string)
   154  	err := c.Call(Web3ClientVersionMethod, nil, version)
   155  	if err != nil {
   156  		return "", err
   157  	}
   158  	return *version, nil
   159  }
   160  
   161  // Wait for a transaction to be mined/confirmed
   162  func (c *EthClient) AwaitTransaction(ctx context.Context, txHash string) (*Receipt, error) {
   163  	for {
   164  		tx, err := c.GetTransactionReceipt(txHash)
   165  		if err != nil {
   166  			return nil, fmt.Errorf("AwaitTransaction failed to get ethereum transaction: %w", err)
   167  		}
   168  		if tx.BlockNumber != "" {
   169  			if tx.BlockHash == "" {
   170  				return nil, fmt.Errorf("expected Blockhash to be non-empty when BlockNumber is non-empty (%s)",
   171  					tx.BlockNumber)
   172  			}
   173  			// Transaction has been confirmed (is included in a block)
   174  			return tx, nil
   175  		}
   176  		time.Sleep(awaitTransactionSleep)
   177  		select {
   178  		case <-ctx.Done():
   179  			return nil, fmt.Errorf("AwaitTransaction interrupted: %w", ctx.Err())
   180  		default:
   181  
   182  		}
   183  	}
   184  }
   185  
   186  func logBound(bound *rpcevents.Bound) string {
   187  	if bound == nil {
   188  		return ""
   189  	}
   190  	switch bound.Type {
   191  	case rpcevents.Bound_FIRST:
   192  		return "earliest"
   193  	case rpcevents.Bound_LATEST:
   194  		return "latest"
   195  	case rpcevents.Bound_ABSOLUTE:
   196  		return web3hex.Encoder.Uint64(bound.Index)
   197  	default:
   198  		return ""
   199  	}
   200  }