From b7f5bea328f51fd595199b96c2f02fc1b0877872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caner=20=C3=87=C4=B1dam?= Date: Mon, 24 Apr 2023 15:27:26 +0300 Subject: [PATCH] first commit --- .github/workflows/build.yml | 27 + .github/workflows/coverage.yml | 45 ++ .github/workflows/pull_request.yml | 25 + .gitignore | 2 + .golangci.yml | 12 + LICENSE | 22 + Makefile | 43 ++ README.md | 92 +++ call.go | 112 ++++ call_test.go | 37 ++ caller.go | 77 +++ caller_test.go | 328 ++++++++++ contracts/contract_multicall/abi.json | 456 +++++++++++++ contracts/contract_multicall/interface.go | 10 + contracts/contract_multicall/multicall.go | 756 ++++++++++++++++++++++ examples/agent_registry/main.go | 108 ++++ examples/balance/main.go | 70 ++ go.mod | 30 + go.sum | 108 ++++ 19 files changed, 2360 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/coverage.yml create mode 100644 .github/workflows/pull_request.yml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 call.go create mode 100644 call_test.go create mode 100644 caller.go create mode 100644 caller_test.go create mode 100644 contracts/contract_multicall/abi.json create mode 100644 contracts/contract_multicall/interface.go create mode 100644 contracts/contract_multicall/multicall.go create mode 100644 examples/agent_registry/main.go create mode 100644 examples/balance/main.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ead3b6d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,27 @@ +name: build + +on: + push: + branches: [master] + +jobs: + go: + name: Validate + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: '1.19' + cache: false + - name: Test + run: make test + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + skip-go-installation: true + skip-pkg-cache: true + skip-build-cache: true + version: v1.52.2 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..49bdc93 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,45 @@ +name: coverage + +on: + push: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + name: Update coverage badge + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: '1.19' + cache: false + - name: Run Test + run: make cover + - name: Go Coverage Badge + uses: tj-actions/coverage-badge-go@v2 + with: + text: coverage + filename: coverage.out + - name: Verify Changed files + uses: tj-actions/verify-changed-files@v12 + id: verify-changed-files + with: + files: README.md + - name: Commit changes + if: steps.verify-changed-files.outputs.files_changed == 'true' + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git add README.md + git commit -m "chore: update coverage badge" + - name: Push changes + if: steps.verify-changed-files.outputs.files_changed == 'true' + uses: ad-m/github-push-action@master + with: + github_token: ${{ github.token }} + branch: ${{ github.head_ref }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000..9f12395 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,25 @@ +name: PR + +on: + pull_request: + +jobs: + go: + name: Validate + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: '1.19' + cache: false + - name: Test + run: make test + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + skip-go-installation: true + skip-pkg-cache: true + skip-build-cache: true + version: v1.52.2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6aa43d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +toolbin +coverage.out diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..b43a04d --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,12 @@ +linters-settings: + gosimple: + go: "1.19" + + staticcheck: + go: "1.19" + + stylecheck: + go: "1.19" + + unused: + go: "1.19" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..86f9995 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2016-2023 Forta Foundation and contributors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b077f07 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +export GOBIN = $(shell pwd)/toolbin + +LINT = $(GOBIN)/golangci-lint +FORMAT = $(GOBIN)/goimports + +ABIGEN = $(GOBIN)/abigen + +.PHONY: tools +tools: + @echo 'Installing tools...' + @rm -rf toolbin + @go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2 + @go install golang.org/x/tools/cmd/goimports@v0.1.11 + + @go install github.com/ethereum/go-ethereum/cmd/abigen@v1.11.5 + +.PHONY: require-tools +require-tools: tools + @echo 'Checking installed tools...' + @file $(LINT) > /dev/null + @file $(FORMAT) > /dev/null + + @file $(ABIGEN) > /dev/null + + @echo "All tools found in $(GOBIN)!" + +.PHONY: generate +generate: require-tools + @$(ABIGEN) --out contracts/contract_multicall/multicall.go \ + --abi contracts/contract_multicall/abi.json --pkg contract_multicall \ + --type Multicall + +.PHONY: test +test: + go test -v -count=1 -covermode=count -coverprofile=coverage.out + +.PHONY: cover +cover: test + go tool cover -func=coverage.out -o=coverage.out + +.PHONY: coverage +coverage: test + go tool cover -func=coverage.out | grep total | awk '{print substr($$3, 1, length($$3)-1)}' diff --git a/README.md b/README.md new file mode 100644 index 0000000..6b52ede --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# go-multicall +![build](https://github.com/forta-network/go-multicall/actions/workflows/build.yml/badge.svg) + +A thin Go client for making multiple function calls in single `eth_call` request + +- Uses the go-ethereum tools and libraries +- Interfaces with the [MakerDAO `Multicall3` contract](https://github.com/mds1/multicall) + +_**Warning:** MakerDAO Multicall contracts are different than the [forta-network Multicall contract](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Multicall.sol). Please see [this thread](https://forum.openzeppelin.com/t/multicall-by-oz-and-makerdao-has-a-difference/9350) in the forta-network forum if you are looking for an explanation._ + +## Install + +``` +go get github.com/forta-network/go-multicall +``` + +## Example + +(See other examples under the `examples` directory!) + +```go +package main + +import ( + "context" + "fmt" + "math/big" + + "github.com/forta-network/go-multicall" + "github.com/ethereum/go-ethereum/common" +) + +const ( + APIURL = "https://cloudflare-eth.com" + ERC20ABI = `[ + { + "constant":true, + "inputs":[ + { + "name":"tokenOwner", + "type":"address" + } + ], + "name":"balanceOf", + "outputs":[ + { + "name":"balance", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + } + ]` +) + +type balanceOutput struct { + Balance *big.Int +} + +func main() { + caller, err := multicall.Dial(context.Background(), APIURL) + if err != nil { + panic(err) + } + + contract, err := multicall.NewContract(ERC20ABI, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") + if err != nil { + panic(err) + } + + calls, err := caller.Call(nil, + contract.NewCall( + new(balanceOutput), + "balanceOf", + common.HexToAddress("0xcEe284F754E854890e311e3280b767F80797180d"), // Arbitrum One gateway + ).Name("Arbitrum One gateway balance"), + contract.NewCall( + new(balanceOutput), + "balanceOf", + common.HexToAddress("0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf"), // Polygon ERC20 bridge + ).Name("Polygon ERC20 bridge balance"), + ) + if err != nil { + panic(err) + } + for _, call := range calls { + fmt.Println(call.CallName, ":", call.Outputs.(*balanceOutput).Balance) + } +} +``` diff --git a/call.go b/call.go new file mode 100644 index 0000000..96fb079 --- /dev/null +++ b/call.go @@ -0,0 +1,112 @@ +package multicall + +import ( + "bytes" + "errors" + "fmt" + "reflect" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" +) + +// Contract wraps the parsed ABI and acts as a call factory. +type Contract struct { + ABI *abi.ABI + Address common.Address +} + +// NewContract creates a new call factory. +func NewContract(rawJson, address string) (*Contract, error) { + parsedABI, err := ParseABI(rawJson) + if err != nil { + return nil, err + } + return &Contract{ + ABI: parsedABI, + Address: common.HexToAddress(address), + }, nil +} + +// ParseABI parses raw ABI JSON. +func ParseABI(rawJson string) (*abi.ABI, error) { + parsed, err := abi.JSON(bytes.NewBufferString(rawJson)) + if err != nil { + return nil, fmt.Errorf("failed to parse abi: %v", err) + } + return &parsed, nil +} + +// Call wraps a multicall call. +type Call struct { + CallName string + Contract *Contract + Method string + Inputs []any + Outputs any + CanFail bool + Failed bool +} + +// NewCall creates a new call using given inputs. +// Outputs type is the expected output struct to unpack and set values in. +func (contract *Contract) NewCall( + outputs any, methodName string, inputs ...any, +) *Call { + return &Call{ + Contract: contract, + Method: methodName, + Inputs: inputs, + Outputs: outputs, + } +} + +// Name sets a name for the call. +func (call *Call) Name(name string) *Call { + call.CallName = name + return call +} + +// AllowFailure sets if the call is allowed to fail. This helps avoiding a revert +// when one of the calls in the array fails. +func (call *Call) AllowFailure() *Call { + call.CanFail = true + return call +} + +// Unpack unpacks and converts EVM outputs and sets struct fields. +func (call *Call) Unpack(b []byte) error { + t := reflect.ValueOf(call.Outputs) + if t.Kind() == reflect.Pointer { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return errors.New("outputs type is not a struct") + } + + out, err := call.Contract.ABI.Unpack(call.Method, b) + if err != nil { + return fmt.Errorf("failed to unpack '%s' outputs: %v", call.Method, err) + } + + fieldCount := t.NumField() + for i := 0; i < fieldCount; i++ { + field := t.Field(i) + converted := abi.ConvertType(out[i], field.Interface()) + field.Set(reflect.ValueOf(converted)) + } + + return nil +} + +// Pack converts and packs EVM inputs. +func (call *Call) Pack() ([]byte, error) { + if len(call.Inputs) == 0 { + return make([]byte, 0), nil + } + b, err := call.Contract.ABI.Pack(call.Method, call.Inputs...) + if err != nil { + return nil, fmt.Errorf("failed to pack '%s' inputs: %v", call.Method, err) + } + return b, nil +} diff --git a/call_test.go b/call_test.go new file mode 100644 index 0000000..c490245 --- /dev/null +++ b/call_test.go @@ -0,0 +1,37 @@ +package multicall + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCall_BadABI(t *testing.T) { + r := require.New(t) + + const oneValueABI = `[ + { + "constant":true, + "inputs": [ + { + "name":"val1", + "type":"bool" + } + ], + "name":"testFunc", + "outputs": [ + { + "name":"val1", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + } + ` // missing closing ] at the end + + _, err := NewContract(oneValueABI, "0x") + r.Error(err) + r.ErrorContains(err, "unexpected EOF") +} diff --git a/caller.go b/caller.go new file mode 100644 index 0000000..ed2c7ce --- /dev/null +++ b/caller.go @@ -0,0 +1,77 @@ +package multicall + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/forta-network/go-multicall/contracts/contract_multicall" +) + +// DefaultAddress is the same for all chains (Multicall3). +// Taken from https://github.com/mds1/multicall +const DefaultAddress = "0xcA11bde05977b3631167028862bE2a173976CA11" + +// Caller makes multicalls. +type Caller struct { + contract contract_multicall.Interface +} + +// New creates a new caller. +func New(client bind.ContractCaller, multicallAddr ...string) (*Caller, error) { + addr := DefaultAddress + if multicallAddr != nil { + addr = multicallAddr[0] + } + contract, err := contract_multicall.NewMulticallCaller(common.HexToAddress(addr), client) + if err != nil { + return nil, err + } + return &Caller{ + contract: contract, + }, nil +} + +// Dial dials and Ethereum JSON-RPC API and uses the client as the +// caller backend. +func Dial(ctx context.Context, rawUrl string, multicallAddr ...string) (*Caller, error) { + client, err := ethclient.DialContext(ctx, rawUrl) + if err != nil { + return nil, err + } + return New(client, multicallAddr...) +} + +// Call makes multicalls. +func (caller *Caller) Call(opts *bind.CallOpts, calls ...*Call) ([]*Call, error) { + var multiCalls []contract_multicall.Multicall3Call3 + + for i, call := range calls { + b, err := call.Pack() + if err != nil { + return calls, fmt.Errorf("failed to pack call inputs at index [%d]: %v", i, err) + } + multiCalls = append(multiCalls, contract_multicall.Multicall3Call3{ + Target: call.Contract.Address, + AllowFailure: call.CanFail, + CallData: b, + }) + } + + results, err := caller.contract.Aggregate3(opts, multiCalls) + if err != nil { + return calls, fmt.Errorf("multicall failed: %v", err) + } + + for i, result := range results { + call := calls[i] // index always matches + call.Failed = !result.Success + if err := call.Unpack(result.ReturnData); err != nil { + return calls, fmt.Errorf("failed to unpack call outputs at index [%d]: %v", i, err) + } + } + + return calls, nil +} diff --git a/caller_test.go b/caller_test.go new file mode 100644 index 0000000..162270e --- /dev/null +++ b/caller_test.go @@ -0,0 +1,328 @@ +package multicall + +import ( + "context" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/forta-network/go-multicall/contracts/contract_multicall" + "github.com/stretchr/testify/require" +) + +type testType struct { + Val1 bool + Val2 string + Val3 []string + Val4 []*big.Int + Val5 *big.Int + Val6 common.Address +} + +const ( + testAddr1 = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" + testAddr2 = "0x64d5192F03bD98dB1De2AA8B4abAC5419eaC32CE" +) + +const testABI = `[ + { + "constant":true, + "inputs":[ + { + "name":"val1", + "type":"bool" + }, + { + "name":"val2", + "type":"string" + }, + { + "name":"val1", + "type":"string[]" + }, + { + "name":"val4", + "type":"uint256[]" + }, + { + "name":"val5", + "type":"uint256" + }, + { + "name":"val6", + "type":"address" + } + ], + "name":"testFunc", + "outputs":[ + { + "name":"val1", + "type":"bool" + }, + { + "name":"val2", + "type":"string" + }, + { + "name":"val1", + "type":"string[]" + }, + { + "name":"val4", + "type":"uint256[]" + }, + { + "name":"val5", + "type":"uint256" + }, + { + "name":"val6", + "type":"address" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + } +]` + +type multicallStub struct { + returnData func(calls []contract_multicall.Multicall3Call3) [][]byte +} + +func (ms *multicallStub) Aggregate3(opts *bind.CallOpts, calls []contract_multicall.Multicall3Call3) (results []contract_multicall.Multicall3Result, err error) { + allReturnData := ms.returnData(calls) + for _, returnData := range allReturnData { + results = append(results, contract_multicall.Multicall3Result{ + Success: true, + ReturnData: returnData, + }) + } + return +} + +func TestCaller_TwoCalls(t *testing.T) { + r := require.New(t) + + testContract1, err := NewContract(testABI, testAddr1) + r.NoError(err) + + testContract2, err := NewContract(testABI, testAddr2) + r.NoError(err) + + values1 := testType{ + Val1: true, + Val2: "val2", + Val3: []string{"val3_1", "val3_2"}, + Val4: []*big.Int{big.NewInt(123), big.NewInt(456)}, + Val5: big.NewInt(678), + Val6: common.HexToAddress(testAddr1), + } + + call1 := testContract1.NewCall( + new(testType), "testFunc", + values1.Val1, values1.Val2, values1.Val3, + values1.Val4, values1.Val5, values1.Val6, + ) + + values2 := testType{ + Val1: false, + Val2: "val2_alt", + Val3: []string{"val3_1_alt", "val3_2_alt"}, + Val4: []*big.Int{big.NewInt(1239), big.NewInt(4569)}, + Val5: big.NewInt(6789), + Val6: common.HexToAddress(testAddr2), + } + + call2 := testContract2.NewCall( + new(testType), "testFunc", + values2.Val1, values2.Val2, values2.Val3, + values2.Val4, values2.Val5, values2.Val6, + ) + + caller := &Caller{ + contract: &multicallStub{ + returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte { + return [][]byte{ + // return inputs as outputs by stripping the method prefix + calls[0].CallData[4:], + calls[1].CallData[4:], + } + }, + }, + } + + calls, err := caller.Call(nil, call1, call2) + r.NoError(err) + + call1Out := calls[0].Outputs.(*testType) + r.Equal(values1.Val1, call1Out.Val1) + r.Equal(values1.Val2, call1Out.Val2) + r.Equal(values1.Val3, call1Out.Val3) + r.Equal(values1.Val4, call1Out.Val4) + r.Equal(values1.Val5, call1Out.Val5) + r.Equal(values1.Val6, call1Out.Val6) + + call2Out := calls[1].Outputs.(*testType) + r.Equal(values2.Val1, call2Out.Val1) + r.Equal(values2.Val2, call2Out.Val2) + r.Equal(values2.Val3, call2Out.Val3) + r.Equal(values2.Val4, call2Out.Val4) + r.Equal(values2.Val5, call2Out.Val5) + r.Equal(values2.Val6, call2Out.Val6) +} + +const emptyABI = `[ + { + "constant":true, + "inputs": [], + "name":"testFunc", + "outputs": [], + "payable":false, + "stateMutability":"view", + "type":"function" + } +]` + +func TestCaller_EmptyCall(t *testing.T) { + r := require.New(t) + + testContract, err := NewContract(emptyABI, testAddr1) + r.NoError(err) + + call := testContract.NewCall( + new(struct{}), "testFunc", + // no inputs + ) + + caller := &Caller{ + contract: &multicallStub{ + returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte { + return [][]byte{ + // return empty output + make([]byte, 0), + } + }, + }, + } + + calls, err := caller.Call(nil, call) + r.NoError(err) + r.Len(calls, 1) +} + +const oneValueABI = `[ + { + "constant":true, + "inputs": [ + { + "name":"val1", + "type":"bool" + } + ], + "name":"testFunc", + "outputs": [ + { + "name":"val1", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + } +]` + +func TestCaller_BadInput(t *testing.T) { + r := require.New(t) + + testContract, err := NewContract(oneValueABI, testAddr1) + r.NoError(err) + + call := testContract.NewCall( + new(struct{}), "testFunc", + 'a', + ) + + caller := &Caller{ + contract: &multicallStub{ + returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte { + return [][]byte{ + // return bad output + {}, + } + }, + }, + } + + calls, err := caller.Call(nil, call) + r.Error(err) + r.ErrorContains(err, "cannot use") + r.Len(calls, 1) +} + +func TestCaller_BadOutput(t *testing.T) { + r := require.New(t) + + testContract, err := NewContract(emptyABI, testAddr1) + r.NoError(err) + + call := testContract.NewCall( + new(struct{}), "testFunc", + // no inputs + ) + + caller := &Caller{ + contract: &multicallStub{ + returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte { + return [][]byte{ + // return bad output + {'a'}, + } + }, + }, + } + + calls, err := caller.Call(nil, call) + r.Error(err) + r.Len(calls, 1) +} + +func TestCaller_WrongOutputsType(t *testing.T) { + r := require.New(t) + + testContract, err := NewContract(oneValueABI, testAddr1) + r.NoError(err) + + call := testContract.NewCall( + new([]struct{}), "testFunc", + true, + ) + + packedOutput, err := testContract.ABI.Pack("testFunc", true) + r.NoError(err) + + caller := &Caller{ + contract: &multicallStub{ + returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte { + return [][]byte{ + packedOutput, + } + }, + }, + } + + calls, err := caller.Call(nil, call) + r.Error(err) + r.ErrorContains(err, "not a struct") + r.Len(calls, 1) +} + +func TestDial(t *testing.T) { + r := require.New(t) + + caller, err := Dial(context.Background(), "https://polygon-rpc.com") + r.NoError(err) + r.NotNil(caller) +} diff --git a/contracts/contract_multicall/abi.json b/contracts/contract_multicall/abi.json new file mode 100644 index 0000000..ddef28a --- /dev/null +++ b/contracts/contract_multicall/abi.json @@ -0,0 +1,456 @@ +[ + { + "inputs":[ + { + "components":[ + { + "internalType":"address", + "name":"target", + "type":"address" + }, + { + "internalType":"bytes", + "name":"callData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Call[]", + "name":"calls", + "type":"tuple[]" + } + ], + "name":"aggregate", + "outputs":[ + { + "internalType":"uint256", + "name":"blockNumber", + "type":"uint256" + }, + { + "internalType":"bytes[]", + "name":"returnData", + "type":"bytes[]" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "components":[ + { + "internalType":"address", + "name":"target", + "type":"address" + }, + { + "internalType":"bool", + "name":"allowFailure", + "type":"bool" + }, + { + "internalType":"bytes", + "name":"callData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Call3[]", + "name":"calls", + "type":"tuple[]" + } + ], + "name":"aggregate3", + "outputs":[ + { + "components":[ + { + "internalType":"bool", + "name":"success", + "type":"bool" + }, + { + "internalType":"bytes", + "name":"returnData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Result[]", + "name":"returnData", + "type":"tuple[]" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "components":[ + { + "internalType":"address", + "name":"target", + "type":"address" + }, + { + "internalType":"bool", + "name":"allowFailure", + "type":"bool" + }, + { + "internalType":"uint256", + "name":"value", + "type":"uint256" + }, + { + "internalType":"bytes", + "name":"callData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Call3Value[]", + "name":"calls", + "type":"tuple[]" + } + ], + "name":"aggregate3Value", + "outputs":[ + { + "components":[ + { + "internalType":"bool", + "name":"success", + "type":"bool" + }, + { + "internalType":"bytes", + "name":"returnData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Result[]", + "name":"returnData", + "type":"tuple[]" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "components":[ + { + "internalType":"address", + "name":"target", + "type":"address" + }, + { + "internalType":"bytes", + "name":"callData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Call[]", + "name":"calls", + "type":"tuple[]" + } + ], + "name":"blockAndAggregate", + "outputs":[ + { + "internalType":"uint256", + "name":"blockNumber", + "type":"uint256" + }, + { + "internalType":"bytes32", + "name":"blockHash", + "type":"bytes32" + }, + { + "components":[ + { + "internalType":"bool", + "name":"success", + "type":"bool" + }, + { + "internalType":"bytes", + "name":"returnData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Result[]", + "name":"returnData", + "type":"tuple[]" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"getBasefee", + "outputs":[ + { + "internalType":"uint256", + "name":"basefee", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"uint256", + "name":"blockNumber", + "type":"uint256" + } + ], + "name":"getBlockHash", + "outputs":[ + { + "internalType":"bytes32", + "name":"blockHash", + "type":"bytes32" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"getBlockNumber", + "outputs":[ + { + "internalType":"uint256", + "name":"blockNumber", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"getChainId", + "outputs":[ + { + "internalType":"uint256", + "name":"chainid", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"getCurrentBlockCoinbase", + "outputs":[ + { + "internalType":"address", + "name":"coinbase", + "type":"address" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"getCurrentBlockDifficulty", + "outputs":[ + { + "internalType":"uint256", + "name":"difficulty", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"getCurrentBlockGasLimit", + "outputs":[ + { + "internalType":"uint256", + "name":"gaslimit", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"getCurrentBlockTimestamp", + "outputs":[ + { + "internalType":"uint256", + "name":"timestamp", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"address", + "name":"addr", + "type":"address" + } + ], + "name":"getEthBalance", + "outputs":[ + { + "internalType":"uint256", + "name":"balance", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + + ], + "name":"getLastBlockHash", + "outputs":[ + { + "internalType":"bytes32", + "name":"blockHash", + "type":"bytes32" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"bool", + "name":"requireSuccess", + "type":"bool" + }, + { + "components":[ + { + "internalType":"address", + "name":"target", + "type":"address" + }, + { + "internalType":"bytes", + "name":"callData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Call[]", + "name":"calls", + "type":"tuple[]" + } + ], + "name":"tryAggregate", + "outputs":[ + { + "components":[ + { + "internalType":"bool", + "name":"success", + "type":"bool" + }, + { + "internalType":"bytes", + "name":"returnData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Result[]", + "name":"returnData", + "type":"tuple[]" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"bool", + "name":"requireSuccess", + "type":"bool" + }, + { + "components":[ + { + "internalType":"address", + "name":"target", + "type":"address" + }, + { + "internalType":"bytes", + "name":"callData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Call[]", + "name":"calls", + "type":"tuple[]" + } + ], + "name":"tryBlockAndAggregate", + "outputs":[ + { + "internalType":"uint256", + "name":"blockNumber", + "type":"uint256" + }, + { + "internalType":"bytes32", + "name":"blockHash", + "type":"bytes32" + }, + { + "components":[ + { + "internalType":"bool", + "name":"success", + "type":"bool" + }, + { + "internalType":"bytes", + "name":"returnData", + "type":"bytes" + } + ], + "internalType":"struct Multicall3.Result[]", + "name":"returnData", + "type":"tuple[]" + } + ], + "stateMutability":"view", + "type":"function" + } + ] \ No newline at end of file diff --git a/contracts/contract_multicall/interface.go b/contracts/contract_multicall/interface.go new file mode 100644 index 0000000..de10de2 --- /dev/null +++ b/contracts/contract_multicall/interface.go @@ -0,0 +1,10 @@ +package contract_multicall + +import ( + "github.com/ethereum/go-ethereum/accounts/abi/bind" +) + +// Interface is an abstraction of the contract. +type Interface interface { + Aggregate3(opts *bind.CallOpts, calls []Multicall3Call3) ([]Multicall3Result, error) +} diff --git a/contracts/contract_multicall/multicall.go b/contracts/contract_multicall/multicall.go new file mode 100644 index 0000000..9f05f66 --- /dev/null +++ b/contracts/contract_multicall/multicall.go @@ -0,0 +1,756 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contract_multicall + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// Multicall3Call is an auto generated low-level Go binding around an user-defined struct. +type Multicall3Call struct { + Target common.Address + CallData []byte +} + +// Multicall3Call3 is an auto generated low-level Go binding around an user-defined struct. +type Multicall3Call3 struct { + Target common.Address + AllowFailure bool + CallData []byte +} + +// Multicall3Call3Value is an auto generated low-level Go binding around an user-defined struct. +type Multicall3Call3Value struct { + Target common.Address + AllowFailure bool + Value *big.Int + CallData []byte +} + +// Multicall3Result is an auto generated low-level Go binding around an user-defined struct. +type Multicall3Result struct { + Success bool + ReturnData []byte +} + +// MulticallMetaData contains all meta data concerning the Multicall contract. +var MulticallMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes[]\",\"name\":\"returnData\",\"type\":\"bytes[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowFailure\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call3[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate3\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"allowFailure\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call3Value[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"aggregate3Value\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"blockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBasefee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"basefee\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBlockNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getChainId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"chainid\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockCoinbase\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"coinbase\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockDifficulty\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"difficulty\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockGasLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"gaslimit\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getCurrentBlockTimestamp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"timestamp\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getEthBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getLastBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryAggregate\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"requireSuccess\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Call[]\",\"name\":\"calls\",\"type\":\"tuple[]\"}],\"name\":\"tryBlockAndAggregate\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"returnData\",\"type\":\"bytes\"}],\"internalType\":\"structMulticall3.Result[]\",\"name\":\"returnData\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", +} + +// MulticallABI is the input ABI used to generate the binding from. +// Deprecated: Use MulticallMetaData.ABI instead. +var MulticallABI = MulticallMetaData.ABI + +// Multicall is an auto generated Go binding around an Ethereum contract. +type Multicall struct { + MulticallCaller // Read-only binding to the contract + MulticallTransactor // Write-only binding to the contract + MulticallFilterer // Log filterer for contract events +} + +// MulticallCaller is an auto generated read-only Go binding around an Ethereum contract. +type MulticallCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MulticallTransactor is an auto generated write-only Go binding around an Ethereum contract. +type MulticallTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MulticallFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type MulticallFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MulticallSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type MulticallSession struct { + Contract *Multicall // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MulticallCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type MulticallCallerSession struct { + Contract *MulticallCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// MulticallTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type MulticallTransactorSession struct { + Contract *MulticallTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MulticallRaw is an auto generated low-level Go binding around an Ethereum contract. +type MulticallRaw struct { + Contract *Multicall // Generic contract binding to access the raw methods on +} + +// MulticallCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type MulticallCallerRaw struct { + Contract *MulticallCaller // Generic read-only contract binding to access the raw methods on +} + +// MulticallTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type MulticallTransactorRaw struct { + Contract *MulticallTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewMulticall creates a new instance of Multicall, bound to a specific deployed contract. +func NewMulticall(address common.Address, backend bind.ContractBackend) (*Multicall, error) { + contract, err := bindMulticall(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Multicall{MulticallCaller: MulticallCaller{contract: contract}, MulticallTransactor: MulticallTransactor{contract: contract}, MulticallFilterer: MulticallFilterer{contract: contract}}, nil +} + +// NewMulticallCaller creates a new read-only instance of Multicall, bound to a specific deployed contract. +func NewMulticallCaller(address common.Address, caller bind.ContractCaller) (*MulticallCaller, error) { + contract, err := bindMulticall(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &MulticallCaller{contract: contract}, nil +} + +// NewMulticallTransactor creates a new write-only instance of Multicall, bound to a specific deployed contract. +func NewMulticallTransactor(address common.Address, transactor bind.ContractTransactor) (*MulticallTransactor, error) { + contract, err := bindMulticall(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &MulticallTransactor{contract: contract}, nil +} + +// NewMulticallFilterer creates a new log filterer instance of Multicall, bound to a specific deployed contract. +func NewMulticallFilterer(address common.Address, filterer bind.ContractFilterer) (*MulticallFilterer, error) { + contract, err := bindMulticall(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &MulticallFilterer{contract: contract}, nil +} + +// bindMulticall binds a generic wrapper to an already deployed contract. +func bindMulticall(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := MulticallMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Multicall *MulticallRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Multicall.Contract.MulticallCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Multicall *MulticallRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Multicall.Contract.MulticallTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Multicall *MulticallRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Multicall.Contract.MulticallTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Multicall *MulticallCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Multicall.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Multicall *MulticallTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Multicall.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Multicall *MulticallTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Multicall.Contract.contract.Transact(opts, method, params...) +} + +// Aggregate is a free data retrieval call binding the contract method 0x252dba42. +// +// Solidity: function aggregate((address,bytes)[] calls) view returns(uint256 blockNumber, bytes[] returnData) +func (_Multicall *MulticallCaller) Aggregate(opts *bind.CallOpts, calls []Multicall3Call) (struct { + BlockNumber *big.Int + ReturnData [][]byte +}, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "aggregate", calls) + + outstruct := new(struct { + BlockNumber *big.Int + ReturnData [][]byte + }) + if err != nil { + return *outstruct, err + } + + outstruct.BlockNumber = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.ReturnData = *abi.ConvertType(out[1], new([][]byte)).(*[][]byte) + + return *outstruct, err + +} + +// Aggregate is a free data retrieval call binding the contract method 0x252dba42. +// +// Solidity: function aggregate((address,bytes)[] calls) view returns(uint256 blockNumber, bytes[] returnData) +func (_Multicall *MulticallSession) Aggregate(calls []Multicall3Call) (struct { + BlockNumber *big.Int + ReturnData [][]byte +}, error) { + return _Multicall.Contract.Aggregate(&_Multicall.CallOpts, calls) +} + +// Aggregate is a free data retrieval call binding the contract method 0x252dba42. +// +// Solidity: function aggregate((address,bytes)[] calls) view returns(uint256 blockNumber, bytes[] returnData) +func (_Multicall *MulticallCallerSession) Aggregate(calls []Multicall3Call) (struct { + BlockNumber *big.Int + ReturnData [][]byte +}, error) { + return _Multicall.Contract.Aggregate(&_Multicall.CallOpts, calls) +} + +// Aggregate3 is a free data retrieval call binding the contract method 0x82ad56cb. +// +// Solidity: function aggregate3((address,bool,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallCaller) Aggregate3(opts *bind.CallOpts, calls []Multicall3Call3) ([]Multicall3Result, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "aggregate3", calls) + + if err != nil { + return *new([]Multicall3Result), err + } + + out0 := *abi.ConvertType(out[0], new([]Multicall3Result)).(*[]Multicall3Result) + + return out0, err + +} + +// Aggregate3 is a free data retrieval call binding the contract method 0x82ad56cb. +// +// Solidity: function aggregate3((address,bool,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallSession) Aggregate3(calls []Multicall3Call3) ([]Multicall3Result, error) { + return _Multicall.Contract.Aggregate3(&_Multicall.CallOpts, calls) +} + +// Aggregate3 is a free data retrieval call binding the contract method 0x82ad56cb. +// +// Solidity: function aggregate3((address,bool,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallCallerSession) Aggregate3(calls []Multicall3Call3) ([]Multicall3Result, error) { + return _Multicall.Contract.Aggregate3(&_Multicall.CallOpts, calls) +} + +// Aggregate3Value is a free data retrieval call binding the contract method 0x174dea71. +// +// Solidity: function aggregate3Value((address,bool,uint256,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallCaller) Aggregate3Value(opts *bind.CallOpts, calls []Multicall3Call3Value) ([]Multicall3Result, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "aggregate3Value", calls) + + if err != nil { + return *new([]Multicall3Result), err + } + + out0 := *abi.ConvertType(out[0], new([]Multicall3Result)).(*[]Multicall3Result) + + return out0, err + +} + +// Aggregate3Value is a free data retrieval call binding the contract method 0x174dea71. +// +// Solidity: function aggregate3Value((address,bool,uint256,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallSession) Aggregate3Value(calls []Multicall3Call3Value) ([]Multicall3Result, error) { + return _Multicall.Contract.Aggregate3Value(&_Multicall.CallOpts, calls) +} + +// Aggregate3Value is a free data retrieval call binding the contract method 0x174dea71. +// +// Solidity: function aggregate3Value((address,bool,uint256,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallCallerSession) Aggregate3Value(calls []Multicall3Call3Value) ([]Multicall3Result, error) { + return _Multicall.Contract.Aggregate3Value(&_Multicall.CallOpts, calls) +} + +// BlockAndAggregate is a free data retrieval call binding the contract method 0xc3077fa9. +// +// Solidity: function blockAndAggregate((address,bytes)[] calls) view returns(uint256 blockNumber, bytes32 blockHash, (bool,bytes)[] returnData) +func (_Multicall *MulticallCaller) BlockAndAggregate(opts *bind.CallOpts, calls []Multicall3Call) (struct { + BlockNumber *big.Int + BlockHash [32]byte + ReturnData []Multicall3Result +}, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "blockAndAggregate", calls) + + outstruct := new(struct { + BlockNumber *big.Int + BlockHash [32]byte + ReturnData []Multicall3Result + }) + if err != nil { + return *outstruct, err + } + + outstruct.BlockNumber = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.BlockHash = *abi.ConvertType(out[1], new([32]byte)).(*[32]byte) + outstruct.ReturnData = *abi.ConvertType(out[2], new([]Multicall3Result)).(*[]Multicall3Result) + + return *outstruct, err + +} + +// BlockAndAggregate is a free data retrieval call binding the contract method 0xc3077fa9. +// +// Solidity: function blockAndAggregate((address,bytes)[] calls) view returns(uint256 blockNumber, bytes32 blockHash, (bool,bytes)[] returnData) +func (_Multicall *MulticallSession) BlockAndAggregate(calls []Multicall3Call) (struct { + BlockNumber *big.Int + BlockHash [32]byte + ReturnData []Multicall3Result +}, error) { + return _Multicall.Contract.BlockAndAggregate(&_Multicall.CallOpts, calls) +} + +// BlockAndAggregate is a free data retrieval call binding the contract method 0xc3077fa9. +// +// Solidity: function blockAndAggregate((address,bytes)[] calls) view returns(uint256 blockNumber, bytes32 blockHash, (bool,bytes)[] returnData) +func (_Multicall *MulticallCallerSession) BlockAndAggregate(calls []Multicall3Call) (struct { + BlockNumber *big.Int + BlockHash [32]byte + ReturnData []Multicall3Result +}, error) { + return _Multicall.Contract.BlockAndAggregate(&_Multicall.CallOpts, calls) +} + +// GetBasefee is a free data retrieval call binding the contract method 0x3e64a696. +// +// Solidity: function getBasefee() view returns(uint256 basefee) +func (_Multicall *MulticallCaller) GetBasefee(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getBasefee") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetBasefee is a free data retrieval call binding the contract method 0x3e64a696. +// +// Solidity: function getBasefee() view returns(uint256 basefee) +func (_Multicall *MulticallSession) GetBasefee() (*big.Int, error) { + return _Multicall.Contract.GetBasefee(&_Multicall.CallOpts) +} + +// GetBasefee is a free data retrieval call binding the contract method 0x3e64a696. +// +// Solidity: function getBasefee() view returns(uint256 basefee) +func (_Multicall *MulticallCallerSession) GetBasefee() (*big.Int, error) { + return _Multicall.Contract.GetBasefee(&_Multicall.CallOpts) +} + +// GetBlockHash is a free data retrieval call binding the contract method 0xee82ac5e. +// +// Solidity: function getBlockHash(uint256 blockNumber) view returns(bytes32 blockHash) +func (_Multicall *MulticallCaller) GetBlockHash(opts *bind.CallOpts, blockNumber *big.Int) ([32]byte, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getBlockHash", blockNumber) + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// GetBlockHash is a free data retrieval call binding the contract method 0xee82ac5e. +// +// Solidity: function getBlockHash(uint256 blockNumber) view returns(bytes32 blockHash) +func (_Multicall *MulticallSession) GetBlockHash(blockNumber *big.Int) ([32]byte, error) { + return _Multicall.Contract.GetBlockHash(&_Multicall.CallOpts, blockNumber) +} + +// GetBlockHash is a free data retrieval call binding the contract method 0xee82ac5e. +// +// Solidity: function getBlockHash(uint256 blockNumber) view returns(bytes32 blockHash) +func (_Multicall *MulticallCallerSession) GetBlockHash(blockNumber *big.Int) ([32]byte, error) { + return _Multicall.Contract.GetBlockHash(&_Multicall.CallOpts, blockNumber) +} + +// GetBlockNumber is a free data retrieval call binding the contract method 0x42cbb15c. +// +// Solidity: function getBlockNumber() view returns(uint256 blockNumber) +func (_Multicall *MulticallCaller) GetBlockNumber(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getBlockNumber") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetBlockNumber is a free data retrieval call binding the contract method 0x42cbb15c. +// +// Solidity: function getBlockNumber() view returns(uint256 blockNumber) +func (_Multicall *MulticallSession) GetBlockNumber() (*big.Int, error) { + return _Multicall.Contract.GetBlockNumber(&_Multicall.CallOpts) +} + +// GetBlockNumber is a free data retrieval call binding the contract method 0x42cbb15c. +// +// Solidity: function getBlockNumber() view returns(uint256 blockNumber) +func (_Multicall *MulticallCallerSession) GetBlockNumber() (*big.Int, error) { + return _Multicall.Contract.GetBlockNumber(&_Multicall.CallOpts) +} + +// GetChainId is a free data retrieval call binding the contract method 0x3408e470. +// +// Solidity: function getChainId() view returns(uint256 chainid) +func (_Multicall *MulticallCaller) GetChainId(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getChainId") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetChainId is a free data retrieval call binding the contract method 0x3408e470. +// +// Solidity: function getChainId() view returns(uint256 chainid) +func (_Multicall *MulticallSession) GetChainId() (*big.Int, error) { + return _Multicall.Contract.GetChainId(&_Multicall.CallOpts) +} + +// GetChainId is a free data retrieval call binding the contract method 0x3408e470. +// +// Solidity: function getChainId() view returns(uint256 chainid) +func (_Multicall *MulticallCallerSession) GetChainId() (*big.Int, error) { + return _Multicall.Contract.GetChainId(&_Multicall.CallOpts) +} + +// GetCurrentBlockCoinbase is a free data retrieval call binding the contract method 0xa8b0574e. +// +// Solidity: function getCurrentBlockCoinbase() view returns(address coinbase) +func (_Multicall *MulticallCaller) GetCurrentBlockCoinbase(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getCurrentBlockCoinbase") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetCurrentBlockCoinbase is a free data retrieval call binding the contract method 0xa8b0574e. +// +// Solidity: function getCurrentBlockCoinbase() view returns(address coinbase) +func (_Multicall *MulticallSession) GetCurrentBlockCoinbase() (common.Address, error) { + return _Multicall.Contract.GetCurrentBlockCoinbase(&_Multicall.CallOpts) +} + +// GetCurrentBlockCoinbase is a free data retrieval call binding the contract method 0xa8b0574e. +// +// Solidity: function getCurrentBlockCoinbase() view returns(address coinbase) +func (_Multicall *MulticallCallerSession) GetCurrentBlockCoinbase() (common.Address, error) { + return _Multicall.Contract.GetCurrentBlockCoinbase(&_Multicall.CallOpts) +} + +// GetCurrentBlockDifficulty is a free data retrieval call binding the contract method 0x72425d9d. +// +// Solidity: function getCurrentBlockDifficulty() view returns(uint256 difficulty) +func (_Multicall *MulticallCaller) GetCurrentBlockDifficulty(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getCurrentBlockDifficulty") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetCurrentBlockDifficulty is a free data retrieval call binding the contract method 0x72425d9d. +// +// Solidity: function getCurrentBlockDifficulty() view returns(uint256 difficulty) +func (_Multicall *MulticallSession) GetCurrentBlockDifficulty() (*big.Int, error) { + return _Multicall.Contract.GetCurrentBlockDifficulty(&_Multicall.CallOpts) +} + +// GetCurrentBlockDifficulty is a free data retrieval call binding the contract method 0x72425d9d. +// +// Solidity: function getCurrentBlockDifficulty() view returns(uint256 difficulty) +func (_Multicall *MulticallCallerSession) GetCurrentBlockDifficulty() (*big.Int, error) { + return _Multicall.Contract.GetCurrentBlockDifficulty(&_Multicall.CallOpts) +} + +// GetCurrentBlockGasLimit is a free data retrieval call binding the contract method 0x86d516e8. +// +// Solidity: function getCurrentBlockGasLimit() view returns(uint256 gaslimit) +func (_Multicall *MulticallCaller) GetCurrentBlockGasLimit(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getCurrentBlockGasLimit") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetCurrentBlockGasLimit is a free data retrieval call binding the contract method 0x86d516e8. +// +// Solidity: function getCurrentBlockGasLimit() view returns(uint256 gaslimit) +func (_Multicall *MulticallSession) GetCurrentBlockGasLimit() (*big.Int, error) { + return _Multicall.Contract.GetCurrentBlockGasLimit(&_Multicall.CallOpts) +} + +// GetCurrentBlockGasLimit is a free data retrieval call binding the contract method 0x86d516e8. +// +// Solidity: function getCurrentBlockGasLimit() view returns(uint256 gaslimit) +func (_Multicall *MulticallCallerSession) GetCurrentBlockGasLimit() (*big.Int, error) { + return _Multicall.Contract.GetCurrentBlockGasLimit(&_Multicall.CallOpts) +} + +// GetCurrentBlockTimestamp is a free data retrieval call binding the contract method 0x0f28c97d. +// +// Solidity: function getCurrentBlockTimestamp() view returns(uint256 timestamp) +func (_Multicall *MulticallCaller) GetCurrentBlockTimestamp(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getCurrentBlockTimestamp") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetCurrentBlockTimestamp is a free data retrieval call binding the contract method 0x0f28c97d. +// +// Solidity: function getCurrentBlockTimestamp() view returns(uint256 timestamp) +func (_Multicall *MulticallSession) GetCurrentBlockTimestamp() (*big.Int, error) { + return _Multicall.Contract.GetCurrentBlockTimestamp(&_Multicall.CallOpts) +} + +// GetCurrentBlockTimestamp is a free data retrieval call binding the contract method 0x0f28c97d. +// +// Solidity: function getCurrentBlockTimestamp() view returns(uint256 timestamp) +func (_Multicall *MulticallCallerSession) GetCurrentBlockTimestamp() (*big.Int, error) { + return _Multicall.Contract.GetCurrentBlockTimestamp(&_Multicall.CallOpts) +} + +// GetEthBalance is a free data retrieval call binding the contract method 0x4d2301cc. +// +// Solidity: function getEthBalance(address addr) view returns(uint256 balance) +func (_Multicall *MulticallCaller) GetEthBalance(opts *bind.CallOpts, addr common.Address) (*big.Int, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getEthBalance", addr) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetEthBalance is a free data retrieval call binding the contract method 0x4d2301cc. +// +// Solidity: function getEthBalance(address addr) view returns(uint256 balance) +func (_Multicall *MulticallSession) GetEthBalance(addr common.Address) (*big.Int, error) { + return _Multicall.Contract.GetEthBalance(&_Multicall.CallOpts, addr) +} + +// GetEthBalance is a free data retrieval call binding the contract method 0x4d2301cc. +// +// Solidity: function getEthBalance(address addr) view returns(uint256 balance) +func (_Multicall *MulticallCallerSession) GetEthBalance(addr common.Address) (*big.Int, error) { + return _Multicall.Contract.GetEthBalance(&_Multicall.CallOpts, addr) +} + +// GetLastBlockHash is a free data retrieval call binding the contract method 0x27e86d6e. +// +// Solidity: function getLastBlockHash() view returns(bytes32 blockHash) +func (_Multicall *MulticallCaller) GetLastBlockHash(opts *bind.CallOpts) ([32]byte, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "getLastBlockHash") + + if err != nil { + return *new([32]byte), err + } + + out0 := *abi.ConvertType(out[0], new([32]byte)).(*[32]byte) + + return out0, err + +} + +// GetLastBlockHash is a free data retrieval call binding the contract method 0x27e86d6e. +// +// Solidity: function getLastBlockHash() view returns(bytes32 blockHash) +func (_Multicall *MulticallSession) GetLastBlockHash() ([32]byte, error) { + return _Multicall.Contract.GetLastBlockHash(&_Multicall.CallOpts) +} + +// GetLastBlockHash is a free data retrieval call binding the contract method 0x27e86d6e. +// +// Solidity: function getLastBlockHash() view returns(bytes32 blockHash) +func (_Multicall *MulticallCallerSession) GetLastBlockHash() ([32]byte, error) { + return _Multicall.Contract.GetLastBlockHash(&_Multicall.CallOpts) +} + +// TryAggregate is a free data retrieval call binding the contract method 0xbce38bd7. +// +// Solidity: function tryAggregate(bool requireSuccess, (address,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallCaller) TryAggregate(opts *bind.CallOpts, requireSuccess bool, calls []Multicall3Call) ([]Multicall3Result, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "tryAggregate", requireSuccess, calls) + + if err != nil { + return *new([]Multicall3Result), err + } + + out0 := *abi.ConvertType(out[0], new([]Multicall3Result)).(*[]Multicall3Result) + + return out0, err + +} + +// TryAggregate is a free data retrieval call binding the contract method 0xbce38bd7. +// +// Solidity: function tryAggregate(bool requireSuccess, (address,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallSession) TryAggregate(requireSuccess bool, calls []Multicall3Call) ([]Multicall3Result, error) { + return _Multicall.Contract.TryAggregate(&_Multicall.CallOpts, requireSuccess, calls) +} + +// TryAggregate is a free data retrieval call binding the contract method 0xbce38bd7. +// +// Solidity: function tryAggregate(bool requireSuccess, (address,bytes)[] calls) view returns((bool,bytes)[] returnData) +func (_Multicall *MulticallCallerSession) TryAggregate(requireSuccess bool, calls []Multicall3Call) ([]Multicall3Result, error) { + return _Multicall.Contract.TryAggregate(&_Multicall.CallOpts, requireSuccess, calls) +} + +// TryBlockAndAggregate is a free data retrieval call binding the contract method 0x399542e9. +// +// Solidity: function tryBlockAndAggregate(bool requireSuccess, (address,bytes)[] calls) view returns(uint256 blockNumber, bytes32 blockHash, (bool,bytes)[] returnData) +func (_Multicall *MulticallCaller) TryBlockAndAggregate(opts *bind.CallOpts, requireSuccess bool, calls []Multicall3Call) (struct { + BlockNumber *big.Int + BlockHash [32]byte + ReturnData []Multicall3Result +}, error) { + var out []interface{} + err := _Multicall.contract.Call(opts, &out, "tryBlockAndAggregate", requireSuccess, calls) + + outstruct := new(struct { + BlockNumber *big.Int + BlockHash [32]byte + ReturnData []Multicall3Result + }) + if err != nil { + return *outstruct, err + } + + outstruct.BlockNumber = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + outstruct.BlockHash = *abi.ConvertType(out[1], new([32]byte)).(*[32]byte) + outstruct.ReturnData = *abi.ConvertType(out[2], new([]Multicall3Result)).(*[]Multicall3Result) + + return *outstruct, err + +} + +// TryBlockAndAggregate is a free data retrieval call binding the contract method 0x399542e9. +// +// Solidity: function tryBlockAndAggregate(bool requireSuccess, (address,bytes)[] calls) view returns(uint256 blockNumber, bytes32 blockHash, (bool,bytes)[] returnData) +func (_Multicall *MulticallSession) TryBlockAndAggregate(requireSuccess bool, calls []Multicall3Call) (struct { + BlockNumber *big.Int + BlockHash [32]byte + ReturnData []Multicall3Result +}, error) { + return _Multicall.Contract.TryBlockAndAggregate(&_Multicall.CallOpts, requireSuccess, calls) +} + +// TryBlockAndAggregate is a free data retrieval call binding the contract method 0x399542e9. +// +// Solidity: function tryBlockAndAggregate(bool requireSuccess, (address,bytes)[] calls) view returns(uint256 blockNumber, bytes32 blockHash, (bool,bytes)[] returnData) +func (_Multicall *MulticallCallerSession) TryBlockAndAggregate(requireSuccess bool, calls []Multicall3Call) (struct { + BlockNumber *big.Int + BlockHash [32]byte + ReturnData []Multicall3Result +}, error) { + return _Multicall.Contract.TryBlockAndAggregate(&_Multicall.CallOpts, requireSuccess, calls) +} diff --git a/examples/agent_registry/main.go b/examples/agent_registry/main.go new file mode 100644 index 0000000..1138ea4 --- /dev/null +++ b/examples/agent_registry/main.go @@ -0,0 +1,108 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/forta-network/go-multicall" +) + +const ( + APIURL = "https://polygon-rpc.com" + AgentRegistryABI = `[ + { + "inputs":[ + { + "internalType":"uint256", + "name":"agentId", + "type":"uint256" + } + ], + "name":"getAgentState", + "outputs":[ + { + "internalType":"bool", + "name":"registered", + "type":"bool" + }, + { + "internalType":"address", + "name":"owner", + "type":"address" + }, + { + "internalType":"uint256", + "name":"agentVersion", + "type":"uint256" + }, + { + "internalType":"string", + "name":"metadata", + "type":"string" + }, + { + "internalType":"uint256[]", + "name":"chainIds", + "type":"uint256[]" + }, + { + "internalType":"bool", + "name":"enabled", + "type":"bool" + }, + { + "internalType":"uint256", + "name":"disabledFlags", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + } + ]` +) + +type agentState struct { + Registered bool + Owner common.Address + AgentVersion *big.Int + Metadata string + ChainIds []*big.Int + Enabled bool + DisabledFlags *big.Int +} + +func main() { + caller, err := multicall.Dial(context.Background(), APIURL) + if err != nil { + panic(err) + } + + // Forta AgentRegistry + agentReg, err := multicall.NewContract(AgentRegistryABI, "0x61447385B019187daa48e91c55c02AF1F1f3F863") + if err != nil { + panic(err) + } + + calls, err := caller.Call(nil, + agentReg.NewCall( + new(agentState), + "getAgentState", + botHexToBigInt("0x80ed808b586aeebe9cdd4088ea4dea0a8e322909c0e4493c993e060e89c09ed1"), + ), + ) + if err != nil { + panic(err) + } + fmt.Println("owner:", calls[0].Outputs.(*agentState).Owner.String()) + + b, _ := json.MarshalIndent(calls[0].Outputs.(*agentState), "", " ") + fmt.Println(string(b)) +} + +func botHexToBigInt(hex string) *big.Int { + return common.HexToHash(hex).Big() +} diff --git a/examples/balance/main.go b/examples/balance/main.go new file mode 100644 index 0000000..05c43c8 --- /dev/null +++ b/examples/balance/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "context" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/forta-network/go-multicall" +) + +const ( + APIURL = "https://cloudflare-eth.com" + ERC20ABI = `[ + { + "constant":true, + "inputs":[ + { + "name":"tokenOwner", + "type":"address" + } + ], + "name":"balanceOf", + "outputs":[ + { + "name":"balance", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + } + ]` +) + +type balanceOutput struct { + Balance *big.Int +} + +func main() { + caller, err := multicall.Dial(context.Background(), APIURL) + if err != nil { + panic(err) + } + + contract, err := multicall.NewContract(ERC20ABI, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") + if err != nil { + panic(err) + } + + calls, err := caller.Call(nil, + contract.NewCall( + new(balanceOutput), + "balanceOf", + common.HexToAddress("0xcEe284F754E854890e311e3280b767F80797180d"), // Arbitrum One gateway + ).Name("Arbitrum One gateway balance"), + contract.NewCall( + new(balanceOutput), + "balanceOf", + common.HexToAddress("0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf"), // Polygon ERC20 bridge + ).Name("Polygon ERC20 bridge balance"), + ) + if err != nil { + panic(err) + } + for _, call := range calls { + fmt.Println(call.CallName, ":", call.Outputs.(*balanceOutput).Balance) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ca96687 --- /dev/null +++ b/go.mod @@ -0,0 +1,30 @@ +module github.com/forta-network/go-multicall + +go 1.19 + +require ( + github.com/ethereum/go-ethereum v1.11.5 + github.com/stretchr/testify v1.8.2 +) + +require ( + github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set/v2 v2.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-ole/go-ole v1.2.1 // indirect + github.com/go-stack/stack v1.8.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/holiman/uint256 v1.2.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/tklauser/go-sysconf v0.3.5 // indirect + github.com/tklauser/numcpus v0.2.2 // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/sys v0.5.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..76fbf20 --- /dev/null +++ b/go.sum @@ -0,0 +1,108 @@ +github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= +github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.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/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/ethereum/go-ethereum v1.11.5 h1:3M1uan+LAUvdn+7wCEFrcMM4LJTeuxDrPTg/f31a5QQ= +github.com/ethereum/go-ethereum v1.11.5/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= +github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= +github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +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.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +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/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= +github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bCzK15ozrqo2sZxkh0FHynJZOTVoV6Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=