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