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 a463340c..19138984 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 @@ -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 @@ -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, @@ -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 @@ -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 @@ -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/components/DocLink.jsx b/docs/components/DocLink.jsx index 144949c1..866787b4 100644 --- a/docs/components/DocLink.jsx +++ b/docs/components/DocLink.jsx @@ -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 ( - + {title} ) diff --git a/docs/package-lock.json b/docs/package-lock.json index fa26dcf7..3320c0f5 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -5,16 +5,16 @@ "packages": { "": { "dependencies": { - "next": "^14.2.8", - "nextra": "3.0.0-alpha.36", - "nextra-theme-docs": "3.0.0-alpha.36", + "next": "^14.2.14", + "nextra": "3.0.0-alpha.40", + "nextra-theme-docs": "3.0.0-alpha.40", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { "autoprefixer": "^10.4.20", - "postcss": "^8.4.45", - "tailwindcss": "^3.4.10" + "postcss": "^8.4.47", + "tailwindcss": "^3.4.13" } }, "node_modules/@alloc/quick-lru": { @@ -98,32 +98,32 @@ "license": "Apache-2.0" }, "node_modules/@floating-ui/core": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", - "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.7" + "@floating-ui/utils": "^0.2.8" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.10", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", - "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", "license": "MIT", "dependencies": { "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.7" + "@floating-ui/utils": "^0.2.8" } }, "node_modules/@floating-ui/react": { - "version": "0.26.23", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.23.tgz", - "integrity": "sha512-9u3i62fV0CFF3nIegiWiRDwOs7OW/KhSUJDNx2MkQM3LbE5zQOY01sL3nelcVBXvX7Ovvo3A49I8ql+20Wg/Hw==", + "version": "0.26.24", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.24.tgz", + "integrity": "sha512-2ly0pCkZIGEQUq5H8bBK0XJmc1xIK/RM3tvVzY3GBER7IOD1UgmC2Y2tjj4AuS+TC+vTE1KJv2053290jua0Sw==", "license": "MIT", "dependencies": { - "@floating-ui/react-dom": "^2.1.1", - "@floating-ui/utils": "^0.2.7", + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", "tabbable": "^6.0.0" }, "peerDependencies": { @@ -132,9 +132,9 @@ } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", - "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.0.0" @@ -145,15 +145,15 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", - "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", "license": "MIT" }, "node_modules/@headlessui/react": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.5.tgz", - "integrity": "sha512-m3vzqwMTyDbgaNiSXQdrw8R4tRdnxVHHm4G/ZGS0TP6T8blEj3Ib1/zIJBzlvTXpBjTpd1DsUnRTonHyONMjSQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.8.tgz", + "integrity": "sha512-uajqVkAcVG/wHwG9Fh5PFMcFpf2VxM4vNRNKxRjuK009kePVur8LkuuygHfIE+2uZ7z7GnlTtYsyUe6glPpTLg==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.26.16", @@ -176,9 +176,9 @@ "license": "MIT" }, "node_modules/@iconify/utils": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.32.tgz", - "integrity": "sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==", + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.33.tgz", + "integrity": "sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==", "license": "MIT", "dependencies": { "@antfu/install-pkg": "^0.4.0", @@ -572,15 +572,15 @@ } }, "node_modules/@next/env": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.8.tgz", - "integrity": "sha512-L44a+ynqkolyNBnYfF8VoCiSrjSZWgEHYKkKLGcs/a80qh7AkfVUD/MduVPgdsWZ31tgROR+yJRA0PZjSVBXWQ==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.14.tgz", + "integrity": "sha512-/0hWQfiaD5//LvGNgc8PjvyqV50vGK0cADYzaoOOGN8fxzBn3iAiaq3S0tCRnFBldq0LVveLcxCTi41ZoYgAgg==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.8.tgz", - "integrity": "sha512-1VrQlG8OzdyvvGZhGJFnaNE2P10Jjy/2FopnqbY0nSa/gr8If3iINxvOEW3cmVeoAYkmW0RsBazQecA2dBFOSw==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.14.tgz", + "integrity": "sha512-bsxbSAUodM1cjYeA4o6y7sp9wslvwjSkWw57t8DtC8Zig8aG8V6r+Yc05/9mDzLKcybb6EN85k1rJDnMKBd9Gw==", "cpu": [ "arm64" ], @@ -594,9 +594,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.8.tgz", - "integrity": "sha512-87t3I86rNRSOJB1gXIUzaQWWSWrkWPDyZGsR0Z7JAPtLeX3uUOW2fHxl7dNWD2BZvbvftctTQjgtfpp7nMtmWg==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.14.tgz", + "integrity": "sha512-cC9/I+0+SK5L1k9J8CInahduTVWGMXhQoXFeNvF0uNs3Bt1Ub0Azb8JzTU9vNCr0hnaMqiWu/Z0S1hfKc3+dww==", "cpu": [ "x64" ], @@ -610,9 +610,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.8.tgz", - "integrity": "sha512-ta2sfVzbOpTbgBrF9HM5m+U58dv6QPuwU4n5EX4LLyCJGKc433Z0D9h9gay/HSOjLEXJ2fJYrMP5JYYbHdxhtw==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.14.tgz", + "integrity": "sha512-RMLOdA2NU4O7w1PQ3Z9ft3PxD6Htl4uB2TJpocm+4jcllHySPkFaUIFacQ3Jekcg6w+LBaFvjSPthZHiPmiAUg==", "cpu": [ "arm64" ], @@ -626,9 +626,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.8.tgz", - "integrity": "sha512-+IoLTPK6Z5uIgDhgeWnQF5/o5GBN7+zyUNrs4Bes1W3g9++YELb8y0unFybS8s87ntAKMDl6jeQ+mD7oNwp/Ng==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.14.tgz", + "integrity": "sha512-WgLOA4hT9EIP7jhlkPnvz49iSOMdZgDJVvbpb8WWzJv5wBD07M2wdJXLkDYIpZmCFfo/wPqFsFR4JS4V9KkQ2A==", "cpu": [ "arm64" ], @@ -642,9 +642,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.8.tgz", - "integrity": "sha512-pO+hVXC+mvzUOQJJRG4RX4wJsRJ5BkURSf6dD6EjUXAX4Ml9es1WsEfkaZ4lcpmFzFvY47IkDaffks/GdCn9ag==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.14.tgz", + "integrity": "sha512-lbn7svjUps1kmCettV/R9oAvEW+eUI0lo0LJNFOXoQM5NGNxloAyFRNByYeZKL3+1bF5YE0h0irIJfzXBq9Y6w==", "cpu": [ "x64" ], @@ -658,9 +658,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.8.tgz", - "integrity": "sha512-bCat9izctychCtf3uL1nqHq31N5e1VxvdyNcBQflkudPMLbxVnlrw45Vi87K+lt1CwrtVayHqzo4ie0Szcpwzg==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.14.tgz", + "integrity": "sha512-7TcQCvLQ/hKfQRgjxMN4TZ2BRB0P7HwrGAYL+p+m3u3XcKTraUFerVbV3jkNZNwDeQDa8zdxkKkw2els/S5onQ==", "cpu": [ "x64" ], @@ -674,9 +674,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.8.tgz", - "integrity": "sha512-gbxfUaSPV7EyUobpavida2Hwi62GhSJaSg7iBjmBWoxkxlmETOD7U4tWt763cGIsyE6jM7IoNavq0BXqwdW2QA==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.14.tgz", + "integrity": "sha512-8i0Ou5XjTLEje0oj0JiI0Xo9L/93ghFtAUYZ24jARSeTMXLUx8yFIdhS55mTExq5Tj4/dC2fJuaT4e3ySvXU1A==", "cpu": [ "arm64" ], @@ -690,9 +690,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.8.tgz", - "integrity": "sha512-PUXzEzjTTlUh3b5VAn1nlpwvujTnuCMMwbiCnaTazoVlN1nA3kWjlmp42IfURA2N/nyrlVEw7pURa/o4Qxj1cw==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.14.tgz", + "integrity": "sha512-2u2XcSaDEOj+96eXpyjHjtVPLhkAFw2nlaz83EPeuK4obF+HmtDJHqgR1dZB7Gb6V/d55FL26/lYVd0TwMgcOQ==", "cpu": [ "ia32" ], @@ -706,9 +706,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.8.tgz", - "integrity": "sha512-EnPKv0ttq02E9/1KZ/8Dn7kuutv6hy1CKc0HlNcvzOQcm4/SQtvfws5gY0zrG9tuupd3HfC2L/zcTrnBhpjTuQ==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.14.tgz", + "integrity": "sha512-MZom+OvZ1NZxuRovKt1ApevjiUJTcU2PmdJKL66xUPaJeRywnbGGRWUlaAOwunD6dX+pm83vj979NTC8QXjGWg==", "cpu": [ "x64" ], @@ -864,29 +864,65 @@ } }, "node_modules/@shikijs/core": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.16.2.tgz", - "integrity": "sha512-XSVH5OZCvE4WLMgdoBqfPMYmGHGmCC3OgZhw0S7KcSi2XKZ+5oHGe71GFnTljgdOxvxx5WrRks6QoTLKrl1eAA==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.18.0.tgz", + "integrity": "sha512-VK4BNVCd2leY62Nm2JjyxtRLkyrZT/tv104O81eyaCjHq4Adceq2uJVFJJAIof6lT1mBwZrEo2qT/T+grv3MQQ==", "license": "MIT", "dependencies": { - "@shikijs/vscode-textmate": "^9.2.0", - "@types/hast": "^3.0.4" + "@shikijs/engine-javascript": "1.18.0", + "@shikijs/engine-oniguruma": "1.18.0", + "@shikijs/types": "1.18.0", + "@shikijs/vscode-textmate": "^9.2.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.3" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.18.0.tgz", + "integrity": "sha512-qoP/aO/ATNwYAUw1YMdaip/YVEstMZEgrwhePm83Ll9OeQPuxDZd48szZR8oSQNQBT8m8UlWxZv8EA3lFuyI5A==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.18.0", + "@shikijs/vscode-textmate": "^9.2.2", + "oniguruma-to-js": "0.4.3" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.18.0.tgz", + "integrity": "sha512-B9u0ZKI/cud+TcmF8Chyh+R4V5qQVvyDOqXC2l2a4x73PBSBc6sZ0JRAX3eqyJswqir6ktwApUUGBYePdKnMJg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.18.0", + "@shikijs/vscode-textmate": "^9.2.2" } }, "node_modules/@shikijs/twoslash": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/@shikijs/twoslash/-/twoslash-1.16.2.tgz", - "integrity": "sha512-WzlCd7KnyfhBvGYb7tAbrxK1a9Rn2tQvAyv36RcggT418O3K5JRygiYAtf11qQjV1Q25TicczaosjPUVStFW0A==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@shikijs/twoslash/-/twoslash-1.18.0.tgz", + "integrity": "sha512-nbv1vEiNlM9GbXpN0++5QiT2NdUbAJ6y8yBuMWIiT04dxD3tdl7Ud3TL6hAZ6CAwMGn5hRaN+2va2oN1Rsy1Ww==", "license": "MIT", "dependencies": { - "@shikijs/core": "1.16.2", - "twoslash": "^0.2.10" + "@shikijs/core": "1.18.0", + "@shikijs/types": "1.18.0", + "twoslash": "^0.2.11" + } + }, + "node_modules/@shikijs/types": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.18.0.tgz", + "integrity": "sha512-O9N36UEaGGrxv1yUrN2nye7gDLG5Uq0/c1LyfmxsvzNPqlHzWo9DI0A4+fhW2y3bGKuQu/fwS7EPdKJJCowcVA==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^9.2.2", + "@types/hast": "^3.0.4" } }, "node_modules/@shikijs/vscode-textmate": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.0.tgz", - "integrity": "sha512-5FinaOp6Vdh/dl4/yaOTh0ZeKch+rYS8DUb38V3GMKYVkdqzxw53lViRKUYkVILRiVQT7dcPC7VvAKOR73zVtQ==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.2.2.tgz", + "integrity": "sha512-TMp15K+GGYrWlZM8+Lnj9EaHEFmOen0WJBrfa17hF7taDOYthuPPV0GWzfd/9iMij0akS/8Yw2ikquH7uVi/fg==", "license": "MIT" }, "node_modules/@swc/counter": { @@ -906,12 +942,12 @@ } }, "node_modules/@tanstack/react-virtual": { - "version": "3.10.7", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.7.tgz", - "integrity": "sha512-yeP+M0G8D+15ZFPivpuQ5hoM4Fa/PzERBx8P8EGcfEsXX3JOb9G9UUrqc47ZXAxvK+YqzM9T5qlJUYUFOwCZJw==", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz", + "integrity": "sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==", "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.10.7" + "@tanstack/virtual-core": "3.10.8" }, "funding": { "type": "github", @@ -923,9 +959,9 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.10.7", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.7.tgz", - "integrity": "sha512-ND5dfsU0n9F4gROzwNNDJmg6y8n9pI8YWxtgbfJ5UcNn7Hx+MxEXtXcQ189tS7sh8pmCObgz2qSiyRKTZxT4dg==", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.8.tgz", + "integrity": "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==", "license": "MIT", "funding": { "type": "github", @@ -974,9 +1010,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -1034,16 +1070,16 @@ } }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", "license": "MIT", "peer": true }, "node_modules/@types/react": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", - "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", + "version": "18.3.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.9.tgz", + "integrity": "sha512-+BpAVyTpJkNWWSSnaLBk6ePpHLOGJKnEQNbINNovPWzvEUyAe3e+/d494QdEh71RekM/qV7lw6jzf1HGrJyAtQ==", "license": "MIT", "peer": true, "dependencies": { @@ -1097,9 +1133,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -1301,9 +1337,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "funding": [ { @@ -1321,8 +1357,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, @@ -1355,9 +1391,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001657", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001657.tgz", - "integrity": "sha512-DPbJAlP8/BAXy3IgiWmZKItubb3TYGP0WscQQlVGIfT4s/YlFYVuJgyOsQNP7rJRChx/qdMeLJQJP0Sgg2yjNA==", + "version": "1.0.30001663", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz", + "integrity": "sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==", "funding": [ { "type": "opencollective", @@ -2154,12 +2190,12 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2242,9 +2278,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.14", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.14.tgz", - "integrity": "sha512-bEfPECb3fJ15eaDnu9LEJ2vPGD6W1vt7vZleSVyFhYuMIKm3vz/g9lt7IvEzgdwj58RjbPKUF2rXTCN/UW47tQ==", + "version": "1.5.28", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.28.tgz", + "integrity": "sha512-VufdJl+rzaKZoYVUijN13QcXVF5dWPZANeFTLNy+OSpHdDL5ynXTF35+60RSBbaQYB1ae723lQXHCrf4pyLsMw==", "dev": true, "license": "ISC" }, @@ -2746,9 +2782,9 @@ } }, "node_modules/hast-util-from-html": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.2.tgz", - "integrity": "sha512-HwOHwxdt2zC5KQ/CNoybBntRook2zJvfZE/u5/Ap7aLPe22bDqen7KwGkOqOyzL5zIqKwiYX/OTtE0FWgr6XXA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -2878,6 +2914,29 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-html": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", + "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", @@ -2906,18 +2965,18 @@ } }, "node_modules/hast-util-to-jsx-runtime/node_modules/inline-style-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz", - "integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", "license": "MIT" }, "node_modules/hast-util-to-jsx-runtime/node_modules/style-to-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.7.tgz", - "integrity": "sha512-uSjr59G5u6fbxUfKbb8GcqMGT3Xs9v5IbPkjb0S16GyOeBLAzSRK0CixBv5YrYvzO6TDLzIS6QCn78tkqWngPw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", "license": "MIT", "dependencies": { - "inline-style-parser": "0.2.3" + "inline-style-parser": "0.2.4" } }, "node_modules/hast-util-to-parse5": { @@ -3609,9 +3668,9 @@ } }, "node_modules/mdast-util-mdx-expression": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", - "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "license": "MIT", "dependencies": { "@types/estree-jsx": "^1.0.0", @@ -3747,9 +3806,9 @@ } }, "node_modules/mermaid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.1.0.tgz", - "integrity": "sha512-ICexrwPRzU1USFcpAdrVVGjCwEajD+iAwu2LVHi59D6VbXmFhwfB9TbCL3sA6NBR1tl5qUjQSAOdc9lOKlXnEw==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.2.1.tgz", + "integrity": "sha512-F8TEaLVVyxTUmvKswVFyOkjPrlJA5h5vNR1f7ZnSWSpqxgEZG1hggtn/QCa7znC28bhlcrNh10qYaIiill7q4A==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.1", @@ -4592,9 +4651,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/mz": { @@ -4628,12 +4687,12 @@ } }, "node_modules/next": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.8.tgz", - "integrity": "sha512-EyEyJZ89r8C5FPlS/401AiF3O8jeMtHIE+bLom9MwcdWJJFBgRl+MR/2VgO0v5bI6tQORNY0a0DR5sjpFNrjbg==", + "version": "14.2.14", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.14.tgz", + "integrity": "sha512-Q1coZG17MW0Ly5x76shJ4dkC23woLAhhnDnw+DfTc7EpZSGuWrlsZ3bZaO8t6u1Yu8FVfhkqJE+U8GC7E0GLPQ==", "license": "MIT", "dependencies": { - "@next/env": "14.2.8", + "@next/env": "14.2.14", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -4648,15 +4707,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.8", - "@next/swc-darwin-x64": "14.2.8", - "@next/swc-linux-arm64-gnu": "14.2.8", - "@next/swc-linux-arm64-musl": "14.2.8", - "@next/swc-linux-x64-gnu": "14.2.8", - "@next/swc-linux-x64-musl": "14.2.8", - "@next/swc-win32-arm64-msvc": "14.2.8", - "@next/swc-win32-ia32-msvc": "14.2.8", - "@next/swc-win32-x64-msvc": "14.2.8" + "@next/swc-darwin-arm64": "14.2.14", + "@next/swc-darwin-x64": "14.2.14", + "@next/swc-linux-arm64-gnu": "14.2.14", + "@next/swc-linux-arm64-musl": "14.2.14", + "@next/swc-linux-x64-gnu": "14.2.14", + "@next/swc-linux-x64-musl": "14.2.14", + "@next/swc-win32-arm64-msvc": "14.2.14", + "@next/swc-win32-ia32-msvc": "14.2.14", + "@next/swc-win32-x64-msvc": "14.2.14" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -4716,9 +4775,9 @@ } }, "node_modules/nextra": { - "version": "3.0.0-alpha.36", - "resolved": "https://registry.npmjs.org/nextra/-/nextra-3.0.0-alpha.36.tgz", - "integrity": "sha512-c6WeiMwgisQgF+dllj8bF8WI4jU4NvDBqqOqM9vstAbveMl95eBNUQ9QOdvfpwNasX9Azf/uPG3PNarMKoZYQg==", + "version": "3.0.0-alpha.40", + "resolved": "https://registry.npmjs.org/nextra/-/nextra-3.0.0-alpha.40.tgz", + "integrity": "sha512-8/JfKASmsCV2mEH0eaJRfvxjwq5XSuUDAP8ByqQScgHQGqiZkmUYkve3zElm/n0v1e3Ksjs3wdz1YMKO4eqYXA==", "license": "MIT", "dependencies": { "@headlessui/react": "^2.1.2", @@ -4765,9 +4824,9 @@ } }, "node_modules/nextra-theme-docs": { - "version": "3.0.0-alpha.36", - "resolved": "https://registry.npmjs.org/nextra-theme-docs/-/nextra-theme-docs-3.0.0-alpha.36.tgz", - "integrity": "sha512-uFixrxEmmqTGOPNvHSDY6IuoxxgqajE9WV6OlU3YnWnDuA3WDDlvYF13S1YIN8FSal+YILH1wuE9KFCSvv7GqA==", + "version": "3.0.0-alpha.40", + "resolved": "https://registry.npmjs.org/nextra-theme-docs/-/nextra-theme-docs-3.0.0-alpha.40.tgz", + "integrity": "sha512-xpCDCm+DFQr/hw/8uLx7w7u02pM6odaNK1Y+GzUIXgmRZtpaifvX6UpvdsAPu4Tvkhy1LbfPw9Qtcl9QM9e9Hg==", "license": "MIT", "dependencies": { "@headlessui/react": "^2.1.2", @@ -4781,7 +4840,7 @@ }, "peerDependencies": { "next": ">=13", - "nextra": "3.0.0-alpha.36", + "nextra": "3.0.0-alpha.40", "react": ">=16.13.1", "react-dom": ">=16.13.1" } @@ -4879,6 +4938,18 @@ "node": ">= 6" } }, + "node_modules/oniguruma-to-js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz", + "integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==", + "license": "MIT", + "dependencies": { + "regex": "^4.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -5102,9 +5173,9 @@ } }, "node_modules/postcss": { - "version": "8.4.45", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", - "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -5123,8 +5194,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -5355,6 +5426,12 @@ "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==", "license": "MIT" }, + "node_modules/regex": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.2.tgz", + "integrity": "sha512-kK/AA3A9K6q2js89+VMymcboLOlF5lZRCYJv3gzszXFHBr6kO6qLGzbm+UIugBEV8SMMKCTR59txoY6ctRHYVw==", + "license": "MIT" + }, "node_modules/rehype-katex": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", @@ -5599,9 +5676,9 @@ } }, "node_modules/remark-rehype": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", - "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -5844,13 +5921,16 @@ } }, "node_modules/shiki": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.16.2.tgz", - "integrity": "sha512-gSym0hZf5a1U0iDPsdoOAZbvoi+e0c6c3NKAi03FoSLTm7oG20tum29+gk0wzzivOasn3loxfGUPT+jZXIUbWg==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.18.0.tgz", + "integrity": "sha512-8jo7tOXr96h9PBQmOHVrltnETn1honZZY76YA79MHheGQg55jBvbm9dtU+MI5pjC5NJCFuA6rvVTLVeSW5cE4A==", "license": "MIT", "dependencies": { - "@shikijs/core": "1.16.2", - "@shikijs/vscode-textmate": "^9.2.0", + "@shikijs/core": "1.18.0", + "@shikijs/engine-javascript": "1.18.0", + "@shikijs/engine-oniguruma": "1.18.0", + "@shikijs/types": "1.18.0", + "@shikijs/vscode-textmate": "^9.2.2", "@types/hast": "^3.0.4" } }, @@ -5889,9 +5969,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -6183,9 +6263,9 @@ "license": "MIT" }, "node_modules/tailwindcss": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", - "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", + "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", "dev": true, "license": "MIT", "dependencies": { @@ -6335,28 +6415,28 @@ "license": "0BSD" }, "node_modules/twoslash": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/twoslash/-/twoslash-0.2.10.tgz", - "integrity": "sha512-EBnFbGSD7VtPYCYe8tnx5wDzTfQ2wDS10J89BnTr97Zu2+wRD/CskNLPvvDEZofb37mLSce/YuTP8GgGj+vSOg==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/twoslash/-/twoslash-0.2.11.tgz", + "integrity": "sha512-392Qkcu5sD2hROLZ+XPywChreDGJ8Yu5nnK/Moxfti/R39q0Q39MaV7iHjz92B5qucyjsQFnKMdYIzafX5T8dg==", "license": "MIT", "dependencies": { "@typescript/vfs": "^1.6.0", - "twoslash-protocol": "0.2.10" + "twoslash-protocol": "0.2.11" }, "peerDependencies": { "typescript": "*" } }, "node_modules/twoslash-protocol": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/twoslash-protocol/-/twoslash-protocol-0.2.10.tgz", - "integrity": "sha512-9sFHqPUexUPKAZM2e0dbAh0Hwkxd5fqSKWWMQ8Ah+O6MeYEUDGuWUUw5aUfS6veAqo+24VfV3xF0Vj0ZbH7x3Q==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/twoslash-protocol/-/twoslash-protocol-0.2.11.tgz", + "integrity": "sha512-rp+nkOWbKfJnBTDZtnIaBGjnU+4CaMhqu6db2UU7byU96rH8X4hao4BOxYw6jdZc85Lhv5pOfcjgfHeQyLzndQ==", "license": "MIT" }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "license": "Apache-2.0", "peer": true, "bin": { @@ -6897,9 +6977,9 @@ } }, "node_modules/zod-validation-error": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.3.1.tgz", - "integrity": "sha512-uFzCZz7FQis256dqw4AhPQgD6f3pzNca/Zh62RNELavlumQB3nDIUFbF5JQfFLcMbO1s02Q7Xg/gpcOBlEnYZA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz", + "integrity": "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==", "license": "MIT", "engines": { "node": ">=18.0.0" diff --git a/docs/package.json b/docs/package.json index 680ef6ad..a186972c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,15 +5,15 @@ "build": "next build" }, "dependencies": { - "next": "^14.2.8", - "nextra": "3.0.0-alpha.36", - "nextra-theme-docs": "3.0.0-alpha.36", + "next": "^14.2.14", + "nextra": "3.0.0-alpha.40", + "nextra-theme-docs": "3.0.0-alpha.40", "react": "^18.3.1", "react-dom": "^18.3.1" }, "devDependencies": { "autoprefixer": "^10.4.20", - "postcss": "^8.4.45", - "tailwindcss": "^3.4.10" + "postcss": "^8.4.47", + "tailwindcss": "^3.4.13" } } 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..e720a1ba --- /dev/null +++ b/docs/pages/examples/index.mdx @@ -0,0 +1,35 @@ +# 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 calls** (and opcodes) of a transaction ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-TraceCalls)) +* **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..0988face 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/rpc-extension.mdx b/docs/pages/rpc-extension.mdx index 91a229da..655f1d7c 100644 --- a/docs/pages/rpc-extension.mdx +++ b/docs/pages/rpc-extension.mdx @@ -15,7 +15,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 diff --git a/docs/pages/rpc-overview.mdx b/docs/pages/rpc-overview.mdx index 70515c81..b11981d0 100644 --- a/docs/pages/rpc-overview.mdx +++ b/docs/pages/rpc-overview.mdx @@ -1,6 +1,6 @@ # RPC Client - is a blazing fast RPC client build on top of `go-ethereum/rpc.Client`. It is designed for batch requests and easy extendibility. + is a blazing fast RPC client, built on top of `go-ethereum/rpc.Client`. It is designed for **batch requests** and **easy extendibility**. ## Get Started @@ -40,8 +40,8 @@ if err := client.Call( - #### Why send batch requests? - Most of the time you need to call multiple RPC methods to get the data you need. When you make separate requests per RPC call you need a single round trip to the server for each call. This can be slow, especially for remote endpoints. Batching multiple RPC calls into a single request only requires a single round trip, and speeds up RPC calls significantly. +#### Why send batch requests? +Most of the time you need to call multiple RPC methods to get the data you need. When you make separate requests per RPC call you need a single round trip to the server for each call. This can be slow, especially for remote endpoints. Batching multiple RPC calls into a single request only requires a single round trip, and speeds up RPC calls significantly. ## Call @@ -54,8 +54,48 @@ if err := client.Call( ## Subscribe -Coming soon... +`w3.Client` supports subscriptions through the and methods. Subscriptions can be used to listen to events, emitted by the Ethereum node. + +### Subscriptions + +* `eth.NewHeads(ch chan<- *types.Header)`: Subscribe to new block headers. +* `eth.NewLogs(ch chan<- *types.Log, q ethereum.FilterQuery)`: Subscribe to new logs. +* `eth.PendingTransactions(ch chan<- *types.Transaction)`: Subscribe to new pending transactions. + +#### Example: Subscribe to Pending Transactions + +Subscribe to new pending transactions ([Playground](https://pkg.go.dev/github.com/lmittmann/w3##example-Client-SubscribeToPendingTransactions)): + +```go +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 + } +} +``` ## Error Handling -Coming soon... +If one or more calls in a batch request fail, `Client.Call` returns an error of type . + +#### Example: `w3.CallErrors` + +Check which RPC calls failed in a batch request ([Playground](https://pkg.go.dev/github.com/lmittmann/w3##example-Client-BatchHandleError)) +```go +var batchErr w3.CallErrors +if err := client.Call(calls...); errors.As(err, &batchErr) { + // handle call errors +} else if err != nil { + // handle other errors +} +``` 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/pages/vm-overview.mdx b/docs/pages/vm-overview.mdx index 63758741..724372ae 100644 --- a/docs/pages/vm-overview.mdx +++ b/docs/pages/vm-overview.mdx @@ -1,46 +1,206 @@ # 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. + is an easy-to-use Ethereum Virtual Machine (EVM), built on top of `go-ethereum`'s `vm.EVM`. It supports **tracing**, **state forking** via RPC, and can be used for simulation, debugging EVM execution, or testing Smart Contracts. -**Example:** Simulate an Uniswap v3 swap ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM)) +* **State forking** via RPC or custom state fetchers enables transaction simulations or Smart Contract tests on live, or historical chain state. +* **Tracing** of EVM execution is supported via `go-ethereum/core/tracing.Hooks`. + + +## Get Started + + + +### Create a VM Instance + +Create a VM instance, that forks the latest Mainnet state. ```go -// 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 +client, err := w3.Dial("https://rpc.ankr.com/eth") +if err != nil { + // ... +} +defer client.Close() + 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 + // ... } +``` + +### Simulate a Simple Message + +Transfer ETH from the zero address to a random recipient. + +```go +recipient := w3vm.RandA() -// 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, - }}, + From: common.Address{}, + To: &recipient, + Value: w3.I("1 ether"), }) +if err != nil { + // ... +} +``` + +### Verify the Recipient's Balance + +Verify the recipient's balance after the applied message. + +```go +balance, err := vm.Balance(recipient) +if err != nil { + // ... +} + +fmt.Printf("Balance: %s ETH\n", w3.FromWei(balance, 18)) +// Output: Balance: 1 ETH +``` + + + + +## Setup + +A new VM instance is created using the `w3vm.New` function, which accepts various options to customize the VM behavior: + +* `WithChainConfig(cfg *params.ChainConfig)`: Sets the chain configuration. If not provided, the VM defaults to the Mainnet configuration. +* `WithNoBaseFee()`: Forces the EIP-1559 base fee to 0. +* `WithBlockContext(ctx *vm.BlockContext)`: Sets the block context for the VM. +* `WithHeader(header *types.Header)`: Configures the block context for the VM using the provided header. +* `WithState(state w3types.State)`: Sets the pre-state of the VM. When used with `WithFork`, the pre-state overrides the forked state. +* `WithStateDB(db *state.StateDB)`: Specifies the state database for the VM, typically a snapshot from `VM.Snapshot`. +* `WithFork(client *w3.Client, blockNumber *big.Int)`: Forks state from a live Ethereum client at the specified block number. +* `WithFetcher(fetcher Fetcher)`: Assigns a fetcher to the VM. +* `WithTB(tb testing.TB)`: Enables persistent state caching when used in conjunction with `WithFork`. + + +## Execution + +Messages represent transactions or contract calls that can be executed by the VM. + +All execution methods support **tracing** via `go-ethereum/core/tracing.Hooks`. [Learn more ➔](/vm-tracing) + +### `Apply` Method + + applies a `w3types.Message` to the VM and returns a `Receipt`. If the execution doesn't revert, the VM's underlying state may change. + +#### Example: Apply a Message + +```go +msg := &w3types.Message{ + From: addrSender, + To: &addrRecipient, + Value: w3.I("1 ether"), + Gas: 21000, +} + +receipt, err := vm.Apply(msg) +if err != nil { + // ... +} +fmt.Printf("Gas Used: %d\n", receipt.GasUsed) +``` + +### `ApplyTx` Method + + is like `Apply`, but takes a `types.Transaction` instead of a message. The given transaction is converted to a message internally, using a signer, that is derived from the VM's chain configuration and fork block. + +### `Call` Method + + is like `Apply`, but any state changes during execution are reverted in the end, so the VM's state is never modified. + +#### Example: Call `balanceOf` + +```go +funcBalanceOf := w3.MustNewFunc("balanceOf(address)", "uint256") + +msg := &w3types.Message{ + To: &addrToken, + Func: funcBalanceOf, + Args: []any{addrOwner}, +} + +receipt, err := vm.Call(msg) if err != nil { // handle error } -// 3. Decode output amount -var amountOut *big.Int -if err := receipt.DecodeReturns(&amountOut); err != nil { +var balance *big.Int +if err := receipt.DecodeReturns(&balance); err != nil { // handle error } +fmt.Printf("Balance: %s\n", balance) ``` + +### `CallFunc` Method + + is a helper, that greatly simplifies common usage of `Call`. It is designed analogues to the `eth.CallFunc` RPC client method. + +#### Example: Call `balanceOf` with `CallFunc` + +This is a simplified version of the [Call `balanceOf`](#example-call-balanceof) example. + +```go +funcBalanceOf := w3.MustNewFunc("balanceOf(address)", "uint256") + +var balance *big.Int +err := vm.CallFunc(addrToken, funcBalanceOf, addrOwner).Returns(&balance) +if err != nil { + // handle error +} +fmt.Printf("Balance: %s\n", balance) +``` + +### `Receipt` Type + +The `Receipt` struct contains the result of an executed message. + +#### Fields + +* `GasUsed uint64`: Gas used for executing the message. +* `GasRefund uint64`: Gas refunded after executing the message. +* `Logs []*types.Log`: Logs emitted while executing the message. +* `Output []byte`: Output of the executed message. +* `ContractAddress *common.Address`: Address of the created contract, if any. +* `Err error`: Execution error, if any. + +#### Methods + +* `DecodeReturns(returns ...any) error`: Decodes the return values. This method only works, if the executed message had `w3types.Message.Func` set. + + +## State + +The VM provides methods to read, and write account state. + +### Reading State + +* `vm.Balance(addr common.Address) (*big.Int, error)`: Returns the balance of the given address. +* `vm.Nonce(addr common.Address) (uint64, error)`: Returns the nonce of the given address. +* `vm.Code(addr common.Address) ([]byte, error)`: Returns the code of the given address. +* `vm.StorageAt(addr common.Address, slot common.Hash) (common.Hash, error)`: Returns the state of the given address at the given storage slot. + + +An error only can only occur, if the VM fails to fetch state via a `w3vm.Fetcher`. Thus, it is safe to ignore the error, if no state fetcher is used by the VM. + + +### Writing State + +* `vm.SetBalance(addr common.Address, balance *big.Int)`: Sets the balance of the given address. +* `vm.SetNonce(addr common.Address, nonce uint64)`: Sets the nonce of the given address. +* `vm.SetCode(addr common.Address, code []byte)`: Sets the code of the given address. +* `vm.SetStorageAt(addr common.Address, slot common.Hash, value common.Hash)`: Sets the state of the given address at the give storage slot. + + +## Helper + +* `w3vm.RandA() common.Address`: Returns a random address. +* `WETHBalanceSlot(addr common.Address) common.Hash`: Returns the storage slot that stores the WETH balance of the given address. +* `WETHAllowanceSlot(owner, spender common.Address) common.Hash`: Returns the storage slot that stores the WETH allowance of the given owner to the spender. +* `Slot(pos, key common.Hash) common.Hash`: Returns the storage slot of a mapping with the given position and key. +* `Slot2(pos, key, key2 common.Hash) common.Hash`: Returns the storage slot of a double mapping with the given position and keys. diff --git a/docs/pages/vm-testing.mdx b/docs/pages/vm-testing.mdx index 8ba6c523..59fbf179 100644 --- a/docs/pages/vm-testing.mdx +++ b/docs/pages/vm-testing.mdx @@ -1,3 +1,183 @@ -# Test Contracts +# Contract Testing -Coming soon... +`w3vm` can be used to test Smart Contracts in Go utilizing Go's handy testing and fuzzing features. + + +`w3vm` **does not** natively support Smart Contract compilation. + + +## Compile Smart Contracts + +The first step to testing a Smart Contract is usually to compile it to bytecode. There are a number of third party packages that provide compiler bindings in Go: + +* [`go-solc`](https://github.com/lmittmann/go-solc): Go bindings for the Solidity compiler (`solc`) +* [`go-huffc`](https://github.com/project-blanc/go-huffc): Go Bindings for the Huff Compiler (`huffc`) +* [`geas`](https://github.com/fjl/geas): The Good Ethereum Assembler + + +## Setup a `w3vm.VM` + +Before a Smart Contract can be tested with a `w3vm.VM` instance, its bytecode must be deployed to the VM. This can be done in two ways, depending on whether constructor logic is present. + +### Without Constructor Logic + +If the Smart Contract does not require constructor logic, its runtime bytecode can be directly set as the bytecode of an address: + + +```go +contractRuntime = w3.B("0x...") +contractAddr := w3vm.RandA() + +vm, _ := w3vm.New( + w3vm.WithState(w3types.State{ + contractAddr: {Code: runtime}, + }), +) +``` + +### With Constructor Logic + +If the Smart Contract requires constructor logic, the constructor bytecode must be sent in a standard deployment transaction (`w3types.Message`) without recipient: + +```go +contractConstructor := w3.B("0x...") +deployerAddr := w3vm.RandA() + +vm, _ := w3vm.New() +receipt, err := vm.Apply(&w3types.Message{ + From: deployerAddr, + Input: contractConstructor, +}) +if err != nil || receipt.ContractAddress == nil { + // ... +} +contractAddr := *receipt.ContractAddress +``` + +### Custom State + +The state of the VM can be fully customized using the `w3vm.WithState` option. This allows, e.g., setting a balance for addresses that interact with the Smart Contract. State can also be modified after the VM is created using the [state write methods](/vm-overview#writing-state). + +### State Forking + +If the tested Smart Contract interacts with other existing contracts, the VM can be configured to fork the state at a specific block number (or the latest block). This enables testing contracts in a real-world environment. + +```go +client := w3.MustDial("https://rpc.ankr.com/eth") +defer client.Close() + +vm, err := w3vm.New( + w3vm.WithFork(client, big.NewInt(20_000_000)), + w3vm.WithNoBaseFee(), + w3vm.WithTB(t), +) +if err != nil { + // ... +} +``` + + +`w3vm.WithTB(t)` can be used in tests or benchmarks to **cache state**. The VM persists this cached state in `{package of test}/testdata/w3vm/`. This is particularly useful when working with public RPC providers, as it reduces the number of requests and significantly speeds up test execution. + + + +## Testing + +Testing Smart Contracts with `w3vm` follows the standard Go testing patterns using the package `testing`. By integrating `w3vm` into your tests, you can simulate blockchain interactions and validate Smart Contract behaviors within your test cases. + +#### Example: Test WETH `deposit` Function + +Test of the WETH `deposit` function. + +```go +func TestWETHDeposit(t *testing.T) { + // setup VM + vm, _ := w3vm.New( + w3vm.WithState(w3types.State{ + addrWETH: {Code: codeWETH}, + addrA: {Balance: w3.I("1 ether")}, + }), + ) + + // pre check + var wethBalanceBefore *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&wethBalanceBefore); err != nil { + t.Fatal(err) + } + if wethBalanceBefore.Sign() != 0 { + t.Fatal("Invalid WETH balance: want 0") + } + + // deposit (via fallback) + if _, err := vm.Apply(&w3types.Message{ + From: addrA, + To: &addrWETH, + Value: w3.I("1 ether"), + }); err != nil { + t.Fatalf("Deposit failed: %v", err) + } + + // post check + var wethBalanceAfter *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&wethBalanceAfter); err != nil { + t.Fatal(err) + } + if w3.I("1 ether").Cmp(wethBalanceAfter) != 0 { + t.Fatalf("Invalid WETH balance: want 1") + } +} +``` + + +## Fuzz Testing + +Fuzzing Smart Contracts with `w3vm` leverages Go's fuzz testing capabilities to automatically generate a wide range of inputs for your contracts. By incorporating `w3vm` into your fuzzing tests, you can effectively discover vulnerabilities and unexpected behaviors in your Smart Contracts. + +#### Example: Fuzz Test WETH `deposit` Function + +Fuzz test of the WETH `deposit` function. + +```go +func FuzzWETHDeposit(f *testing.F) { + f.Add([]byte{1}) + f.Fuzz(func(t *testing.T, amountBytes []byte) { + if len(amountBytes) > 32 { + t.Skip() + } + amount := new(big.Int).SetBytes(amountBytes) + + // setup VM + vm, _ := w3vm.New( + w3vm.WithState(w3types.State{ + addrWETH: {Code: codeWETH}, + addrA: {Balance: w3.BigMaxUint256}, + }), + ) + + // Pre-check WETH balance + var wethBalanceBefore *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&wethBalanceBefore); err != nil { + t.Fatal(err) + } + + // Attempt deposit + vm.Apply(&w3types.Message{ + From: addrA, + To: &addrWETH, + Value: amount, + }) + + // Post-check WETH balance + var wethBalanceAfter *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&wethBalanceAfter); err != nil { + t.Fatal(err) + } + + // Verify balance increment + wantBalance := new(big.Int).Add(wethBalanceBefore, amount) + if wethBalanceAfter.Cmp(wantBalance) != 0 { + t.Fatalf("Invalid WETH balance: want %s, got %s", wantBalance, wethBalanceAfter) + } + }) +} +``` diff --git a/docs/pages/vm-tracing.mdx b/docs/pages/vm-tracing.mdx index 288bc214..2fda29be 100644 --- a/docs/pages/vm-tracing.mdx +++ b/docs/pages/vm-tracing.mdx @@ -1,3 +1,59 @@ # Tracing -Coming soon... +Tracing can give detailed insights into the execution of EVM contracts. `w3vm.VM` supports tracing via `go‑ethereum`'s . + +## Usage + +A `tracing.Hooks` can be passed to , , and . These methods can also be called with multiple hooks at the same time. + +#### Example: Trace Calls and OpCodes of an Execution + +`w3vm` contains a powerful call an opcode tracer that can be used gain detailed insights into the execution of EVM contracts ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-Trace)): + +```go +callTracer := hooks.NewCallTracer(os.Stdout, &hooks.CallTracerOptions{ + ShowStaticcall: true, + DecodeABI: true, +}) +vm.ApplyTx(tx, callTracer) +``` + +![Example Call Trace](/assets/call-trace.png) + +#### Example: Generate an Access List + +Access list tracing using `go-ethereum`'s ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-TraceAccessList)): + +```go {2-8} +// setup access list tracer +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()) +``` + +#### Example: Trace the Execution of all OpCodes in a Block + +Trace the execution of all op's in a block ([Playground](https://pkg.go.dev/github.com/lmittmann/w3/w3vm#example-VM-TraceBlock)): + +```go {2-7} +// setup block op's tracer +var opCount [256]uint64 +tracer := &tracing.Hooks{ + OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + opCount[op]++ + }, +} + +for _, tx := range block.Transactions() { + vm.ApplyTx(tx, tracer) +} +``` diff --git a/docs/public/_redirects b/docs/public/_redirects index 4ccfb0c3..923e35ff 100644 --- a/docs/public/_redirects +++ b/docs/public/_redirects @@ -1 +1,3 @@ -/examples https://github.com/lmittmann/w3/tree/main/examples +/vm /vm-overview +/rpc /rpc-overview +/abi /helper-abi diff --git a/docs/public/assets/call-trace.png b/docs/public/assets/call-trace.png new file mode 100644 index 00000000..94edee30 Binary files /dev/null and b/docs/public/assets/call-trace.png differ diff --git a/docs/public/assets/ef-logo-dark.svg b/docs/public/assets/ef-logo-dark.svg new file mode 100644 index 00000000..9b6c5298 --- /dev/null +++ b/docs/public/assets/ef-logo-dark.svg @@ -0,0 +1 @@ + diff --git a/docs/public/assets/ef-logo.svg b/docs/public/assets/ef-logo.svg new file mode 100644 index 00000000..ebffdce7 --- /dev/null +++ b/docs/public/assets/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 9f95b81e..00000000 --- a/examples/go.mod +++ /dev/null @@ -1,39 +0,0 @@ -module examples - -go 1.22 - -require ( - github.com/ethereum/go-ethereum v1.14.11 - 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.13.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-ipa v0.0.0-20240223125850-b1e8a79f509c // 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/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // 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.13 // 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.22.0 // indirect - golang.org/x/time v0.6.0 // indirect - rsc.io/tmplfunc v0.0.3 // indirect -) diff --git a/examples/go.sum b/examples/go.sum deleted file mode 100644 index 101cb16e..00000000 --- a/examples/go.sum +++ /dev/null @@ -1,147 +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.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= -github.com/bits-and-blooms/bitset v1.13.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.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA= -github.com/cockroachdb/pebble v1.1.2/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.11 h1:8nFDCUUE67rPc6AKxFj7JKaOa2W/W1Rse3oS6LvvxEY= -github.com/ethereum/go-ethereum v1.14.11/go.mod h1:+l/fr42Mma+xBnhefL/+z11/hcmJ2egl+ScIVPjhc7E= -github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= -github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= -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/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= -github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -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.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/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.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -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.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= -github.com/supranational/blst v0.3.13/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.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.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.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.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/go.mod b/go.mod index 3474476e..df9b316d 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,11 @@ module github.com/lmittmann/w3 go 1.22 require ( + github.com/charmbracelet/lipgloss v1.0.0 github.com/ethereum/go-ethereum v1.14.11 github.com/google/go-cmp v0.6.0 - github.com/holiman/uint256 v1.3.1 - golang.org/x/time v0.6.0 + github.com/holiman/uint256 v1.3.2 + golang.org/x/time v0.8.0 ) require ( @@ -14,10 +15,12 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charmbracelet/x/ansi v0.4.2 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect @@ -43,16 +46,19 @@ require ( github.com/klauspost/compress v1.16.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.12.0 // indirect github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/supranational/blst v0.3.13 // indirect diff --git a/go.sum b/go.sum index 938de2b9..dd91a15d 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -64,6 +66,10 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41kof+qk= +github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -216,8 +222,8 @@ github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6w github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= -github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= @@ -251,13 +257,15 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 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/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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= @@ -273,6 +281,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= @@ -318,8 +328,9 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 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.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 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= @@ -502,6 +513,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.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.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -519,8 +531,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/internal/fourbyte/events.go b/internal/fourbyte/events.go new file mode 100644 index 00000000..20d065cb --- /dev/null +++ b/internal/fourbyte/events.go @@ -0,0 +1,14 @@ +// Code generated by "go generate"; DO NOT EDIT. +package fourbyte + +import "github.com/lmittmann/w3" + +var events = map[[32]byte]*w3.Event{ + {0x0d, 0x36, 0x48, 0xbd, 0x0f, 0x6b, 0xa8, 0x01, 0x34, 0xa3, 0x3b, 0xa9, 0x27, 0x5a, 0xc5, 0x85, 0xd9, 0xd3, 0x15, 0xf0, 0xad, 0x83, 0x55, 0xcd, 0xde, 0xfd, 0xe3, 0x1a, 0xfa, 0x28, 0xd0, 0xe9}: w3.MustNewEvent("PairCreated(address indexed token0, address indexed token1, address pair, uint256)"), + {0x1c, 0x41, 0x1e, 0x9a, 0x96, 0xe0, 0x71, 0x24, 0x1c, 0x2f, 0x21, 0xf7, 0x72, 0x6b, 0x17, 0xae, 0x89, 0xe3, 0xca, 0xb4, 0xc7, 0x8b, 0xe5, 0x0e, 0x06, 0x2b, 0x03, 0xa9, 0xff, 0xfb, 0xba, 0xd1}: w3.MustNewEvent("Sync(uint112 reserve0, uint112 reserve1)"), + {0x4c, 0x20, 0x9b, 0x5f, 0xc8, 0xad, 0x50, 0x75, 0x8f, 0x13, 0xe2, 0xe1, 0x08, 0x8b, 0xa5, 0x6a, 0x56, 0x0d, 0xff, 0x69, 0x0a, 0x1c, 0x6f, 0xef, 0x26, 0x39, 0x4f, 0x4c, 0x03, 0x82, 0x1c, 0x4f}: w3.MustNewEvent("Mint(address indexed sender, uint256 amount0, uint256 amount1)"), + {0x8c, 0x5b, 0xe1, 0xe5, 0xeb, 0xec, 0x7d, 0x5b, 0xd1, 0x4f, 0x71, 0x42, 0x7d, 0x1e, 0x84, 0xf3, 0xdd, 0x03, 0x14, 0xc0, 0xf7, 0xb2, 0x29, 0x1e, 0x5b, 0x20, 0x0a, 0xc8, 0xc7, 0xc3, 0xb9, 0x25}: w3.MustNewEvent("Approval(address indexed owner, address indexed spender, uint256 value)"), + {0xd7, 0x8a, 0xd9, 0x5f, 0xa4, 0x6c, 0x99, 0x4b, 0x65, 0x51, 0xd0, 0xda, 0x85, 0xfc, 0x27, 0x5f, 0xe6, 0x13, 0xce, 0x37, 0x65, 0x7f, 0xb8, 0xd5, 0xe3, 0xd1, 0x30, 0x84, 0x01, 0x59, 0xd8, 0x22}: w3.MustNewEvent("Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to)"), + {0xdc, 0xcd, 0x41, 0x2f, 0x0b, 0x12, 0x52, 0x81, 0x9c, 0xb1, 0xfd, 0x33, 0x0b, 0x93, 0x22, 0x4c, 0xa4, 0x26, 0x12, 0x89, 0x2b, 0xb3, 0xf4, 0xf7, 0x89, 0x97, 0x6e, 0x6d, 0x81, 0x93, 0x64, 0x96}: w3.MustNewEvent("Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to)"), + {0xdd, 0xf2, 0x52, 0xad, 0x1b, 0xe2, 0xc8, 0x9b, 0x69, 0xc2, 0xb0, 0x68, 0xfc, 0x37, 0x8d, 0xaa, 0x95, 0x2b, 0xa7, 0xf1, 0x63, 0xc4, 0xa1, 0x16, 0x28, 0xf5, 0x5a, 0x4d, 0xf5, 0x23, 0xb3, 0xef}: w3.MustNewEvent("Transfer(address indexed from, address indexed to, uint256 value)"), +} diff --git a/internal/fourbyte/events.txt b/internal/fourbyte/events.txt new file mode 100644 index 00000000..aafbafe8 --- /dev/null +++ b/internal/fourbyte/events.txt @@ -0,0 +1,7 @@ +Approval(address indexed owner, address indexed spender, uint256 value) +Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to) +Mint(address indexed sender, uint256 amount0, uint256 amount1) +PairCreated(address indexed token0, address indexed token1, address pair, uint256) +Swap(address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to) +Sync(uint112 reserve0, uint112 reserve1) +Transfer(address indexed from, address indexed to, uint256 value) diff --git a/internal/fourbyte/fourbyte.go b/internal/fourbyte/fourbyte.go new file mode 100644 index 00000000..2d1d28c8 --- /dev/null +++ b/internal/fourbyte/fourbyte.go @@ -0,0 +1,38 @@ +//go:generate go run gen.go + +package fourbyte + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/lmittmann/w3" +) + +var ( + precompile1 = w3.MustNewFunc("ecRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)", "address") + precompile2 = w3.MustNewFunc("keccak(bytes)", "bytes32") + precompile3 = w3.MustNewFunc("ripemd160(bytes)", "bytes32") + precompile4 = w3.MustNewFunc("identity(bytes)", "bytes") + + addr1 = common.BytesToAddress([]byte{0x01}) + addr2 = common.BytesToAddress([]byte{0x02}) + addr3 = common.BytesToAddress([]byte{0x03}) + addr4 = common.BytesToAddress([]byte{0x04}) +) + +func Function(sig [4]byte, addr common.Address) (fn *w3.Func, isPrecompile bool) { + switch addr { + case addr1: + return precompile1, true + case addr2: + return precompile2, true + case addr3: + return precompile3, true + case addr4: + return precompile4, true + } + return functions[sig], false +} + +func Event(topic0 [32]byte, addr common.Address) *w3.Event { + return events[topic0] +} diff --git a/internal/fourbyte/funcs.go b/internal/fourbyte/funcs.go new file mode 100644 index 00000000..e9205ec0 --- /dev/null +++ b/internal/fourbyte/funcs.go @@ -0,0 +1,202 @@ +// Code generated by "go generate"; DO NOT EDIT. +package fourbyte + +import "github.com/lmittmann/w3" + +var functions = map[[4]byte]*w3.Func{ + {0x01, 0xff, 0xc9, 0xa7}: w3.MustNewFunc("supportsInterface(bytes4)", "bool"), + {0x02, 0x2c, 0x0d, 0x9f}: w3.MustNewFunc("swap(uint256 amount0Out, uint256 amount1Out, address to, bytes data)", ""), + {0x02, 0x75, 0x1c, 0xec}: w3.MustNewFunc("removeLiquidityETH(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline)", "uint256 amountToken, uint256 amountETH"), + {0x02, 0xcc, 0x25, 0x0d}: w3.MustNewFunc("isSolver(address)", "bool"), + {0x05, 0x4d, 0x50, 0xd4}: w3.MustNewFunc("getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut)", "uint256 amountOut"), + {0x05, 0x8a, 0x62, 0x8f}: w3.MustNewFunc("setAuthorizer(address newAuthorizer)", ""), + {0x06, 0xfd, 0xde, 0x03}: w3.MustNewFunc("name()", "string"), + {0x07, 0x21, 0x1e, 0xf7}: w3.MustNewFunc("get_dy_underlying(int128 i, int128 j, uint256 amount)", "uint256"), + {0x08, 0x18, 0x12, 0xfc}: w3.MustNewFunc("getApproved(uint256 tokenId)", "address operator"), + {0x08, 0xc3, 0x79, 0xa0}: w3.MustNewFunc("Error(string)", ""), + {0x09, 0x02, 0xf1, 0xac}: w3.MustNewFunc("getReserves()", "uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast"), + {0x09, 0x5e, 0xa7, 0xb3}: w3.MustNewFunc("approve(address spender, uint256 amount)", "bool success"), + {0x0c, 0x03, 0x3b, 0x9b}: w3.MustNewFunc("swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 amountLimit, uint256 deadline, bytes data)", "int256 amount0, int256 amount1"), + {0x0d, 0x58, 0xb1, 0xdb}: w3.MustNewFunc("transferFrom((address from, address to, uint160 amount, address token)[] transferDetails)", ""), + {0x0d, 0xfe, 0x16, 0x81}: w3.MustNewFunc("token0()", "address"), + {0x0f, 0x5a, 0x6e, 0xfa}: w3.MustNewFunc("getInternalBalance(address user, address[] tokens)", "uint256[]"), + {0x12, 0x49, 0xc5, 0x8b}: w3.MustNewFunc("mint()", ""), + {0x12, 0x8a, 0xcb, 0x08}: w3.MustNewFunc("swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes data)", "int256 amount0, int256 amount1"), + {0x13, 0xd7, 0x9a, 0x0b}: w3.MustNewFunc("settle(address[] tokens, uint256[] clearingPrices, (uint256 sellTokenIndex, uint256 buyTokenIndex, address receiver, uint256 sellAmount, uint256 buyAmount, uint32 validTo, bytes32 appData, uint256 feeAmount, uint256 flags, uint256 executedAmount, bytes signature)[] trades, (address target, uint256 value, bytes callData)[][3] interactions)", ""), + {0x15, 0x0b, 0x7a, 0x02}: w3.MustNewFunc("onERC721Received(address operator, address from, uint256 tokenId, bytes data)", "bytes4"), + {0x16, 0x26, 0xba, 0x7e}: w3.MustNewFunc("isValidSignature(bytes32 hash, bytes data)", "bytes4"), + {0x17, 0x32, 0x60, 0xeb}: w3.MustNewFunc("settleInternal((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers) order, (uint8 type, bytes signatureBytes) signature, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle) hooks, (uint256[] increasedBuyAmounts, uint16 curFillPercent) makerData)", ""), + {0x17, 0x7e, 0x80, 0x2f}: w3.MustNewFunc("ERC721InsufficientApproval(address operator, uint256 tokenId)", ""), + {0x18, 0x16, 0x0d, 0xdd}: w3.MustNewFunc("totalSupply()", "uint256"), + {0x18, 0xcb, 0xaf, 0xe5}: w3.MustNewFunc("swapExactTokensForETH(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline)", "uint256[] amounts"), + {0x18, 0xfd, 0xd0, 0x6b}: w3.MustNewFunc("getGrossAmountFromTotalSold(uint256 totalAmount)", "uint256"), + {0x1a, 0x68, 0x65, 0x02}: w3.MustNewFunc("liquidity()", "uint128"), + {0x1c, 0x56, 0x03, 0x05}: w3.MustNewFunc("claimMintRewardAndShare(address other, uint256 pct)", ""), + {0x1f, 0x00, 0xca, 0x74}: w3.MustNewFunc("getAmountsIn(uint256 amountOut, address[] path)", "uint256[] amounts"), + {0x20, 0xc1, 0x3b, 0x0b}: w3.MustNewFunc("isValidSignature(bytes data, bytes signature)", "bytes4"), + {0x21, 0x4a, 0x6f, 0xe2}: w3.MustNewFunc("accumulateReward(uint32 currTimestamp)", ""), + {0x21, 0x95, 0x99, 0x5c}: w3.MustNewFunc("removeLiquidityWithPermit(address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s)", "uint256 amountA, uint256 amountB"), + {0x23, 0xa6, 0x9e, 0x75}: w3.MustNewFunc("pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes data)", ""), + {0x23, 0xb8, 0x72, 0xdd}: w3.MustNewFunc("transferFrom(address from, address to, uint256 amount)", "bool success"), + {0x24, 0x85, 0x6b, 0xc3}: w3.MustNewFunc("execute(bytes commands, bytes[] inputs)", ""), + {0x24, 0x8a, 0x9c, 0xa3}: w3.MustNewFunc("getRoleAdmin(bytes32 role)", "bytes32"), + {0x28, 0xb2, 0xa7, 0x96}: w3.MustNewFunc("settleInternalWithPermitsSignatures((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers) order, (uint8 type, bytes signatureBytes) signature, (bytes[] permitSignatures, bytes signatureBytesPermit2, uint48[] noncesPermit2, uint48 deadline) takerPermitsInfo, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle) hooks, (uint256[] increasedBuyAmounts, uint16 curFillPercent) makerData)", ""), + {0x2a, 0x2d, 0x80, 0xd1}: w3.MustNewFunc("permit(address owner, ((address token, uint160 amount, uint48 expiration, uint48 nonce)[] details, address spender, uint256 sigDeadline) permitBatch, bytes signature)", ""), + {0x2e, 0x1a, 0x7d, 0x4d}: w3.MustNewFunc("withdraw(uint256)", ""), + {0x2f, 0x2f, 0xf1, 0x5d}: w3.MustNewFunc("grantRole(bytes32 role, address account)", ""), + {0x31, 0x3c, 0xe5, 0x67}: w3.MustNewFunc("decimals()", "uint8"), + {0x35, 0x93, 0x56, 0x4c}: w3.MustNewFunc("execute(bytes commands, bytes[] inputs, uint256 deadline)", ""), + {0x36, 0x44, 0xe5, 0x15}: w3.MustNewFunc("DOMAIN_SEPARATOR()", "bytes32"), + {0x36, 0x56, 0x8a, 0xbe}: w3.MustNewFunc("renounceRole(bytes32 role, address account)", ""), + {0x36, 0xc7, 0x85, 0x16}: w3.MustNewFunc("transferFrom(address from, address to, uint160 amount, address token)", ""), + {0x38, 0x50, 0xc7, 0xbd}: w3.MustNewFunc("slot0()", "(uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked)"), + {0x38, 0xe9, 0x92, 0x2e}: w3.MustNewFunc("setSwapFeePercentage(uint256)", ""), + {0x38, 0xed, 0x17, 0x39}: w3.MustNewFunc("swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline)", "uint256[] amounts"), + {0x39, 0x47, 0x47, 0xc5}: w3.MustNewFunc("exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, bool use_eth)", ""), + {0x39, 0x50, 0x93, 0x51}: w3.MustNewFunc("increaseAllowance(address spender, uint256 addedValue)", "bool success"), + {0x3a, 0x98, 0xef, 0x39}: w3.MustNewFunc("totalShares()", "uint256"), + {0x3d, 0xf0, 0x21, 0x24}: w3.MustNewFunc("exchange(int128 i, int128 j, uint256 dx, uint256 min_dy)", ""), + {0x3f, 0x44, 0x28, 0x64}: w3.MustNewFunc("swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 amountLimit, uint256 deadline, bytes data, uint256 trackingCode)", "int256 amount0, int256 amount1"), + {0x40, 0xc1, 0x0f, 0x19}: w3.MustNewFunc("mint(address to, uint256 amount)", ""), + {0x40, 0xfb, 0x07, 0xa0}: w3.MustNewFunc("buyAsset(uint256 minAmount, address receiver)", "uint256, uint256"), + {0x42, 0x84, 0x2e, 0x0e}: w3.MustNewFunc("safeTransferFrom(address from, address to, uint256 tokenId)", ""), + {0x42, 0x96, 0x6c, 0x68}: w3.MustNewFunc("burn(uint256)", ""), + {0x45, 0xd6, 0x49, 0x4d}: w3.MustNewFunc("getBuyFee(uint256 grossAmount)", "uint256"), + {0x46, 0x09, 0x85, 0xe8}: w3.MustNewFunc("swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 trackingCode)", "int256 amount0, int256 amount1"), + {0x46, 0x0d, 0xd9, 0x49}: w3.MustNewFunc("settleBatch((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers)[] orders, (uint8 type, bytes signatureBytes)[] signatures, (bytes[] permitSignatures, bytes signatureBytesPermit2, uint48[] noncesPermit2, uint48 deadline)[] takersPermitsInfo, (bool result, address to, uint256 value, bytes data)[] interactions, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle)[] hooks, (address balanceRecipient, uint16[] curFillPercents, bool[] takersPermitsUsage, bool transferExactAmounts) solverData)", ""), + {0x46, 0x14, 0x13, 0x19}: w3.MustNewFunc("feeGrowthGlobal1X128()", "uint256"), + {0x48, 0x5c, 0xc9, 0x55}: w3.MustNewFunc("initialize(address token0, address token1)", ""), + {0x49, 0x0e, 0x6c, 0xbc}: w3.MustNewFunc("flash(address recipient, uint256 amount0, uint256 amount1, bytes data)", ""), + {0x49, 0x3f, 0x4f, 0x74}: w3.MustNewFunc("get_address(uint256 idx)", "address"), + {0x4a, 0x25, 0xd9, 0x4a}: w3.MustNewFunc("swapTokensForExactETH(uint256 amountOut, uint256 amountInMax, address[] path, address to, uint256 deadline)", "uint256[] amounts"), + {0x4a, 0x2a, 0xb3, 0xbe}: w3.MustNewFunc("get_y(uint256 ANN, uint256 gamma, uint256[3] x, uint256 D, uint256 i)", "uint256[2]"), + {0x4c, 0x84, 0xc1, 0xc8}: w3.MustNewFunc("wrapAll()", ""), + {0x52, 0xbb, 0xbe, 0x29}: w3.MustNewFunc("swap((bytes32 poolId, uint8 swapKind, address assetIn, address assetOut, uint256 amount, bytes data) singleSwap, (address sender, bool fromInternalBalance, address recipient, bool toInternalBalance) funds, uint256 limit, uint256 deadline)", "uint256"), + {0x52, 0xc7, 0xf8, 0xdc}: w3.MustNewFunc("claimMintReward()", ""), + {0x54, 0x0d, 0x49, 0x18}: w3.MustNewFunc("lmPool()", "address"), + {0x54, 0xfd, 0x4d, 0x50}: w3.MustNewFunc("version()", "uint256"), + {0x57, 0x4f, 0x2b, 0xa3}: w3.MustNewFunc("allPairsLength()", "uint256"), + {0x59, 0x09, 0xc0, 0xd5}: w3.MustNewFunc("price0CumulativeLast()", "uint256"), + {0x59, 0x21, 0xc8, 0xe0}: w3.MustNewFunc("burnAfterSeize(uint256 amount)", "uint256"), + {0x5a, 0x3d, 0x54, 0x93}: w3.MustNewFunc("price1CumulativeLast()", "uint256"), + {0x5b, 0x08, 0xba, 0x18}: w3.MustNewFunc("ERC721InvalidOperator(address operator)", ""), + {0x5b, 0x0d, 0x59, 0x84}: w3.MustNewFunc("removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s)", "uint256 amountETH"), + {0x5b, 0x41, 0xb9, 0x08}: w3.MustNewFunc("exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy)", ""), + {0x5b, 0xcc, 0xb4, 0xc4}: w3.MustNewFunc("claimMintRewardAndStake(uint256 pct, uint256 term)", ""), + {0x5c, 0x11, 0xd7, 0x95}: w3.MustNewFunc("swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline)", ""), + {0x5c, 0x60, 0xda, 0x1b}: w3.MustNewFunc("implementation()", "address"), + {0x5c, 0x97, 0x5a, 0xbb}: w3.MustNewFunc("paused()", "bool"), + {0x5d, 0xed, 0x74, 0xa5}: w3.MustNewFunc("getGhoPriceInAsset(uint256 ghoAmount, bool roundUp)", "uint256"), + {0x5e, 0x0d, 0x44, 0x3f}: w3.MustNewFunc("get_dy(int128 i, int128 j, uint256 amount)", "uint256"), + {0x60, 0x52, 0x97, 0xe1}: w3.MustNewFunc("seize()", "uint256"), + {0x63, 0x52, 0x21, 0x1e}: w3.MustNewFunc("ownerOf(uint256 tokenId)", "address"), + {0x64, 0x28, 0x3d, 0x7b}: w3.MustNewFunc("ERC721IncorrectOwner(address sender, uint256 tokenId, address owner)", ""), + {0x64, 0xa0, 0xae, 0x92}: w3.MustNewFunc("ERC721InvalidReceiver(address receiver)", ""), + {0x69, 0x62, 0xf8, 0x45}: w3.MustNewFunc("mint_relative(address to, uint256 frac)", "uint256"), + {0x6d, 0x54, 0x33, 0xe6}: w3.MustNewFunc("max(uint256, uint256)", "uint256"), + {0x70, 0xa0, 0x82, 0x31}: w3.MustNewFunc("balanceOf(address)", "uint256"), + {0x71, 0x50, 0x18, 0xa6}: w3.MustNewFunc("renounceOwnership()", ""), + {0x72, 0x84, 0xe4, 0x16}: w3.MustNewFunc("description()", "string"), + {0x73, 0xc6, 0xac, 0x6e}: w3.MustNewFunc("ERC721InvalidSender(address sender)", ""), + {0x74, 0x64, 0xfc, 0x3d}: w3.MustNewFunc("kLast()", "uint256"), + {0x75, 0x19, 0xab, 0x50}: w3.MustNewFunc("updatedAt()", "uint256"), + {0x75, 0x4b, 0x76, 0xb3}: w3.MustNewFunc("get_p(uint256[3] xp, uint256 D, uint256[2] A_gamma)", "uint256[2]"), + {0x75, 0xd3, 0x9e, 0xcb}: w3.MustNewFunc("swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 amountLimit, uint256 deadline)", "int256 amount0, int256 amount1"), + {0x79, 0x1a, 0xc9, 0x47}: w3.MustNewFunc("swapExactTokensForETHSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline)", ""), + {0x79, 0xcc, 0x67, 0x90}: w3.MustNewFunc("burnFrom(address to, uint256 value)", "bool"), + {0x7a, 0xe2, 0xb5, 0xc7}: w3.MustNewFunc("min(uint256, uint256)", "uint256"), + {0x7b, 0x04, 0x72, 0xf0}: w3.MustNewFunc("stake(uint256 amount, uint256 term)", ""), + {0x7b, 0x12, 0xe0, 0x09}: w3.MustNewFunc("newton_D(uint256 ANN, uint256 gamma, uint256[3] x_unsorted, uint256 K0_prev)", "uint256"), + {0x7d, 0x10, 0xd1, 0x1f}: w3.MustNewFunc("transferFromAccounts((address account, address token, uint256 amount, bytes32 balance)[])", ""), + {0x7e, 0x27, 0x32, 0x89}: w3.MustNewFunc("ERC721NonexistentToken(uint256 tokenId)", ""), + {0x7f, 0xf3, 0x6a, 0xb5}: w3.MustNewFunc("swapExactETHForTokens(uint256 amountOutMin, address[] path, address to, uint256 deadline)", "uint256[] amounts"), + {0x81, 0xd1, 0x8d, 0x87}: w3.MustNewFunc("wad_exp(int256 power)", "uint256"), + {0x85, 0xf8, 0xc2, 0x59}: w3.MustNewFunc("getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut)", "uint256 amountIn"), + {0x87, 0x52, 0x8f, 0x13}: w3.MustNewFunc("settle((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers) order, (uint8 type, bytes signatureBytes) signature, (bool result, address to, uint256 value, bytes data)[] interactions, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle) hooks, (address balanceRecipient, uint16 curFillPercent) solverData)", ""), + {0x88, 0x03, 0xdb, 0xee}: w3.MustNewFunc("swapTokensForExactTokens(uint256 amountOut, uint256 amountInMax, address[] path, address to, uint256 deadline)", "uint256[] amounts"), + {0x89, 0xc6, 0x2b, 0x64}: w3.MustNewFunc("ERC721InvalidOwner(address owner)", ""), + {0x8d, 0xa5, 0xcb, 0x5b}: w3.MustNewFunc("owner()", "address"), + {0x90, 0x10, 0xd0, 0x7c}: w3.MustNewFunc("getRoleMember(bytes32 role, uint256 index)", "address"), + {0x91, 0xd1, 0x48, 0x54}: w3.MustNewFunc("hasRole(bytes32 role, address account)", "bool"), + {0x92, 0x7d, 0xa1, 0x05}: w3.MustNewFunc("allowance(address user, address token, address spender)", "uint160 amount, uint48 expiration, uint48 nonce"), + {0x94, 0x28, 0x0d, 0x62}: w3.MustNewFunc("ERC20InvalidSpender(address spender)", ""), + {0x94, 0x5b, 0xce, 0xc9}: w3.MustNewFunc("batchSwap(uint8 swapKind, (bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes data)[] swaps, address[] assets, (address sender, bool fromInternalBalance, address recipient, bool toInternalBalance) funds, int256[] limits, uint256 deadline)", "int256[]"), + {0x95, 0x4c, 0x1c, 0x0d}: w3.MustNewFunc("buyAssetWithSig(address originator, uint256 minAmount, address receiver, uint256 deadline, bytes signature)", "uint256, uint256"), + {0x95, 0x9d, 0x6d, 0x29}: w3.MustNewFunc("getSellFee(uint256 grossAmount)", "uint256"), + {0x95, 0xd8, 0x9b, 0x41}: w3.MustNewFunc("symbol()", "string"), + {0x96, 0xc6, 0xfd, 0x1e}: w3.MustNewFunc("ERC20InvalidSender(address sender)", ""), + {0x9a, 0x6f, 0xc8, 0xf5}: w3.MustNewFunc("getRoundData(uint80 roundId)", "uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound"), + {0x9b, 0xe2, 0xa8, 0x84}: w3.MustNewFunc("canPerform(bytes32 actionId, address account, address where)", "bool"), + {0x9d, 0xc2, 0x9f, 0xac}: w3.MustNewFunc("burn(address user, uint256 amount)", ""), + {0xa0, 0x71, 0x2d, 0x68}: w3.MustNewFunc("mint(uint256)", ""), + {0xa1, 0x6f, 0x5f, 0xd2}: w3.MustNewFunc("cumulativeSum()", "uint256"), + {0xa2, 0x2c, 0xb4, 0x65}: w3.MustNewFunc("setApprovalForAll(address operator, bool approved)", ""), + {0xa2, 0x3b, 0xa0, 0x9c}: w3.MustNewFunc("swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 amountLimit, uint256 deadline, uint256 trackingCode)", "int256 amount0, int256 amount1"), + {0xa2, 0x62, 0x90, 0x4b}: w3.MustNewFunc("get_registry()", "address"), + {0xa4, 0x57, 0xc2, 0xd7}: w3.MustNewFunc("decreaseAllowance(address spender, uint256 subtractedValue)", "bool success"), + {0xa4, 0x98, 0x46, 0x33}: w3.MustNewFunc("crossLmTick(int24 tick, bool zeroForOne)", ""), + {0xa4, 0xa2, 0x1f, 0xa2}: w3.MustNewFunc("getGrossAmountFromTotalBought(uint256 totalAmount)", "uint256"), + {0xa6, 0x41, 0x7e, 0xd6}: w3.MustNewFunc("exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy)", ""), + {0xa7, 0xb7, 0x8c, 0x3c}: w3.MustNewFunc("getAssetPriceInGho(uint256 assetAmount, bool roundUp)", "uint256"), + {0xa9, 0x05, 0x9c, 0xbb}: w3.MustNewFunc("transfer(address to, uint256 amount)", "bool success"), + {0xa9, 0xfb, 0xf5, 0x1f}: w3.MustNewFunc("ERC721InvalidApprover(address approver)", ""), + {0xaa, 0x44, 0x3a, 0xc0}: w3.MustNewFunc("sellAsset(uint256 maxAmount, address receiver)", "uint256, uint256"), + {0xaa, 0xab, 0xad, 0xc5}: w3.MustNewFunc("getAuthorizer()", "address"), + {0xad, 0x5c, 0x46, 0x48}: w3.MustNewFunc("WETH()", "address"), + {0xad, 0x61, 0x5d, 0xec}: w3.MustNewFunc("quote(uint256 amountA, uint256 reserveA, uint256 reserveB)", "uint256 amountB"), + {0xae, 0x5b, 0x25, 0x40}: w3.MustNewFunc("setApp(bytes32 namespace, bytes32 appId, address app)", ""), + {0xaf, 0x29, 0x79, 0xeb}: w3.MustNewFunc("removeLiquidityETHSupportingFeeOnTransferTokens(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline)", "uint256 amountETH"), + {0xb0, 0x34, 0x43, 0x04}: w3.MustNewFunc("swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes data, uint256 trackingCode)", "int256 amount0, int256 amount1"), + {0xb6, 0xf9, 0xde, 0x95}: w3.MustNewFunc("swapExactETHForTokensSupportingFeeOnTransferTokens(uint256 amountOutMin, address[] path, address to, uint256 deadline)", ""), + {0xb8, 0x8d, 0x4f, 0xde}: w3.MustNewFunc("safeTransferFrom(address from, address to, uint256 tokenId, bytes data)", ""), + {0xba, 0x9a, 0x7a, 0x56}: w3.MustNewFunc("MINIMUM_LIQUIDITY()", "uint256"), + {0xba, 0xa2, 0xab, 0xde}: w3.MustNewFunc("removeLiquidity(address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline)", "uint256 amountA, uint256 amountB"), + {0xba, 0xd1, 0xdc, 0x26}: w3.MustNewFunc("geometric_mean(uint256[3] x)", "uint256"), + {0xbc, 0x25, 0xcf, 0x77}: w3.MustNewFunc("skim(address to)", ""), + {0xbe, 0x00, 0xbb, 0xd8}: w3.MustNewFunc("getApp(bytes32 namespace, bytes32 appId)", "address"), + {0xc3, 0x1b, 0x8d, 0x7a}: w3.MustNewFunc("swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96)", "int256 amount0, int256 amount1"), + {0xc4, 0x5a, 0x01, 0x55}: w3.MustNewFunc("factory()", "address"), + {0xc6, 0x61, 0x06, 0x57}: w3.MustNewFunc("coins(uint256 i)", "address"), + {0xc8, 0x7b, 0x56, 0xdd}: w3.MustNewFunc("tokenURI(uint256 tokenId)", "string"), + {0xc9, 0xc6, 0x53, 0x96}: w3.MustNewFunc("createPair(address tokenA, address tokenB)", "address pair"), + {0xca, 0x15, 0xc8, 0x73}: w3.MustNewFunc("getRoleMemberCount(bytes32 role)", "uint256"), + {0xca, 0xb4, 0x6b, 0xc6}: w3.MustNewFunc("setSwapFreeze(bool enable)", ""), + {0xce, 0x7d, 0x65, 0x03}: w3.MustNewFunc("exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, bool use_eth, address receiver)", "uint256"), + {0xce, 0xa9, 0xd2, 0x6f}: w3.MustNewFunc("rescueTokens(address token, address to, uint256 amount)", ""), + {0xd0, 0x6c, 0xa6, 0x1f}: w3.MustNewFunc("getAmountsOut(uint256 amountIn, address[] path)", "uint256[] amounts"), + {0xd0, 0xc9, 0x3a, 0x7c}: w3.MustNewFunc("tickSpacing()", "int24"), + {0xd0, 0xe3, 0x0d, 0xb0}: w3.MustNewFunc("deposit()", ""), + {0xd2, 0x12, 0x20, 0xa7}: w3.MustNewFunc("token1()", "address"), + {0xd5, 0x05, 0xac, 0xcf}: w3.MustNewFunc("permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)", ""), + {0xd5, 0x47, 0x74, 0x1f}: w3.MustNewFunc("revokeRole(bytes32 role, address account)", ""), + {0xdd, 0x62, 0xed, 0x3e}: w3.MustNewFunc("allowance(address owner, address spender)", "uint256"), + {0xdd, 0xc1, 0xf5, 0x9d}: w3.MustNewFunc("exchange(int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver)", ""), + {0xdd, 0xca, 0x3f, 0x43}: w3.MustNewFunc("fee()", "uint24"), + {0xde, 0x28, 0x73, 0x59}: w3.MustNewFunc("acl()", "address"), + {0xde, 0xd9, 0x38, 0x2a}: w3.MustNewFunc("removeLiquidityETHWithPermit(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s)", "uint256 amountToken, uint256 amountETH"), + {0xe4, 0x49, 0x02, 0x2e}: w3.MustNewFunc("uniswapV3Swap(uint256 amount, uint256 minReturn, uint256[] pools)", "uint256 returnAmount"), + {0xe4, 0x50, 0xd3, 0x8c}: w3.MustNewFunc("ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed)", ""), + {0xe6, 0x02, 0xdf, 0x05}: w3.MustNewFunc("ERC20InvalidApprover(address approver)", ""), + {0xe6, 0xa4, 0x39, 0x05}: w3.MustNewFunc("getPair(address tokenA, address tokenB)", "address pair"), + {0xe8, 0xe3, 0x37, 0x00}: w3.MustNewFunc("addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline)", "uint256 amountA, uint256 amountB, uint256 liquidity"), + {0xe9, 0x85, 0xe9, 0xc5}: w3.MustNewFunc("isApprovedForAll(address owner, address operator)", "bool"), + {0xeb, 0x8d, 0x21, 0x16}: w3.MustNewFunc("transferTokens((address from, address receiver, address[] tokens, uint256[] amounts, uint256[] nftIds, bytes tokenTransferTypes, uint16 fillPercent) transferData)", ""), + {0xec, 0x44, 0x2f, 0x05}: w3.MustNewFunc("ERC20InvalidReceiver(address receiver)", ""), + {0xec, 0x98, 0x37, 0x76}: w3.MustNewFunc("settleWithPermitsSignatures((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers) order, (uint8 type, bytes signatureBytes) signature, (bytes[] permitSignatures, bytes signatureBytesPermit2, uint48[] noncesPermit2, uint48 deadline) takerPermitsInfo, (bool result, address to, uint256 value, bytes data)[] interactions, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle) hooks, (address balanceRecipient, uint16 curFillPercent) solverData)", ""), + {0xf2, 0xfd, 0xe3, 0x8b}: w3.MustNewFunc("transferOwnership(address newOwner)", ""), + {0xf3, 0x05, 0x83, 0x99}: w3.MustNewFunc("feeGrowthGlobal0X128()", "uint256"), + {0xf3, 0x05, 0xd7, 0x19}: w3.MustNewFunc("addLiquidityETH(address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline)", "uint256 amountToken, uint256 amountETH, uint256 liquidity"), + {0xf4, 0x2c, 0x56, 0xc2}: w3.MustNewFunc("cbrt(uint256 x)", "uint256"), + {0xf4, 0x93, 0xca, 0x70}: w3.MustNewFunc("sellAssetWithSig(address originator, uint256 maxAmount, address receiver, uint256 deadline, bytes signature)", "uint256, uint256"), + {0xf8, 0x51, 0xa4, 0x40}: w3.MustNewFunc("admin()", "address"), + {0xfa, 0x18, 0x04, 0x2d}: w3.MustNewFunc("reduction_coefficient(uint256[3] x, uint256 fee_gamma)", "uint256"), + {0xfa, 0x46, 0x1e, 0x33}: w3.MustNewFunc("uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes data)", ""), + {0xfa, 0x6e, 0x67, 0x1d}: w3.MustNewFunc("setRelayerApproval(address sender, address relayer, bool approved)", ""), + {0xfb, 0x3b, 0xdb, 0x41}: w3.MustNewFunc("swapETHForExactTokens(uint256 amountOut, address[] path, address to, uint256 deadline)", "uint256[] amounts"), + {0xfb, 0x8f, 0x41, 0xb2}: w3.MustNewFunc("ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed)", ""), + {0xfc, 0x0c, 0x54, 0x6a}: w3.MustNewFunc("token()", "address"), + {0xfd, 0xef, 0x91, 0x06}: w3.MustNewFunc("hasPermission(address who, address where, bytes32 what, bytes how)", "bool"), + {0xfe, 0xaf, 0x96, 0x8c}: w3.MustNewFunc("latestRoundData()", "uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound"), + {0xfe, 0xc9, 0x0d, 0x72}: w3.MustNewFunc("hasApprovedRelayer(address user, address relayer)", "bool"), + {0xff, 0xf6, 0xca, 0xe9}: w3.MustNewFunc("sync()", ""), +} diff --git a/internal/fourbyte/funcs.txt b/internal/fourbyte/funcs.txt new file mode 100644 index 00000000..a8b6ce33 --- /dev/null +++ b/internal/fourbyte/funcs.txt @@ -0,0 +1,194 @@ +accumulateReward(uint32 currTimestamp) +acl() address +addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) uint256 amountA, uint256 amountB, uint256 liquidity +addLiquidityETH(address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline) uint256 amountToken, uint256 amountETH, uint256 liquidity +admin() address +allowance(address owner, address spender) uint256 +allowance(address user, address token, address spender) uint160 amount, uint48 expiration, uint48 nonce +allPairsLength() uint256 +approve(address spender, uint256 amount) bool success +balanceOf(address) uint256 +batchSwap(uint8 swapKind, (bytes32 poolId, uint256 assetInIndex, uint256 assetOutIndex, uint256 amount, bytes data)[] swaps, address[] assets, (address sender, bool fromInternalBalance, address recipient, bool toInternalBalance) funds, int256[] limits, uint256 deadline) int256[] +burn(address user, uint256 amount) +burn(uint256) +burnAfterSeize(uint256 amount) uint256 +burnFrom(address to, uint256 value) bool +buyAsset(uint256 minAmount, address receiver) uint256, uint256 +buyAssetWithSig(address originator, uint256 minAmount, address receiver, uint256 deadline, bytes signature) uint256, uint256 +canPerform(bytes32 actionId, address account, address where) bool +cbrt(uint256 x) uint256 +claimMintReward() +claimMintRewardAndShare(address other, uint256 pct) +claimMintRewardAndStake(uint256 pct, uint256 term) +coins(uint256 i) address +createPair(address tokenA, address tokenB) address pair +crossLmTick(int24 tick, bool zeroForOne) +cumulativeSum() uint256 +decimals() uint8 +decreaseAllowance(address spender, uint256 subtractedValue) bool success +deposit() +description() string +DOMAIN_SEPARATOR() bytes32 +ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed) +ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed) +ERC20InvalidApprover(address approver) +ERC20InvalidReceiver(address receiver) +ERC20InvalidSender(address sender) +ERC20InvalidSpender(address spender) +ERC721IncorrectOwner(address sender, uint256 tokenId, address owner) +ERC721InsufficientApproval(address operator, uint256 tokenId) +ERC721InvalidApprover(address approver) +ERC721InvalidOperator(address operator) +ERC721InvalidOwner(address owner) +ERC721InvalidReceiver(address receiver) +ERC721InvalidSender(address sender) +ERC721NonexistentToken(uint256 tokenId) +Error(string) +exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) +exchange(int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver) +exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) +exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, bool use_eth) +exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy, bool use_eth, address receiver) uint256 +exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) +execute(bytes commands, bytes[] inputs) +execute(bytes commands, bytes[] inputs, uint256 deadline) +factory() address +fee() uint24 +feeGrowthGlobal0X128() uint256 +feeGrowthGlobal1X128() uint256 +flash(address recipient, uint256 amount0, uint256 amount1, bytes data) +geometric_mean(uint256[3] x) uint256 +get_address(uint256 idx) address +get_dy(int128 i, int128 j, uint256 amount) uint256 +get_dy_underlying(int128 i, int128 j, uint256 amount) uint256 +get_p(uint256[3] xp, uint256 D, uint256[2] A_gamma) uint256[2] +get_registry() address +get_y(uint256 ANN, uint256 gamma, uint256[3] x, uint256 D, uint256 i) uint256[2] +getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) uint256 amountIn +getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) uint256 amountOut +getAmountsIn(uint256 amountOut, address[] path) uint256[] amounts +getAmountsOut(uint256 amountIn, address[] path) uint256[] amounts +getApp(bytes32 namespace, bytes32 appId) address +getApproved(uint256 tokenId) address operator +getAssetPriceInGho(uint256 assetAmount, bool roundUp) uint256 +getAuthorizer() address +getBuyFee(uint256 grossAmount) uint256 +getGhoPriceInAsset(uint256 ghoAmount, bool roundUp) uint256 +getGrossAmountFromTotalBought(uint256 totalAmount) uint256 +getGrossAmountFromTotalSold(uint256 totalAmount) uint256 +getInternalBalance(address user, address[] tokens) uint256[] +getPair(address tokenA, address tokenB) address pair +getReserves() uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast +getRoleAdmin(bytes32 role) bytes32 +getRoleMember(bytes32 role, uint256 index) address +getRoleMemberCount(bytes32 role) uint256 +getRoundData(uint80 roundId) uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound +getSellFee(uint256 grossAmount) uint256 +grantRole(bytes32 role, address account) +hasApprovedRelayer(address user, address relayer) bool +hasPermission(address who, address where, bytes32 what, bytes how) bool +hasRole(bytes32 role, address account) bool +implementation() address +increaseAllowance(address spender, uint256 addedValue) bool success +initialize(address token0, address token1) +isApprovedForAll(address owner, address operator) bool +isSolver(address) bool +isValidSignature(bytes32 hash, bytes data) magic bytes4 +kLast() uint256 +latestRoundData() uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound +liquidity() uint128 +lmPool() address +max(uint256, uint256) uint256 +min(uint256, uint256) uint256 +MINIMUM_LIQUIDITY() uint256 +mint() +mint(address to, uint256 amount) +mint(uint256) +mint_relative(address to, uint256 frac) uint256 +name() string +newton_D(uint256 ANN, uint256 gamma, uint256[3] x_unsorted, uint256 K0_prev) uint256 +onERC721Received(address operator, address from, uint256 tokenId, bytes data) bytes4 +owner() address +ownerOf(uint256 tokenId) address +pancakeV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes data) +paused() bool +permit(address owner, ((address token, uint160 amount, uint48 expiration, uint48 nonce)[] details, address spender, uint256 sigDeadline) permitBatch, bytes signature) +permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) +price0CumulativeLast() uint256 +price1CumulativeLast() uint256 +quote(uint256 amountA, uint256 reserveA, uint256 reserveB) uint256 amountB +reduction_coefficient(uint256[3] x, uint256 fee_gamma) uint256 +removeLiquidity(address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) uint256 amountA, uint256 amountB +removeLiquidityETH(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline) uint256 amountToken, uint256 amountETH +removeLiquidityETHSupportingFeeOnTransferTokens(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline) uint256 amountETH +removeLiquidityETHWithPermit(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s) uint256 amountToken, uint256 amountETH +removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(address token, uint256 liquidity, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s) uint256 amountETH +removeLiquidityWithPermit(address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s) uint256 amountA, uint256 amountB +renounceOwnership() +renounceRole(bytes32 role, address account) +rescueTokens(address token, address to, uint256 amount) +revokeRole(bytes32 role, address account) +safeTransferFrom(address from, address to, uint256 tokenId) +safeTransferFrom(address from, address to, uint256 tokenId, bytes data) +seize() uint256 +sellAsset(uint256 maxAmount, address receiver) uint256, uint256 +sellAssetWithSig(address originator, uint256 maxAmount, address receiver, uint256 deadline, bytes signature) uint256, uint256 +setApp(bytes32 namespace, bytes32 appId, address app) +setApprovalForAll(address operator, bool approved) +setAuthorizer(address newAuthorizer) +setRelayerApproval(address sender, address relayer, bool approved) +setSwapFeePercentage(uint256) +setSwapFreeze(bool enable) +settle((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers) order, (uint8 type, bytes signatureBytes) signature, (bool result, address to, uint256 value, bytes data)[] interactions, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle) hooks, (address balanceRecipient, uint16 curFillPercent) solverData) +settle(address[] tokens, uint256[] clearingPrices, (uint256 sellTokenIndex, uint256 buyTokenIndex, address receiver, uint256 sellAmount, uint256 buyAmount, uint32 validTo, bytes32 appData, uint256 feeAmount, uint256 flags, uint256 executedAmount, bytes signature)[] trades, (address target, uint256 value, bytes callData)[][3] interactions) +settleBatch((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers)[] orders, (uint8 type, bytes signatureBytes)[] signatures, (bytes[] permitSignatures, bytes signatureBytesPermit2, uint48[] noncesPermit2, uint48 deadline)[] takersPermitsInfo, (bool result, address to, uint256 value, bytes data)[] interactions, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle)[] hooks, (address balanceRecipient, uint16[] curFillPercents, bool[] takersPermitsUsage, bool transferExactAmounts) solverData) +settleInternal((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers) order, (uint8 type, bytes signatureBytes) signature, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle) hooks, (uint256[] increasedBuyAmounts, uint16 curFillPercent) makerData) +settleInternalWithPermitsSignatures((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers) order, (uint8 type, bytes signatureBytes) signature, (bytes[] permitSignatures, bytes signatureBytesPermit2, uint48[] noncesPermit2, uint48 deadline) takerPermitsInfo, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle) hooks, (uint256[] increasedBuyAmounts, uint16 curFillPercent) makerData) +settleWithPermitsSignatures((address taker, address receiver, uint256 expiry, uint256 nonce, address executor, uint16 minFillPercent, bytes32 hooksHash, address[] sellTokens, address[] buyTokens, uint256[] sellAmounts, uint256[] buyAmounts, uint256[] sellNFTIds, uint256[] buyNFTIds, bytes sellTokenTransfers, bytes buyTokenTransfers) order, (uint8 type, bytes signatureBytes) signature, (bytes[] permitSignatures, bytes signatureBytesPermit2, uint48[] noncesPermit2, uint48 deadline) takerPermitsInfo, (bool result, address to, uint256 value, bytes data)[] interactions, ((bool result, address to, uint256 value, bytes data)[] beforeSettle, (bool result, address to, uint256 value, bytes data)[] afterSettle) hooks, (address balanceRecipient, uint16 curFillPercent) solverData) +skim(address to) +slot0() (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked) +stake(uint256 amount, uint256 term) +supportsInterface(bytes4) bool +swap((bytes32 poolId, uint8 swapKind, address assetIn, address assetOut, uint256 amount, bytes data) singleSwap, (address sender, bool fromInternalBalance, address recipient, bool toInternalBalance) funds, uint256 limit, uint256 deadline) uint256 +swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96) int256 amount0, int256 amount1 +swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes data) int256 amount0, int256 amount1 +swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes data, uint256 trackingCode) int256 amount0, int256 amount1 +swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 amountLimit, uint256 deadline) int256 amount0, int256 amount1 +swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 amountLimit, uint256 deadline, bytes data) int256 amount0, int256 amount1 +swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 amountLimit, uint256 deadline, bytes data, uint256 trackingCode) int256 amount0, int256 amount1 +swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 amountLimit, uint256 deadline, uint256 trackingCode) int256 amount0, int256 amount1 +swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, uint256 trackingCode) int256 amount0, int256 amount1 +swap(uint256 amount0Out, uint256 amount1Out, address to, bytes data) +swapETHForExactTokens(uint256 amountOut, address[] path, address to, uint256 deadline) uint256[] amounts +swapExactETHForTokens(uint256 amountOutMin, address[] path, address to, uint256 deadline) uint256[] amounts +swapExactETHForTokensSupportingFeeOnTransferTokens(uint256 amountOutMin, address[] path, address to, uint256 deadline) +swapExactTokensForETH(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) uint256[] amounts +swapExactTokensForETHSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) +swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) uint256[] amounts +swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) +swapTokensForExactETH(uint256 amountOut, uint256 amountInMax, address[] path, address to, uint256 deadline) uint256[] amounts +swapTokensForExactTokens(uint256 amountOut, uint256 amountInMax, address[] path, address to, uint256 deadline) uint256[] amounts +symbol() string +sync() +tickSpacing() int24 +token() address +token0() address +token1() address +tokenURI(uint256 tokenId) string +totalShares() uint256 +totalSupply() uint256 +transfer(address to, uint256 amount) bool success +transferFrom((address from, address to, uint160 amount, address token)[] transferDetails) +transferFrom(address from, address to, uint160 amount, address token) +transferFrom(address from, address to, uint256 amount) bool success +transferFromAccounts((address account, address token, uint256 amount, bytes32 balance)[]) +transferOwnership(address newOwner) +transferTokens((address from, address receiver, address[] tokens, uint256[] amounts, uint256[] nftIds, bytes tokenTransferTypes, uint16 fillPercent) transferData) +uniswapV3Swap(uint256 amount, uint256 minReturn, uint256[] pools) uint256 returnAmount +uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes data) +updatedAt() uint256 +version() uint256 +wad_exp(int256 power) uint256 +WETH() address +withdraw(uint256) +wrapAll() diff --git a/internal/fourbyte/gen.go b/internal/fourbyte/gen.go new file mode 100644 index 00000000..d8211584 --- /dev/null +++ b/internal/fourbyte/gen.go @@ -0,0 +1,272 @@ +//go:build ignore + +package main + +import ( + "bufio" + "bytes" + "fmt" + "os" + "slices" + "strings" + "sync" + "text/template" + + "github.com/lmittmann/w3" +) + +func main() { + var ( + errFuncs, errEvents error + wg sync.WaitGroup + ) + + wg.Add(2) + go func() { + defer wg.Done() + errFuncs = genFuncs("funcs.txt", "funcs.go") + }() + go func() { + defer wg.Done() + errEvents = genEvents("events.txt", "events.go") + }() + + wg.Wait() + if errFuncs != nil { + fmt.Printf("error generating functions: %v\n", errFuncs) + } + if errEvents != nil { + fmt.Printf("error generating events: %v\n", errEvents) + } + if errFuncs != nil || errEvents != nil { + os.Exit(1) + } +} + +func genFuncs(fn, goFn string) error { + // open function definitions + f, err := os.Open(fn) + if err != nil { + return err + } + defer f.Close() + + // parse function definitions from file + var functions []function + knownIdentifiers := make(map[[4]byte]struct{}) + + scanner := bufio.NewScanner(f) + for i := 0; scanner.Scan(); i++ { + line := scanner.Text() + tokens := strings.Split(line, "\t") + if len(tokens) == 1 && strings.HasSuffix(tokens[0], ")") { + tokens = append(tokens, "") // no returns + } + if len(tokens) != 2 { + return fmt.Errorf("line %d: invalid line %q", i, line) + } + + fn, err := w3.NewFunc(tokens[0], tokens[1]) + if err != nil { + return fmt.Errorf("line %d: %v (%q)", i, err, line) + } + + if _, ok := knownIdentifiers[fn.Selector]; ok { + return fmt.Errorf("line %d: duplicate function selector %q", i, line) + } + knownIdentifiers[fn.Selector] = struct{}{} + + functions = append(functions, function{ + Selector: fn.Selector, + Signature: tokens[0], + Returns: tokens[1], + }) + } + if err := scanner.Err(); err != nil { + return fmt.Errorf("scan lines: %v", err) + } + + // make sure function definitions stay in alphabetical order + slices.SortFunc(functions, func(a, b function) int { + return strings.Compare(strings.ToLower(a.Signature), strings.ToLower(b.Signature)) + }) + f, err = os.OpenFile(fn, os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + + for _, fn := range functions { + if _, err := f.WriteString(strings.TrimSpace(fn.Signature+"\t"+fn.Returns) + "\n"); err != nil { + return err + } + } + + // generate go file + goF, err := os.Create(goFn) + if err != nil { + return err + } + defer goF.Close() + + slices.SortFunc(functions, func(a, b function) int { + return bytes.Compare(a.Selector[:], b.Selector[:]) + }) + + if err := tmplFuncs.Execute(goF, &model{Functions: functions}); err != nil { + return fmt.Errorf("execute template: %v", err) + } + + return nil +} + +func genEvents(fn, goFn string) error { + // open event definitions + f, err := os.Open(fn) + if err != nil { + return err + } + defer f.Close() + + var events []event + knownTopic0s := make(map[[32]byte]struct{}) + scanner := bufio.NewScanner(f) + for i := 0; scanner.Scan(); i++ { + line := scanner.Text() + + evt, err := w3.NewEvent(line) + if err != nil { + return fmt.Errorf("line %d: %v (%q)", i, err, line) + } + + if _, ok := knownTopic0s[evt.Topic0]; ok { + return fmt.Errorf("line %d: duplicate function selector %q", i, line) + } + knownTopic0s[evt.Topic0] = struct{}{} + + events = append(events, event{ + Topic0: evt.Topic0, + Signature: line, + }) + } + if err := scanner.Err(); err != nil { + return fmt.Errorf("scan lines: %v", err) + } + + // make sure event definitions stay in alphabetical order + slices.SortFunc(events, func(a, b event) int { + return strings.Compare(strings.ToLower(a.Signature), strings.ToLower(b.Signature)) + }) + f, err = os.OpenFile(fn, os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + + for _, evt := range events { + if _, err := f.WriteString(evt.Signature + "\n"); err != nil { + return err + } + } + + // generate go file + goF, err := os.Create(goFn) + if err != nil { + return err + } + defer goF.Close() + + slices.SortFunc(events, func(a, b event) int { + return bytes.Compare(a.Topic0[:], b.Topic0[:]) + }) + + if err := tmplEvents.Execute(goF, &model{Events: events}); err != nil { + return fmt.Errorf("execute template: %v", err) + } + + return nil + +} + +type model struct { + Functions []function + Events []event +} + +type function struct { + Selector [4]byte + Signature string + Returns string +} + +type event struct { + Topic0 [32]byte + Signature string +} + +var ( + tmplFuncs = template.Must(template.New("funcs").Parse(`// Code generated by "go generate"; DO NOT EDIT. +package fourbyte + +import "github.com/lmittmann/w3" + +var functions = map[[4]byte]*w3.Func{ + {{- range .Functions }} + {{ printf "{0x%02x, 0x%02x, 0x%02x, 0x%02x}: w3.MustNewFunc(%q, %q)" + (index .Selector 0) + (index .Selector 1) + (index .Selector 2) + (index .Selector 3) + .Signature + .Returns + }}, + {{- end }} +} +`)) + + tmplEvents = template.Must(template.New("events").Parse(`// Code generated by "go generate"; DO NOT EDIT. +package fourbyte + +import "github.com/lmittmann/w3" + +var events = map[[32]byte]*w3.Event{ + {{- range .Events }} + {{ printf "{0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x}: w3.MustNewEvent(%q)" + (index .Topic0 0) + (index .Topic0 1) + (index .Topic0 2) + (index .Topic0 3) + (index .Topic0 4) + (index .Topic0 5) + (index .Topic0 6) + (index .Topic0 7) + (index .Topic0 8) + (index .Topic0 9) + (index .Topic0 10) + (index .Topic0 11) + (index .Topic0 12) + (index .Topic0 13) + (index .Topic0 14) + (index .Topic0 15) + (index .Topic0 16) + (index .Topic0 17) + (index .Topic0 18) + (index .Topic0 19) + (index .Topic0 20) + (index .Topic0 21) + (index .Topic0 22) + (index .Topic0 23) + (index .Topic0 24) + (index .Topic0 25) + (index .Topic0 26) + (index .Topic0 27) + (index .Topic0 28) + (index .Topic0 29) + (index .Topic0 30) + (index .Topic0 31) + .Signature + }}, + {{- end }} +} +`)) +) diff --git a/w3types/interfaces_test.go b/w3types/interfaces_test.go index 398e784c..b15cf42a 100644 --- a/w3types/interfaces_test.go +++ b/w3types/interfaces_test.go @@ -19,7 +19,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 diff --git a/w3vm/example_test.go b/w3vm/example_test.go new file mode 100644 index 00000000..58b2e6c5 --- /dev/null +++ b/w3vm/example_test.go @@ -0,0 +1,462 @@ +package w3vm_test + +import ( + "fmt" + "math/big" + "os" + "testing" + "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" + "github.com/lmittmann/w3/w3vm/hooks" +) + +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 calls (and opcodes) of a transaction. +func ExampleVM_traceCalls() { + txHash := w3.H("0xc0679fedfe8d7c376d599cbab03de7b527347a3d135d7d8d698047f34a6611f8") + + var ( + tx *types.Transaction + receipt *types.Receipt + ) + if err := client.Call( + eth.Tx(txHash).Returns(&tx), + eth.TxReceipt(txHash).Returns(&receipt), + ); err != nil { + // ... + } + + vm, err := w3vm.New( + w3vm.WithFork(client, receipt.BlockNumber), + ) + if err != nil { + // ... + } + + callTracer := hooks.NewCallTracer(os.Stdout, &hooks.CallTracerOptions{ + ShowStaticcall: true, + DecodeABI: true, + }) + vm.ApplyTx(tx, callTracer) +} + +// 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 tracer + 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 opCount [256]uint64 + tracer := &tracing.Hooks{ + OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + opCount[op]++ + }, + } + + for _, tx := range block.Transactions() { + vm.ApplyTx(tx, tracer) + } + + for op, count := range opCount { + if count > 0 { + fmt.Printf("0x%02x %-14s %d\n", op, gethVm.OpCode(op), count) + } + } +} + +func TestWETHDeposit(t *testing.T) { + // setup VM + vm, _ := w3vm.New( + w3vm.WithState(w3types.State{ + addrWETH: {Code: codeWETH}, + addrA: {Balance: w3.I("1 ether")}, + }), + ) + + // pre check + var wethBalanceBefore *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&wethBalanceBefore); err != nil { + t.Fatal(err) + } + if wethBalanceBefore.Sign() != 0 { + t.Fatal("Invalid WETH balance: want 0") + } + + // deposit (via fallback) + if _, err := vm.Apply(&w3types.Message{ + From: addrA, + To: &addrWETH, + Value: w3.I("1 ether"), + }); err != nil { + t.Fatalf("Deposit failed: %v", err) + } + + // post check + var wethBalanceAfter *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&wethBalanceAfter); err != nil { + t.Fatal(err) + } + if w3.I("1 ether").Cmp(wethBalanceAfter) != 0 { + t.Fatalf("Invalid WETH balance: want 1") + } +} + +func FuzzWETHDeposit(f *testing.F) { + f.Add([]byte{1}) + f.Fuzz(func(t *testing.T, amountBytes []byte) { + if len(amountBytes) > 32 { + t.Skip() + } + amount := new(big.Int).SetBytes(amountBytes) + + // setup VM + vm, _ := w3vm.New( + w3vm.WithState(w3types.State{ + addrWETH: {Code: codeWETH}, + addrA: {Balance: w3.BigMaxUint256}, + }), + ) + + // Pre-check WETH balance + var wethBalanceBefore *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&wethBalanceBefore); err != nil { + t.Fatal(err) + } + + // Attempt deposit + vm.Apply(&w3types.Message{ + From: addrA, + To: &addrWETH, + Value: amount, + }) + + // Post-check WETH balance + var wethBalanceAfter *big.Int + if err := vm.CallFunc(addrWETH, funcBalanceOf, addrA).Returns(&wethBalanceAfter); err != nil { + t.Fatal(err) + } + + // Verify balance increment + wantBalance := new(big.Int).Add(wethBalanceBefore, amount) + if wethBalanceAfter.Cmp(wantBalance) != 0 { + t.Fatalf("Invalid WETH balance: want %s, got %s", wantBalance, wethBalanceAfter) + } + }) +} diff --git a/w3vm/hooks/call_tracer.go b/w3vm/hooks/call_tracer.go new file mode 100644 index 00000000..e3d40d77 --- /dev/null +++ b/w3vm/hooks/call_tracer.go @@ -0,0 +1,487 @@ +package hooks + +import ( + "encoding/hex" + "fmt" + "io" + "math/big" + "reflect" + "strings" + "sync" + + "github.com/charmbracelet/lipgloss" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/lmittmann/w3" + "github.com/lmittmann/w3/internal/fourbyte" +) + +var ( + // styles + styleDim = lipgloss.NewStyle().Faint(true) + styleTarget = lipgloss.NewStyle().Foreground(lipgloss.Color("#EBFF71")) + styleValue = lipgloss.NewStyle().Foreground(lipgloss.Color("#71FF71")) + styleRevert = lipgloss.NewStyle().Foreground(lipgloss.Color("#FE5F86")) + stylesStaticcall = lipgloss.NewStyle().Faint(true) + stylesDelegatecall = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500")) +) + +// TargetAddress can be used to match the target (to) address in the TargetStyler +// of [CallTracerOptions]. +var TargetAddress = common.BytesToAddress([]byte("target")) + +// CallTracerOptions configures the CallTracer hook. A zero CallTracerOptions +// consists entirely of default values. +type CallTracerOptions struct { + TargetStyler func(addr common.Address) lipgloss.Style + targetAddr common.Address + + ShowStaticcall bool + + ShowOp func(op byte, pc uint64, addr common.Address) bool + OpStyler func(op byte) lipgloss.Style + + DecodeABI bool +} + +func (opts *CallTracerOptions) targetStyler(addr common.Address) lipgloss.Style { + if addr == opts.targetAddr { + addr = TargetAddress + } + + if opts.TargetStyler == nil { + return defaultTargetStyler(addr) + } + return opts.TargetStyler(addr) +} + +func (opts *CallTracerOptions) showOp(op byte, pc uint64, addr common.Address) bool { + if opts.ShowOp == nil { + return false + } + return opts.ShowOp(op, pc, addr) +} + +func (opts *CallTracerOptions) opStyler(op byte) lipgloss.Style { + if opts.OpStyler == nil { + return defaultOpStyler(op) + } + return opts.OpStyler(op) +} + +func defaultTargetStyler(addr common.Address) lipgloss.Style { + switch addr { + case TargetAddress: + return styleTarget + default: + return lipgloss.NewStyle() + } +} + +func defaultOpStyler(byte) lipgloss.Style { + return lipgloss.NewStyle() +} + +// NewCallTracer returns a new hook that writes to w and is configured with opts. +func NewCallTracer(w io.Writer, opts *CallTracerOptions) *tracing.Hooks { + if opts == nil { + opts = new(CallTracerOptions) + } + tracer := &callTracer{w: w, opts: opts} + + return &tracing.Hooks{ + OnEnter: tracer.EnterHook, + OnExit: tracer.ExitHook, + OnOpcode: tracer.OpcodeHook, + OnLog: tracer.OnLog, + } +} + +type callTracer struct { + w io.Writer + opts *CallTracerOptions + once sync.Once + + callStack []call + + // isInStaticcall is the number of nested staticcalls on the callStack. + // It is only incremented if opts.ShowStatic is true. + isInStaticcall int +} + +func (c *callTracer) EnterHook(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { + c.once.Do(func() { + c.opts.targetAddr = to + }) + + var ( + fn *w3.Func + isPrecompile bool + ) + if c.opts.DecodeABI && len(input) >= 4 { + sig := ([4]byte)(input[:4]) + fn, isPrecompile = fourbyte.Function(sig, to) + } + + callType := vm.OpCode(typ) + defer func() { c.callStack = append(c.callStack, call{callType, to, fn}) }() + if !c.opts.ShowStaticcall && callType == vm.STATICCALL { + c.isInStaticcall++ + } + if c.isInStaticcall > 0 { + return + } + + fmt.Fprintf(c.w, "%s%s %s%s%s\n", renderIdent(c.callStack, c.opts.targetStyler, 1), renderAddr(to, c.opts.targetStyler), renderCallType(typ), renderValue(c.opts.DecodeABI, value), renderInput(fn, isPrecompile, input, c.opts.targetStyler)) +} + +func (c *callTracer) ExitHook(depth int, output []byte, gasUsed uint64, err error, reverted bool) { + call := c.callStack[len(c.callStack)-1] + defer func() { c.callStack = c.callStack[:depth] }() + + if !c.opts.ShowStaticcall && call.Type == vm.STATICCALL { + defer func() { c.isInStaticcall-- }() + } + if c.isInStaticcall > 0 { + return + } + + if reverted { + reason, unpackErr := abi.UnpackRevert(output) + if unpackErr != nil { + reason = hex.EncodeToString(output) + } + fmt.Fprintf(c.w, "%s%s\n", renderIdent(c.callStack, c.opts.targetStyler, -1), styleRevert.Render(fmt.Sprintf("[%d]", gasUsed), err.Error()+":", reason)) + } else { + fmt.Fprintf(c.w, "%s[%d] %s\n", renderIdent(c.callStack, c.opts.targetStyler, -1), gasUsed, renderOutput(call.Func, output, c.opts.targetStyler)) + } +} + +func (c *callTracer) OpcodeHook(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { + if c.isInStaticcall > 0 || + !c.opts.showOp(op, pc, scope.Address()) { + return + } + fmt.Fprintln(c.w, renderIdent(c.callStack, c.opts.targetStyler, 0)+renderOp(op, c.opts.opStyler, pc, scope)) +} + +func (c *callTracer) OnLog(log *types.Log) { + if c.isInStaticcall > 0 { + return + } +} + +func renderIdent(callStack []call, styler func(addr common.Address) lipgloss.Style, kind int) (ident string) { + for i, call := range callStack { + style := styler(call.Target) + + s := "│ " + if isLast := i == len(callStack)-1; isLast { + switch kind { + case 1: + s = "├╴" + case -1: + s = "└╴" + } + } + ident += style.Faint(true).Render(s) + } + return ident +} + +func renderAddr(addr common.Address, styler func(addr common.Address) lipgloss.Style) string { + return styler(addr).Render(addr.Hex()) +} + +func renderCallType(typ byte) string { + switch vm.OpCode(typ) { + case vm.CALL: + return "" + case vm.STATICCALL: + return stylesStaticcall.Render("static") + " " + case vm.DELEGATECALL: + return stylesDelegatecall.Render("delegate") + " " + case vm.CREATE: + return "create " + case vm.CREATE2: + return "create2 " + default: + panic(fmt.Sprintf("unknown call type %92x", typ)) + } +} + +func renderValue(decodeABI bool, val *big.Int) string { + if val == nil || val.Sign() == 0 { + return "" + } + if !decodeABI { + return styleValue.Render(val.String(), "ETH") + " " + } + return styleValue.Render(w3.FromWei(val, 18), "ETH") + " " +} + +func renderInput(fn *w3.Func, isPrecompile bool, input []byte, styler func(addr common.Address) lipgloss.Style) string { + if fn != nil && len(input) >= 4 { + s, err := renderAbiInput(fn, isPrecompile, input, styler) + if err == nil { + return s + } + } + return renderRawInput(input, styler) +} + +func renderOutput(fn *w3.Func, output []byte, styler func(addr common.Address) lipgloss.Style) string { + if fn != nil && len(output) >= 4 { + s, err := renderAbiOutput(fn, output, styler) + if err == nil { + return s + } + } + return renderRawOutput(output, styler) +} + +func renderRawInput(input []byte, styler func(addr common.Address) lipgloss.Style) (s string) { + s = "0x" + if len(input)%32 == 4 { + s += hex.EncodeToString(input[:4]) + for i := 4; i < len(input); i += 32 { + s += renderWord(input[i:i+32], styler) + } + } else { + s += hex.EncodeToString(input) + } + return +} + +func renderRawOutput(output []byte, styler func(addr common.Address) lipgloss.Style) (s string) { + s = "0x" + if len(output)%32 == 0 { + for i := 0; i < len(output); i += 32 { + s += renderWord(output[i:i+32], styler) + } + } else { + s += hex.EncodeToString(output) + } + return +} + +func renderWord(word []byte, _ func(addr common.Address) lipgloss.Style) string { + s := hex.EncodeToString(word) + nonZeroWord := strings.TrimLeft(s, "0") + if len(nonZeroWord) < len(s) { + s = styleDim.Render(strings.Repeat("0", len(s)-len(nonZeroWord))) + nonZeroWord + } + return s +} + +func renderAbiInput(fn *w3.Func, isPrecompile bool, input []byte, styler func(addr common.Address) lipgloss.Style) (string, error) { + if !isPrecompile { + input = input[4:] + } + + args, err := fn.Args.Unpack(input) + if err != nil { + return "", err + } + + funcName := strings.Split(fn.Signature, "(")[0] + return funcName + "(" + renderAbiArgs(fn.Args, args, styler) + ")", nil +} + +func renderAbiOutput(fn *w3.Func, output []byte, styler func(addr common.Address) lipgloss.Style) (string, error) { + returns, err := fn.Returns.Unpack(output) + if err != nil { + return "", err + } + + return renderAbiArgs(fn.Returns, returns, styler), nil +} + +func renderAbiArgs(args abi.Arguments, vals []any, styler func(addr common.Address) lipgloss.Style) (s string) { + for i, val := range vals { + arg := args[i] + s += renderAbiTyp(&arg.Type, arg.Name, val, styler) + if i < len(vals)-1 { + s += styleDim.Render(",") + " " + } + } + return +} + +func renderAbiTyp(typ *abi.Type, name string, val any, styler func(addr common.Address) lipgloss.Style) (s string) { + if name != "" { + s += styleDim.Render(name+":") + " " + } + + switch val := val.(type) { + case []byte: + s += "0x" + hex.EncodeToString(val) + case [32]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [31]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [30]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [29]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [28]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [27]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [26]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [25]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [24]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [23]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [22]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [21]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [20]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [19]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [18]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [17]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [16]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [15]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [14]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [13]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [12]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [11]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [10]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [9]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [8]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [7]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [6]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [5]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [4]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [3]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [2]byte: + s += "0x" + hex.EncodeToString(val[:]) + case [1]byte: + s += "0x" + hex.EncodeToString(val[:]) + case common.Address: + style := styler(val) + s += style.Render(val.Hex()) + case common.Hash: + s += val.Hex() + case any: // tuple, array, or slice + switch refVal := reflect.ValueOf(val); refVal.Kind() { + case reflect.Slice: + s += "[" + for i := range refVal.Len() { + s += renderAbiTyp(typ.Elem, "", refVal.Index(i).Interface(), styler) + + if i < refVal.Len()-1 { + s += styleDim.Render(",") + " " + } + } + s += "]" + case reflect.Array: + s += "[" + for i := range refVal.Len() { + s += renderAbiTyp(typ.Elem, "", refVal.Index(i).Interface(), styler) + + if i < refVal.Len()-1 { + s += styleDim.Render(",") + " " + } + } + s += "]" + case reflect.Struct: + s += "(" + for i := range refVal.NumField() { + s += renderAbiTyp(typ.TupleElems[i], typ.TupleRawNames[i], refVal.Field(i).Interface(), styler) + + if i < refVal.NumField()-1 { + s += styleDim.Render(",") + " " + } + } + s += ")" + default: + s += fmt.Sprintf("%v", val) + } + default: + s += fmt.Sprintf("%v", val) + } + return s +} + +func renderOp(op byte, style func(byte) lipgloss.Style, pc uint64, scope tracing.OpContext) string { + const maxStackDepth = 7 + sb := new(strings.Builder) + sb.WriteString(styleDim.Render(fmt.Sprintf("0x%04x ", pc))) + sb.WriteString(style(op).Render(fmt.Sprintf("%-12s ", vm.OpCode(op)))) + + stack := scope.StackData() + for i, j := len(stack)-1, 0; i >= 0 && i >= len(stack)-maxStackDepth; i, j = i-1, j+1 { + notLast := i > 0 && i > len(stack)-maxStackDepth + if isAccessed := opAccessesStackElem(op, j); isAccessed { + sb.WriteString(stack[i].Hex()) + } else { + sb.WriteString(styleDim.Render(stack[i].Hex())) + } + if notLast { + sb.WriteString(" ") + } + } + + if len(stack) > maxStackDepth { + sb.WriteString(styleDim.Render(fmt.Sprintf(" …%d", len(stack)-maxStackDepth))) + } + + return sb.String() +} + +// call stores state of the current call in execution. +type call struct { + Type vm.OpCode + Target common.Address + Func *w3.Func +} + +// opAccessesStackElem returns true, if the given opcode accesses the stack at +// index i, otherwise false. +func opAccessesStackElem(op byte, i int) bool { + switch { + case vm.SWAP1 <= op && op <= vm.SWAP16: + return i == 0 || i == int(op)-int(vm.SWAP1)+1 + case vm.DUP1 <= op && op <= vm.DUP16: + return i == int(op)-int(vm.DUP1) + default: + return i < pops[op] + } +} + +var pops = [256]int{ + vm.STOP: 0, vm.ADD: 2, vm.MUL: 2, vm.SUB: 2, vm.DIV: 2, vm.SDIV: 2, vm.MOD: 2, vm.SMOD: 2, vm.ADDMOD: 3, vm.MULMOD: 3, vm.EXP: 2, vm.SIGNEXTEND: 2, + vm.LT: 2, vm.GT: 2, vm.SLT: 2, vm.SGT: 2, vm.EQ: 2, vm.ISZERO: 1, vm.AND: 2, vm.OR: 2, vm.XOR: 2, vm.NOT: 1, vm.BYTE: 2, vm.SHL: 2, vm.SHR: 2, vm.SAR: 2, + vm.KECCAK256: 2, + vm.BALANCE: 1, vm.CALLDATALOAD: 1, vm.CALLDATACOPY: 3, vm.CODECOPY: 3, vm.EXTCODESIZE: 1, vm.EXTCODECOPY: 4, vm.RETURNDATACOPY: 3, vm.EXTCODEHASH: 1, + vm.BLOCKHASH: 1, vm.BLOBHASH: 1, + vm.POP: 1, vm.MLOAD: 1, vm.MSTORE: 2, vm.MSTORE8: 2, vm.SLOAD: 1, vm.SSTORE: 2, vm.JUMP: 1, vm.JUMPI: 2, vm.TLOAD: 1, vm.TSTORE: 2, vm.MCOPY: 3, + vm.LOG0: 2, vm.LOG1: 3, vm.LOG2: 4, vm.LOG3: 5, vm.LOG4: 6, + vm.CREATE: 3, vm.CALL: 7, vm.CALLCODE: 7, vm.RETURN: 2, vm.DELEGATECALL: 6, vm.CREATE2: 4, vm.STATICCALL: 6, vm.REVERT: 2, vm.SELFDESTRUCT: 1, +} diff --git a/w3vm/receipt.go b/w3vm/receipt.go index a00ab3ec..350cfd28 100644 --- a/w3vm/receipt.go +++ b/w3vm/receipt.go @@ -16,11 +16,11 @@ type Receipt struct { GasUsed uint64 // Gas used for executing the message GasRefund uint64 // Gas refunded after executing the message - Logs []*types.Log // Logs emitted by the message - Output []byte // Output bytes of the applied message - ContractAddress *common.Address // Contract address created by a contract creation transaction + Logs []*types.Log // Logs emitted while executing the message + Output []byte // Output of the executed message + ContractAddress *common.Address // Address of the created contract, if any - Err error // Revert reason + Err error // Execution error, if any } // DecodeReturns is like [w3types.Func.DecodeReturns], but returns [ErrMissingFunc] diff --git a/w3vm/util.go b/w3vm/util.go index 1b78439f..88482a41 100644 --- a/w3vm/util.go +++ b/w3vm/util.go @@ -28,8 +28,8 @@ func RandA() (addr common.Address) { } var ( - weth9BalancePos = common.BigToHash(big.NewInt(3)) - weth9AllowancePos = common.BigToHash(big.NewInt(4)) + weth9BalancePos = common.BytesToHash([]byte{3}) + weth9AllowancePos = common.BytesToHash([]byte{4}) ) // WETHBalanceSlot returns the storage slot that stores the WETH balance of @@ -39,18 +39,26 @@ func WETHBalanceSlot(addr common.Address) common.Hash { } // WETHAllowanceSlot returns the storage slot that stores the WETH allowance -// of the given owner and spender. +// of the given owner to the spender. func WETHAllowanceSlot(owner, spender common.Address) common.Hash { return Slot2(weth9AllowancePos, common.BytesToHash(owner[:]), common.BytesToHash(spender[:])) } // Slot returns the storage slot of a mapping with the given position and key. +// +// Slot follows the Solidity storage layout for: +// +// mapping(bytes32 => bytes32) func Slot(pos, key common.Hash) common.Hash { return crypto.Keccak256Hash(key[:], pos[:]) } // Slot2 returns the storage slot of a double mapping with the given position // and keys. +// +// Slot2 follows the Solidity storage layout for: +// +// mapping(bytes32 => mapping(bytes32 => bytes32)) func Slot2(pos, key, key2 common.Hash) common.Hash { return crypto.Keccak256Hash( key2[:], diff --git a/w3vm/vm.go b/w3vm/vm.go index 2272f934..391b8c96 100644 --- a/w3vm/vm.go +++ b/w3vm/vm.go @@ -77,8 +77,8 @@ func New(opts ...Option) (*VM, error) { return vm, nil } -// Apply the given message to the VM and return its receipt. Multiple tracing hooks -// can be given to trace the execution of the message. +// Apply the given message to the VM, and return its receipt. Multiple tracing hooks +// may be given to trace the execution of the message. func (vm *VM) Apply(msg *w3types.Message, hooks ...*tracing.Hooks) (*Receipt, error) { return vm.apply(msg, false, joinHooks(hooks)) } @@ -158,15 +158,15 @@ func (v *VM) apply(msg *w3types.Message, isCall bool, hooks *tracing.Hooks) (*Re return receipt, receipt.Err } -// Call calls the given message on the VM and returns a receipt. Any state changes -// of a call are reverted. Multiple tracing hooks can be passed to trace the execution +// Call the given message on the VM, and returns its receipt. Any state changes +// of a call are reverted. Multiple tracing hooks may be given to trace the execution // of the message. func (vm *VM) Call(msg *w3types.Message, hooks ...*tracing.Hooks) (*Receipt, error) { return vm.apply(msg, true, joinHooks(hooks)) } // CallFunc is a utility function for [VM.Call] that calls the given function -// on the given contract address with the given arguments and parses the +// on the given contract address with the given arguments and decodes the // output into the given returns. // // Example: @@ -460,7 +460,7 @@ type Option func(*VM) // WithChainConfig sets the chain config for the VM. // -// If not explicitly set, the chain config is set to [params.MainnetChainConfig]. +// If not provided, the chain config defaults to [params.MainnetChainConfig]. func WithChainConfig(cfg *params.ChainConfig) Option { return func(vm *VM) { vm.opts.chainConfig = cfg } } @@ -478,9 +478,8 @@ func WithState(state w3types.State) Option { return func(vm *VM) { vm.opts.preState = state } } -// WithStateDB sets the state DB for the VM. -// -// The state DB can originate from a snapshot of the VM. +// WithStateDB sets the state DB for the VM, that is usually a snapshot +// obtained from [VM.Snapshot]. func WithStateDB(db *state.StateDB) Option { return func(vm *VM) { vm.db = db @@ -506,7 +505,7 @@ func WithFork(client *w3.Client, blockNumber *big.Int) Option { } } -// WithHeader sets the block context for the VM based on the given header +// WithHeader sets the block context for the VM based on the given header. func WithHeader(header *types.Header) Option { return func(vm *VM) { vm.opts.header = header } } diff --git a/w3vm/vm_test.go b/w3vm/vm_test.go index 5fde5981..c74746d3 100644 --- a/w3vm/vm_test.go +++ b/w3vm/vm_test.go @@ -810,114 +810,3 @@ func BenchmarkTransferWETH9(b *testing.B) { } func ptr[T any](t T) *T { return &t } - -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 -}