github.com/dim4egster/coreth@v0.10.2/accounts/abi/bind/base_test.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2019 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package bind_test
    28  
    29  import (
    30  	"context"
    31  	"errors"
    32  	"fmt"
    33  	"math/big"
    34  	"reflect"
    35  	"strings"
    36  	"testing"
    37  
    38  	"github.com/dim4egster/coreth/accounts/abi"
    39  	"github.com/dim4egster/coreth/accounts/abi/bind"
    40  	"github.com/dim4egster/coreth/core/types"
    41  	"github.com/dim4egster/coreth/core/vm"
    42  	"github.com/dim4egster/coreth/interfaces"
    43  	"github.com/ethereum/go-ethereum/common"
    44  	"github.com/ethereum/go-ethereum/common/hexutil"
    45  	"github.com/ethereum/go-ethereum/crypto"
    46  	"github.com/ethereum/go-ethereum/rlp"
    47  	"github.com/stretchr/testify/assert"
    48  )
    49  
    50  func mockSign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return tx, nil }
    51  
    52  type mockTransactor struct {
    53  	baseFee                *big.Int
    54  	gasTipCap              *big.Int
    55  	gasPrice               *big.Int
    56  	suggestGasTipCapCalled bool
    57  	suggestGasPriceCalled  bool
    58  }
    59  
    60  func (mt *mockTransactor) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
    61  	return &types.Header{BaseFee: mt.baseFee}, nil
    62  }
    63  
    64  func (mt *mockTransactor) AcceptedCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
    65  	return []byte{1}, nil
    66  }
    67  
    68  func (mt *mockTransactor) AcceptedNonceAt(ctx context.Context, account common.Address) (uint64, error) {
    69  	return 0, nil
    70  }
    71  
    72  func (mt *mockTransactor) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
    73  	mt.suggestGasPriceCalled = true
    74  	return mt.gasPrice, nil
    75  }
    76  
    77  func (mt *mockTransactor) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
    78  	mt.suggestGasTipCapCalled = true
    79  	return mt.gasTipCap, nil
    80  }
    81  
    82  func (mt *mockTransactor) EstimateGas(ctx context.Context, call interfaces.CallMsg) (gas uint64, err error) {
    83  	return 0, nil
    84  }
    85  
    86  func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transaction) error {
    87  	return nil
    88  }
    89  
    90  type mockCaller struct {
    91  	codeAtBlockNumber       *big.Int
    92  	callContractBlockNumber *big.Int
    93  	callContractBytes       []byte
    94  	callContractErr         error
    95  	codeAtBytes             []byte
    96  	codeAtErr               error
    97  }
    98  
    99  func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
   100  	mc.codeAtBlockNumber = blockNumber
   101  	return mc.codeAtBytes, mc.codeAtErr
   102  }
   103  
   104  func (mc *mockCaller) CallContract(ctx context.Context, call interfaces.CallMsg, blockNumber *big.Int) ([]byte, error) {
   105  	mc.callContractBlockNumber = blockNumber
   106  	return mc.callContractBytes, mc.callContractErr
   107  }
   108  
   109  type mockAcceptedCaller struct {
   110  	*mockCaller
   111  	acceptedCodeAtBytes        []byte
   112  	acceptedCodeAtErr          error
   113  	acceptedCodeAtCalled       bool
   114  	acceptedCallContractCalled bool
   115  	acceptedCallContractBytes  []byte
   116  	acceptedCallContractErr    error
   117  }
   118  
   119  func (mc *mockAcceptedCaller) AcceptedCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
   120  	mc.acceptedCodeAtCalled = true
   121  	return mc.acceptedCodeAtBytes, mc.acceptedCodeAtErr
   122  }
   123  
   124  func (mc *mockAcceptedCaller) AcceptedCallContract(ctx context.Context, call interfaces.CallMsg) ([]byte, error) {
   125  	mc.acceptedCallContractCalled = true
   126  	return mc.acceptedCallContractBytes, mc.acceptedCallContractErr
   127  }
   128  func TestPassingBlockNumber(t *testing.T) {
   129  	mc := &mockAcceptedCaller{
   130  		mockCaller: &mockCaller{
   131  			codeAtBytes: []byte{1, 2, 3},
   132  		},
   133  	}
   134  
   135  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
   136  		Methods: map[string]abi.Method{
   137  			"something": {
   138  				Name:    "something",
   139  				Outputs: abi.Arguments{},
   140  			},
   141  		},
   142  	}, mc, nil, nil)
   143  
   144  	blockNumber := big.NewInt(42)
   145  
   146  	bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something")
   147  
   148  	if mc.callContractBlockNumber != blockNumber {
   149  		t.Fatalf("CallContract() was not passed the block number")
   150  	}
   151  
   152  	if mc.codeAtBlockNumber != blockNumber {
   153  		t.Fatalf("CodeAt() was not passed the block number")
   154  	}
   155  
   156  	bc.Call(&bind.CallOpts{}, nil, "something")
   157  
   158  	if mc.callContractBlockNumber != nil {
   159  		t.Fatalf("CallContract() was passed a block number when it should not have been")
   160  	}
   161  
   162  	if mc.codeAtBlockNumber != nil {
   163  		t.Fatalf("CodeAt() was passed a block number when it should not have been")
   164  	}
   165  
   166  	bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Accepted: true}, nil, "something")
   167  
   168  	if !mc.acceptedCallContractCalled {
   169  		t.Fatalf("CallContract() was not passed the block number")
   170  	}
   171  
   172  	if !mc.acceptedCodeAtCalled {
   173  		t.Fatalf("CodeAt() was not passed the block number")
   174  	}
   175  }
   176  
   177  const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158"
   178  
   179  func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
   180  	hash := crypto.Keccak256Hash([]byte("testName"))
   181  	topics := []common.Hash{
   182  		crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")),
   183  		hash,
   184  	}
   185  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   186  
   187  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"string"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   188  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   189  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   190  
   191  	expectedReceivedMap := map[string]interface{}{
   192  		"name":   hash,
   193  		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   194  		"amount": big.NewInt(1),
   195  		"memo":   []byte{88},
   196  	}
   197  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   198  }
   199  
   200  func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
   201  	sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	hash := crypto.Keccak256Hash(sliceBytes)
   206  	topics := []common.Hash{
   207  		crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")),
   208  		hash,
   209  	}
   210  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   211  
   212  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"names","type":"string[]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   213  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   214  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   215  
   216  	expectedReceivedMap := map[string]interface{}{
   217  		"names":  hash,
   218  		"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   219  		"amount": big.NewInt(1),
   220  		"memo":   []byte{88},
   221  	}
   222  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   223  }
   224  
   225  func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
   226  	arrBytes, err := rlp.EncodeToBytes([2]common.Address{common.HexToAddress("0x0"), common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")})
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	hash := crypto.Keccak256Hash(arrBytes)
   231  	topics := []common.Hash{
   232  		crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
   233  		hash,
   234  	}
   235  	mockLog := newMockLog(topics, common.HexToHash("0x0"))
   236  
   237  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"addresses","type":"address[2]"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   238  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   239  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   240  
   241  	expectedReceivedMap := map[string]interface{}{
   242  		"addresses": hash,
   243  		"sender":    common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   244  		"amount":    big.NewInt(1),
   245  		"memo":      []byte{88},
   246  	}
   247  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   248  }
   249  
   250  func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
   251  	mockAddress := common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2")
   252  	addrBytes := mockAddress.Bytes()
   253  	hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
   254  	functionSelector := hash[:4]
   255  	functionTyBytes := append(addrBytes, functionSelector...)
   256  	var functionTy [24]byte
   257  	copy(functionTy[:], functionTyBytes[0:24])
   258  	topics := []common.Hash{
   259  		crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")),
   260  		common.BytesToHash(functionTyBytes),
   261  	}
   262  	mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
   263  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"function","type":"function"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   264  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   265  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   266  
   267  	expectedReceivedMap := map[string]interface{}{
   268  		"function": functionTy,
   269  		"sender":   common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   270  		"amount":   big.NewInt(1),
   271  		"memo":     []byte{88},
   272  	}
   273  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   274  }
   275  
   276  func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
   277  	bytes := []byte{1, 2, 3, 4, 5}
   278  	hash := crypto.Keccak256Hash(bytes)
   279  	topics := []common.Hash{
   280  		crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")),
   281  		hash,
   282  	}
   283  	mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
   284  
   285  	abiString := `[{"anonymous":false,"inputs":[{"indexed":true,"name":"content","type":"bytes"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"}]`
   286  	parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
   287  	bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
   288  
   289  	expectedReceivedMap := map[string]interface{}{
   290  		"content": hash,
   291  		"sender":  common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
   292  		"amount":  big.NewInt(1),
   293  		"memo":    []byte{88},
   294  	}
   295  	unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
   296  }
   297  
   298  func TestTransactNativeAssetCallNilAssetAmount(t *testing.T) {
   299  	assert := assert.New(t)
   300  	mt := &mockTransactor{}
   301  	bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
   302  	opts := &bind.TransactOpts{
   303  		Signer: mockSign,
   304  	}
   305  	// fails if asset amount is nil
   306  	opts.NativeAssetCall = &bind.NativeAssetCallOpts{
   307  		AssetID:     common.Hash{},
   308  		AssetAmount: nil,
   309  	}
   310  	_, err := bc.Transact(opts, "")
   311  	assert.ErrorIs(err, bind.ErrNilAssetAmount)
   312  }
   313  
   314  func TestTransactNativeAssetCallNonZeroValue(t *testing.T) {
   315  	assert := assert.New(t)
   316  	mt := &mockTransactor{}
   317  	bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
   318  	opts := &bind.TransactOpts{
   319  		Signer: mockSign,
   320  	}
   321  	opts.NativeAssetCall = &bind.NativeAssetCallOpts{
   322  		AssetID:     common.Hash{},
   323  		AssetAmount: big.NewInt(11),
   324  	}
   325  	// fails if value > 0
   326  	opts.Value = big.NewInt(11)
   327  	_, err := bc.Transact(opts, "")
   328  	assert.Equal(err.Error(), fmt.Sprintf("value must be 0 when performing native asset call, found %v", opts.Value))
   329  	// fails if value < 0
   330  	opts.Value = big.NewInt(-11)
   331  	_, err = bc.Transact(opts, "")
   332  	assert.Equal(err.Error(), fmt.Sprintf("value must be 0 when performing native asset call, found %v", opts.Value))
   333  }
   334  
   335  func TestTransactNativeAssetCall(t *testing.T) {
   336  	assert := assert.New(t)
   337  	json := `[{"type":"function","name":"method","inputs":[{"type":"uint256" },{"type":"string"}]}]`
   338  	parsed, err := abi.JSON(strings.NewReader(json))
   339  	assert.Nil(err)
   340  	mt := &mockTransactor{}
   341  	contractAddr := common.Address{11}
   342  	bc := bind.NewBoundContract(contractAddr, parsed, nil, mt, nil)
   343  	opts := &bind.TransactOpts{
   344  		Signer: mockSign,
   345  	}
   346  	// normal call tx
   347  	methodName := "method"
   348  	arg1 := big.NewInt(22)
   349  	arg2 := "33"
   350  	normalCallTx, err := bc.Transact(opts, methodName, arg1, arg2)
   351  	assert.Nil(err)
   352  	// native asset call tx
   353  	assetID := common.Hash{44}
   354  	assetAmount := big.NewInt(55)
   355  	opts.NativeAssetCall = &bind.NativeAssetCallOpts{
   356  		AssetID:     assetID,
   357  		AssetAmount: assetAmount,
   358  	}
   359  	nativeCallTx, err := bc.Transact(opts, methodName, arg1, arg2)
   360  	assert.Nil(err)
   361  	// verify transformations
   362  	assert.Equal(vm.NativeAssetCallAddr, *nativeCallTx.To())
   363  	unpackedAddr, unpackedAssetID, unpackedAssetAmount, unpackedData, err := vm.UnpackNativeAssetCallInput(nativeCallTx.Data())
   364  	assert.Nil(err)
   365  	assert.NotEmpty(unpackedData)
   366  	assert.Equal(unpackedData, normalCallTx.Data())
   367  	assert.Equal(unpackedAddr, contractAddr)
   368  	assert.Equal(unpackedAssetID, assetID)
   369  	assert.Equal(unpackedAssetAmount, assetAmount)
   370  }
   371  
   372  func TestTransactGasFee(t *testing.T) {
   373  	assert := assert.New(t)
   374  
   375  	// GasTipCap and GasFeeCap
   376  	// When opts.GasTipCap and opts.GasFeeCap are nil
   377  	mt := &mockTransactor{baseFee: big.NewInt(100), gasTipCap: big.NewInt(5)}
   378  	bc := bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
   379  	opts := &bind.TransactOpts{Signer: mockSign}
   380  	tx, err := bc.Transact(opts, "")
   381  	assert.Nil(err)
   382  	assert.Equal(big.NewInt(5), tx.GasTipCap())
   383  	assert.Equal(big.NewInt(205), tx.GasFeeCap())
   384  	assert.Nil(opts.GasTipCap)
   385  	assert.Nil(opts.GasFeeCap)
   386  	assert.True(mt.suggestGasTipCapCalled)
   387  
   388  	// Second call to Transact should use latest suggested GasTipCap
   389  	mt.gasTipCap = big.NewInt(6)
   390  	mt.suggestGasTipCapCalled = false
   391  	tx, err = bc.Transact(opts, "")
   392  	assert.Nil(err)
   393  	assert.Equal(big.NewInt(6), tx.GasTipCap())
   394  	assert.Equal(big.NewInt(206), tx.GasFeeCap())
   395  	assert.True(mt.suggestGasTipCapCalled)
   396  
   397  	// GasPrice
   398  	// When opts.GasPrice is nil
   399  	mt = &mockTransactor{gasPrice: big.NewInt(5)}
   400  	bc = bind.NewBoundContract(common.Address{}, abi.ABI{}, nil, mt, nil)
   401  	opts = &bind.TransactOpts{Signer: mockSign}
   402  	tx, err = bc.Transact(opts, "")
   403  	assert.Nil(err)
   404  	assert.Equal(big.NewInt(5), tx.GasPrice())
   405  	assert.Nil(opts.GasPrice)
   406  	assert.True(mt.suggestGasPriceCalled)
   407  
   408  	// Second call to Transact should use latest suggested GasPrice
   409  	mt.gasPrice = big.NewInt(6)
   410  	mt.suggestGasPriceCalled = false
   411  	tx, err = bc.Transact(opts, "")
   412  	assert.Nil(err)
   413  	assert.Equal(big.NewInt(6), tx.GasPrice())
   414  	assert.True(mt.suggestGasPriceCalled)
   415  }
   416  
   417  func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
   418  	received := make(map[string]interface{})
   419  	if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
   420  		t.Error(err)
   421  	}
   422  
   423  	if len(received) != len(expected) {
   424  		t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
   425  	}
   426  	for name, elem := range expected {
   427  		if !reflect.DeepEqual(elem, received[name]) {
   428  			t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
   429  		}
   430  	}
   431  }
   432  
   433  func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
   434  	return types.Log{
   435  		Address:     common.HexToAddress("0x0"),
   436  		Topics:      topics,
   437  		Data:        hexutil.MustDecode(hexData),
   438  		BlockNumber: uint64(26),
   439  		TxHash:      txHash,
   440  		TxIndex:     111,
   441  		BlockHash:   common.BytesToHash([]byte{1, 2, 3, 4, 5}),
   442  		Index:       7,
   443  		Removed:     false,
   444  	}
   445  }
   446  
   447  func TestCall(t *testing.T) {
   448  	var method, methodWithArg = "something", "somethingArrrrg"
   449  	tests := []struct {
   450  		name, method string
   451  		opts         *bind.CallOpts
   452  		mc           bind.ContractCaller
   453  		results      *[]interface{}
   454  		wantErr      bool
   455  		wantErrExact error
   456  	}{{
   457  		name: "ok not accepted",
   458  		mc: &mockCaller{
   459  			codeAtBytes: []byte{0},
   460  		},
   461  		method: method,
   462  	}, {
   463  		name: "ok accepted",
   464  		mc: &mockAcceptedCaller{
   465  			acceptedCodeAtBytes: []byte{0},
   466  		},
   467  		opts: &bind.CallOpts{
   468  			Accepted: true,
   469  		},
   470  		method: method,
   471  	}, {
   472  		name:    "pack error, no method",
   473  		mc:      new(mockCaller),
   474  		method:  "else",
   475  		wantErr: true,
   476  	}, {
   477  		name: "interface error, accepted but not a AcceptedContractCaller",
   478  		mc:   new(mockCaller),
   479  		opts: &bind.CallOpts{
   480  			Accepted: true,
   481  		},
   482  		method:       method,
   483  		wantErrExact: bind.ErrNoAcceptedState,
   484  	}, {
   485  		name: "accepted call canceled",
   486  		mc: &mockAcceptedCaller{
   487  			acceptedCallContractErr: context.DeadlineExceeded,
   488  		},
   489  		opts: &bind.CallOpts{
   490  			Accepted: true,
   491  		},
   492  		method:       method,
   493  		wantErrExact: context.DeadlineExceeded,
   494  	}, {
   495  		name: "accepted code at error",
   496  		mc: &mockAcceptedCaller{
   497  			acceptedCodeAtErr: errors.New(""),
   498  		},
   499  		opts: &bind.CallOpts{
   500  			Accepted: true,
   501  		},
   502  		method:  method,
   503  		wantErr: true,
   504  	}, {
   505  		name: "no accepted code at",
   506  		mc:   new(mockAcceptedCaller),
   507  		opts: &bind.CallOpts{
   508  			Accepted: true,
   509  		},
   510  		method:       method,
   511  		wantErrExact: bind.ErrNoCode,
   512  	}, {
   513  		name: "call contract error",
   514  		mc: &mockCaller{
   515  			callContractErr: context.DeadlineExceeded,
   516  		},
   517  		method:       method,
   518  		wantErrExact: context.DeadlineExceeded,
   519  	}, {
   520  		name: "code at error",
   521  		mc: &mockCaller{
   522  			codeAtErr: errors.New(""),
   523  		},
   524  		method:  method,
   525  		wantErr: true,
   526  	}, {
   527  		name:         "no code at",
   528  		mc:           new(mockCaller),
   529  		method:       method,
   530  		wantErrExact: bind.ErrNoCode,
   531  	}, {
   532  		name: "unpack error missing arg",
   533  		mc: &mockCaller{
   534  			codeAtBytes: []byte{0},
   535  		},
   536  		method:  methodWithArg,
   537  		wantErr: true,
   538  	}, {
   539  		name: "interface unpack error",
   540  		mc: &mockCaller{
   541  			codeAtBytes: []byte{0},
   542  		},
   543  		method:  method,
   544  		results: &[]interface{}{0},
   545  		wantErr: true,
   546  	}}
   547  	for _, test := range tests {
   548  		bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
   549  			Methods: map[string]abi.Method{
   550  				method: {
   551  					Name:    method,
   552  					Outputs: abi.Arguments{},
   553  				},
   554  				methodWithArg: {
   555  					Name:    methodWithArg,
   556  					Outputs: abi.Arguments{abi.Argument{}},
   557  				},
   558  			},
   559  		}, test.mc, nil, nil)
   560  		err := bc.Call(test.opts, test.results, test.method)
   561  		if test.wantErr || test.wantErrExact != nil {
   562  			if err == nil {
   563  				t.Fatalf("%q expected error", test.name)
   564  			}
   565  			if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) {
   566  				t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err)
   567  			}
   568  			continue
   569  		}
   570  		if err != nil {
   571  			t.Fatalf("%q unexpected error: %v", test.name, err)
   572  		}
   573  	}
   574  }
   575  
   576  // TestCrashers contains some strings which previously caused the abi codec to crash.
   577  func TestCrashers(t *testing.T) {
   578  	abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"_1"}]}]}]`))
   579  	abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"&"}]}]}]`))
   580  	abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"----"}]}]}]`))
   581  	abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"foo.Bar"}]}]}]`))
   582  }