/
multicall.go
82 lines (72 loc) · 1.89 KB
/
multicall.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
package batching
import (
"context"
"fmt"
"io"
"github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock"
"github.com/ethereum/go-ethereum/rpc"
)
var DefaultBatchSize = 100
type EthRpc interface {
CallContext(ctx context.Context, out interface{}, method string, args ...interface{}) error
BatchCallContext(ctx context.Context, b []rpc.BatchElem) error
}
type MultiCaller struct {
rpc EthRpc
batchSize int
}
func NewMultiCaller(rpc EthRpc, batchSize int) *MultiCaller {
return &MultiCaller{
rpc: rpc,
batchSize: batchSize,
}
}
func (m *MultiCaller) BatchSize() int {
return m.batchSize
}
func (m *MultiCaller) SingleCall(ctx context.Context, block rpcblock.Block, call Call) (*CallResult, error) {
results, err := m.Call(ctx, block, call)
if err != nil {
return nil, err
}
return results[0], nil
}
func (m *MultiCaller) Call(ctx context.Context, block rpcblock.Block, calls ...Call) ([]*CallResult, error) {
keys := make([]BatchElementCreator, len(calls))
for i := 0; i < len(calls); i++ {
creator, err := calls[i].ToBatchElemCreator()
if err != nil {
return nil, err
}
keys[i] = creator
}
fetcher := NewIterativeBatchCall[BatchElementCreator, any](
keys,
func(key BatchElementCreator) (any, rpc.BatchElem) {
return key(block)
},
m.rpc.BatchCallContext,
m.rpc.CallContext,
m.batchSize)
for {
if err := fetcher.Fetch(ctx); err == io.EOF {
break
} else if err != nil {
return nil, fmt.Errorf("failed to fetch batch: %w", err)
}
}
results, err := fetcher.Result()
if err != nil {
return nil, fmt.Errorf("failed to get batch call results: %w", err)
}
callResults := make([]*CallResult, len(results))
for i, result := range results {
call := calls[i]
out, err := call.HandleResult(result)
if err != nil {
return nil, fmt.Errorf("failed to unpack result: %w", err)
}
callResults[i] = out
}
return callResults, nil
}