diff --git a/caller.go b/caller.go index 4267752..67cb935 100644 --- a/caller.go +++ b/caller.go @@ -78,21 +78,41 @@ func (caller *Caller) Call(opts *bind.CallOpts, calls ...*Call) ([]*Call, error) // CallChunked makes multiple multicalls by chunking given calls. func (caller *Caller) CallChunked(opts *bind.CallOpts, chunkSize int, calls ...*Call) ([]*Call, error) { - if chunkSize <= 0 || len(calls) < 2 { - return caller.Call(opts, calls...) - } - callCount := len(calls) / chunkSize - var allCalls []*Call - for i := 0; i < callCount; i++ { - start := i * chunkSize - end := start + chunkSize - chunk, err := caller.Call(opts, calls[start:end]...) + for i, chunk := range chunkInputs(chunkSize, calls) { + chunk, err := caller.Call(opts, chunk...) if err != nil { return calls, fmt.Errorf("call chunk [%d] failed: %v", i, err) } allCalls = append(allCalls, chunk...) } - return allCalls, nil } + +func chunkInputs[T any](chunkSize int, inputs []T) (chunks [][]T) { + if len(inputs) == 0 { + return + } + + if chunkSize <= 0 || len(inputs) < 2 || chunkSize > len(inputs) { + return [][]T{inputs} + } + + lastChunkSize := len(inputs) % chunkSize + + chunkCount := len(inputs) / chunkSize + + for i := 0; i < chunkCount; i++ { + start := i * chunkSize + end := start + chunkSize + chunks = append(chunks, inputs[start:end]) + } + + if lastChunkSize > 0 { + start := chunkCount * chunkSize + end := start + lastChunkSize + chunks = append(chunks, inputs[start:end]) + } + + return +} diff --git a/caller_test.go b/caller_test.go index 6144763..0fcc08b 100644 --- a/caller_test.go +++ b/caller_test.go @@ -325,3 +325,57 @@ func TestDial(t *testing.T) { r.NoError(err) r.NotNil(caller) } + +func TestChunkInputs(t *testing.T) { + testCases := []struct { + name string + chunkSize int + inputs []int + expected [][]int + }{ + { + name: "zero inputs large chunk size", + chunkSize: 12345, + inputs: []int{}, + expected: nil, + }, + { + name: "single input large chunk size", + chunkSize: 12345, + inputs: []int{10}, + expected: [][]int{{10}}, + }, + { + name: "2 inputs chunk size 1", + chunkSize: 1, + inputs: []int{10, 20}, + expected: [][]int{{10}, {20}}, + }, + { + name: "2 inputs chunk size 2", + chunkSize: 2, + inputs: []int{10, 20}, + expected: [][]int{{10, 20}}, + }, + { + name: "5 inputs chunk size 3", + chunkSize: 3, + inputs: []int{10, 20, 30, 40, 50}, + expected: [][]int{{10, 20, 30}, {40, 50}}, + }, + { + name: "3 inputs chunk size 5", + chunkSize: 5, + inputs: []int{10, 20, 30}, + expected: [][]int{{10, 20, 30}}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + r := require.New(t) + + r.Equal(testCase.expected, chunkInputs(testCase.chunkSize, testCase.inputs)) + }) + } +}