github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/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/tacshi/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/tacshi/go-ethereum/accounts/abi"
    33  	"github.com/tacshi/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 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  
   137  			// Ensure there is no duplicated identifier
   138  			var identifiers = callIdentifiers
   139  			if !original.IsConstant() {
   140  				identifiers = transactIdentifiers
   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[lang](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 = capitalise(output.Name)
   163  				}
   164  				if hasStruct(output.Type) {
   165  					bindStructType[lang](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 := methodNormalizer[lang](alias(aliases, original.Name))
   185  			if eventIdentifiers[normalizedName] {
   186  				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
   187  			}
   188  			eventIdentifiers[normalizedName] = true
   189  			normalized.Name = normalizedName
   190  
   191  			used := make(map[string]bool)
   192  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
   193  			copy(normalized.Inputs, original.Inputs)
   194  			for j, input := range normalized.Inputs {
   195  				if input.Name == "" || isKeyWord(input.Name) {
   196  					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
   197  				}
   198  				// Event is a bit special, we need to define event struct in binding,
   199  				// ensure there is no camel-case-style name conflict.
   200  				for index := 0; ; index++ {
   201  					if !used[capitalise(normalized.Inputs[j].Name)] {
   202  						used[capitalise(normalized.Inputs[j].Name)] = true
   203  						break
   204  					}
   205  					normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
   206  				}
   207  				if hasStruct(input.Type) {
   208  					bindStructType[lang](input.Type, structs)
   209  				}
   210  			}
   211  			// Append the event to the accumulator list
   212  			events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
   213  		}
   214  		// Add two special fallback functions if they exist
   215  		if evmABI.HasFallback() {
   216  			fallback = &tmplMethod{Original: evmABI.Fallback}
   217  		}
   218  		if evmABI.HasReceive() {
   219  			receive = &tmplMethod{Original: evmABI.Receive}
   220  		}
   221  		contracts[types[i]] = &tmplContract{
   222  			Type:        capitalise(types[i]),
   223  			InputABI:    strings.ReplaceAll(strippedABI, "\"", "\\\""),
   224  			InputBin:    strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
   225  			Constructor: evmABI.Constructor,
   226  			Calls:       calls,
   227  			Transacts:   transacts,
   228  			Fallback:    fallback,
   229  			Receive:     receive,
   230  			Events:      events,
   231  			Libraries:   make(map[string]string),
   232  		}
   233  		// Function 4-byte signatures are stored in the same sequence
   234  		// as types, if available.
   235  		if len(fsigs) > i {
   236  			contracts[types[i]].FuncSigs = fsigs[i]
   237  		}
   238  		// Parse library references.
   239  		for pattern, name := range libs {
   240  			matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
   241  			if err != nil {
   242  				log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
   243  			}
   244  			if matched {
   245  				contracts[types[i]].Libraries[pattern] = name
   246  				// keep track that this type is a library
   247  				if _, ok := isLib[name]; !ok {
   248  					isLib[name] = struct{}{}
   249  				}
   250  			}
   251  		}
   252  	}
   253  	// Check if that type has already been identified as a library
   254  	for i := 0; i < len(types); i++ {
   255  		_, ok := isLib[types[i]]
   256  		contracts[types[i]].Library = ok
   257  	}
   258  	// Generate the contract template data content and render it
   259  	data := &tmplData{
   260  		Package:   pkg,
   261  		Contracts: contracts,
   262  		Libraries: libs,
   263  		Structs:   structs,
   264  	}
   265  	buffer := new(bytes.Buffer)
   266  
   267  	funcs := map[string]interface{}{
   268  		"bindtype":      bindType[lang],
   269  		"bindtopictype": bindTopicType[lang],
   270  		"namedtype":     namedType[lang],
   271  		"capitalise":    capitalise,
   272  		"decapitalise":  decapitalise,
   273  	}
   274  	tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
   275  	if err := tmpl.Execute(buffer, data); err != nil {
   276  		return "", err
   277  	}
   278  	// For Go bindings pass the code through gofmt to clean it up
   279  	if lang == LangGo {
   280  		code, err := format.Source(buffer.Bytes())
   281  		if err != nil {
   282  			return "", fmt.Errorf("%v\n%s", err, buffer)
   283  		}
   284  		return string(code), nil
   285  	}
   286  	// For all others just return as is for now
   287  	return buffer.String(), nil
   288  }
   289  
   290  // bindType is a set of type binders that convert Solidity types to some supported
   291  // programming language types.
   292  var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   293  	LangGo: bindTypeGo,
   294  }
   295  
   296  // bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
   297  func bindBasicTypeGo(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  // bindTypeGo 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 bindTypeGo(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) + bindTypeGo(*kind.Elem, structs)
   329  	case abi.SliceTy:
   330  		return "[]" + bindTypeGo(*kind.Elem, structs)
   331  	default:
   332  		return bindBasicTypeGo(kind)
   333  	}
   334  }
   335  
   336  // bindTopicType is a set of type binders that convert Solidity types to some
   337  // supported programming language topic types.
   338  var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   339  	LangGo: bindTopicTypeGo,
   340  }
   341  
   342  // bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
   343  // functionality as for simple types, but dynamic types get converted to hashes.
   344  func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   345  	bound := bindTypeGo(kind, structs)
   346  
   347  	// todo(rjl493456442) according solidity documentation, indexed event
   348  	// parameters that are not value types i.e. arrays and structs are not
   349  	// stored directly but instead a keccak256-hash of an encoding is stored.
   350  	//
   351  	// We only convert stringS and bytes to hash, still need to deal with
   352  	// array(both fixed-size and dynamic-size) and struct.
   353  	if bound == "string" || bound == "[]byte" {
   354  		bound = "common.Hash"
   355  	}
   356  	return bound
   357  }
   358  
   359  // bindStructType is a set of type binders that convert Solidity tuple types to some supported
   360  // programming language struct definition.
   361  var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   362  	LangGo: bindStructTypeGo,
   363  }
   364  
   365  // bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
   366  // in the given map.
   367  // Notably, this function will resolve and record nested struct recursively.
   368  func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   369  	switch kind.T {
   370  	case abi.TupleTy:
   371  		// We compose a raw struct name and a canonical parameter expression
   372  		// together here. The reason is before solidity v0.5.11, kind.TupleRawName
   373  		// is empty, so we use canonical parameter expression to distinguish
   374  		// different struct definition. From the consideration of backward
   375  		// compatibility, we concat these two together so that if kind.TupleRawName
   376  		// is not empty, it can have unique id.
   377  		id := kind.TupleRawName + kind.String()
   378  		if s, exist := structs[id]; exist {
   379  			return s.Name
   380  		}
   381  		var (
   382  			names  = make(map[string]bool)
   383  			fields []*tmplField
   384  		)
   385  		for i, elem := range kind.TupleElems {
   386  			name := capitalise(kind.TupleRawNames[i])
   387  			name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
   388  			names[name] = true
   389  			fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
   390  		}
   391  		name := kind.TupleRawName
   392  		if name == "" {
   393  			name = fmt.Sprintf("Struct%d", len(structs))
   394  		}
   395  		name = capitalise(name)
   396  
   397  		structs[id] = &tmplStruct{
   398  			Name:   name,
   399  			Fields: fields,
   400  		}
   401  		return name
   402  	case abi.ArrayTy:
   403  		return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
   404  	case abi.SliceTy:
   405  		return "[]" + bindStructTypeGo(*kind.Elem, structs)
   406  	default:
   407  		return bindBasicTypeGo(kind)
   408  	}
   409  }
   410  
   411  // namedType is a set of functions that transform language specific types to
   412  // named versions that may be used inside method names.
   413  var namedType = map[Lang]func(string, abi.Type) string{
   414  	LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
   415  }
   416  
   417  // alias returns an alias of the given string based on the aliasing rules
   418  // or returns itself if no rule is matched.
   419  func alias(aliases map[string]string, n string) string {
   420  	if alias, exist := aliases[n]; exist {
   421  		return alias
   422  	}
   423  	return n
   424  }
   425  
   426  // methodNormalizer is a name transformer that modifies Solidity method names to
   427  // conform to target language naming conventions.
   428  var methodNormalizer = map[Lang]func(string) string{
   429  	LangGo: abi.ToCamelCase,
   430  }
   431  
   432  // capitalise makes a camel-case string which starts with an upper case character.
   433  var capitalise = abi.ToCamelCase
   434  
   435  // decapitalise makes a camel-case string which starts with a lower case character.
   436  func decapitalise(input string) string {
   437  	if len(input) == 0 {
   438  		return input
   439  	}
   440  
   441  	goForm := abi.ToCamelCase(input)
   442  	return strings.ToLower(goForm[:1]) + goForm[1:]
   443  }
   444  
   445  // structured checks whether a list of ABI data types has enough information to
   446  // operate through a proper Go struct or if flat returns are needed.
   447  func structured(args abi.Arguments) bool {
   448  	if len(args) < 2 {
   449  		return false
   450  	}
   451  	exists := make(map[string]bool)
   452  	for _, out := range args {
   453  		// If the name is anonymous, we can't organize into a struct
   454  		if out.Name == "" {
   455  			return false
   456  		}
   457  		// If the field name is empty when normalized or collides (var, Var, _var, _Var),
   458  		// we can't organize into a struct
   459  		field := capitalise(out.Name)
   460  		if field == "" || exists[field] {
   461  			return false
   462  		}
   463  		exists[field] = true
   464  	}
   465  	return true
   466  }
   467  
   468  // hasStruct returns an indicator whether the given type is struct, struct slice
   469  // or struct array.
   470  func hasStruct(t abi.Type) bool {
   471  	switch t.T {
   472  	case abi.SliceTy:
   473  		return hasStruct(*t.Elem)
   474  	case abi.ArrayTy:
   475  		return hasStruct(*t.Elem)
   476  	case abi.TupleTy:
   477  		return true
   478  	default:
   479  		return false
   480  	}
   481  }