1
0
Fork 0
forked from forks/go-multicall
go-multicall-modded/caller_test.go
2023-04-24 15:27:26 +03:00

328 lines
6.2 KiB
Go

package multicall
import (
"context"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/forta-network/go-multicall/contracts/contract_multicall"
"github.com/stretchr/testify/require"
)
type testType struct {
Val1 bool
Val2 string
Val3 []string
Val4 []*big.Int
Val5 *big.Int
Val6 common.Address
}
const (
testAddr1 = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
testAddr2 = "0x64d5192F03bD98dB1De2AA8B4abAC5419eaC32CE"
)
const testABI = `[
{
"constant":true,
"inputs":[
{
"name":"val1",
"type":"bool"
},
{
"name":"val2",
"type":"string"
},
{
"name":"val1",
"type":"string[]"
},
{
"name":"val4",
"type":"uint256[]"
},
{
"name":"val5",
"type":"uint256"
},
{
"name":"val6",
"type":"address"
}
],
"name":"testFunc",
"outputs":[
{
"name":"val1",
"type":"bool"
},
{
"name":"val2",
"type":"string"
},
{
"name":"val1",
"type":"string[]"
},
{
"name":"val4",
"type":"uint256[]"
},
{
"name":"val5",
"type":"uint256"
},
{
"name":"val6",
"type":"address"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
}
]`
type multicallStub struct {
returnData func(calls []contract_multicall.Multicall3Call3) [][]byte
}
func (ms *multicallStub) Aggregate3(opts *bind.CallOpts, calls []contract_multicall.Multicall3Call3) (results []contract_multicall.Multicall3Result, err error) {
allReturnData := ms.returnData(calls)
for _, returnData := range allReturnData {
results = append(results, contract_multicall.Multicall3Result{
Success: true,
ReturnData: returnData,
})
}
return
}
func TestCaller_TwoCalls(t *testing.T) {
r := require.New(t)
testContract1, err := NewContract(testABI, testAddr1)
r.NoError(err)
testContract2, err := NewContract(testABI, testAddr2)
r.NoError(err)
values1 := testType{
Val1: true,
Val2: "val2",
Val3: []string{"val3_1", "val3_2"},
Val4: []*big.Int{big.NewInt(123), big.NewInt(456)},
Val5: big.NewInt(678),
Val6: common.HexToAddress(testAddr1),
}
call1 := testContract1.NewCall(
new(testType), "testFunc",
values1.Val1, values1.Val2, values1.Val3,
values1.Val4, values1.Val5, values1.Val6,
)
values2 := testType{
Val1: false,
Val2: "val2_alt",
Val3: []string{"val3_1_alt", "val3_2_alt"},
Val4: []*big.Int{big.NewInt(1239), big.NewInt(4569)},
Val5: big.NewInt(6789),
Val6: common.HexToAddress(testAddr2),
}
call2 := testContract2.NewCall(
new(testType), "testFunc",
values2.Val1, values2.Val2, values2.Val3,
values2.Val4, values2.Val5, values2.Val6,
)
caller := &Caller{
contract: &multicallStub{
returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte {
return [][]byte{
// return inputs as outputs by stripping the method prefix
calls[0].CallData[4:],
calls[1].CallData[4:],
}
},
},
}
calls, err := caller.Call(nil, call1, call2)
r.NoError(err)
call1Out := calls[0].Outputs.(*testType)
r.Equal(values1.Val1, call1Out.Val1)
r.Equal(values1.Val2, call1Out.Val2)
r.Equal(values1.Val3, call1Out.Val3)
r.Equal(values1.Val4, call1Out.Val4)
r.Equal(values1.Val5, call1Out.Val5)
r.Equal(values1.Val6, call1Out.Val6)
call2Out := calls[1].Outputs.(*testType)
r.Equal(values2.Val1, call2Out.Val1)
r.Equal(values2.Val2, call2Out.Val2)
r.Equal(values2.Val3, call2Out.Val3)
r.Equal(values2.Val4, call2Out.Val4)
r.Equal(values2.Val5, call2Out.Val5)
r.Equal(values2.Val6, call2Out.Val6)
}
const emptyABI = `[
{
"constant":true,
"inputs": [],
"name":"testFunc",
"outputs": [],
"payable":false,
"stateMutability":"view",
"type":"function"
}
]`
func TestCaller_EmptyCall(t *testing.T) {
r := require.New(t)
testContract, err := NewContract(emptyABI, testAddr1)
r.NoError(err)
call := testContract.NewCall(
new(struct{}), "testFunc",
// no inputs
)
caller := &Caller{
contract: &multicallStub{
returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte {
return [][]byte{
// return empty output
make([]byte, 0),
}
},
},
}
calls, err := caller.Call(nil, call)
r.NoError(err)
r.Len(calls, 1)
}
const oneValueABI = `[
{
"constant":true,
"inputs": [
{
"name":"val1",
"type":"bool"
}
],
"name":"testFunc",
"outputs": [
{
"name":"val1",
"type":"bool"
}
],
"payable":false,
"stateMutability":"view",
"type":"function"
}
]`
func TestCaller_BadInput(t *testing.T) {
r := require.New(t)
testContract, err := NewContract(oneValueABI, testAddr1)
r.NoError(err)
call := testContract.NewCall(
new(struct{}), "testFunc",
'a',
)
caller := &Caller{
contract: &multicallStub{
returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte {
return [][]byte{
// return bad output
{},
}
},
},
}
calls, err := caller.Call(nil, call)
r.Error(err)
r.ErrorContains(err, "cannot use")
r.Len(calls, 1)
}
func TestCaller_BadOutput(t *testing.T) {
r := require.New(t)
testContract, err := NewContract(emptyABI, testAddr1)
r.NoError(err)
call := testContract.NewCall(
new(struct{}), "testFunc",
// no inputs
)
caller := &Caller{
contract: &multicallStub{
returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte {
return [][]byte{
// return bad output
{'a'},
}
},
},
}
calls, err := caller.Call(nil, call)
r.Error(err)
r.Len(calls, 1)
}
func TestCaller_WrongOutputsType(t *testing.T) {
r := require.New(t)
testContract, err := NewContract(oneValueABI, testAddr1)
r.NoError(err)
call := testContract.NewCall(
new([]struct{}), "testFunc",
true,
)
packedOutput, err := testContract.ABI.Pack("testFunc", true)
r.NoError(err)
caller := &Caller{
contract: &multicallStub{
returnData: func(calls []contract_multicall.Multicall3Call3) [][]byte {
return [][]byte{
packedOutput,
}
},
},
}
calls, err := caller.Call(nil, call)
r.Error(err)
r.ErrorContains(err, "not a struct")
r.Len(calls, 1)
}
func TestDial(t *testing.T) {
r := require.New(t)
caller, err := Dial(context.Background(), "https://polygon-rpc.com")
r.NoError(err)
r.NotNil(caller)
}