support multicall via codegeneration

This commit is contained in:
Justin Brower 2024-09-09 11:08:42 -04:00
parent 4ad88e9463
commit 71b760a168
5 changed files with 352 additions and 0 deletions

View file

@ -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 {

View file

@ -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}}

File diff suppressed because one or more lines are too long

View 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
}

View 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
}