Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/go_modules/github.com/ethereum/go…
Browse files Browse the repository at this point in the history
…-ethereum-1.14.9
  • Loading branch information
lmittmann committed Dec 10, 2024
2 parents c4058c1 + 3282e5a commit cad510e
Show file tree
Hide file tree
Showing 45 changed files with 3,049 additions and 1,171 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ jobs:
- name: staticcheck
run: staticcheck ./...
- name: go mod tidy
run: |
go mod tidy -diff
cd examples/ && go mod tidy -diff
run: go mod tidy -diff

test:
name: Test
Expand Down
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ go get github.com/lmittmann/w3

[`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.

**Example:** Batch Request ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client))
**Example:** Batch Request ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchEOAState))

```go
// 1. Connect to an RPC endpoint
Expand Down Expand Up @@ -57,12 +57,12 @@ if err := client.Call(
#### Error Handling

If one ore 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).
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).

**Example:** Check which RPC calls failed in a batch request ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-CallErrors))
**Example:** Check which RPC calls failed in a batch request ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchHandleError))
```go
var errs w3.CallErrors
if err := client.Call(rpcCalls...); errors.As(err, &errs) {
var batchErr w3.CallErrors
if err := client.Call(calls...); errors.As(err, &batchErr) {
// handle call errors
} else if err != nil {
// handle other errors
Expand All @@ -77,7 +77,7 @@ if err := client.Call(rpcCalls...); errors.As(err, &errs) {

[`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.

**Example:** Simulate an Uniswap v3 swap ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM))
**Example:** Simulate an Uniswap v3 swap ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-UniswapV3Swap))

```go
// 1. Create a VM that forks the Mainnet state from the latest block,
Expand Down Expand Up @@ -144,7 +144,7 @@ A [`Func`](https://pkg.go.dev/github.com/lmittmann/w3#Func) can be used to

* encode arguments to the contracts input data ([`Func.EncodeArgs`](https://pkg.go.dev/github.com/lmittmann/w3#Func.EncodeArgs)),
* decode arguments from the contracts input data ([`Func.DecodeArgs`](https://pkg.go.dev/github.com/lmittmann/w3#Func.DecodeArgs)), and
* decode returns form the contracts output data ([`Func.DecodeReturns`](https://pkg.go.dev/github.com/lmittmann/w3#Func.DecodeReturns)).
* decode returns from the contracts output data ([`Func.DecodeReturns`](https://pkg.go.dev/github.com/lmittmann/w3#Func.DecodeReturns)).

### Utils

Expand Down Expand Up @@ -240,7 +240,7 @@ func TxBySenderAndNonceFactory(sender common.Address, nonce uint64) w3types.RPCC

// getTransactionBySenderAndNonceFactory implements the w3types.RPCCaller and
// w3types.RPCCallerFactory interfaces. It stores the method parameters and
// the the reference to the return value.
// the reference to the return value.
type getTransactionBySenderAndNonceFactory struct {
// params
sender common.Address
Expand Down Expand Up @@ -273,3 +273,11 @@ func (f *getTransactionBySenderAndNonceFactory) HandleResponse(elem rpc.BatchEle
return nil
}
```

## Sponsors

<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/public/assets/ef-logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="docs/public/assets/ef-logo.svg">
<img src="docs/public/assets/ef-logo.svg" alt="ef logo" width="256" height="auto">
</picture>
213 changes: 0 additions & 213 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,21 @@ import (
"context"
"errors"
"flag"
"fmt"
"math/big"
"strconv"
"testing"
"time"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/google/go-cmp/cmp"
"github.com/lmittmann/w3"
"github.com/lmittmann/w3/internal"
"github.com/lmittmann/w3/module/eth"
"github.com/lmittmann/w3/rpctest"
"github.com/lmittmann/w3/w3types"
"golang.org/x/time/rate"
)

var (
Expand All @@ -36,189 +31,6 @@ var (
`< [{"jsonrpc":"2.0","id":1,"result":"0x1"},{"jsonrpc":"2.0","id":2,"result":"0x1"}]`
)

func ExampleClient() {
addr := w3.A("0x0000000000000000000000000000000000000000")

// 1. Connect to an RPC endpoint
client, err := w3.Dial("https://rpc.ankr.com/eth")
if err != nil {
// handle error
}
defer client.Close()

// 2. Make a batch request
var (
balance *big.Int
nonce uint64
)
if err := client.Call(
eth.Balance(addr, nil).Returns(&balance),
eth.Nonce(addr, nil).Returns(&nonce),
); err != nil {
// handle error
}

fmt.Printf("balance: %s\nnonce: %d\n", w3.FromWei(balance, 18), nonce)
}

func ExampleClient_Call_balanceOf() {
// 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),
)
}

