w3

package module
v0.19.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2024 License: MIT Imports: 17 Imported by: 2

README

w3-celo

Go Reference Go Report Card Coverage Status Latest Release

W3 Gopher

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

w3-celo

  • 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-celo is closely linked to celo-blockchain and uses a variety of its types, such as common.Address or types.Transaction.

Batch requests with w3-celo are up to 85x faster than sequential requests with celo-blockchain/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)

About

Note Check out the original w3!

w3-celo is a fork of the original library replacing all incompatible Ethereum types with Celo types.

w3VM changes will not be included in this library as it closely follows Ethereum Geth.

Versioning closely follows the original.

Install

go get github.com/grassrootseconomics/w3-celo

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/celo")
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(weth9, funcBalanceOf, addr).Returns(&weth9Balance),
	eth.CallFunc(dai, funcBalanceOf, 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 celo-blockchain/types.
signer := types.LatestSigner(params.MainnetChainConfig)
tx := types.MustSignNewTx(privKey, signer, &types.CeloDynamicFeeTx{
	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 celo-blockchain/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(contract common.Address, f w3types.Func, 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_maxPriorityFeePerGas eth.GasTipCap().Returns(gasTipCap *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 celo.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)
Packages using this library
Package Description
github.com/grassrootseconomics/celoutils High level Celo utilities specific to Grassroots Economics.

Documentation

Overview

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

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidABI       = errors.New("w3: invalid ABI")
	ErrArgumentMismatch = errors.New("w3: argument mismatch")
	ErrReturnsMismatch  = errors.New("w3: returns mismatch")
	ErrInvalidType      = errors.New("w3: invalid type")
	ErrEvmRevert        = errors.New("w3: evm reverted")
)
View Source
var (
	Big0          = new(big.Int)
	Big1          = big.NewInt(1)
	Big2          = big.NewInt(2)
	BigGwei       = big.NewInt(1_000000000)
	BigEther      = big.NewInt(1_000000000_000000000)
	BigMaxUint256 = new(big.Int).Sub(new(big.Int).Lsh(Big1, 256), Big1)
)

Common big.Int's.

Functions

func A

func A(hexAddress string) (addr common.Address)

A returns an address from a hexstring or panics if the hexstring does not represent a valid address.

Use common.HexToAddress to get the address from a hexstring without panicking.

func APtr

func APtr(hexAddress string) *common.Address

APtr returns an address pointer from a hexstring or panics if the hexstring does not represent a valid address.

func B

func B(hexBytes ...string) (bytes []byte)

B returns a byte slice from a hexstring or panics if the hexstring does not represent a valid byte slice.

Use common.FromHex to get the byte slice from a hexstring without panicking.

func FromWei

func FromWei(wei *big.Int, decimals uint8) string

FromWei returns the given Wei as decimal with the given number of decimals.

Example
wei := big.NewInt(1_230000000_000000000)
fmt.Printf("%s Ether\n", w3.FromWei(wei, 18))
Output:

1.23 Ether

func H

func H(hexHash string) (hash common.Hash)

H returns a hash from a hexstring or panics if the hexstring does not represent a valid hash.

Use common.HexToHash to get the hash from a hexstring without panicking.

func I

func I(strInt string) *big.Int

I returns a big.Int from a hexstring or decimal number string (with optional unit) or panics if the parsing fails.

I supports the units "ether" or "eth" and "gwei" for decimal number strings. E.g.:

w3.I("1 ether")   -> 1000000000000000000
w3.I("10 gwei")   -> 10000000000

Fractional digits that exceed the units maximum number of fractional digits are ignored. E.g.:

w3.I("0.000000123456 gwei") -> 123
Example
fmt.Printf("%v wei\n", w3.I("0x2b98d99b09e3c000"))
fmt.Printf("%v wei\n", w3.I("3141500000000000000"))
fmt.Printf("%v wei\n", w3.I("3.1415 ether"))
fmt.Printf("%v wei\n", w3.I("31.415 gwei"))
Output:

3141500000000000000 wei
3141500000000000000 wei
3141500000000000000 wei
31415000000 wei

func RandA

func RandA() (addr common.Address)

RandA returns a random address.

Types

type CallErrors

type CallErrors []error

CallErrors is an error type that contains the errors of multiple calls. The length of the error slice is equal to the number of calls. Each error at a given index corresponds to the call at the same index. An error is nil if the corresponding call was successful.

func (CallErrors) Error

func (e CallErrors) Error() string

func (CallErrors) Is

func (e CallErrors) Is(target error) bool

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client represents a connection to an RPC endpoint.

func Dial

func Dial(rawurl string, opts ...Option) (*Client, error)

Dial returns a new Client connected to the URL rawurl. An error is returned if the connection establishment fails.

The supported URL schemes are "http", "https", "ws" and "wss". If rawurl is a file name with no URL scheme, a local IPC socket connection is established.

Example
client, err := w3.Dial("https://rpc.ankr.com/eth")
if err != nil {
	// ...
}
defer client.Close()
Output:

func MustDial

func MustDial(rawurl string, opts ...Option) *Client

MustDial is like Dial but panics if the connection establishment fails.

Example
client := w3.MustDial("https://rpc.ankr.com/eth")
defer client.Close()
Output:

func NewClient

func NewClient(client *rpc.Client, opts ...Option) *Client

NewClient returns a new Client given an rpc.Client client.

func (*Client) Call

func (c *Client) Call(calls ...w3types.RPCCaller) error

Call is like Client.CallCtx with ctx equal to context.Background().

Example
// Connect to RPC endpoint (or panic on error) and
// close the connection when you are done.
client := w3.MustDial("https://rpc.ankr.com/eth")
defer client.Close()

var (
	addr  = w3.A("0x000000000000000000000000000000000000dEaD")
	weth9 = w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")

	// Declare a Smart Contract function using Solidity syntax,
	// no "abigen" and ABI JSON file needed.
	balanceOf = w3.MustNewFunc("balanceOf(address)", "uint256")

	// Declare variables for the RPC responses.
	ethBalance   big.Int
	weth9Balance big.Int
)

// Do batch request (both RPC requests are send in the same
// HTTP request).
if err := client.Call(
	eth.Balance(addr, nil).Returns(&ethBalance),
	eth.CallFunc(weth9, balanceOf, addr).Returns(&weth9Balance),
); err != nil {
	fmt.Printf("Request failed: %v\n", err)
	return
}

fmt.Printf("Combined balance: %v wei",
	new(big.Int).Add(&ethBalance, &weth9Balance),
)
Output:

Example (NonceAndBalance)
client := w3.MustDial("https://rpc.ankr.com/eth")
defer client.Close()

var (
	addr = w3.A("0x000000000000000000000000000000000000c0Fe")

	nonce   uint64
	balance big.Int
)

if err := client.Call(
	eth.Nonce(addr, nil).Returns(&nonce),
	eth.Balance(addr, nil).Returns(&balance),
); err != nil {
	fmt.Printf("Request failed: %v\n", err)
	return
}

fmt.Printf("%s: Nonce: %d, Balance: ♦%s\n", addr, nonce, w3.FromWei(&balance, 18))
Output:

func (*Client) CallCtx

func (c *Client) CallCtx(ctx context.Context, calls ...w3types.RPCCaller) error

CallCtx creates the final RPC request, sends it, and handles the RPC response.

An error is returned if RPC request creation, networking, or RPC response handling fails.

func (*Client) Close

func (c *Client) Close() error

Close closes the RPC connection and cancels any in-flight requests.

Close implements the io.Closer interface.

func (*Client) Subscribe added in v0.18.0

Subscribe is like Client.SubscribeCtx with ctx equal to context.Background().

func (*Client) SubscribeCtx added in v0.18.0

func (c *Client) SubscribeCtx(ctx context.Context, s w3types.RPCSubscriber) (*rpc.ClientSubscription, error)

SubscribeCtx creates a new subscription and returns a rpc.ClientSubscription.

type Event

type Event struct {
	Signature string        // Event signature
	Topic0    common.Hash   // Hash of event signature (Topic 0)
	Args      abi.Arguments // Arguments
	// contains filtered or unexported fields
}

Event represents a Smart Contract event decoder.

func MustNewEvent

func MustNewEvent(signature string) *Event

MustNewEvent is like NewEvent but panics if the signature parsing fails.

func NewEvent

func NewEvent(signature string) (*Event, error)

NewEvent returns a new Smart Contract event log decoder from the given Solidity event signature.

An error is returned if the signature parsing fails.

func (*Event) DecodeArgs

func (e *Event) DecodeArgs(log *types.Log, args ...any) error

DecodeArgs decodes the topics and data of the given log to the given args.

Example
var (
	eventTransfer = w3.MustNewEvent("Transfer(address indexed from, address indexed to, uint256 value)")
	log           = &types.Log{
		Address: w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
		Topics: []common.Hash{
			w3.H("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
			w3.H("0x000000000000000000000000000000000000000000000000000000000000c0fe"),
			w3.H("0x000000000000000000000000000000000000000000000000000000000000dead"),
		},
		Data: w3.B("0x0000000000000000000000000000000000000000000000001111d67bb1bb0000"),
	}

	from  common.Address
	to    common.Address
	value big.Int
)

if err := eventTransfer.DecodeArgs(log, &from, &to, &value); err != nil {
	fmt.Printf("Failed to decode event log: %v\n", err)
	return
}
fmt.Printf("Transferred %s WETH9 from %s to %s", w3.FromWei(&value, 18), from, to)
Output:

Transferred 1.23 WETH9 from 0x000000000000000000000000000000000000c0Fe to 0x000000000000000000000000000000000000dEaD

type Func

type Func struct {
	Signature string        // Function signature
	Selector  [4]byte       // 4-byte selector
	Args      abi.Arguments // Arguments (input)
	Returns   abi.Arguments // Returns (output)
	// contains filtered or unexported fields
}

Func represents a Smart Contract function ABI binding.

Func implements the w3types.Func interface.

func MustNewFunc

func MustNewFunc(signature, returns string) *Func

MustNewFunc is like NewFunc but panics if the signature or returns parsing fails.

func NewFunc

func NewFunc(signature, returns string) (*Func, error)

NewFunc returns a new Smart Contract function ABI binding from the given Solidity function signature and its returns.

An error is returned if the signature or returns parsing fails.

Example
// ABI binding to the balanceOf function of an ERC20 Token.
funcBalanceOf, _ := w3.NewFunc("balanceOf(address)", "uint256")

// Optionally names can be specified for function arguments. This is
// especially useful for more complex functions with many arguments.
funcBalanceOf, _ = w3.NewFunc("balanceOf(address who)", "uint256 amount")

// ABI-encode the functions args.
input, _ := funcBalanceOf.EncodeArgs(w3.A("0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"))
fmt.Printf("balanceOf input: 0x%x\n", input)

// ABI-decode the functions args from a given input.
var (
	who common.Address
)
funcBalanceOf.DecodeArgs(input, &who)
fmt.Printf("balanceOf args: %v\n", who)

// ABI-decode the functions output.
var (
	output = w3.B("0x000000000000000000000000000000000000000000000000000000000000c0fe")
	amount = new(big.Int)
)
funcBalanceOf.DecodeReturns(output, amount)
fmt.Printf("balanceOf returns: %v\n", amount)
Output:

balanceOf input: 0x70a08231000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b
balanceOf args: 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B
balanceOf returns: 49406

func (*Func) DecodeArgs

func (f *Func) DecodeArgs(input []byte, args ...any) error

DecodeArgs ABI-decodes the given input to the given args.

func (*Func) DecodeReturns

func (f *Func) DecodeReturns(output []byte, returns ...any) error

DecodeReturns ABI-decodes the given output to the given returns.

func (*Func) EncodeArgs

func (f *Func) EncodeArgs(args ...any) ([]byte, error)

EncodeArgs ABI-encodes the given args and prepends the Func's 4-byte selector.

type Option

type Option func(*Client)

An Option configures a Client.

func WithRateLimiter

func WithRateLimiter(rl *rate.Limiter, costFunc func(methods []string) (cost int)) Option

WithRateLimiter sets the rate limiter for the client. Set the optional argument costFunc to nil to limit the number of requests. Supply a costFunc to limit the the number of requests based on individual RPC calls for advanced rate limiting by e.g. Compute Units (CUs). Note that only if len(methods) > 1, the calls are sent in a batch request.

Example
// Limit the client to 30 requests per second and allow bursts of up to
// 100 requests.
client := w3.MustDial("https://rpc.ankr.com/eth",
	w3.WithRateLimiter(rate.NewLimiter(rate.Every(time.Second/30), 100), nil),
)
defer client.Close()
Output:

Example (CostFunc)
// Limit the client to 30 calls per second and allow bursts of up to
// 100 calls using a cost function. Batch requests have an additional charge.
client := w3.MustDial("https://rpc.ankr.com/eth",
	w3.WithRateLimiter(rate.NewLimiter(rate.Every(time.Second/30), 100),
		func(methods []string) (cost int) {
			cost = len(methods) // charge 1 CU per call
			if len(methods) > 1 {
				cost += 1 // charge 1 CU extra for the batch itself
			}
			return cost
		},
	))
defer client.Close()
Output:

Directories

Path Synopsis
abi
Package abi implements a Solidity ABI lexer and parser.
Package abi implements a Solidity ABI lexer and parser.
module
debug
Package debug implements RPC API bindings for methods in the "debug" namespace.
Package debug implements RPC API bindings for methods in the "debug" namespace.
eth
Package eth implements RPC API bindings for methods in the "eth" namespace.
Package eth implements RPC API bindings for methods in the "eth" namespace.
txpool
Package txpool implements RPC API bindings for methods in the "txpool" namespace.
Package txpool implements RPC API bindings for methods in the "txpool" namespace.
web3
Package web3 implements RPC API bindings for methods in the "web3" namespace.
Package web3 implements RPC API bindings for methods in the "web3" namespace.
Package rpctest provides utilities for testing RPC methods.
Package rpctest provides utilities for testing RPC methods.
Package w3types implements common types.
Package w3types implements common types.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL