🚧 This site is currently under construction 🚧

w3

Go ReferenceGo Report CardCoverage StatusLatest Release
W3 Gopher

Package w3 implements a blazing fast and modular Ethereum JSON RPC client with first-class ABI support.

  • Batch request support significantly reduces the duration of requests to both remote and local endpoints.
  • ABI bindings are specified for individual functions using Solidity syntax. No need for abigen and ABI JSON files.
  • Modular API allows to create custom RPC method integrations that can be used alongside the methods implemented by the package.

w3 is closely linked to go-ethereum (opens in a new tab) and uses a variety of its types, such as common.Address (opens in a new tab) or types.Transaction (opens in a new tab).

Batch requests with w3 are up to 85x faster than sequential requests with go-ethereum/ethclient.

Benchmarks

name ethclient time/op w3 time/op delta Call_BalanceNonce 78.3ms ± 2% 39.0ms ± 1% -50.15% (p=0.000 n=23+22) Call_Balance100 3.90s ± 5% 0.05s ± 2% -98.84% (p=0.000 n=20+24) Call_BalanceOf100 3.99s ± 3% 0.05s ± 2% -98.73% (p=0.000 n=22+23) Call_Block100 6.89s ± 7% 1.94s ±11% -71.77% (p=0.000 n=24+23)

Install

go get github.com/lmittmann/w3

Getting Started

Note Check out the examples!

Connect to an RPC endpoint via HTTP, WebSocket, or IPC using Dial (opens in a new tab) or MustDial (opens in a new tab).

// Connect (or panic on error)
client := w3.MustDial("https://rpc.ankr.com/eth")
defer client.Close()

Batch Requests

Batch request support in the Client (opens in a new tab) allows to send multiple RPC requests in a single HTTP request. The speed gains to remote endpoints are huge. Fetching 100 blocks in a single batch request with w3 is ~80x faster compared to sequential requests with ethclient.

Example: Request the nonce and balance of an address in a single request

var (
	addr = w3.A("0x000000000000000000000000000000000000c0Fe")
 
	nonce   uint64
	balance big.Int
)
err := client.Call(
	eth.Nonce(addr, nil).Returns(&nonce),
	eth.Balance(addr, nil).Returns(&balance),
)

ABI Bindings

ABI bindings in w3 are specified for individual functions using Solidity syntax and are usable for any contract that supports that function.

Example: ABI binding for the ERC20-function balanceOf

funcBalanceOf := w3.MustNewFunc("balanceOf(address)", "uint256")

A Func (opens in a new tab) can be used to

Reading Contracts

Func (opens in a new tab)'s can be used with eth.CallFunc (opens in a new tab) in the client to read contract data.

var (
	weth9 = w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
	dai   = w3.A("0x6B175474E89094C44Da98b954EedeAC495271d0F")
 
	weth9Balance big.Int
	daiBalance   big.Int
)
 
err := client.Call(
	eth.CallFunc(funcBalanceOf, weth9, addr).Returns(&weth9Balance),
	eth.CallFunc(funcBalanceOf, dai, addr).Returns(&daiBalance),
)

Writing Contracts

Sending a transaction to a contract requires three steps.

  1. Encode the transaction input data using Func.EncodeArgs (opens in a new tab).
var funcTransfer = w3.MustNewFunc("transfer(address,uint256)", "bool")
 
input, err := funcTransfer.EncodeArgs(w3.A("0x…"), w3.I("1 ether"))
  1. Create a signed transaction to the contract using go-ethereum/core/types (opens in a new tab).
signer := types.LatestSigner(params.MainnetChainConfig)
tx := types.MustSignNewTx(privKey, signer, &types.DynamicFeeTx{
	To:        w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
	Nonce:     0,
	Data:      input,
	Gas:       75000,
	GasFeeCap: w3.I("100 gwei"),
	GasTipCap: w3.I("1 gwei"),
})
  1. Send the signed transaction.
var txHash common.Hash
err := client.Call(
	eth.SendTx(tx).Returns(&txHash),
)

Custom RPC Methods

Custom RPC methods can be called with the w3 client by creating a core.Caller (opens in a new tab) implementation. The w3/module/eth package can be used as implementation reference.

Utils

Static addresses, hashes, hex byte slices or big.Int's can be parsed from strings with the following utility functions.

