github.com/lmittmann/w3@v0.20.0/README.md (about) 1 # `w3`: Enhanced Ethereum Integration for Go 2 3 [](https://pkg.go.dev/github.com/lmittmann/w3) 4 [](https://goreportcard.com/report/github.com/lmittmann/w3) 5 [](https://coveralls.io/github/lmittmann/w3?branch=main) 6 [](https://github.com/lmittmann/w3/releases) 7 [](https://t.me/w3_golang) 8 <img src="https://w3.cool/gopher.png" align="right" alt="W3 Gopher" width="158" height="224"> 9 10 `w3` is your toolbelt for integrating with Ethereum in Go. Closely linked to `go‑ethereum`, it provides an ergonomic wrapper for working with **RPC**, **ABI's**, and the **EVM**. 11 12 13 ``` 14 go get github.com/lmittmann/w3 15 ``` 16 17 18 ## At a Glance 19 20 * Use `w3.Client` to connect to an RPC endpoint. The client features batch request support for up to **80x faster requests** and easy extendibility. [learn more ↗](#rpc-client) 21 * Use `w3vm.VM` to simulate EVM execution with optional tracing and Mainnet state forking, or test Smart Contracts. [learn more ↗](#vm) 22 * Use `w3.Func` and `w3.Event` to create ABI bindings from Solidity function and event signatures. [learn more ↗](#abi-bindings) 23 * Use `w3.A`, `w3.H`, and many other utility functions to parse addresses, hashes, and other common types from strings. [learn more ↗](#utils) 24 25 26 ## Sponsors 27 28 <picture> 29 <source media="(prefers-color-scheme: dark)" srcset="docs/public/assets/ef-logo-dark.svg"> 30 <source media="(prefers-color-scheme: light)" srcset="docs/public/assets/ef-logo.svg"> 31 <img src="docs/public/assets/ef-logo.svg" alt="ef logo" width="256" height="auto"> 32 </picture> 33 34 35 ## Getting Started 36 37 ### RPC Client 38 39 [`w3.Client`](https://pkg.go.dev/github.com/lmittmann/w3#Client) is a batch request focused RPC client that can be used to connect to an Ethereum node via HTTP, WebSocket, or IPC. Its modular API allows to create custom RPC method integrations that can be used alongside the common methods implemented by this package. 40 41 **Example:** Batch Request ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchEOAState)) 42 43 ```go 44 // 1. Connect to an RPC endpoint 45 client, err := w3.Dial("https://eth.llamarpc.com") 46 if err != nil { 47 // handle error 48 } 49 defer client.Close() 50 51 // 2. Make a batch request 52 var ( 53 balance *big.Int 54 nonce uint64 55 ) 56 if err := client.Call( 57 eth.Balance(addr, nil).Returns(&balance), 58 eth.Nonce(addr, nil).Returns(&nonce), 59 ); err != nil { 60 // handle error 61 } 62 ``` 63 64 > [!NOTE] 65 > #### Why send batch requests? 66 > Most of the time you need to call multiple RPC methods to get the data you need. When you make separate requests per RPC call you need a single round trip to the server for each call. This can be slow, especially for remote endpoints. Batching multiple RPC calls into a single request only requires a single round trip, and speeds up RPC calls significantly. 67 68 #### Error Handling 69 70 If one or more calls in a batch request fail, `Client.Call` returns an error of type [`w3.CallErrors`](https://pkg.go.dev/github.com/lmittmann/w3#CallErrors). 71 72 **Example:** Check which RPC calls failed in a batch request ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchHandleError)) 73 ```go 74 var batchErr w3.CallErrors 75 if err := client.Call(calls...); errors.As(err, &batchErr) { 76 // handle call errors 77 } else if err != nil { 78 // handle other errors 79 } 80 ``` 81 82 #### Learn More 83 * List of supported [**RPC methods**](#rpc-methods) 84 * Learn how to create [**custom RPC method bindings**](#custom-rpc-method-bindings) 85 86 ### VM 87 88 [`w3vm.VM`](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#VM) is a high-level EVM environment with a simple but powerful API to simulate EVM execution, test Smart Contracts, or trace transactions. It supports Mainnet state forking via RPC and state caching for faster testing. 89 90 **Example:** Simulate an Uniswap v3 swap ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-UniswapV3Swap)) 91 92 ```go 93 // 1. Create a VM that forks the Mainnet state from the latest block, 94 // disables the base fee, and has a fake WETH balance and approval for the router 95 vm, err := w3vm.New( 96 w3vm.WithFork(client, nil), 97 w3vm.WithNoBaseFee(), 98 w3vm.WithState(w3types.State{ 99 addrWETH: {Storage: w3types.Storage{ 100 w3vm.WETHBalanceSlot(addrEOA): common.BigToHash(w3.I("1 ether")), 101 w3vm.WETHAllowanceSlot(addrEOA, addrRouter): common.BigToHash(w3.I("1 ether")), 102 }}, 103 }), 104 ) 105 if err != nil { 106 // handle error 107 } 108 109 // 2. Simulate a Uniswap v3 swap 110 receipt, err := vm.Apply(&w3types.Message{ 111 From: addrEOA, 112 To: &addrRouter, 113 Func: funcExactInput, 114 Args: []any{&ExactInputParams{ 115 Path: encodePath(addrWETH, 500, addrUNI), 116 Recipient: addrEOA, 117 Deadline: big.NewInt(time.Now().Unix()), 118 AmountIn: w3.I("1 ether"), 119 AmountOutMinimum: w3.Big0, 120 }}, 121 }) 122 if err != nil { 123 // handle error 124 } 125 126 // 3. Decode output amount 127 var amountOut *big.Int 128 if err := receipt.DecodeReturns(&amountOut); err != nil { 129 // handle error 130 } 131 ``` 132 133 ### ABI Bindings 134 135 ABI bindings in `w3` are specified for individual functions using Solidity syntax and are usable for any contract that supports that function. 136 137 **Example:** ABI binding for the ERC20 `balanceOf` function ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-NewFunc-BalanceOf)) 138 139 ```go 140 funcBalanceOf := w3.MustNewFunc("balanceOf(address)", "uint256") 141 ``` 142 143 **Example:** ABI binding for the Uniswap v4 `swap` function ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-NewFunc-UniswapV4Swap)) 144 145 ```go 146 funcSwap := w3.MustNewFunc(`swap( 147 (address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) key, 148 (bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96) params, 149 bytes hookData 150 )`, "int256 delta") 151 ``` 152 153 A [`Func`](https://pkg.go.dev/github.com/lmittmann/w3#Func) can be used to 154 155 * encode arguments to the contracts input data ([`Func.EncodeArgs`](https://pkg.go.dev/github.com/lmittmann/w3#Func.EncodeArgs)), 156 * decode arguments from the contracts input data ([`Func.DecodeArgs`](https://pkg.go.dev/github.com/lmittmann/w3#Func.DecodeArgs)), and 157 * decode returns from the contracts output data ([`Func.DecodeReturns`](https://pkg.go.dev/github.com/lmittmann/w3#Func.DecodeReturns)). 158 159 ### Utils 160 161 Static addresses, hashes, bytes or integers can be parsed from (hex-)strings with the following utility functions that panic if the string is not valid. 162 163 ```go 164 addr := w3.A("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") 165 hash := w3.H("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") 166 bytes := w3.B("0x27c5342c") 167 amount := w3.I("12.34 ether") 168 ``` 169 170 Use [go-ethereum/common](https://pkg.go.dev/github.com/ethereum/go-ethereum/common) to parse strings that may not be valid instead. 171 172 173 ## RPC Methods 174 175 List of supported RPC methods for [`w3.Client`](https://pkg.go.dev/github.com/lmittmann/w3#Client). 176 177 ### [`eth`](https://pkg.go.dev/github.com/lmittmann/w3/module/eth) 178 179 | Method | Go Code 180 | :---------------------------------------- | :------- 181 | `eth_blockNumber` | `eth.BlockNumber().Returns(blockNumber **big.Int)` 182 | `eth_call` | `eth.Call(msg *w3types.Message, blockNumber *big.Int, overrides w3types.State).Returns(output *[]byte)`<br>`eth.CallFunc(contract common.Address, f w3types.Func, args ...any).Returns(returns ...any)` 183 | `eth_chainId` | `eth.ChainID().Returns(chainID *uint64)` 184 | `eth_createAccessList` | `eth.AccessList(msg *w3types.Message, blockNumber *big.Int).Returns(resp **eth.AccessListResponse)` 185 | `eth_estimateGas` | `eth.EstimateGas(msg *w3types.Message, blockNumber *big.Int).Returns(gas *uint64)` 186 | `eth_gasPrice` | `eth.GasPrice().Returns(gasPrice **big.Int)` 187 | `eth_maxPriorityFeePerGas` | `eth.GasTipCap().Returns(gasTipCap **big.Int)` 188 | `eth_getBalance` | `eth.Balance(addr common.Address, blockNumber *big.Int).Returns(balance **big.Int)` 189 | `eth_getBlockByHash` | `eth.BlockByHash(hash common.Hash).Returns(block *types.Block)`<br>`eth.HeaderByHash(hash common.Hash).Returns(header **types.Header)` 190 | `eth_getBlockByNumber` | `eth.BlockByNumber(number *big.Int).Returns(block *types.Block)`<br>`eth.HeaderByNumber(number *big.Int).Returns(header **types.Header)` 191 | `eth_getBlockReceipts` | `eth.BlockReceipts(blockNumber *big.Int).Returns(receipts *types.Receipts)` 192 | `eth_getBlockTransactionCountByHash` | `eth.BlockTxCountByHash(hash common.Hash).Returns(count *uint)` 193 | `eth_getBlockTransactionCountByNumber` | `eth.BlockTxCountByNumber(number *big.Int).Returns(count *uint)` 194 | `eth_getCode` | `eth.Code(addr common.Address, blockNumber *big.Int).Returns(code *[]byte)` 195 | `eth_getLogs` | `eth.Logs(q ethereum.FilterQuery).Returns(logs *[]types.Log)` 196 | `eth_getStorageAt` | `eth.StorageAt(addr common.Address, slot common.Hash, blockNumber *big.Int).Returns(storage *common.Hash)` 197 | `eth_getTransactionByHash` | `eth.Tx(hash common.Hash).Returns(tx **types.Transaction)` 198 | `eth_getTransactionByBlockHashAndIndex` | `eth.TxByBlockHashAndIndex(blockHash common.Hash, index uint).Returns(tx **types.Transaction)` 199 | `eth_getTransactionByBlockNumberAndIndex` | `eth.TxByBlockNumberAndIndex(blockNumber *big.Int, index uint).Returns(tx **types.Transaction)` 200 | `eth_getTransactionCount` | `eth.Nonce(addr common.Address, blockNumber *big.Int).Returns(nonce *uint)` 201 | `eth_getTransactionReceipt` | `eth.TxReceipt(txHash common.Hash).Returns(receipt **types.Receipt)` 202 | `eth_sendRawTransaction` | `eth.SendRawTx(rawTx []byte).Returns(hash *common.Hash)`<br>`eth.SendTx(tx *types.Transaction).Returns(hash *common.Hash)` 203 | `eth_getUncleByBlockHashAndIndex` | `eth.UncleByBlockHashAndIndex(hash common.Hash, index uint).Returns(uncle **types.Header)` 204 | `eth_getUncleByBlockNumberAndIndex` | `eth.UncleByBlockNumberAndIndex(number *big.Int, index uint).Returns(uncle **types.Header)` 205 | `eth_getUncleCountByBlockHash` | `eth.UncleCountByBlockHash(hash common.Hash).Returns(count *uint)` 206 | `eth_getUncleCountByBlockNumber` | `eth.UncleCountByBlockNumber(number *big.Int).Returns(count *uint)` 207 | `eth_syncing` | `eth.Syncing().Returns(syncing *bool)` 208 209 ### [`debug`](https://pkg.go.dev/github.com/lmittmann/w3/module/debug) 210 211 | Method | Go Code 212 | :----------------------- | :------- 213 | `debug_traceCall` | `debug.TraceCall(msg *w3types.Message, blockNumber *big.Int, config *debug.TraceConfig).Returns(trace **debug.Trace)`<br>`debug.CallTraceCall(msg *w3types.Message, blockNumber *big.Int, overrides w3types.State).Returns(trace **debug.CallTrace)` 214 | `debug_traceTransaction` | `debug.TraceTx(txHash common.Hash, config *debug.TraceConfig).Returns(trace **debug.Trace)`<br>`debug.CallTraceTx(txHash common.Hash, overrides w3types.State).Returns(trace **debug.CallTrace)` 215 216 ### [`txpool`](https://pkg.go.dev/github.com/lmittmann/w3/module/txpool) 217 218 | Method | Go Code 219 | :--------------------| :------- 220 | `txpool_content` | `txpool.Content().Returns(resp **txpool.ContentResponse)` 221 | `txpool_contentFrom` | `txpool.ContentFrom(addr common.Address).Returns(resp **txpool.ContentFromResponse)` 222 | `txpool_status` | `txpool.Status().Returns(resp **txpool.StatusResponse)` 223 224 ### [`admin`](https://pkg.go.dev/github.com/lmittmann/w3/module/admin) 225 226 | Method | Go Code 227 | :------------------------ | :------- 228 | `admin_addPeer` | `admin.AddPeer(url *enode.Node).Returns(resp *bool)` 229 | `admin_removePeer` | `admin.RemovePeer(url *enode.Node).Returns(resp *bool)` 230 | `admin_addTrustedPeer` | `admin.AddTrustedPeer(url *enode.Node).Returns(resp *bool)` 231 | `admin_removeTrustedPeer` | `admin.RemoveTrustedPeer(url *enode.Node).Returns(resp *bool)` 232 | `admin_nodeInfo` | `admin.NodeInfo().Returns(resp **admin.NodeInfoResponse)` 233 234 ### [`net`](https://pkg.go.dev/github.com/lmittmann/w3/module/net) 235 236 | Method | Go Code 237 | :------------------- | :------- 238 | `net_listening` | `net.Listening().Returns(resp *bool)` 239 | `net_peerCount` | `net.PeerCount().Returns(resp *int)` 240 | `net_version` | `net.Version().Returns(resp *int)` 241 242 ### [`web3`](https://pkg.go.dev/github.com/lmittmann/w3/module/web3) 243 244 | Method | Go Code 245 | :------------------- | :------- 246 | `web3_clientVersion` | `web3.ClientVersion().Returns(clientVersion *string)` 247 248 ### Third Party RPC Method Packages 249 250 | Package | Description 251 | :----------------------------------------------------------------------- | :----------- 252 | [github.com/lmittmann/flashbots](https://github.com/lmittmann/flashbots) | Package `flashbots` implements RPC API bindings for the Flashbots relay and mev-geth. 253 254 255 ## Custom RPC Method Bindings 256 257 Custom RPC method bindings can be created by implementing the [`w3types.RPCCaller`](https://pkg.go.dev/github.com/lmittmann/w3/w3types#RPCCaller) interface. 258 259 **Example:** RPC binding for the Otterscan `ots_getTransactionBySenderAndNonce` method ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3types#example-RPCCaller-GetTransactionBySenderAndNonce)) 260 261 ```go 262 // TxBySenderAndNonceFactory requests the senders transaction hash by the nonce. 263 func TxBySenderAndNonceFactory(sender common.Address, nonce uint64) w3types.RPCCallerFactory[common.Hash] { 264 return &getTransactionBySenderAndNonceFactory{ 265 sender: sender, 266 nonce: nonce, 267 } 268 } 269 270 // getTransactionBySenderAndNonceFactory implements the w3types.RPCCaller and 271 // w3types.RPCCallerFactory interfaces. It stores the method parameters and 272 // the reference to the return value. 273 type getTransactionBySenderAndNonceFactory struct { 274 // params 275 sender common.Address 276 nonce uint64 277 278 // returns 279 returns *common.Hash 280 } 281 282 // Returns sets the reference to the return value. 283 func (f *getTransactionBySenderAndNonceFactory) Returns(txHash *common.Hash) w3types.RPCCaller { 284 f.returns = txHash 285 return f 286 } 287 288 // CreateRequest creates a batch request element for the Otterscan getTransactionBySenderAndNonce method. 289 func (f *getTransactionBySenderAndNonceFactory) CreateRequest() (rpc.BatchElem, error) { 290 return rpc.BatchElem{ 291 Method: "ots_getTransactionBySenderAndNonce", 292 Args: []any{f.sender, f.nonce}, 293 Result: f.returns, 294 }, nil 295 } 296 297 // HandleResponse handles the response of the Otterscan getTransactionBySenderAndNonce method. 298 func (f *getTransactionBySenderAndNonceFactory) HandleResponse(elem rpc.BatchElem) error { 299 if err := elem.Error; err != nil { 300 return err 301 } 302 return nil 303 } 304 ```