fix: multiple bug fixes and safety improvements

- call.go: safe type assertion in UnpackResult() to prevent panic
- call.go: warn when ABI returns more values than struct fields
- caller.go: use len() check instead of nil check for variadic multicallAddr
- caller.go: return allCalls (partial results) on CallChunked error
- caller_test.go: strip 4-byte method selector from stub return data

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
cary 2026-04-18 16:32:52 +08:00
parent ff027d0059
commit c6cbb984a3
3 changed files with 12 additions and 4 deletions

10
call.go
View file

@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"log"
"reflect"
"github.com/ethereum/go-ethereum/accounts/abi"
@ -77,7 +78,11 @@ func (call *Call) UnpackResult() []interface{} {
if call.Outputs == nil {
return nil
}
return call.Outputs.([]interface{})
result, ok := call.Outputs.([]interface{})
if !ok {
return nil
}
return result
}
// AllowFailure sets if the call is allowed to fail. This helps avoiding a revert
@ -111,6 +116,9 @@ func (call *Call) Unpack(b []byte) error {
if fieldCount > len(out) {
return fmt.Errorf("struct has %d fields but ABI returned %d values", fieldCount, len(out))
}
if len(out) > fieldCount {
log.Printf("warning: method '%s' returned %d values but struct only has %d fields; extra values ignored", call.Method, len(out), fieldCount)
}
for i := 0; i < fieldCount; i++ {
field := t.Field(i)
if !field.CanSet() {

View file

@ -24,7 +24,7 @@ type Caller struct {
// New creates a new caller.
func New(client bind.ContractCaller, multicallAddr ...string) (*Caller, error) {
addr := DefaultAddress
if multicallAddr != nil {
if len(multicallAddr) > 0 {
addr = multicallAddr[0]
}
contract, err := contract_multicall.NewMulticallCaller(common.HexToAddress(addr), client)
@ -107,7 +107,7 @@ func (caller *Caller) CallChunked(opts *bind.CallOpts, chunkSize int, cooldown t
chunk, err := caller.calls(opts, chunk...)
if err != nil {
return calls, fmt.Errorf("call chunk [%d] failed: %v", i, err)
return allCalls, fmt.Errorf("call chunk [%d] failed: %v", i, err)
}
allCalls = append(allCalls, chunk...)
}

View file

@ -306,7 +306,7 @@ func TestCaller_WrongOutputsType(t *testing.T) {
contract: &multicallStub{
returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte {
return [][]byte{
packedOutput,
packedOutput[4:], // strip 4-byte method selector; return data is ABI-encoded outputs only
}
},
},