github.com/ethereum/go-ethereum@v1.14.3/accounts/abi/bind/bind.go (about)

     1  // Copyright 2016 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 generates Ethereum contract Go bindings.
    18  //
    19  // Detailed usage document and tutorial available on the go-ethereum Wiki page:
    20  // https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
    21  package bind
    22  
    23  import (
    24  	"bytes"
    25  	"fmt"
    26  	"go/format"
    27  	"regexp"
    28  	"strings"
    29  	"text/template"
    30  	"unicode"
    31  
    32  	"github.com/ethereum/go-ethereum/accounts/abi"
    33  	"github.com/ethereum/go-ethereum/log"
    34  )
    35  
    36  // Lang is a target programming language selector to generate bindings for.
    37  type Lang int
    38  
    39  const (
    40  	LangGo Lang = iota
    41  )
    42  
    43  func isKeyWord(arg string) bool {
    44  	switch arg {
    45  	case "break":
    46  	case "case":
    47  	case "chan":
    48  	case "const":
    49  	case "continue":
    50  	case "default":
    51  	case "defer":
    52  	case "else":
    53  	case "fallthrough":
    54  	case "for":
    55  	case "func":
    56  	case "go":
    57  	case "goto":
    58  	case "if":
    59  	case "import":
    60  	case "interface":
    61  	case "iota":
    62  	case "map":
    63  	case "make":
    64  	case "new":
    65  	case "package":
    66  	case "range":
    67  	case "return":
    68  	case "select":
    69  	case "struct":
    70  	case "switch":
    71  	case "type":
    72  	case "var":
    73  	default:
    74  		return false
    75  	}
    76  
    77  	return true
    78  }
    79  
    80  // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
    81  // to be used as is in client code, but rather as an intermediate struct which
    82  // enforces compile time type safety and naming convention as opposed to having to
    83  // manually maintain hard coded strings that break on runtime.
    84  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) {
    85  	var (
    86  		// contracts is the map of each individual contract requested binding
    87  		contracts = make(map[string]*tmplContract)
    88  
    89  		// structs is the map of all redeclared structs shared by passed contracts.
    90  		structs = make(map[string]*tmplStruct)
    91  
    92  		// isLib is the map used to flag each encountered library as such
    93  		isLib = make(map[string]struct{})
    94  	)
    95  	for i := 0; i < len(types); i++ {
    96  		// Parse the actual ABI to generate the binding for
    97  		evmABI, err := abi.JSON(strings.NewReader(abis[i]))
    98  		if err != nil {
    99  			return "", err
   100  		}
   101  		// Strip any whitespace from the JSON ABI
   102  		strippedABI := strings.Map(func(r rune) rune {
   103  			if unicode.IsSpace(r) {
   104  				return -1
   105  			}
   106  			return r
   107  		}, abis[i])
   108  
   109  		// Extract the call and transact methods; events, struct definitions; and sort them alphabetically
   110  		var (
   111  			calls     = make(map[string]*tmplMethod)
   112  			transacts = make(map[string]*tmplMethod)
   113  			events    = make(map[string]*tmplEvent)
   114  			fallback  *tmplMethod
   115  			receive   *tmplMethod
   116  
   117  			// identifiers are used to detect duplicated identifiers of functions
   118  			// and events. For all calls, transacts and events, abigen will generate
   119  			// corresponding bindings. However we have to ensure there is no
   120  			// identifier collisions in the bindings of these categories.
   121  			callIdentifiers     = make(map[string]bool)
   122  			transactIdentifiers = make(map[string]bool)
   123  			eventIdentifiers    = make(map[string]bool)
   124  		)
   125  
   126  		for _, input := range evmABI.Constructor.Inputs {
   127  			if hasStruct(input.Type) {
   128  				bindStructType[lang](input.Type, structs)
   129  			}
   130  		}
   131  
   132  		for _, original := range evmABI.Methods {
   133  			// Normalize the method for capital cases and non-anonymous inputs/outputs
   134  			normalized := original
   135  			normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
   136  			// Ensure there is no duplicated identifier
   137  			var identifiers = callIdentifiers
   138  			if !original.IsConstant() {
   139  				identifiers = transactIdentifiers
   140  			}
   141  			// Name shouldn't start with a digit. It will make the generated code invalid.
   142  			if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) {
   143  				normalizedName = fmt.Sprintf("M%s", normalizedName)
   144  				normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool {
   145  					_, ok := identifiers[name]
   146  					return ok
   147  				})
   148  			}
   149  			if identifiers[normalizedName] {
   150  				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
   151  			}
   152  			identifiers[normalizedName] = true
   153  
   154  			normalized.Name = normalizedName
   155  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
   156  			copy(normalized.Inputs, original.Inputs)
   157  			for j, input := range normalized.Inputs {
   158  				if input.Name == "" || isKeyWord(input.Name) {
   159  					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
   160  				}
   161  				if hasStruct(input.Type) {
   162  					bindStructType[lang](input.Type, structs)
   163  				}
   164  			}
   165  			normalized.Outputs = make([]abi.Argument, len(original.Outputs))
   166  			copy(normalized.Outputs, original.Outputs)
   167  			for j, output := range normalized.Outputs {
   168  				if output.Name != "" {
   169  					normalized.Outputs[j].Name = capitalise(output.Name)
   170  				}
   171  				if hasStruct(output.Type) {
   172  					bindStructType[lang](output.Type, structs)
   173  				}
   174  			}
   175  			// Append the methods to the call or transact lists
   176  			if original.IsConstant() {
   177  				calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
   178  			} else {
   179  				transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
   180  			}
   181  		}
   182  		for _, original := range evmABI.Events {
   183  			// Skip anonymous events as they don't support explicit filtering
   184  			if original.Anonymous {
   185  				continue
   186  			}
   187  			// Normalize the event for capital cases and non-anonymous outputs
   188  			normalized := original
   189  
   190  			// Ensure there is no duplicated identifier
   191  			normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
   192  			// Name shouldn't start with a digit. It will make the generated code invalid.
   193  			if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) {
   194  				normalizedName = fmt.Sprintf("E%s", normalizedName)
   195  				normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool {
   196  					_, ok := eventIdentifiers[name]
   197  					return ok
   198  				})
   199  			}
   200  			if eventIdentifiers[normalizedName] {
   201  				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
   202  			}
   203  			eventIdentifiers[normalizedName] = true
   204  			normalized.Name = normalizedName
   205  
   206  			used := make(map[string]bool)
   207  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
   208  			copy(normalized.Inputs, original.Inputs)
   209  			for j, input := range normalized.Inputs {
   210  				if input.Name == "" || isKeyWord(input.Name) {
   211  					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
   212  				}
   213  				// Event is a bit special, we need to define event struct in binding,
   214  				// ensure there is no camel-case-style name conflict.
   215  				for index := 0; ; index++ {
   216  					if !used[capitalise(normalized.Inputs[j].Name)] {
   217  						used[capitalise(normalized.Inputs[j].Name)] = true
   218  						break
   219  					}
   220  					normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
   221  				}
   222  				if hasStruct(input.Type) {
   223  					bindStructType[lang](input.Type, structs)
   224  				}
   225  			}
   226  			// Append the event to the accumulator list
   227  			events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
   228  		}
   229  		// Add two special fallback functions if they exist
   230  		if evmABI.HasFallback() {
   231  			fallback = &tmplMethod{Original: evmABI.Fallback}
   232  		}
   233  		if evmABI.HasReceive() {
   234  			receive = &tmplMethod{Original: evmABI.Receive}
   235  		}
   236  		contracts[types[i]] = &tmplContract{
   237  			Type:        capitalise(types[i]),
   238  			InputABI:    strings.ReplaceAll(strippedABI, "\"", "\\\""),
   239  			InputBin:    strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
   240  			Constructor: evmABI.Constructor,
   241  			Calls:       calls,
   242  			Transacts:   transacts,
   243  			Fallback:    fallback,
   244  			Receive:     receive,
   245  			Events:      events,
   246  			Libraries:   make(map[string]string),
   247  		}
   248  		// Function 4-byte signatures are stored in the same sequence
   249  		// as types, if available.
   250  		if len(fsigs) > i {
   251  			contracts[types[i]].FuncSigs = fsigs[i]
   252  		}
   253  		// Parse library references.
   254  		for pattern, name := range libs {
   255  			matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
   256  			if err != nil {
   257  				log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
   258  			}
   259  			if matched {
   260  				contracts[types[i]].Libraries[pattern] = name
   261  				// keep track that this type is a library
   262  				if _, ok := isLib[name]; !ok {
   263  					isLib[name] = struct{}{}
   264  				}
   265  			}
   266  		}
   267  	}
   268  	// Check if that type has already been identified as a library
   269  	for i := 0; i < len(types); i++ {
   270  		_, ok := isLib[types[i]]
   271  		contracts[types[i]].Library = ok
   272  	}
   273  	// Generate the contract template data content and render it
   274  	data := &tmplData{
   275  		Package:   pkg,
   276  		Contracts: contracts,
   277  		Libraries: libs,
   278  		Structs:   structs,
   279  	}
   280  	buffer := new(bytes.Buffer)
   281  
   282  	funcs := map[string]interface{}{
   283  		"bindtype":      bindType[lang],
   284  		"bindtopictype": bindTopicType[lang],
   285  		"namedtype":     namedType[lang],
   286  		"capitalise":    capitalise,
   287  		"decapitalise":  decapitalise,
   288  	}
   289  	tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
   290  	if err := tmpl.Execute(buffer, data); err != nil {
   291  		return "", err
   292  	}
   293  	// For Go bindings pass the code through gofmt to clean it up
   294  	if lang == LangGo {
   295  		code, err := format.Source(buffer.Bytes())
   296  		if err != nil {
   297  			return "", fmt.Errorf("%v\n%s", err, buffer)
   298  		}
   299  		return string(code), nil
   300  	}
   301  	// For all others just return as is for now
   302  	return buffer.String(), nil
   303  }
   304  
   305  // bindType is a set of type binders that convert Solidity types to some supported
   306  // programming language types.
   307  var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   308  	LangGo: bindTypeGo,
   309  }
   310  
   311  // bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
   312  func bindBasicTypeGo(kind abi.Type) string {
   313  	switch kind.T {
   314  	case abi.AddressTy:
   315  		return "common.Address"
   316  	case abi.IntTy, abi.UintTy:
   317  		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
   318  		switch parts[2] {
   319  		case "8", "16", "32", "64":
   320  			return fmt.Sprintf("%sint%s", parts[1], parts[2])
   321  		}
   322  		return "*big.Int"
   323  	case abi.FixedBytesTy:
   324  		return fmt.Sprintf("[%d]byte", kind.Size)
   325  	case abi.BytesTy:
   326  		return "[]byte"
   327  	case abi.FunctionTy:
   328  		return "[24]byte"
   329  	default:
   330  		// string, bool types
   331  		return kind.String()
   332  	}
   333  }
   334  
   335  // bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
   336  // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
   337  // mapped will use an upscaled type (e.g. BigDecimal).
   338  func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   339  	switch kind.T {
   340  	case abi.TupleTy:
   341  		return structs[kind.TupleRawName+kind.String()].Name
   342  	case abi.ArrayTy:
   343  		return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
   344  	case abi.SliceTy:
   345  		return "[]" + bindTypeGo(*kind.Elem, structs)
   346  	default:
   347  		return bindBasicTypeGo(kind)
   348  	}
   349  }
   350  
   351  // bindTopicType is a set of type binders that convert Solidity types to some
   352  // supported programming language topic types.
   353  var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   354  	LangGo: bindTopicTypeGo,
   355  }
   356  
   357  // bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
   358  // functionality as for simple types, but dynamic types get converted to hashes.
   359  func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   360  	bound := bindTypeGo(kind, structs)
   361  
   362  	// todo(rjl493456442) according solidity documentation, indexed event
   363  	// parameters that are not value types i.e. arrays and structs are not
   364  	// stored directly but instead a keccak256-hash of an encoding is stored.
   365  	//
   366  	// We only convert strings and bytes to hash, still need to deal with
   367  	// array(both fixed-size and dynamic-size) and struct.
   368  	if bound == "string" || bound == "[]byte" {
   369  		bound = "common.Hash"
   370  	}
   371  	return bound
   372  }
   373  
   374  // bindStructType is a set of type binders that convert Solidity tuple types to some supported
   375  // programming language struct definition.
   376  var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   377  	LangGo: bindStructTypeGo,
   378  }
   379  
   380  // bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
   381  // in the given map.
   382  // Notably, this function will resolve and record nested struct recursively.
   383  func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   384  	switch kind.T {
   385  	case abi.TupleTy:
   386  		// We compose a raw struct name and a canonical parameter expression
   387  		// together here. The reason is before solidity v0.5.11, kind.TupleRawName
   388  		// is empty, so we use canonical parameter expression to distinguish
   389  		// different struct definition. From the consideration of backward
   390  		// compatibility, we concat these two together so that if kind.TupleRawName
   391  		// is not empty, it can have unique id.
   392  		id := kind.TupleRawName + kind.String()
   393  		if s, exist := structs[id]; exist {
   394  			return s.Name
   395  		}
   396  		var (
   397  			names  = make(map[string]bool)
   398  			fields []*tmplField
   399  		)
   400  		for i, elem := range kind.TupleElems {
   401  			name := capitalise(kind.TupleRawNames[i])
   402  			name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
   403  			names[name] = true
   404  			fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
   405  		}
   406  		name := kind.TupleRawName
   407  		if name == "" {
   408  			name = fmt.Sprintf("Struct%d", len(structs))
   409  		}
   410  		name = capitalise(name)
   411  
   412  		structs[id] = &tmplStruct{
   413  			Name:   name,
   414  			Fields: fields,
   415  		}
   416  		return name
   417  	case abi.ArrayTy:
   418  		return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
   419  	case abi.SliceTy:
   420  		return "[]" + bindStructTypeGo(*kind.Elem, structs)
   421  	default:
   422  		return bindBasicTypeGo(kind)
   423  	}
   424  }
   425  
   426  // namedType is a set of functions that transform language specific types to
   427  // named versions that may be used inside method names.
   428  var namedType = map[Lang]func(string, abi.Type) string{
   429  	LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
   430  }
   431  
   432  // alias returns an alias of the given string based on the aliasing rules
   433  // or returns itself if no rule is matched.
   434  func alias(aliases map[string]string, n string) string {
   435  	if alias, exist := aliases[n]; exist {
   436  		return alias
   437  	}
   438  	return n
   439  }
   440  
   441  // methodNormalizer is a name transformer that modifies Solidity method names to
   442  // conform to target language naming conventions.
   443  var methodNormalizer = map[Lang]func(string) string{
   444  	LangGo: abi.ToCamelCase,
   445  }
   446  
   447  // capitalise makes a camel-case string which starts with an upper case character.
   448  var capitalise = abi.ToCamelCase
   449  
   450  // decapitalise makes a camel-case string which starts with a lower case character.
   451  func decapitalise(input string) string {
   452  	if len(input) == 0 {
   453  		return input
   454  	}
   455  
   456  	goForm := abi.ToCamelCase(input)
   457  	return strings.ToLower(goForm[:1]) + goForm[1:]
   458  }
   459  
   460  // structured checks whether a list of ABI data types has enough information to
   461  // operate through a proper Go struct or if flat returns are needed.
   462  func structured(args abi.Arguments) bool {
   463  	if len(args) < 2 {
   464  		return false
   465  	}
   466  	exists := make(map[string]bool)
   467  	for _, out := range args {
   468  		// If the name is anonymous, we can't organize into a struct
   469  		if out.Name == "" {
   470  			return false
   471  		}
   472  		// If the field name is empty when normalized or collides (var, Var, _var, _Var),
   473  		// we can't organize into a struct
   474  		field := capitalise(out.Name)
   475  		if field == "" || exists[field] {
   476  			return false
   477  		}
   478  		exists[field] = true
   479  	}
   480  	return true
   481  }
   482  
   483  // hasStruct returns an indicator whether the given type is struct, struct slice
   484  // or struct array.
   485  func hasStruct(t abi.Type) bool {
   486  	switch t.T {
   487  	case abi.SliceTy:
   488  		return hasStruct(*t.Elem)
   489  	case abi.ArrayTy:
   490  		return hasStruct(*t.Elem)
   491  	case abi.TupleTy:
   492  		return true
   493  	default:
   494  		return false
   495  	}
   496  }