var (
	addr  = w3.A("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045")
	hash  = w3.H("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
	bytes = w3.B("0x27c5342c")
	big   = w3.I("12.34 ether")
)

Note that these functions panic if the string cannot be parsed. Use go-ethereum/common (opens in a new tab) to parse strings that may not be valid instead.

RPC Methods

List of supported RPC methods.

eth (opens in a new tab)

MethodGo Code
eth_blockNumbereth.BlockNumber().Returns(blockNumber *big.Int)
eth_calleth.Call(msg *w3types.Message, blockNumber *big.Int, overrides w3types.State).Returns(output *[]byte)
eth.CallFunc(fn core.Func, contract common.Address, args ...any).Returns(returns ...any)
eth_chainIdeth.ChainID().Returns(chainID *uint64)
eth_createAccessListeth.AccessList(msg *w3types.Message, blockNumber *big.Int).Returns(resp *eth.AccessListResponse)
eth_estimateGaseth.EstimateGas(msg *w3types.Message, blockNumber *big.Int).Returns(gas *uint64)
eth_gasPriceeth.GasPrice().Returns(gasPrice *big.Int)
eth_getBalanceeth.Balance(addr common.Address, blockNumber *big.Int).Returns(balance *big.Int)
eth_getBlockByHasheth.BlockByHash(hash common.Hash).Returns(block *types.Block)
eth.HeaderByHash(hash common.Hash).Returns(header *types.Header)
eth_getBlockByNumbereth.BlockByNumber(number *big.Int).Returns(block *types.Block)
eth.HeaderByNumber(number *big.Int).Returns(header *types.Header)
eth_getBlockTransactionCountByHasheth.BlockTxCountByHash(hash common.Hash).Returns(count *uint)
eth_getBlockTransactionCountByNumbereth.BlockTxCountByNumber(number *big.Int).Returns(count *uint)
eth_getCodeeth.Code(addr common.Address, blockNumber *big.Int).Returns(code *[]byte)
eth_getLogseth.Logs(q ethereum.FilterQuery).Returns(logs *[]types.Log)
eth_getStorageAteth.StorageAt(addr common.Address, slot common.Hash, blockNumber *big.Int).Returns(storage *common.Hash)
eth_getTransactionByHasheth.Tx(hash common.Hash).Returns(tx *types.Transaction)
eth_getTransactionByBlockHashAndIndexeth.TxByBlockHashAndIndex(blockHash common.Hash, index uint).Returns(tx *types.Transaction)
eth_getTransactionByBlockNumberAndIndexeth.TxByBlockNumberAndIndex(blockNumber *big.Int, index uint).Returns(tx *types.Transaction)
eth_getTransactionCounteth.Nonce(addr common.Address, blockNumber *big.Int).Returns(nonce *uint)
eth_getTransactionReceipteth.TxReceipt(txHash common.Hash).Returns(receipt *types.Receipt)
eth_sendRawTransactioneth.SendRawTx(rawTx []byte).Returns(hash *common.Hash)
eth.SendTx(tx *types.Transaction).Returns(hash *common.Hash)
eth_getUncleByBlockHashAndIndexeth.UncleByBlockHashAndIndex(hash common.Hash, index uint).Returns(uncle *types.Header)
eth_getUncleByBlockNumberAndIndexeth.UncleByBlockNumberAndIndex(number *big.Int, index uint).Returns(uncle *types.Header)
eth_getUncleCountByBlockHasheth.UncleCountByBlockHash(hash common.Hash).Returns(count *uint)
eth_getUncleCountByBlockNumbereth.UncleCountByBlockNumber(number *big.Int).Returns(count *uint)

debug (opens in a new tab)

MethodGo Code
debug_traceCalldebug.TraceCall(msg *w3types.Message, blockNumber *big.Int, config *debug.TraceConfig).Returns(trace *debug.Trace)
debug.CallTraceCall(msg *w3types.Message, blockNumber *big.Int, overrides w3types.State).Returns(trace *debug.CallTrace)
debug_traceTransactiondebug.TraceTx(txHash common.Hash, config *debug.TraceConfig).Returns(trace *debug.Trace)
debug.CallTraceTx(txHash common.Hash, overrides w3types.State).Returns(trace *debug.CallTrace)

txpool (opens in a new tab)

MethodGo Code
txpool_contenttxpool.Content().Returns(resp *txpool.ContentResponse)
txpool_contentFromtxpool.ContentFrom(addr common.Address).Returns(resp *txpool.ContentFromResponse)
txpool_statustxpool.Status().Returns(resp *txpool.StatusResponse)

web3 (opens in a new tab)

MethodGo Code
web3_clientVersionweb3.ClientVersion().Returns(clientVersion *string)

Third Party RPC Method Packages

PackageDescription
github.com/lmittmann/flashbots (opens in a new tab)Package flashbots implements RPC API bindings for the Flashbots relay and mev-geth.