github.com/cgcardona/r-subnet-evm@v0.1.5/accounts/abi/bind/bind.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 2016 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 generates Ethereum contract Go bindings. 28 // 29 // Detailed usage document and tutorial available on the go-ethereum Wiki page: 30 // https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts 31 package bind 32 33 import ( 34 "bytes" 35 "errors" 36 "fmt" 37 "go/format" 38 "regexp" 39 "strings" 40 "text/template" 41 "unicode" 42 43 "github.com/cgcardona/r-subnet-evm/accounts/abi" 44 "github.com/ethereum/go-ethereum/log" 45 ) 46 47 // BindHook is a callback function that can be used to customize the binding. 48 type BindHook func(lang Lang, pkg string, types []string, contracts map[string]*TmplContract, structs map[string]*TmplStruct) (data interface{}, templateSource string, err error) 49 50 // Lang is a target programming language selector to generate bindings for. 51 type Lang int 52 53 const ( 54 LangGo Lang = iota 55 LangJava 56 LangObjC 57 ) 58 59 func isKeyWord(arg string) bool { 60 switch arg { 61 case "break": 62 case "case": 63 case "chan": 64 case "const": 65 case "continue": 66 case "default": 67 case "defer": 68 case "else": 69 case "fallthrough": 70 case "for": 71 case "func": 72 case "go": 73 case "goto": 74 case "if": 75 case "import": 76 case "interface": 77 case "iota": 78 case "map": 79 case "make": 80 case "new": 81 case "package": 82 case "range": 83 case "return": 84 case "select": 85 case "struct": 86 case "switch": 87 case "type": 88 case "var": 89 default: 90 return false 91 } 92 93 return true 94 } 95 96 // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant 97 // to be used as is in client code, but rather as an intermediate struct which 98 // enforces compile time type safety and naming convention opposed to having to 99 // manually maintain hard coded strings that break on runtime. 100 func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) { 101 return BindHelper(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases, nil) 102 } 103 104 func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string, bindHook BindHook) (string, error) { 105 var ( 106 // contracts is the map of each individual contract requested binding 107 contracts = make(map[string]*TmplContract) 108 109 // structs is the map of all redeclared structs shared by passed contracts. 110 structs = make(map[string]*TmplStruct) 111 112 // isLib is the map used to flag each encountered library as such 113 isLib = make(map[string]struct{}) 114 ) 115 for i := 0; i < len(types); i++ { 116 // Parse the actual ABI to generate the binding for 117 evmABI, err := abi.JSON(strings.NewReader(abis[i])) 118 if err != nil { 119 return "", err 120 } 121 // Strip any whitespace from the JSON ABI 122 strippedABI := strings.Map(func(r rune) rune { 123 if unicode.IsSpace(r) { 124 return -1 125 } 126 return r 127 }, abis[i]) 128 129 // Extract the call and transact methods; events, struct definitions; and sort them alphabetically 130 var ( 131 calls = make(map[string]*TmplMethod) 132 transacts = make(map[string]*TmplMethod) 133 events = make(map[string]*tmplEvent) 134 fallback *TmplMethod 135 receive *TmplMethod 136 137 // identifiers are used to detect duplicated identifiers of functions 138 // and events. For all calls, transacts and events, abigen will generate 139 // corresponding bindings. However we have to ensure there is no 140 // identifier collisions in the bindings of these categories. 141 callIdentifiers = make(map[string]bool) 142 transactIdentifiers = make(map[string]bool) 143 eventIdentifiers = make(map[string]bool) 144 ) 145 146 for _, input := range evmABI.Constructor.Inputs { 147 if hasStruct(input.Type) { 148 bindStructType[lang](input.Type, structs) 149 } 150 } 151 152 for _, original := range evmABI.Methods { 153 // Normalize the method for capital cases and non-anonymous inputs/outputs 154 normalized := original 155 normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) 156 157 // Ensure there is no duplicated identifier 158 identifiers := callIdentifiers 159 if !original.IsConstant() { 160 identifiers = transactIdentifiers 161 } 162 if identifiers[normalizedName] { 163 return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) 164 } 165 identifiers[normalizedName] = true 166 167 normalized.Name = normalizedName 168 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 169 copy(normalized.Inputs, original.Inputs) 170 for j, input := range normalized.Inputs { 171 if input.Name == "" || isKeyWord(input.Name) { 172 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 173 } 174 if hasStruct(input.Type) { 175 bindStructType[lang](input.Type, structs) 176 } 177 } 178 normalized.Outputs = make([]abi.Argument, len(original.Outputs)) 179 copy(normalized.Outputs, original.Outputs) 180 for j, output := range normalized.Outputs { 181 if output.Name != "" { 182 normalized.Outputs[j].Name = capitalise(output.Name) 183 } 184 if hasStruct(output.Type) { 185 bindStructType[lang](output.Type, structs) 186 } 187 } 188 // Append the methods to the call or transact lists 189 if original.IsConstant() { 190 calls[original.Name] = &TmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 191 } else { 192 transacts[original.Name] = &TmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 193 } 194 } 195 for _, original := range evmABI.Events { 196 // Skip anonymous events as they don't support explicit filtering 197 if original.Anonymous { 198 continue 199 } 200 // Normalize the event for capital cases and non-anonymous outputs 201 normalized := original 202 203 // Ensure there is no duplicated identifier 204 normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) 205 if eventIdentifiers[normalizedName] { 206 return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) 207 } 208 eventIdentifiers[normalizedName] = true 209 normalized.Name = normalizedName 210 211 used := make(map[string]bool) 212 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 213 copy(normalized.Inputs, original.Inputs) 214 for j, input := range normalized.Inputs { 215 if input.Name == "" || isKeyWord(input.Name) { 216 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 217 } 218 // Event is a bit special, we need to define event struct in binding, 219 // ensure there is no camel-case-style name conflict. 220 for index := 0; ; index++ { 221 if !used[capitalise(normalized.Inputs[j].Name)] { 222 used[capitalise(normalized.Inputs[j].Name)] = true 223 break 224 } 225 normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index) 226 } 227 if hasStruct(input.Type) { 228 bindStructType[lang](input.Type, structs) 229 } 230 } 231 // Append the event to the accumulator list 232 events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} 233 } 234 // Add two special fallback functions if they exist 235 if evmABI.HasFallback() { 236 fallback = &TmplMethod{Original: evmABI.Fallback} 237 } 238 if evmABI.HasReceive() { 239 receive = &TmplMethod{Original: evmABI.Receive} 240 } 241 // There is no easy way to pass arbitrary java objects to the Go side. 242 if len(structs) > 0 && lang == LangJava { 243 return "", errors.New("java binding for tuple arguments is not supported yet") 244 } 245 246 contracts[types[i]] = &TmplContract{ 247 Type: capitalise(types[i]), 248 InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""), 249 InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"), 250 Constructor: evmABI.Constructor, 251 Calls: calls, 252 Transacts: transacts, 253 Fallback: fallback, 254 Receive: receive, 255 Events: events, 256 Libraries: make(map[string]string), 257 } 258 // Function 4-byte signatures are stored in the same sequence 259 // as types, if available. 260 if len(fsigs) > i { 261 contracts[types[i]].FuncSigs = fsigs[i] 262 } 263 // Parse library references. 264 for pattern, name := range libs { 265 matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin)) 266 if err != nil { 267 log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err) 268 } 269 if matched { 270 contracts[types[i]].Libraries[pattern] = name 271 // keep track that this type is a library 272 if _, ok := isLib[name]; !ok { 273 isLib[name] = struct{}{} 274 } 275 } 276 } 277 } 278 // Check if that type has already been identified as a library 279 for i := 0; i < len(types); i++ { 280 _, ok := isLib[types[i]] 281 contracts[types[i]].Library = ok 282 } 283 284 var ( 285 data interface{} 286 templateSource string 287 ) 288 289 // Generate the contract template data according to hook 290 if bindHook != nil { 291 var err error 292 data, templateSource, err = bindHook(lang, pkg, types, contracts, structs) 293 if err != nil { 294 return "", err 295 } 296 } else { // default to generate contract binding 297 templateSource = tmplSource[lang] 298 data = &tmplData{ 299 Package: pkg, 300 Contracts: contracts, 301 Libraries: libs, 302 Structs: structs, 303 } 304 } 305 buffer := new(bytes.Buffer) 306 307 funcs := map[string]interface{}{ 308 "bindtype": bindType[lang], 309 "bindtopictype": bindTopicType[lang], 310 "namedtype": namedType[lang], 311 "capitalise": capitalise, 312 "decapitalise": decapitalise, 313 "convertToNil": convertToNil, 314 "mkList": mkList, 315 } 316 317 // render the template 318 tmpl := template.Must(template.New("").Funcs(funcs).Parse(templateSource)) 319 if err := tmpl.Execute(buffer, data); err != nil { 320 return "", err 321 } 322 // For Go bindings pass the code through gofmt to clean it up 323 if lang == LangGo { 324 code, err := format.Source(buffer.Bytes()) 325 if err != nil { 326 return "", fmt.Errorf("%v\n%s", err, buffer) 327 } 328 return string(code), nil 329 } 330 // For all others just return as is for now 331 return buffer.String(), nil 332 } 333 334 // bindType is a set of type binders that convert Solidity types to some supported 335 // programming language types. 336 var bindType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{ 337 LangGo: bindTypeGo, 338 LangJava: bindTypeJava, 339 } 340 341 // bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones. 342 func bindBasicTypeGo(kind abi.Type) string { 343 switch kind.T { 344 case abi.AddressTy: 345 return "common.Address" 346 case abi.IntTy, abi.UintTy: 347 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) 348 switch parts[2] { 349 case "8", "16", "32", "64": 350 return fmt.Sprintf("%sint%s", parts[1], parts[2]) 351 } 352 return "*big.Int" 353 case abi.FixedBytesTy: 354 return fmt.Sprintf("[%d]byte", kind.Size) 355 case abi.BytesTy: 356 return "[]byte" 357 case abi.FunctionTy: 358 return "[24]byte" 359 default: 360 // string, bool types 361 return kind.String() 362 } 363 } 364 365 // bindTypeGo converts solidity types to Go ones. Since there is no clear mapping 366 // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly 367 // mapped will use an upscaled type (e.g. BigDecimal). 368 func bindTypeGo(kind abi.Type, structs map[string]*TmplStruct) string { 369 switch kind.T { 370 case abi.TupleTy: 371 return structs[kind.TupleRawName+kind.String()].Name 372 case abi.ArrayTy: 373 return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) 374 case abi.SliceTy: 375 return "[]" + bindTypeGo(*kind.Elem, structs) 376 default: 377 return bindBasicTypeGo(kind) 378 } 379 } 380 381 // bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java ones. 382 func bindBasicTypeJava(kind abi.Type) string { 383 switch kind.T { 384 case abi.AddressTy: 385 return "Address" 386 case abi.IntTy, abi.UintTy: 387 // Note that uint and int (without digits) are also matched, 388 // these are size 256, and will translate to BigInt (the default). 389 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) 390 if len(parts) != 3 { 391 return kind.String() 392 } 393 // All unsigned integers should be translated to BigInt since gomobile doesn't 394 // support them. 395 if parts[1] == "u" { 396 return "BigInt" 397 } 398 399 namedSize := map[string]string{ 400 "8": "byte", 401 "16": "short", 402 "32": "int", 403 "64": "long", 404 }[parts[2]] 405 406 // default to BigInt 407 if namedSize == "" { 408 namedSize = "BigInt" 409 } 410 return namedSize 411 case abi.FixedBytesTy, abi.BytesTy: 412 return "byte[]" 413 case abi.BoolTy: 414 return "boolean" 415 case abi.StringTy: 416 return "String" 417 case abi.FunctionTy: 418 return "byte[24]" 419 default: 420 return kind.String() 421 } 422 } 423 424 // pluralizeJavaType explicitly converts multidimensional types to predefined 425 // types in go side. 426 func pluralizeJavaType(typ string) string { 427 switch typ { 428 case "boolean": 429 return "Bools" 430 case "String": 431 return "Strings" 432 case "Address": 433 return "Addresses" 434 case "byte[]": 435 return "Binaries" 436 case "BigInt": 437 return "BigInts" 438 } 439 return typ + "[]" 440 } 441 442 // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping 443 // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly 444 // mapped will use an upscaled type (e.g. BigDecimal). 445 func bindTypeJava(kind abi.Type, structs map[string]*TmplStruct) string { 446 switch kind.T { 447 case abi.TupleTy: 448 return structs[kind.TupleRawName+kind.String()].Name 449 case abi.ArrayTy, abi.SliceTy: 450 return pluralizeJavaType(bindTypeJava(*kind.Elem, structs)) 451 default: 452 return bindBasicTypeJava(kind) 453 } 454 } 455 456 // bindTopicType is a set of type binders that convert Solidity types to some 457 // supported programming language topic types. 458 var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{ 459 LangGo: bindTopicTypeGo, 460 LangJava: bindTopicTypeJava, 461 } 462 463 // bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same 464 // functionality as for simple types, but dynamic types get converted to hashes. 465 func bindTopicTypeGo(kind abi.Type, structs map[string]*TmplStruct) string { 466 bound := bindTypeGo(kind, structs) 467 468 // todo(rjl493456442) according solidity documentation, indexed event 469 // parameters that are not value types i.e. arrays and structs are not 470 // stored directly but instead a keccak256-hash of an encoding is stored. 471 // 472 // We only convert stringS and bytes to hash, still need to deal with 473 // array(both fixed-size and dynamic-size) and struct. 474 if bound == "string" || bound == "[]byte" { 475 bound = "common.Hash" 476 } 477 return bound 478 } 479 480 // bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same 481 // functionality as for simple types, but dynamic types get converted to hashes. 482 func bindTopicTypeJava(kind abi.Type, structs map[string]*TmplStruct) string { 483 bound := bindTypeJava(kind, structs) 484 485 // todo(rjl493456442) according solidity documentation, indexed event 486 // parameters that are not value types i.e. arrays and structs are not 487 // stored directly but instead a keccak256-hash of an encoding is stored. 488 // 489 // We only convert strings and bytes to hash, still need to deal with 490 // array(both fixed-size and dynamic-size) and struct. 491 if bound == "String" || bound == "byte[]" { 492 bound = "Hash" 493 } 494 return bound 495 } 496 497 // bindStructType is a set of type binders that convert Solidity tuple types to some supported 498 // programming language struct definition. 499 var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{ 500 LangGo: bindStructTypeGo, 501 LangJava: bindStructTypeJava, 502 } 503 504 // bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping 505 // in the given map. 506 // Notably, this function will resolve and record nested struct recursively. 507 func bindStructTypeGo(kind abi.Type, structs map[string]*TmplStruct) string { 508 switch kind.T { 509 case abi.TupleTy: 510 // We compose a raw struct name and a canonical parameter expression 511 // together here. The reason is before solidity v0.5.11, kind.TupleRawName 512 // is empty, so we use canonical parameter expression to distinguish 513 // different struct definition. From the consideration of backward 514 // compatibility, we concat these two together so that if kind.TupleRawName 515 // is not empty, it can have unique id. 516 id := kind.TupleRawName + kind.String() 517 if s, exist := structs[id]; exist { 518 return s.Name 519 } 520 var ( 521 names = make(map[string]bool) 522 fields []*tmplField 523 ) 524 for i, elem := range kind.TupleElems { 525 name := capitalise(kind.TupleRawNames[i]) 526 name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] }) 527 names[name] = true 528 fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem}) 529 } 530 name := kind.TupleRawName 531 if name == "" { 532 name = fmt.Sprintf("Struct%d", len(structs)) 533 } 534 name = capitalise(name) 535 536 structs[id] = &TmplStruct{ 537 Name: name, 538 Fields: fields, 539 } 540 return name 541 case abi.ArrayTy: 542 return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs) 543 case abi.SliceTy: 544 return "[]" + bindStructTypeGo(*kind.Elem, structs) 545 default: 546 return bindBasicTypeGo(kind) 547 } 548 } 549 550 // bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping 551 // in the given map. 552 // Notably, this function will resolve and record nested struct recursively. 553 func bindStructTypeJava(kind abi.Type, structs map[string]*TmplStruct) string { 554 switch kind.T { 555 case abi.TupleTy: 556 // We compose a raw struct name and a canonical parameter expression 557 // together here. The reason is before solidity v0.5.11, kind.TupleRawName 558 // is empty, so we use canonical parameter expression to distinguish 559 // different struct definition. From the consideration of backward 560 // compatibility, we concat these two together so that if kind.TupleRawName 561 // is not empty, it can have unique id. 562 id := kind.TupleRawName + kind.String() 563 if s, exist := structs[id]; exist { 564 return s.Name 565 } 566 var fields []*tmplField 567 for i, elem := range kind.TupleElems { 568 field := bindStructTypeJava(*elem, structs) 569 fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem}) 570 } 571 name := kind.TupleRawName 572 if name == "" { 573 name = fmt.Sprintf("Class%d", len(structs)) 574 } 575 structs[id] = &TmplStruct{ 576 Name: name, 577 Fields: fields, 578 } 579 return name 580 case abi.ArrayTy, abi.SliceTy: 581 return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs)) 582 default: 583 return bindBasicTypeJava(kind) 584 } 585 } 586 587 // namedType is a set of functions that transform language specific types to 588 // named versions that may be used inside method names. 589 var namedType = map[Lang]func(string, abi.Type) string{ 590 LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, 591 LangJava: namedTypeJava, 592 } 593 594 // namedTypeJava converts some primitive data types to named variants that can 595 // be used as parts of method names. 596 func namedTypeJava(javaKind string, solKind abi.Type) string { 597 switch javaKind { 598 case "byte[]": 599 return "Binary" 600 case "boolean": 601 return "Bool" 602 default: 603 parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String()) 604 if len(parts) != 4 { 605 return javaKind 606 } 607 switch parts[2] { 608 case "8", "16", "32", "64": 609 if parts[3] == "" { 610 return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2])) 611 } 612 return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2])) 613 614 default: 615 return javaKind 616 } 617 } 618 } 619 620 // alias returns an alias of the given string based on the aliasing rules 621 // or returns itself if no rule is matched. 622 func alias(aliases map[string]string, n string) string { 623 if alias, exist := aliases[n]; exist { 624 return alias 625 } 626 return n 627 } 628 629 // methodNormalizer is a name transformer that modifies Solidity method names to 630 // conform to target language naming conventions. 631 var methodNormalizer = map[Lang]func(string) string{ 632 LangGo: abi.ToCamelCase, 633 LangJava: decapitalise, 634 } 635 636 // capitalise makes a camel-case string which starts with an upper case character. 637 var capitalise = abi.ToCamelCase 638 639 // decapitalise makes a camel-case string which starts with a lower case character. 640 func decapitalise(input string) string { 641 if len(input) == 0 { 642 return input 643 } 644 645 goForm := abi.ToCamelCase(input) 646 return strings.ToLower(goForm[:1]) + goForm[1:] 647 } 648 649 // convertToNil converts any type to its proper nil form. 650 func convertToNil(input abi.Type) string { 651 switch input.T { 652 case abi.IntTy, abi.UintTy: 653 return "big.NewInt(0)" 654 case abi.StringTy: 655 return "\"\"" 656 case abi.BoolTy: 657 return "false" 658 case abi.AddressTy: 659 return "common.Address{}" 660 case abi.HashTy: 661 return "common.Hash{}" 662 default: 663 return "nil" 664 } 665 } 666 667 // structured checks whether a list of ABI data types has enough information to 668 // operate through a proper Go struct or if flat returns are needed. 669 func structured(args abi.Arguments) bool { 670 if len(args) < 2 { 671 return false 672 } 673 exists := make(map[string]bool) 674 for _, out := range args { 675 // If the name is anonymous, we can't organize into a struct 676 if out.Name == "" { 677 return false 678 } 679 // If the field name is empty when normalized or collides (var, Var, _var, _Var), 680 // we can't organize into a struct 681 field := capitalise(out.Name) 682 if field == "" || exists[field] { 683 return false 684 } 685 exists[field] = true 686 } 687 return true 688 } 689 690 // hasStruct returns an indicator whether the given type is struct, struct slice 691 // or struct array. 692 func hasStruct(t abi.Type) bool { 693 switch t.T { 694 case abi.SliceTy: 695 return hasStruct(*t.Elem) 696 case abi.ArrayTy: 697 return hasStruct(*t.Elem) 698 case abi.TupleTy: 699 return true 700 default: 701 return false 702 } 703 } 704 705 func mkList(args ...interface{}) []interface{} { 706 return args 707 }