mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
support multicall via codegeneration
This commit is contained in:
parent
4ad88e9463
commit
71b760a168
5 changed files with 352 additions and 0 deletions
|
|
@ -120,6 +120,14 @@ type BoundContract struct {
|
|||
filterer ContractFilterer // Event filtering to interact with the blockchain
|
||||
}
|
||||
|
||||
func (b *BoundContract) Address() common.Address {
|
||||
return b.address
|
||||
}
|
||||
|
||||
func (b *BoundContract) ABI() abi.ABI {
|
||||
return b.abi
|
||||
}
|
||||
|
||||
// NewBoundContract creates a low level contract interface through which calls
|
||||
// and transactions may be made through.
|
||||
func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
ethereum "github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/multicall"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -257,6 +258,41 @@ var (
|
|||
{{end}}
|
||||
}
|
||||
|
||||
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}Multicall(
|
||||
{{range .Normalized.Inputs}}{{.Name}} {{bindtype .Type $structs}},{{end}}
|
||||
) *multicall.MultiCallMetaData[{{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}]{{end}}{{end}} {
|
||||
return multicall.Describe(
|
||||
_{{$contract.Type}}.contract.Address(),
|
||||
_{{$contract.Type}}.contract.ABI(),
|
||||
func(data []byte) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}*{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}}, error) {
|
||||
out, err := _{{$contract.Type}}.contract.ABI().Unpack("{{.Original.Name}}", data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
{{if .Structured}}
|
||||
outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} })
|
||||
if err != nil {
|
||||
return *outstruct, err
|
||||
}
|
||||
{{range $i, $t := .Normalized.Outputs}}
|
||||
outstruct.{{.Name}} = *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
|
||||
|
||||
return *outstruct, err
|
||||
{{else}}
|
||||
if err != nil {
|
||||
return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err
|
||||
}
|
||||
{{range $i, $t := .Normalized.Outputs}}
|
||||
out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}}
|
||||
|
||||
return {{range $i, $t := .Normalized.Outputs}}out{{$i}}{{end}}, nil
|
||||
{{end}}
|
||||
},
|
||||
"{{.Original.Name}}",{{range .Normalized.Inputs}}
|
||||
{{.Name}},{{end}}
|
||||
)
|
||||
}
|
||||
|
||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||
//
|
||||
// Solidity: {{.Original.String}}
|
||||
|
|
@ -287,6 +323,13 @@ var (
|
|||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||
}
|
||||
|
||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||
//
|
||||
// Solidity: {{.Original.String}}
|
||||
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}Multicall({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
|
||||
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
|
||||
}
|
||||
|
||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
||||
//
|
||||
// Solidity: {{.Original.String}}
|
||||
|
|
|
|||
181
accounts/abi/multicall/api.go
Normal file
181
accounts/abi/multicall/api.go
Normal file
File diff suppressed because one or more lines are too long
85
accounts/abi/multicall/types.go
Normal file
85
accounts/abi/multicall/types.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package multicall
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type MultiCallMetaData[T interface{}] struct {
|
||||
Address common.Address
|
||||
Data []byte
|
||||
Deserialize func([]byte) (T, error)
|
||||
}
|
||||
|
||||
type DeserializedMulticall3Result struct {
|
||||
Success bool
|
||||
Value any
|
||||
}
|
||||
|
||||
func (md *MultiCallMetaData[T]) Raw() RawMulticall {
|
||||
return RawMulticall{
|
||||
Address: md.Address,
|
||||
Data: md.Data,
|
||||
Deserialize: func(data []byte) (any, error) {
|
||||
res, err := md.Deserialize(data)
|
||||
return any(res), err
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type RawMulticall struct {
|
||||
Address common.Address
|
||||
Data []byte
|
||||
Deserialize func([]byte) (any, error)
|
||||
}
|
||||
|
||||
type MulticallClient struct {
|
||||
Contract *bind.BoundContract
|
||||
ABI *abi.ABI
|
||||
Context context.Context
|
||||
MaxBatchSize *uint64
|
||||
}
|
||||
|
||||
type Multicall3Result struct {
|
||||
Success bool
|
||||
ReturnData []byte
|
||||
}
|
||||
|
||||
type ParamMulticall3Call3 struct {
|
||||
Target common.Address
|
||||
AllowFailure bool
|
||||
CallData []byte
|
||||
}
|
||||
|
||||
/*
|
||||
* Some RPC providers may limit the amount of calldata you can send in one eth_call. This utility
|
||||
* provides a mechanism for chunking calls by the len(CallData) used.
|
||||
*
|
||||
* This function checks whether the calldata appended exceeds maxBatchSizeBytes
|
||||
*/
|
||||
func chunkCalls(allCalls []ParamMulticall3Call3, maxBatchSizeBytes uint64) [][]ParamMulticall3Call3 {
|
||||
results := [][]ParamMulticall3Call3{}
|
||||
|
||||
currentBatchSize := uint64(0)
|
||||
currentBatch := []ParamMulticall3Call3{}
|
||||
|
||||
for _, call := range allCalls {
|
||||
if (currentBatchSize + uint64(len(call.CallData))) > maxBatchSizeBytes {
|
||||
results = append(results, currentBatch)
|
||||
currentBatchSize = 0
|
||||
currentBatch = []ParamMulticall3Call3{}
|
||||
}
|
||||
|
||||
currentBatch = append(currentBatch, call)
|
||||
currentBatchSize += uint64(len(call.CallData))
|
||||
}
|
||||
|
||||
if len(currentBatch) > 0 {
|
||||
results = append(results, currentBatch)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
35
accounts/abi/multicall/utils.go
Normal file
35
accounts/abi/multicall/utils.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package multicall
|
||||
|
||||
func mapArray[A any, B any](coll []A, mapper func(i A, index uint64) B) []B {
|
||||
out := make([]B, len(coll))
|
||||
for i, item := range coll {
|
||||
out[i] = mapper(item, uint64(i))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func filterArray[A any](coll []A, criteria func(i A) bool) []A {
|
||||
out := []A{}
|
||||
for _, item := range coll {
|
||||
if criteria(item) {
|
||||
out = append(out, item)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func reduceArray[A any, B any](coll []A, processor func(accum B, next A) B, initialState B) B {
|
||||
val := initialState
|
||||
for _, item := range coll {
|
||||
val = processor(val, item)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func flattenArrays[A any](coll [][]A) []A {
|
||||
out := []A{}
|
||||
for _, arr := range coll {
|
||||
out = append(out, arr...)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Loading…
Reference in a new issue