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