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 }