• Stars
    star
    174
  • Rank 218,464 (Top 5 %)
  • Language
    Go
  • License
    MIT License
  • Created almost 3 years ago
  • Updated about 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Enhanced Ethereum Integration for Go

w3

Go Reference Go Report Card Coverage Status Latest 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 and uses a variety of its types, such as common.Address or types.Transaction.

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 or MustDial.

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

Batch Requests

Batch request support in the Client 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 can be used to

Reading Contracts

Func's can be used with eth.CallFunc 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.
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/types.
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 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 to parse strings that may not be valid instead.

RPC Methods

List of supported RPC methods.

eth

Method Go Code
eth_blockNumber eth.BlockNumber().Returns(blockNumber *big.Int)
eth_call eth.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_chainId eth.ChainID().Returns(chainID *uint64)
eth_createAccessList eth.AccessList(msg *w3types.Message, blockNumber *big.Int).Returns(resp *eth.AccessListResponse)
eth_estimateGas eth.EstimateGas(msg *w3types.Message, blockNumber *big.Int).Returns(gas *uint64)
eth_gasPrice eth.GasPrice().Returns(gasPrice *big.Int)
eth_getBalance eth.Balance(addr common.Address, blockNumber *big.Int).Returns(balance *big.Int)
eth_getBlockByHash eth.BlockByHash(hash common.Hash).Returns(block *types.Block)
eth.HeaderByHash(hash common.Hash).Returns(header *types.Header)
eth_getBlockByNumber eth.BlockByNumber(number *big.Int).Returns(block *types.Block)
eth.HeaderByNumber(number *big.Int).Returns(header *types.Header)
eth_getBlockTransactionCountByHash eth.BlockTxCountByHash(hash common.Hash).Returns(count *uint)
eth_getBlockTransactionCountByNumber eth.BlockTxCountByNumber(number *big.Int).Returns(count *uint)
eth_getCode eth.Code(addr common.Address, blockNumber *big.Int).Returns(code *[]byte)
eth_getLogs eth.Logs(q ethereum.FilterQuery).Returns(logs *[]types.Log)
eth_getStorageAt eth.StorageAt(addr common.Address, slot common.Hash, blockNumber *big.Int).Returns(storage *common.Hash)
eth_getTransactionByHash eth.Tx(hash common.Hash).Returns(tx *types.Transaction)
eth_getTransactionByBlockHashAndIndex eth.TxByBlockHashAndIndex(blockHash common.Hash, index uint).Returns(tx *types.Transaction)
eth_getTransactionByBlockNumberAndIndex eth.TxByBlockNumberAndIndex(blockNumber *big.Int, index uint).Returns(tx *types.Transaction)
eth_getTransactionCount eth.Nonce(addr common.Address, blockNumber *big.Int).Returns(nonce *uint)
eth_getTransactionReceipt eth.TxReceipt(txHash common.Hash).Returns(receipt *types.Receipt)
eth_sendRawTransaction eth.SendRawTx(rawTx []byte).Returns(hash *common.Hash)
eth.SendTx(tx *types.Transaction).Returns(hash *common.Hash)
eth_getUncleByBlockHashAndIndex eth.UncleByBlockHashAndIndex(hash common.Hash, index uint).Returns(uncle *types.Header)
eth_getUncleByBlockNumberAndIndex eth.UncleByBlockNumberAndIndex(number *big.Int, index uint).Returns(uncle *types.Header)
eth_getUncleCountByBlockHash eth.UncleCountByBlockHash(hash common.Hash).Returns(count *uint)
eth_getUncleCountByBlockNumber eth.UncleCountByBlockNumber(number *big.Int).Returns(count *uint)

debug

Method Go Code
debug_traceCall debug.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_traceTransaction debug.TraceTx(txHash common.Hash, config *debug.TraceConfig).Returns(trace *debug.Trace)
debug.CallTraceTx(txHash common.Hash, overrides w3types.State).Returns(trace *debug.CallTrace)

txpool

Method Go Code
txpool_content txpool.Content().Returns(resp *txpool.ContentResponse)
txpool_contentFrom txpool.ContentFrom(addr common.Address).Returns(resp *txpool.ContentFromResponse)
txpool_status txpool.Status().Returns(resp *txpool.StatusResponse)

web3

Method Go Code
web3_clientVersion web3.ClientVersion().Returns(clientVersion *string)

Third Party RPC Method Packages

Package Description
github.com/lmittmann/flashbots Package flashbots implements RPC API bindings for the Flashbots relay and mev-geth.