func ExampleClient_Call_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))
}

func ExampleClient_Call_sendERC20transferTx() {
client := w3.MustDial("https://rpc.ankr.com/eth")
defer client.Close()

var (
weth9 = w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")
receiver = w3.A("0x000000000000000000000000000000000000c0Fe")
eoaPrv, _ = crypto.GenerateKey()
)

funcTransfer := w3.MustNewFunc("transfer(address receiver, uint256 amount)", "bool")
input, err := funcTransfer.EncodeArgs(receiver, w3.I("1 ether"))
if err != nil {
fmt.Printf("Failed to encode args: %v\n", err)
return
}

signer := types.LatestSigner(params.MainnetChainConfig)
var txHash common.Hash
if err := client.Call(
eth.SendTx(types.MustSignNewTx(eoaPrv, signer, &types.DynamicFeeTx{
Nonce: 0,
To: &weth9,
Data: input,
GasTipCap: w3.I("1 gwei"),
GasFeeCap: w3.I("100 gwei"),
Gas: 100_000,
})).Returns(&txHash),
); err != nil {
fmt.Printf("Failed to send tx: %v\n", err)
return
}

fmt.Printf("Sent tx: %s\n", txHash)
}

func ExampleCallErrors() {
client := w3.MustDial("https://rpc.ankr.com/eth")
defer client.Close()

funcSymbol := w3.MustNewFunc("symbol()", "string")

// list of addresses that might be an ERC20 token
potentialTokens := []common.Address{
w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
w3.A("0x00000000219ab540356cBB839Cbe05303d7705Fa"),
}

// build symbol()-call for each potential ERC20 token
tokenSymbols := make([]string, len(potentialTokens))
rpcCalls := make([]w3types.RPCCaller, len(potentialTokens))
for i, addr := range potentialTokens {
rpcCalls[i] = eth.CallFunc(addr, funcSymbol).Returns(&tokenSymbols[i])
}

// execute batch request
var errs w3.CallErrors
if err := client.Call(rpcCalls...); errors.As(err, &errs) {
// handle call errors
} else if err != nil {
// handle other errors
fmt.Printf("Request failed: %v\n", err)
return
}

for i, addr := range potentialTokens {
var symbol string
if errs == nil || errs[i] == nil {
symbol = tokenSymbols[i]
} else {
symbol = fmt.Sprintf("unknown symbol: %v", errs[i].Error())
}
fmt.Printf("%s: %s\n", addr, symbol)
}

// Output:
// 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2: WETH
// 0x00000000219ab540356cBB839Cbe05303d7705Fa: unknown symbol: execution reverted
}

func ExampleClient_Subscribe() {
client := w3.MustDial("wss://mainnet.gateway.tenderly.co")
defer client.Close()

txCh := make(chan *types.Transaction)
sub, err := client.Subscribe(eth.PendingTransactions(txCh))
if err != nil {
fmt.Printf("Failed to subscribe: %v\n", err)
return
}

for {
select {
case tx := <-txCh:
fmt.Printf("New pending tx: %s\n", tx.Hash())
case err := <-sub.Err():
fmt.Printf("Subscription error: %v\n", err)
return
}
}
}

func TestClientCall(t *testing.T) {
tests := []struct {
Buf *bytes.Buffer
Expand Down Expand Up @@ -480,28 +292,3 @@ func BenchmarkCall_Block100(b *testing.B) {
}
})
}

func ExampleWithRateLimiter() {
// 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()
}

func ExampleWithRateLimiter_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()
}
14 changes: 10 additions & 4 deletions docs/components/DocLink.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ const pkgNameToPath = {
'web3': 'github.com/lmittmann/w3/module/web3',
'w3types': 'github.com/lmittmann/w3/w3types',
'w3vm': 'github.com/lmittmann/w3/w3vm',
'hooks': 'github.com/lmittmann/w3/w3vm/hooks',
'tracing': 'github.com/ethereum/go-ethereum/core/tracing',
'logger': 'github.com/ethereum/go-ethereum/eth/tracers/logger',
}

export const DocLink = ({ title }) => {
let [pkg, comp] = title.split('.', 2)
let url = `https://pkg.go.dev/${pkgNameToPath[pkg]}#${comp}`
export const DocLink = ({ title, id }) => {
if (typeof id === 'undefined') { id = title }
let dotIndex = id.indexOf('.');
let pkg = id.substring(0, dotIndex);
let comp = id.substring(dotIndex + 1);

return (
<Link href={url}>
<Link href={`https://pkg.go.dev/${pkgNameToPath[pkg]}#${comp}`}>
<Code>{title}</Code>
</Link>
)
Expand Down
Loading

0 comments on commit cad510e

Please sign in to comment.