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