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