github.com/lmittmann/w3@v0.20.0/README.md (about)

     1  # `w3`: Enhanced Ethereum Integration for Go
     2  
     3  [![Go Reference](https://pkg.go.dev/badge/github.com/lmittmann/w3.svg)](https://pkg.go.dev/github.com/lmittmann/w3)
     4  [![Go Report Card](https://goreportcard.com/badge/github.com/lmittmann/w3)](https://goreportcard.com/report/github.com/lmittmann/w3)
     5  [![Coverage Status](https://coveralls.io/repos/github/lmittmann/w3/badge.svg?branch=main)](https://coveralls.io/github/lmittmann/w3?branch=main)
     6  [![Latest Release](https://img.shields.io/github/v/release/lmittmann/w3)](https://github.com/lmittmann/w3/releases)
     7  [![Chat on Telegram](https://img.shields.io/badge/Telegram-blue?logo=telegram&logoColor=white)](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&nbsp;more&nbsp;↗](#rpc-client)
    21  * Use `w3vm.VM` to simulate EVM execution with optional tracing and Mainnet state forking, or test Smart Contracts. [learn&nbsp;more&nbsp;↗](#vm)
    22  * Use `w3.Func` and `w3.Event` to create ABI bindings from Solidity function and event signatures. [learn&nbsp;more&nbsp;↗](#abi-bindings)
    23  * Use `w3.A`, `w3.H`, and many other utility functions to parse addresses, hashes, and other common types from strings. [learn&nbsp;more&nbsp;↗](#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  ```