/
multicaller.go
133 lines (116 loc) · 3.4 KB
/
multicaller.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
* This code was derived from https://github.com/depocket/multicall-go
*/
package multicall
import (
"context"
"fmt"
"strings"
"github.com/Seb369888/poolsea-go/rocketpool"
"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"
)
type Call struct {
Method string `json:"method"`
Target common.Address `json:"target"`
CallData []byte `json:"call_data"`
Contract *rocketpool.Contract
output interface{}
}
type CallResponse struct {
Method string
Status bool
ReturnDataRaw []byte `json:"returnData"`
}
type Result struct {
Success bool `json:"success"`
Output interface{}
}
func (call Call) GetMultiCall() MultiCall {
return MultiCall{Target: call.Target, CallData: call.CallData}
}
type MultiCaller struct {
Client rocketpool.ExecutionClient
ABI abi.ABI
ContractAddress common.Address
calls []Call
}
func NewMultiCaller(client rocketpool.ExecutionClient, multicallerAddress common.Address) (*MultiCaller, error) {
mcAbi, err := abi.JSON(strings.NewReader(MulticallABI))
if err != nil {
return nil, err
}
return &MultiCaller{
Client: client,
ABI: mcAbi,
ContractAddress: multicallerAddress,
calls: []Call{},
}, nil
}
func (caller *MultiCaller) AddCall(contract *rocketpool.Contract, output interface{}, method string, args ...interface{}) error {
callData, err := contract.ABI.Pack(method, args...)
if err != nil {
return fmt.Errorf("error adding call [%s]: %w", method, err)
}
call := Call{
Method: method,
Target: *contract.Address,
CallData: callData,
Contract: contract,
output: output,
}
caller.calls = append(caller.calls, call)
return nil
}
func (caller *MultiCaller) Execute(requireSuccess bool, opts *bind.CallOpts) ([]CallResponse, error) {
var multiCalls = make([]MultiCall, 0, len(caller.calls))
for _, call := range caller.calls {
multiCalls = append(multiCalls, call.GetMultiCall())
}
callData, err := caller.ABI.Pack("tryAggregate", requireSuccess, multiCalls)
if err != nil {
return nil, err
}
resp, err := caller.Client.CallContract(context.Background(), ethereum.CallMsg{To: &caller.ContractAddress, Data: callData}, opts.BlockNumber)
if err != nil {
return nil, err
}
responses, err := caller.ABI.Unpack("tryAggregate", resp)
if err != nil {
return nil, err
}
results := make([]CallResponse, len(caller.calls))
for i, response := range responses[0].([]struct {
Success bool `json:"success"`
ReturnData []byte `json:"returnData"`
}) {
results[i].Method = caller.calls[i].Method
results[i].ReturnDataRaw = response.ReturnData
results[i].Status = response.Success
}
return results, nil
}
func (caller *MultiCaller) FlexibleCall(requireSuccess bool, opts *bind.CallOpts) ([]Result, error) {
res := make([]Result, len(caller.calls))
results, err := caller.Execute(requireSuccess, opts)
if err != nil {
caller.calls = []Call{}
return nil, err
}
for i, call := range caller.calls {
callSuccess := results[i].Status
if callSuccess {
err := call.Contract.ABI.UnpackIntoInterface(call.output, call.Method, results[i].ReturnDataRaw)
if err != nil {
caller.calls = []Call{}
return nil, err
}
}
res[i].Success = callSuccess
res[i].Output = call.output
}
caller.calls = []Call{}
return res, err
}