diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 92398cff..c6eba1fb 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -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 diff --git a/README.md b/README.md index 7daff0d8..2cf85562 100644 --- a/README.md +++ b/README.md @@ -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 @@ -59,10 +59,10 @@ if err := client.Call( 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 @@ -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, @@ -273,3 +273,11 @@ func (f *getTransactionBySenderAndNonceFactory) HandleResponse(elem rpc.BatchEle return nil } ``` + +## Sponsors + + + + + ef logo + diff --git a/client_test.go b/client_test.go index abb0e0d9..94e4729a 100644 --- a/client_test.go +++ b/client_test.go @@ -5,18 +5,14 @@ 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" @@ -24,7 +20,6 @@ import ( "github.com/lmittmann/w3/module/eth" "github.com/lmittmann/w3/rpctest" "github.com/lmittmann/w3/w3types" - "golang.org/x/time/rate" ) var ( @@ -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(ðBalance), - 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 @@ -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() -} diff --git a/docs/pages/_meta.js b/docs/pages/_meta.js index 025426dd..e65285a0 100644 --- a/docs/pages/_meta.js +++ b/docs/pages/_meta.js @@ -7,7 +7,7 @@ export default { '404': { title: '404', display: 'hidden', - 'theme': { + theme: { breadcrumb: false, toc: false, layout: 'full', @@ -69,7 +69,11 @@ export default { examples: { title: 'Examples', type: 'page', - href: 'https://github.com/lmittmann/w3/tree/main/examples', + }, + releases: { + title: 'Releases', + type: 'page', + href: 'https://github.com/lmittmann/w3/releases', newWindow: true }, godoc: { diff --git a/docs/pages/examples.mdx b/docs/pages/examples.mdx new file mode 100644 index 00000000..e69de29b diff --git a/docs/pages/examples/_meta.js b/docs/pages/examples/_meta.js new file mode 100644 index 00000000..d7bd5dc7 --- /dev/null +++ b/docs/pages/examples/_meta.js @@ -0,0 +1,7 @@ +export default { + index: { + title: 'Examples', + display: 'hidden', + theme: { breadcrumb: false, toc: true, pagination: false }, + }, +} diff --git a/docs/pages/examples/index.mdx b/docs/pages/examples/index.mdx new file mode 100644 index 00000000..29fe4852 --- /dev/null +++ b/docs/pages/examples/index.mdx @@ -0,0 +1,34 @@ +# Examples + +## `w3.Client` Examples + +* **Batch Fetch** 1000 blocks ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchBlocks)) +* **Batch Call** the name, symbol, decimals, and balanceOf functions of the Wrapped Ether in a single batch ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchCallFunc)) +* **Batch Call** the Uniswap V3 Quoter for quotes on swapping 100 WETH for DAI in pools of all fee tiers in a single batch ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchCallFuncUniswapQuoter)) +* **Batch Fetch** the nonce and balance of an EOA in a single batch ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchEOAState)) +* **Batch Fetch** a transaction and its receipt in a single batch ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchTxDetails)) +* **Call** the token balance of an address ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-CallFunc)) +* **Call** the token balance of an address, with state override ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-CallFuncWithStateOverride)) +* **Handle errors** of individual calls in a batch ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-BatchHandleError)) +* **Rate Limit** the number of requests to 300 compute units (CUs) per second, with bursts of up to 300 CUs. An individual CU can be charged per RPC method call ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-RateLimitByComputeUnits)) +* **Rate Limit** the number of requests to 10 per second, with bursts of up to 20 requests ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-RateLimitByRequest)) +* **Send Ether** transfer ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-SendETHTransfer)) +* **Send ERC20 token** transfer (Wrapped Ether) ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-SendTokenTransfer)) +* **Subscribe** to pending transactions ([Playground](https://pkg.go.dev/github.com/lmittmann/w3#example-Client-SubscribeToPendingTransactions)) + +**[See all examples ↗](https://pkg.go.dev/github.com/lmittmann/w3#pkg-examples)** + + +## `w3vm.VM` Examples + +* **Ether transfer** ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-SimpleTransfer)) +* **ERC20 token transfer** with faked token balance (Wrapped Ether) ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-FakeTokenBalance)) +* **Uniswap V3 swap** ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-UniswapV3Swap)) + +* **Call ERC20 balanceOf** with raw a `w3types.Message` using the `Message.{Func,Args}` helper ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-Call)) +* **Call ERC20 balanceOf**, using the `VM.CallFunc` helper ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-CallFunc)) +* **Prank** a sender ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-PrankZeroAddress)) +* **Trace** the execution to obtain the access list ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-TraceAccessList)) +* **Trace** the execution of all op's in a block ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-TraceBlock)) + +**[See all examples ↗](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#pkg-examples)** diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx index b3d55ebc..78e51067 100644 --- a/docs/pages/index.mdx +++ b/docs/pages/index.mdx @@ -19,7 +19,7 @@ description: 'w3: Enhanced Ethereum Integration for Go' -Hello +W3 Gopher `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**. @@ -35,3 +35,11 @@ go get github.com/lmittmann/w3 * Use `w3vm.VM` to simulate EVM execution with optional tracing and Mainnet state forking, or test Smart Contracts. [learn more ➔](/vm-overview) * Use `w3.Func` and `w3.Event` to create ABI bindings from Solidity function and event signatures. [learn more ➔](/helper-abi) * Use `w3.A`, `w3.H`, and many other utility functions to parse addresses, hashes, and other common types from strings. [learn more ➔](/helper-utils) + + +## Sponsors + +
+ EF Logo + +
diff --git a/docs/pages/style.css b/docs/pages/style.css index 3c362910..b939958f 100644 --- a/docs/pages/style.css +++ b/docs/pages/style.css @@ -36,6 +36,7 @@ tr._border-gray-300 { /* Customize header */ .nextra-nav-container nav a[target="_blank"]:nth-child(2):after, .nextra-nav-container nav a[target="_blank"]:nth-child(3):after, +.nextra-nav-container nav a[target="_blank"]:nth-child(4):after, aside.nextra-sidebar-container li a[target="_blank"]:after { content: url('data:image/svg+xml,'); @apply pl-2; diff --git a/docs/public/_redirects b/docs/public/_redirects index 4ccfb0c3..e69de29b 100644 --- a/docs/public/_redirects +++ b/docs/public/_redirects @@ -1 +0,0 @@ -/examples https://github.com/lmittmann/w3/tree/main/examples diff --git a/docs/public/ef-logo-dark.svg b/docs/public/ef-logo-dark.svg new file mode 100644 index 00000000..9b6c5298 --- /dev/null +++ b/docs/public/ef-logo-dark.svg @@ -0,0 +1 @@ + diff --git a/docs/public/ef-logo.svg b/docs/public/ef-logo.svg new file mode 100644 index 00000000..ebffdce7 --- /dev/null +++ b/docs/public/ef-logo.svg @@ -0,0 +1 @@ +f1f5f9 diff --git a/event_test.go b/event_test.go index b8c037aa..62113302 100644 --- a/event_test.go +++ b/event_test.go @@ -1,7 +1,6 @@ package w3_test import ( - "fmt" "math/big" "strconv" "testing" @@ -13,33 +12,6 @@ import ( "github.com/lmittmann/w3" ) -func ExampleEvent_DecodeArgs() { - 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 -} - func TestNewEvent(t *testing.T) { tests := []struct { Signature string diff --git a/example_test.go b/example_test.go new file mode 100644 index 00000000..5ddd4d71 --- /dev/null +++ b/example_test.go @@ -0,0 +1,496 @@ +package w3_test + +import ( + "crypto/ecdsa" + "errors" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + "github.com/lmittmann/w3" + "github.com/lmittmann/w3/module/eth" + "github.com/lmittmann/w3/w3types" + "github.com/lmittmann/w3/w3vm" + "golang.org/x/time/rate" +) + +var ( + funcName = w3.MustNewFunc("name()", "string") + funcSymbol = w3.MustNewFunc("symbol()", "string") + funcDecimals = w3.MustNewFunc("decimals()", "uint8") + funcBalanceOf = w3.MustNewFunc("balanceOf(address)", "uint256") + + addrA = common.Address{0x0a} + addrB = common.Address{0x0b} + + prvA *ecdsa.PrivateKey // dummy private key for addrA + + addrWETH = w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + addrDAI = w3.A("0x6B175474E89094C44Da98b954EedeAC495271d0F") + + client = w3.MustDial("https://rpc.ankr.com/eth") +) + +// Call the name, symbol, decimals, and balanceOf functions of the Wrapped Ether +// in a single batch. +func ExampleClient_batchCallFunc() { + blockNumber := big.NewInt(20_000_000) + + var ( + name, symbol string + decimals uint8 + balance big.Int + ) + if err := client.Call( + eth.CallFunc(addrWETH, funcName).Returns(&name), + eth.CallFunc(addrWETH, funcSymbol).Returns(&symbol), + eth.CallFunc(addrWETH, funcDecimals).Returns(&decimals), + eth.CallFunc(addrWETH, funcBalanceOf, addrWETH).AtBlock(blockNumber).Returns(&balance), + ); err != nil { + // ... + } + + fmt.Printf("%s's own balance: %s %s\n", name, w3.FromWei(&balance, decimals), symbol) + // Output: + // Wrapped Ether's own balance: 748.980125465356473638 WETH +} + +// Call the Uniswap V3 Quoter for quotes on swapping 100 WETH for DAI in pools +// of all fee tiers in a single batch. +func ExampleClient_batchCallFuncUniswapQuoter() { + blockNumber := big.NewInt(20_000_000) + + var ( + addrUniswapV3Quoter = w3.A("0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6") + addrTokenIn = addrWETH + addrTokenOut = addrDAI + + funcQuote = w3.MustNewFunc(`quoteExactInputSingle( + address tokenIn, + address tokenOut, + uint24 fee, + uint256 amountIn, + uint160 sqrtPriceLimitX96clear + )`, "uint256 amountOut") + ) + + var ( + amountIn = w3.I("100 ether") + amountOut100 *big.Int + amountOut500 *big.Int + amountOut3000 *big.Int + amountOut10000 *big.Int + ) + if err := client.Call( + eth.CallFunc(addrUniswapV3Quoter, funcQuote, addrTokenIn, addrTokenOut, big.NewInt(100), amountIn, w3.Big0).AtBlock(blockNumber).Returns(&amountOut100), + eth.CallFunc(addrUniswapV3Quoter, funcQuote, addrTokenIn, addrTokenOut, big.NewInt(500), amountIn, w3.Big0).AtBlock(blockNumber).Returns(&amountOut500), + eth.CallFunc(addrUniswapV3Quoter, funcQuote, addrTokenIn, addrTokenOut, big.NewInt(3000), amountIn, w3.Big0).AtBlock(blockNumber).Returns(&amountOut3000), + eth.CallFunc(addrUniswapV3Quoter, funcQuote, addrTokenIn, addrTokenOut, big.NewInt(10000), amountIn, w3.Big0).AtBlock(blockNumber).Returns(&amountOut10000), + ); err != nil { + // ... + } + fmt.Println("Swap 100 WETH for DAI:") + fmt.Printf("Pool with 0.01%% fee: %s DAI\n", w3.FromWei(amountOut100, 18)) + fmt.Printf("Pool with 0.05%% fee: %s DAI\n", w3.FromWei(amountOut500, 18)) + fmt.Printf("Pool with 0.3%% fee: %s DAI\n", w3.FromWei(amountOut3000, 18)) + fmt.Printf("Pool with 1%% fee: %s DAI\n", w3.FromWei(amountOut10000, 18)) + // Output: + // Swap 100 WETH for DAI: + // Pool with 0.01% fee: 0.840975419471618588 DAI + // Pool with 0.05% fee: 371877.453117609415215338 DAI + // Pool with 0.3% fee: 378532.856217317782434539 DAI + // Pool with 1% fee: 3447.634026125332130689 DAI +} + +// Fetch the nonce and balance of an EOA in a single batch. +func ExampleClient_batchEOAState() { + var ( + nonce uint64 + balance *big.Int + ) + if err := client.Call( + eth.Nonce(addrA, nil).Returns(&nonce), + eth.Balance(addrA, nil).Returns(&balance), + ); err != nil { + // ... + } + + fmt.Printf("Nonce: %d\nBalance: %d\n", nonce, balance) +} + +// Fetch a transaction and its receipt in a single batch. +func ExampleClient_batchTxDetails() { + txHash := w3.H("0xc31d7e7e85cab1d38ce1b8ac17e821ccd47dbde00f9d57f2bd8613bff9428396") + + var ( + tx *types.Transaction + receipt *types.Receipt + ) + if err := client.Call( + eth.Tx(txHash).Returns(&tx), + eth.TxReceipt(txHash).Returns(&receipt), + ); err != nil { + // ... + } + + fmt.Printf("Tx: %#v\nReceipt: %#v\n", tx, receipt) +} + +// Fetch 1000 blocks in batches. +func ExampleClient_batchBlocks() { + const ( + startBlock = 20_000_000 + nBlocks = 1000 + batchSize = 100 + ) + + blocks := make([]*types.Block, nBlocks) + calls := make([]w3types.RPCCaller, batchSize) + for i := 0; i < nBlocks; i += batchSize { + for j := 0; j < batchSize; j++ { + blockNumber := new(big.Int).SetUint64(uint64(startBlock + i + j)) + calls[j] = eth.BlockByNumber(blockNumber).Returns(&blocks[i+j]) + } + if err := client.Call(calls...); err != nil { + // ... + } + fmt.Printf("Fetched %d blocks\n", i+batchSize) + } +} + +// Handle errors of individual calls in a batch. +func ExampleClient_batchHandleError() { + tokens := []common.Address{addrWETH, addrA, addrB} + symbols := make([]string, len(tokens)) + + // build rpc calls + calls := make([]w3types.RPCCaller, len(tokens)) + for i, token := range tokens { + calls[i] = eth.CallFunc(token, funcSymbol).Returns(&symbols[i]) + } + + var batchErr w3.CallErrors + if err := client.Call(calls...); errors.As(err, &batchErr) { + } else if err != nil { + // all calls failed + } + + for i, symbol := range symbols { + if len(batchErr) > 0 && batchErr[i] != nil { + symbol = "call failed" + } + fmt.Printf("%s: %s\n", tokens[i], symbol) + } + // Output: + // 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2: WETH + // 0x0a00000000000000000000000000000000000000: call failed + // 0x0B00000000000000000000000000000000000000: call failed +} + +// Fetch the token balance of an address. +func ExampleClient_callFunc() { + var balance *big.Int + if err := client.Call( + eth.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&balance), + ); err != nil { + // ... + } + + fmt.Printf("Balance: %s WETH\n", w3.FromWei(balance, 18)) + // Output: + // Balance: 0 WETH +} + +// Fetch the token balance of an address, with state override. +func ExampleClient_callFuncWithStateOverride() { + var balance *big.Int + if err := client.Call( + eth.CallFunc(addrWETH, funcBalanceOf, addrA).Overrides(w3types.State{ + addrWETH: {Storage: w3types.Storage{ + w3vm.WETHBalanceSlot(addrA): common.BigToHash(w3.I("100 ether")), + }}, + }).Returns(&balance), + ); err != nil { + // ... + } + + fmt.Printf("Balance: %s WETH\n", w3.FromWei(balance, 18)) + // Output: + // Balance: 100 WETH +} + +// Send Ether transfer. +func ExampleClient_sendETHTransfer() { + var ( + nonce uint64 + gasPrice *big.Int + ) + if err := client.Call( + eth.Nonce(addrA, nil).Returns(&nonce), + eth.GasPrice().Returns(&gasPrice), + ); err != nil { + // ... + } + + signer := types.LatestSigner(params.MainnetChainConfig) + tx := types.MustSignNewTx(prvA, signer, &types.LegacyTx{ + Nonce: nonce, + Gas: 21_000, + GasPrice: gasPrice, + To: &addrB, + Value: w3.I("1 ether"), + }) + + var txHash common.Hash + if err := client.Call(eth.SendTx(tx).Returns(&txHash)); err != nil { + // ... + } + + fmt.Printf("Sent tx: %s\n", txHash) +} + +// Send ERC20 token transfer (Wrapped Ether). +func ExampleClient_sendTokenTransfer() { + var ( + nonce uint64 + gasPrice *big.Int + ) + if err := client.Call( + eth.Nonce(addrA, nil).Returns(&nonce), + eth.GasPrice().Returns(&gasPrice), + ); err != nil { + // ... + } + + funcTransfer := w3.MustNewFunc("transfer(address receiver, uint256 amount)", "bool") + data, err := funcTransfer.EncodeArgs(addrB, w3.I("1 ether")) + if err != nil { + // ... + } + + signer := types.LatestSigner(params.MainnetChainConfig) + tx := types.MustSignNewTx(prvA, signer, &types.LegacyTx{ + Nonce: nonce, + Gas: 100_000, + GasPrice: gasPrice, + To: &addrWETH, + Data: data, + }) + + var txHash common.Hash + if err := client.Call(eth.SendTx(tx).Returns(&txHash)); err != nil { + // ... + } + + fmt.Printf("Sent tx: %s\n", txHash) +} + +// Subscribe to pending transactions. +func ExampleClient_subscribeToPendingTransactions() { + client, err := w3.Dial("wss://mainnet.gateway.tenderly.co") + if err != nil { + // ... + } + defer client.Close() + + pendingTxCh := make(chan *types.Transaction) + sub, err := client.Subscribe(eth.PendingTransactions(pendingTxCh)) + if err != nil { + // ... + } + + for { + select { + case tx := <-pendingTxCh: + fmt.Printf("New pending tx: %s\n", tx.Hash()) + case err := <-sub.Err(): + fmt.Printf("Subscription error: %v\n", err) + return + } + } +} + +// Rate Limit the number of requests to 10 per second, with bursts of up to 20 +// requests. +func ExampleClient_rateLimitByRequest() { + client, err := w3.Dial("https://rpc.ankr.com/eth", + w3.WithRateLimiter(rate.NewLimiter(rate.Every(time.Second/10), 20), nil), + ) + if err != nil { + // ... + } + defer client.Close() +} + +// Rate Limit the number of requests to 300 compute units (CUs) per second, with +// bursts of up to 300 CUs. +// An individual CU can be charged per RPC method call. +func ExampleClient_rateLimitByComputeUnits() { + // cu returns the CU cost for all method calls in a batch. + cu := func(methods []string) (cost int) { + for _, method := range methods { + switch method { + case "eth_blockNumber": + cost += 5 + case "eth_getBalance", + "eth_getBlockByNumber", + "eth_getCode", + "eth_getStorageAt", + "eth_getTransactionByHash", + "eth_getTransactionReceipt": + cost += 15 + case "eth_call": + cost += 20 + case "eth_getTransactionCount": + cost += 25 + default: + panic(fmt.Sprintf("unknown costs for %q", method)) + } + } + return cost + } + + client, err := w3.Dial("https://rpc.ankr.com/eth", + w3.WithRateLimiter(rate.NewLimiter(rate.Every(time.Second/300), 300), cu), + ) + if err != nil { + // ... + } + defer client.Close() +} + +// ABI bindings for the ERC20 functions. +func ExampleFunc_erc20() { + var ( + funcTotalSupply = w3.MustNewFunc("totalSupply()", "uint256") + funcBalanceOf = w3.MustNewFunc("balanceOf(address)", "uint256") + funcTransfer = w3.MustNewFunc("transfer(address to, uint256 amount)", "bool") + funcAllowance = w3.MustNewFunc("allowance(address owner, address spender)", "uint256") + funcApprove = w3.MustNewFunc("approve(address spender, uint256 amount)", "bool") + funcTransferFrom = w3.MustNewFunc("transferFrom(address from, address to, uint256 amount)", "bool") + ) + _ = funcTotalSupply + _ = funcBalanceOf + _ = funcTransfer + _ = funcAllowance + _ = funcApprove + _ = funcTransferFrom +} + +// Encode and decode the arguments of the balanceOf function. +func ExampleFunc_balanceOf() { + // encode + input, err := funcBalanceOf.EncodeArgs(addrA) + if err != nil { + // ... + } + fmt.Printf("encoded: 0x%x\n", input) + + // decode + var who common.Address + if err := funcBalanceOf.DecodeArgs(input, &who); err != nil { + // ... + } + fmt.Printf("decoded: balanceOf(%s)\n", who) + // Output: + // encoded: 0x70a082310000000000000000000000000a00000000000000000000000000000000000000 + // decoded: balanceOf(0x0a00000000000000000000000000000000000000) +} + +// ABI bindings for the Uniswap v4 swap function. +func ExampleFunc_uniswapV4Swap() { + funcSwap := w3.MustNewFunc(`swap( + (address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) key, + (bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96) params, + bytes hookData + )`, "int256 delta") + + // ABI binding for the PoolKey struct. + type PoolKey struct { + Currency0 common.Address + Currency1 common.Address + Fee *big.Int + TickSpacing *big.Int + Hooks common.Address + } + + // ABI binding for the SwapParams struct. + type SwapParams struct { + ZeroForOne bool + AmountSpecified *big.Int + SqrtPriceLimitX96 *big.Int + } + + // encode + input, _ := funcSwap.EncodeArgs( + &PoolKey{ + Currency0: addrWETH, + Currency1: addrDAI, + Fee: big.NewInt(0), + TickSpacing: big.NewInt(0), + }, + &SwapParams{ + ZeroForOne: false, + AmountSpecified: big.NewInt(0), + SqrtPriceLimitX96: big.NewInt(0), + }, + []byte{}, + ) + fmt.Printf("encoded: 0x%x\n", input) + // Output: + // encoded: 0xf3cd914c000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000 +} + +func ExampleFunc_DecodeReturns_getReserves() { + funcGetReserves := w3.MustNewFunc("getReserves()", "uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast") + output := w3.B( + "0x00000000000000000000000000000000000000000000003635c9adc5dea00000", + "0x0000000000000000000000000000000000000000000000a2a15d09519be00000", + "0x0000000000000000000000000000000000000000000000000000000064373057", + ) + + var ( + reserve0, reserve1 *big.Int + blockTimestampLast uint32 + ) + if err := funcGetReserves.DecodeReturns(output, &reserve0, &reserve1, &blockTimestampLast); err != nil { + // ... + } + fmt.Println("Reserve0:", reserve0) + fmt.Println("Reserve1:", reserve1) + fmt.Println("BlockTimestampLast:", blockTimestampLast) + // Output: + // Reserve0: 1000000000000000000000 + // Reserve1: 3000000000000000000000 + // BlockTimestampLast: 1681338455 +} + +func ExampleEvent_decodeTransferEvent() { + 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 +} diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 766ab6fe..00000000 --- a/examples/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Examples - -The following example projects can help you understand `w3`: - -* [scan_blocks](scan_blocks/): Scan blocks blazing fast using batch requests. -* [token_balance](token_balance/): Fetch an accounts token balance and info in a single batch request. -* [uniswap_quote](uniswap_quote/): Get the best Uniswap V3 quote for the given token pair. - -Please [open an issue](https://github.com/lmittmann/w3/issues/new) if you -would like to see another example. diff --git a/examples/go.mod b/examples/go.mod deleted file mode 100644 index 7b05dc64..00000000 --- a/examples/go.mod +++ /dev/null @@ -1,37 +0,0 @@ -module examples - -go 1.22 - -require ( - github.com/ethereum/go-ethereum v1.14.8 - github.com/lmittmann/w3 v0.0.0 -) - -replace github.com/lmittmann/w3 => ../ - -require ( - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/StackExchange/wmi v1.2.1 // indirect - github.com/bits-and-blooms/bitset v1.10.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect - github.com/consensys/bavard v0.1.13 // indirect - github.com/consensys/gnark-crypto v0.12.1 // indirect - github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect - github.com/deckarep/golang-set/v2 v2.6.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/ethereum/c-kzg-4844 v1.0.0 // indirect - github.com/go-ole/go-ole v1.3.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/holiman/uint256 v1.3.1 // indirect - github.com/mmcloughlin/addchain v0.4.0 // indirect - github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect - github.com/supranational/blst v0.3.11 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect - golang.org/x/crypto v0.22.0 // indirect - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/time v0.7.0 // indirect - rsc.io/tmplfunc v0.0.3 // indirect -) diff --git a/examples/go.sum b/examples/go.sum deleted file mode 100644 index f6ecb976..00000000 --- a/examples/go.sum +++ /dev/null @@ -1,145 +0,0 @@ -github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= -github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= -github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI= -github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= -github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= -github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= -github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= -github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= -github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= -github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw= -github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= -github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= -github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= -github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= -github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= -github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= -github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= -github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= -github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= -github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= -github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.14.8 h1:NgOWvXS+lauK+zFukEvi85UmmsS/OkV0N23UZ1VTIig= -github.com/ethereum/go-ethereum v1.14.8/go.mod h1:TJhyuDq0JDppAkFXgqjwpdlQApywnu/m10kFPxh8vvs= -github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 h1:KrE8I4reeVvf7C1tm8elRjj4BdscTYzz/WAbYyf/JI4= -github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0/go.mod h1:D9AJLVXSyZQXJQVk8oh1EwjISE+sJTn2duYIZC0dy3w= -github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= -github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= -github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= -github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= -github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= -github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= -github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= -github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= -github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= -github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= -github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= -github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= -github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= -golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/examples/scan_blocks/main.go b/examples/scan_blocks/main.go deleted file mode 100644 index 5a8e50d9..00000000 --- a/examples/scan_blocks/main.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -scan_blocks iterates over blocks with their transactions from a given start block. - -Usage: - - scan_blocks [flags] - -Flags: - - -start uint - Start block (default 10_000_000) - -h, --help - help for scan_blocks -*/ -package main - -import ( - "flag" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/lmittmann/w3" - "github.com/lmittmann/w3/module/eth" - "github.com/lmittmann/w3/w3types" -) - -var ( - // number of blocks to fetch in a single request - bulkSize = 100 - - // flags - startBlock uint64 -) - -func main() { - // parse flags - flag.Uint64Var(&startBlock, "start", 10_000_000, "Start block") - flag.Usage = func() { - fmt.Println("scan_blocks iterates over blocks with their transactions from a given start block.") - flag.PrintDefaults() - } - flag.Parse() - - // connect to RPC endpoint - client := w3.MustDial("https://rpc.ankr.com/eth") - defer client.Close() - - // fetch blocks in bulk - calls := make([]w3types.RPCCaller, bulkSize) - blocks := make([]*types.Block, bulkSize) - - for i, txCount := 0, 0; ; i++ { - j := i % bulkSize - calls[j] = eth.BlockByNumber(new(big.Int).SetUint64(startBlock + uint64(i))).Returns(&blocks[j]) - - if j == bulkSize-1 { - if err := client.Call(calls...); err != nil { - fmt.Printf("Call failed: %v\r", err) - return - } - - for _, block := range blocks { - txCount += len(block.Transactions()) - processBlock(block) - } - fmt.Printf("\rFetched %d blocks with a total of %d transactions", i+1, txCount) - } - } -} - -func processBlock(b *types.Block) { - // Do something with the block and its transactions... -} diff --git a/examples/token_balance/main.go b/examples/token_balance/main.go deleted file mode 100644 index 4687cf92..00000000 --- a/examples/token_balance/main.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -token_balance prints the balance of an ERC20 token for a given account. - -Usage: - - token_balance [flags] - -Flags: - - -acc string - Account address (default "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") - -token string - Token address (default "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") - -h, --help - help for token_balance -*/ -package main - -import ( - "flag" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/lmittmann/w3" - "github.com/lmittmann/w3/module/eth" -) - -var ( - // smart contract functions - funcName = w3.MustNewFunc("name()", "string") - funcSymbol = w3.MustNewFunc("symbol()", "string") - funcDecimals = w3.MustNewFunc("decimals()", "uint8") - funcBalanceOf = w3.MustNewFunc("balanceOf(address)", "uint256") - - // flags - addrAcc common.Address - addrToken common.Address -) - -func main() { - // parse flags - flag.TextVar(&addrAcc, "acc", w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), "Account address") - flag.TextVar(&addrToken, "token", w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), "Token address") - flag.Usage = func() { - fmt.Println("token_balance prints the balance of an ERC20 token for a given account.") - flag.PrintDefaults() - } - flag.Parse() - - // connect to RPC endpoint - client := w3.MustDial("https://rpc.ankr.com/eth") - defer client.Close() - - // fetch token details and account balance - var ( - name, symbol string - decimals uint8 - balance big.Int - ) - if err := client.Call( - eth.CallFunc(addrToken, funcName).Returns(&name), - eth.CallFunc(addrToken, funcSymbol).Returns(&symbol), - eth.CallFunc(addrToken, funcDecimals).Returns(&decimals), - eth.CallFunc(addrToken, funcBalanceOf, addrAcc).Returns(&balance), - ); err != nil { - fmt.Printf("Call failed: %v\n", err) - return - } - - fmt.Printf("%s balance of %s: %s %s\n", name, addrAcc, w3.FromWei(&balance, decimals), symbol) -} diff --git a/examples/uniswap_quote/main.go b/examples/uniswap_quote/main.go deleted file mode 100644 index 6e2cca22..00000000 --- a/examples/uniswap_quote/main.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -uniswap_quote prints the UniSwap V3 exchange rate to swap amontIn of tokenIn for -tokenOut. - -Usage: - - uniswap_quote [flags] - -Flags: - - -amountIn string - Amount of tokenIn to exchange for tokenOut (default "1 ether") - -tokenIn string - Token in address (default "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") - -tokenOut string - Token out address (default "0x6B175474E89094C44Da98b954EedeAC495271d0F") - -h, --help - help for uniswap_quote -*/ -package main - -import ( - "flag" - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/lmittmann/w3" - "github.com/lmittmann/w3/module/eth" - "github.com/lmittmann/w3/w3types" -) - -var ( - addrUniV3Quoter = w3.A("0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6") - - funcQuoteExactInputSingle = w3.MustNewFunc("quoteExactInputSingle(address tokenIn, address tokenOut, uint24 fee, uint256 amountIn, uint160 sqrtPriceLimitX96)", "uint256 amountOut") - funcName = w3.MustNewFunc("name()", "string") - funcSymbol = w3.MustNewFunc("symbol()", "string") - funcDecimals = w3.MustNewFunc("decimals()", "uint8") - - // flags - addrTokenIn common.Address - addrTokenOut common.Address - amountIn big.Int -) - -func main() { - // parse flags - flag.TextVar(&amountIn, "amountIn", w3.I("1 ether"), "Token address") - flag.TextVar(&addrTokenIn, "tokenIn", w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), "Token in") - flag.TextVar(&addrTokenOut, "tokenOut", w3.A("0x6B175474E89094C44Da98b954EedeAC495271d0F"), "Token out") - flag.Usage = func() { - fmt.Println("uniswap_quote prints the UniSwap V3 exchange rate to swap amontIn of tokenIn for tokenOut.") - flag.PrintDefaults() - } - flag.Parse() - - // connect to RPC endpoint - client := w3.MustDial("https://rpc.ankr.com/eth") - defer client.Close() - - // fetch token details - var ( - tokenInName string - tokenInSymbol string - tokenInDecimals uint8 - tokenOutName string - tokenOutSymbol string - tokenOutDecimals uint8 - ) - if err := client.Call( - eth.CallFunc(addrTokenIn, funcName).Returns(&tokenInName), - eth.CallFunc(addrTokenIn, funcSymbol).Returns(&tokenInSymbol), - eth.CallFunc(addrTokenIn, funcDecimals).Returns(&tokenInDecimals), - eth.CallFunc(addrTokenOut, funcName).Returns(&tokenOutName), - eth.CallFunc(addrTokenOut, funcSymbol).Returns(&tokenOutSymbol), - eth.CallFunc(addrTokenOut, funcDecimals).Returns(&tokenOutDecimals), - ); err != nil { - fmt.Printf("Failed to fetch token details: %v\n", err) - return - } - - // fetch quotes - var ( - fees = []*big.Int{big.NewInt(100), big.NewInt(500), big.NewInt(3000), big.NewInt(10000)} - calls = make([]w3types.RPCCaller, len(fees)) - amountsOut = make([]big.Int, len(fees)) - ) - for i, fee := range fees { - calls[i] = eth.CallFunc(addrUniV3Quoter, funcQuoteExactInputSingle, addrTokenIn, addrTokenOut, fee, &amountIn, w3.Big0).Returns(&amountsOut[i]) - } - err := client.Call(calls...) - callErrs, ok := err.(w3.CallErrors) - if err != nil && !ok { - fmt.Printf("Failed to fetch quotes: %v\n", err) - return - - } - - // print quotes - fmt.Printf("Exchange %q for %q\n", tokenInName, tokenOutName) - fmt.Printf("Amount in:\n %s %s\n", w3.FromWei(&amountIn, tokenInDecimals), tokenInSymbol) - fmt.Printf("Amount out:\n") - for i, fee := range fees { - if ok && callErrs[i] != nil { - fmt.Printf(" Pool (fee=%5v): Pool does not exist\n", fee) - continue - } - fmt.Printf(" Pool (fee=%5v): %s %s\n", fee, w3.FromWei(&amountsOut[i], tokenOutDecimals), tokenOutSymbol) - } -} diff --git a/func_test.go b/func_test.go index 73137a39..74930210 100644 --- a/func_test.go +++ b/func_test.go @@ -3,7 +3,6 @@ package w3_test import ( "bytes" "errors" - "fmt" "math/big" "strconv" "testing" @@ -16,106 +15,6 @@ import ( "github.com/lmittmann/w3/w3types" ) -func ExampleNewFunc_balanceOf() { - // 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 *big.Int - ) - funcBalanceOf.DecodeReturns(output, &amount) - fmt.Printf("balanceOf returns: %v\n", amount) - // Output: - // balanceOf input: 0x70a08231000000000000000000000000ab5801a7d398351b8be11c439e05c5b3259aec9b - // balanceOf args: 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B - // balanceOf returns: 49406 -} - -func ExampleNewFunc_uniswapV4Swap() { - // ABI binding for the Uniswap v4 swap function. - funcSwap, _ := w3.NewFunc(`swap( - (address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) key, - (bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96) params, - bytes hookData - )`, "int256 delta") - - // ABI binding for the PoolKey struct. - type PoolKey struct { - Currency0 common.Address - Currency1 common.Address - Fee *big.Int - TickSpacing *big.Int - Hooks common.Address - } - - // ABI binding for the SwapParams struct. - type SwapParams struct { - ZeroForOne bool - AmountSpecified *big.Int - SqrtPriceLimitX96 *big.Int - } - - // ABI-encode the functions args. - input, _ := funcSwap.EncodeArgs( - &PoolKey{ - Currency0: w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), - Currency1: w3.A("0x6B175474E89094C44Da98b954EedeAC495271d0F"), - Fee: big.NewInt(0), - TickSpacing: big.NewInt(0), - }, - &SwapParams{ - ZeroForOne: false, - AmountSpecified: big.NewInt(0), - SqrtPriceLimitX96: big.NewInt(0), - }, - []byte{}, - ) - fmt.Printf("swap input: 0x%x\n", input) - // Output: - // swap input: 0xf3cd914c000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000000 -} - -func ExampleFunc_DecodeReturns_getReserves() { - funcGetReserves := w3.MustNewFunc("getReserves()", "uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast") - output := w3.B( - "0x00000000000000000000000000000000000000000000003635c9adc5dea00000", - "0x0000000000000000000000000000000000000000000000a2a15d09519be00000", - "0x0000000000000000000000000000000000000000000000000000000064373057", - ) - - var ( - reserve0, reserve1 *big.Int - blockTimestampLast uint32 - ) - if err := funcGetReserves.DecodeReturns(output, &reserve0, &reserve1, &blockTimestampLast); err != nil { - // ... - } - fmt.Println("Reserve0:", reserve0) - fmt.Println("Reserve1:", reserve1) - fmt.Println("BlockTimestampLast:", blockTimestampLast) - // Output: - // Reserve0: 1000000000000000000000 - // Reserve1: 3000000000000000000000 - // BlockTimestampLast: 1681338455 -} - func TestNewFunc(t *testing.T) { tests := []struct { Signature string diff --git a/w3vm/example_test.go b/w3vm/example_test.go new file mode 100644 index 00000000..f0be1dc6 --- /dev/null +++ b/w3vm/example_test.go @@ -0,0 +1,351 @@ +package w3vm_test + +import ( + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + gethVm "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers/logger" + "github.com/ethereum/go-ethereum/params" + "github.com/lmittmann/w3" + "github.com/lmittmann/w3/module/eth" + "github.com/lmittmann/w3/w3types" + "github.com/lmittmann/w3/w3vm" +) + +var ( + addrA = common.Address{0x0a} + addrB = common.Address{0x0b} +) + +// Execute an Ether transfer. +func ExampleVM_simpleTransfer() { + vm, _ := w3vm.New( + w3vm.WithState(w3types.State{ + addrA: {Balance: w3.I("100 ether")}, + }), + ) + + // Print balances + balA, _ := vm.Balance(addrA) + balB, _ := vm.Balance(addrB) + fmt.Printf("Before transfer:\nA: %s ETH, B: %s ETH\n", w3.FromWei(balA, 18), w3.FromWei(balB, 18)) + + // Transfer 10 ETH from A to B + vm.Apply(&w3types.Message{ + From: addrA, + To: &addrB, + Value: w3.I("10 ether"), + }) + + // Print balances + balA, _ = vm.Balance(addrA) + balB, _ = vm.Balance(addrB) + fmt.Printf("After transfer:\nA: %s ETH, B: %s ETH\n", w3.FromWei(balA, 18), w3.FromWei(balB, 18)) + // Output: + // Before transfer: + // A: 100 ETH, B: 0 ETH + // After transfer: + // A: 90 ETH, B: 10 ETH +} + +// Execute an ERC20 token transfer with faked token balance (Wrapped Ether). +func ExampleVM_fakeTokenBalance() { + vm, err := w3vm.New( + w3vm.WithFork(client, nil), + w3vm.WithNoBaseFee(), + w3vm.WithState(w3types.State{ + addrWETH: {Storage: w3types.Storage{ + w3vm.WETHBalanceSlot(addrA): common.BigToHash(w3.I("100 ether")), + }}, + }), + ) + if err != nil { + // ... + } + + // Print WETH balance + var balA, balB *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&balA); err != nil { + // ... + } + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrB).Returns(&balB); err != nil { + // ... + } + fmt.Printf("Before transfer:\nA: %s WETH, B: %s WETH\n", w3.FromWei(balA, 18), w3.FromWei(balB, 18)) + + // Transfer 10 WETH from A to B + if _, err := vm.Apply(&w3types.Message{ + From: addrA, + To: &addrWETH, + Func: funcTransfer, + Args: []any{addrB, w3.I("10 ether")}, + }); err != nil { + // ... + } + + // Print WETH balance + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&balA); err != nil { + // ... + } + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrB).Returns(&balB); err != nil { + // ... + } + fmt.Printf("After transfer:\nA: %s WETH, B: %s WETH\n", w3.FromWei(balA, 18), w3.FromWei(balB, 18)) + // Output: + // Before transfer: + // A: 100 WETH, B: 0 WETH + // After transfer: + // A: 90 WETH, B: 10 WETH +} + +// Execute an ERC20 balanceOf call with raw a [w3types.Message] using the +// messages Func and Args helper. +func ExampleVM_call() { + vm, err := w3vm.New( + w3vm.WithFork(client, nil), + w3vm.WithState(w3types.State{ + addrWETH: {Storage: w3types.Storage{ + w3vm.WETHBalanceSlot(addrA): common.BigToHash(w3.I("100 ether")), + }}, + }), + ) + if err != nil { + // ... + } + + receipt, err := vm.Call(&w3types.Message{ + To: &addrWETH, + Func: funcBalanceOf, + Args: []any{addrA}, + }) + if err != nil { + // ... + } + + var balance *big.Int + if err := receipt.DecodeReturns(&balance); err != nil { + // ... + } + fmt.Printf("Balance: %s WETH\n", w3.FromWei(balance, 18)) + // Output: + // Balance: 100 WETH +} + +// Execute an ERC20 balanceOf call using the [VM.CallFunc] helper. +func ExampleVM_callFunc() { + vm, err := w3vm.New( + w3vm.WithFork(client, nil), + w3vm.WithState(w3types.State{ + addrWETH: {Storage: w3types.Storage{ + w3vm.WETHBalanceSlot(addrA): common.BigToHash(w3.I("100 ether")), + }}, + }), + ) + if err != nil { + // ... + } + + var balance *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&balance); err != nil { + // ... + } + fmt.Printf("Balance: %s WETH\n", w3.FromWei(balance, 18)) + // Output: + // Balance: 100 WETH +} + +// Execute an Uniswap V3 swap. +func ExampleVM_uniswapV3Swap() { + var ( + addrRouter = w3.A("0xE592427A0AEce92De3Edee1F18E0157C05861564") + addrUNI = w3.A("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984") + + funcExactInput = w3.MustNewFunc(`exactInput( + ( + bytes path, + address recipient, + uint256 deadline, + uint256 amountIn, + uint256 amountOutMinimum + ) params + )`, "uint256 amountOut") + ) + + // mapping for the exactInput-function params-tuple + type ExactInputParams struct { + Path []byte + Recipient common.Address + Deadline *big.Int + AmountIn *big.Int + AmountOutMinimum *big.Int + } + + encodePath := func(tokenA, tokenB common.Address, fee uint32) []byte { + path := make([]byte, 43) + copy(path, tokenA[:]) + path[20], path[21], path[22] = byte(fee>>16), byte(fee>>8), byte(fee) + copy(path[23:], tokenB[:]) + return path + } + + // 1. Create a VM that forks the Mainnet state from the latest block, + // disables the base fee, and has a fake WETH balance and approval for the router + vm, err := w3vm.New( + w3vm.WithFork(client, big.NewInt(20_000_000)), + w3vm.WithNoBaseFee(), + w3vm.WithState(w3types.State{ + addrWETH: {Storage: w3types.Storage{ + w3vm.WETHBalanceSlot(addrA): common.BigToHash(w3.I("1 ether")), + w3vm.WETHAllowanceSlot(addrA, addrRouter): common.BigToHash(w3.I("1 ether")), + }}, + }), + ) + if err != nil { + // ... + } + + // 2. Simulate a Uniswap v3 swap + receipt, err := vm.Apply(&w3types.Message{ + From: addrA, + To: &addrRouter, + Func: funcExactInput, + Args: []any{&ExactInputParams{ + Path: encodePath(addrWETH, addrUNI, 500), + Recipient: addrA, + Deadline: big.NewInt(time.Now().Unix()), + AmountIn: w3.I("1 ether"), + AmountOutMinimum: w3.Big0, + }}, + }) + if err != nil { + // ... + } + + // 3. Decode output amount + var amountOut *big.Int + if err := receipt.DecodeReturns(&amountOut); err != nil { + // ... + } + + fmt.Printf("AmountOut: %s UNI\n", w3.FromWei(amountOut, 18)) + // Output: + // AmountOut: 278.327327986946583271 UNI +} + +// Execute a message sent from the zero address. +// The [w3types.Message] sender can be freely chosen, making it possible to +// execute a message from any address. +func ExampleVM_prankZeroAddress() { + vm, err := w3vm.New( + w3vm.WithFork(client, big.NewInt(20_000_000)), + w3vm.WithNoBaseFee(), + ) + if err != nil { + // ... + } + + balZero, err := vm.Balance(w3.Addr0) + if err != nil { + // ... + } + + _, err = vm.Apply(&w3types.Message{ + From: w3.Addr0, + To: &addrA, + Value: balZero, + }) + if err != nil { + // ... + } + + balance, err := vm.Balance(addrA) + if err != nil { + // ... + } + + fmt.Printf("Received %s ETH from zero address\n", w3.FromWei(balance, 18)) + // Output: + // Received 13365.401185473565028721 ETH from zero address +} + +// Trace a message execution to obtain the access list. +func ExampleVM_traceAccessList() { + txHash := w3.H("0xbb4b3fc2b746877dce70862850602f1d19bd890ab4db47e6b7ee1da1fe578a0d") + + var ( + tx *types.Transaction + receipt *types.Receipt + ) + if err := client.Call( + eth.Tx(txHash).Returns(&tx), + eth.TxReceipt(txHash).Returns(&receipt), + ); err != nil { + // ... + } + + var header *types.Header + if err := client.Call(eth.HeaderByNumber(receipt.BlockNumber).Returns(&header)); err != nil { + // ... + } + + vm, err := w3vm.New( + w3vm.WithFork(client, receipt.BlockNumber), + ) + if err != nil { + // ... + } + + // setup access list hook + signer := types.MakeSigner(params.MainnetChainConfig, header.Number, header.Time) + from, _ := signer.Sender(tx) + + accessListTracer := logger.NewAccessListTracer( + nil, + from, *tx.To(), + gethVm.ActivePrecompiles(params.MainnetChainConfig.Rules(header.Number, header.Difficulty.Sign() == 0, header.Time)), + ) + + if _, err := vm.ApplyTx(tx, accessListTracer.Hooks()); err != nil { + // ... + } + fmt.Println("Access List:", accessListTracer.AccessList()) +} + +// Trace the execution of all op's in a block. +func ExampleVM_traceBlock() { + blockNumber := big.NewInt(20_000_000) + + var block *types.Block + if err := client.Call(eth.BlockByNumber(blockNumber).Returns(&block)); err != nil { + // ... + } + + vm, err := w3vm.New( + w3vm.WithFork(client, blockNumber), + ) + if err != nil { + // ... + } + + var ops [256]uint64 + tracer := &tracing.Hooks{ + OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + ops[op]++ + }, + } + + for _, tx := range block.Transactions() { + vm.ApplyTx(tx, tracer) + } + + for op, count := range ops { + if count > 0 { + fmt.Printf("0x%02x %-14s %d\n", op, gethVm.OpCode(op), count) + } + } +} diff --git a/w3vm/vm_test.go b/w3vm/vm_test.go index dc9fbd1c..218420f9 100644 --- a/w3vm/vm_test.go +++ b/w3vm/vm_test.go @@ -810,149 +810,3 @@ func BenchmarkTransferWETH9(b *testing.B) { } func ptr[T any](t T) *T { return &t } - -func ExampleVM_transferEthFromZeroAddress() { - client, err := w3.Dial("https://rpc.ankr.com/eth") - if err != nil { - // handle error - } - defer client.Close() - - vm, err := w3vm.New( - w3vm.WithFork(client, nil), - w3vm.WithNoBaseFee(), - ) - if err != nil { - // handle error - } - - recipient := w3vm.RandA() - - _, err = vm.Apply(&w3types.Message{ - From: common.Address{}, - To: &recipient, - Value: w3.I("1 ether"), - }) - if err != nil { - // handle error - } - - balance, err := vm.Balance(recipient) - if err != nil { - // handle error - } - - fmt.Printf("Balance: %s ETH\n", w3.FromWei(balance, 18)) - // Output: Balance: 1 ETH -} - -func ExampleVM() { - var ( - addrEOA = w3.A("0x000000000000000000000000000000000000c0Fe") - addrWETH = w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") - addrUNI = w3.A("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984") - addrRouter = w3.A("0xE592427A0AEce92De3Edee1F18E0157C05861564") - - funcExactInput = w3.MustNewFunc(`exactInput( - ( - bytes path, - address recipient, - uint256 deadline, - uint256 amountIn, - uint256 amountOutMinimum - ) params - )`, "uint256 amountOut") - ) - - type ExactInputParams struct { - Path []byte - Recipient common.Address - Deadline *big.Int - AmountIn *big.Int - AmountOutMinimum *big.Int - } - - encodePath := func(tokenA common.Address, fee uint32, tokenB common.Address) []byte { - path := make([]byte, 43) - copy(path, tokenA[:]) - path[20], path[21], path[22] = byte(fee>>16), byte(fee>>8), byte(fee) - copy(path[23:], tokenB[:]) - return path - } - - client, err := w3.Dial("https://rpc.ankr.com/eth") - if err != nil { - // handle error - } - defer client.Close() - - // 1. Create a VM that forks the Mainnet state from the latest block, - // disables the base fee, and has a fake WETH balance and approval for the router - vm, err := w3vm.New( - w3vm.WithFork(client, nil), - w3vm.WithNoBaseFee(), - w3vm.WithState(w3types.State{ - addrWETH: {Storage: w3types.Storage{ - w3vm.WETHBalanceSlot(addrEOA): common.BigToHash(w3.I("1 ether")), - w3vm.WETHAllowanceSlot(addrEOA, addrRouter): common.BigToHash(w3.I("1 ether")), - }}, - }), - ) - if err != nil { - // handle error - } - - // 2. Simulate a UniSwap v3 swap - receipt, err := vm.Apply(&w3types.Message{ - From: addrEOA, - To: &addrRouter, - Func: funcExactInput, - Args: []any{&ExactInputParams{ - Path: encodePath(addrWETH, 500, addrUNI), - Recipient: addrEOA, - Deadline: big.NewInt(time.Now().Unix()), - AmountIn: w3.I("1 ether"), - AmountOutMinimum: w3.Big0, - }}, - }) - if err != nil { - // handle error - } - - // 3. Decode output amount - var amountOut *big.Int - if err := receipt.DecodeReturns(&amountOut); err != nil { - // handle error - } - - fmt.Printf("amount out: %s UNI\n", w3.FromWei(amountOut, 18)) -} - -func ExampleVM_Call() { - client := w3.MustDial("https://rpc.ankr.com/eth") - defer client.Close() - - addrWETH := w3.A("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") - addrEOA := w3.A("0x000000000000000000000000000000000000c0Fe") - - vm, err := w3vm.New( - w3vm.WithFork(client, nil), - w3vm.WithState(w3types.State{ - addrWETH: {Storage: w3types.Storage{ - w3vm.WETHBalanceSlot(addrEOA): common.BigToHash(w3.I("1 ether")), - }}, - }), - ) - if err != nil { - // handle error - } - - balanceOf := w3.MustNewFunc("balanceOf(address)", "uint256") - var balance *big.Int - if err := vm.CallFunc(addrWETH, balanceOf, addrEOA).Returns(&balance); err != nil { - // handle error - } - fmt.Printf("%s: Balance: %s WETH\n", addrEOA, w3.FromWei(balance, 18)) - // Output: - // 0x000000000000000000000000000000000000c0Fe: Balance: 1 WETH -}