github.com/p202io/bor@v0.1.4/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/maticnetwork/bor/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts
    21  package bind
    22  
    23  import (
    24  	"bytes"
    25  	"errors"
    26  	"fmt"
    27  	"go/format"
    28  	"regexp"
    29  	"strings"
    30  	"text/template"
    31  	"unicode"
    32  
    33  	"github.com/maticnetwork/bor/accounts/abi"
    34  	"github.com/maticnetwork/bor/log"
    35  )
    36  
    37  // Lang is a target programming language selector to generate bindings for.
    38  type Lang int
    39  
    40  const (
    41  	LangGo Lang = iota
    42  	LangJava
    43  	LangObjC
    44  )
    45  
    46  // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
    47  // to be used as is in client code, but rather as an intermediate struct which
    48  // enforces compile time type safety and naming convention opposed to having to
    49  // manually maintain hard coded strings that break on runtime.
    50  func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string) (string, error) {
    51  	// Process each individual contract requested binding
    52  	contracts := make(map[string]*tmplContract)
    53  
    54  	// Map used to flag each encountered library as such
    55  	isLib := make(map[string]struct{})
    56  
    57  	for i := 0; i < len(types); i++ {
    58  		// Parse the actual ABI to generate the binding for
    59  		evmABI, err := abi.JSON(strings.NewReader(abis[i]))
    60  		if err != nil {
    61  			return "", err
    62  		}
    63  		// Strip any whitespace from the JSON ABI
    64  		strippedABI := strings.Map(func(r rune) rune {
    65  			if unicode.IsSpace(r) {
    66  				return -1
    67  			}
    68  			return r
    69  		}, abis[i])
    70  
    71  		// Extract the call and transact methods; events, struct definitions; and sort them alphabetically
    72  		var (
    73  			calls     = make(map[string]*tmplMethod)
    74  			transacts = make(map[string]*tmplMethod)
    75  			events    = make(map[string]*tmplEvent)
    76  			structs   = make(map[string]*tmplStruct)
    77  		)
    78  		for _, original := range evmABI.Methods {
    79  			// Normalize the method for capital cases and non-anonymous inputs/outputs
    80  			normalized := original
    81  			normalized.Name = methodNormalizer[lang](original.Name)
    82  
    83  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
    84  			copy(normalized.Inputs, original.Inputs)
    85  			for j, input := range normalized.Inputs {
    86  				if input.Name == "" {
    87  					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
    88  				}
    89  				if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist {
    90  					bindStructType[lang](input.Type, structs)
    91  				}
    92  			}
    93  			normalized.Outputs = make([]abi.Argument, len(original.Outputs))
    94  			copy(normalized.Outputs, original.Outputs)
    95  			for j, output := range normalized.Outputs {
    96  				if output.Name != "" {
    97  					normalized.Outputs[j].Name = capitalise(output.Name)
    98  				}
    99  				if _, exist := structs[output.Type.String()]; output.Type.T == abi.TupleTy && !exist {
   100  					bindStructType[lang](output.Type, structs)
   101  				}
   102  			}
   103  			// Append the methods to the call or transact lists
   104  			if original.Const {
   105  				calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
   106  			} else {
   107  				transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
   108  			}
   109  		}
   110  		for _, original := range evmABI.Events {
   111  			// Skip anonymous events as they don't support explicit filtering
   112  			if original.Anonymous {
   113  				continue
   114  			}
   115  			// Normalize the event for capital cases and non-anonymous outputs
   116  			normalized := original
   117  			normalized.Name = methodNormalizer[lang](original.Name)
   118  
   119  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
   120  			copy(normalized.Inputs, original.Inputs)
   121  			for j, input := range normalized.Inputs {
   122  				// Indexed fields are input, non-indexed ones are outputs
   123  				if input.Indexed {
   124  					if input.Name == "" {
   125  						normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
   126  					}
   127  					if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist {
   128  						bindStructType[lang](input.Type, structs)
   129  					}
   130  				}
   131  			}
   132  			// Append the event to the accumulator list
   133  			events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
   134  		}
   135  
   136  		// There is no easy way to pass arbitrary java objects to the Go side.
   137  		if len(structs) > 0 && lang == LangJava {
   138  			return "", errors.New("java binding for tuple arguments is not supported yet")
   139  		}
   140  
   141  		contracts[types[i]] = &tmplContract{
   142  			Type:        capitalise(types[i]),
   143  			InputABI:    strings.Replace(strippedABI, "\"", "\\\"", -1),
   144  			InputBin:    strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
   145  			Constructor: evmABI.Constructor,
   146  			Calls:       calls,
   147  			Transacts:   transacts,
   148  			Events:      events,
   149  			Libraries:   make(map[string]string),
   150  			Structs:     structs,
   151  		}
   152  		// Function 4-byte signatures are stored in the same sequence
   153  		// as types, if available.
   154  		if len(fsigs) > i {
   155  			contracts[types[i]].FuncSigs = fsigs[i]
   156  		}
   157  		// Parse library references.
   158  		for pattern, name := range libs {
   159  			matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
   160  			if err != nil {
   161  				log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
   162  			}
   163  			if matched {
   164  				contracts[types[i]].Libraries[pattern] = name
   165  				// keep track that this type is a library
   166  				if _, ok := isLib[name]; !ok {
   167  					isLib[name] = struct{}{}
   168  				}
   169  			}
   170  		}
   171  	}
   172  	// Check if that type has already been identified as a library
   173  	for i := 0; i < len(types); i++ {
   174  		_, ok := isLib[types[i]]
   175  		contracts[types[i]].Library = ok
   176  	}
   177  	// Generate the contract template data content and render it
   178  	data := &tmplData{
   179  		Package:   pkg,
   180  		Contracts: contracts,
   181  		Libraries: libs,
   182  	}
   183  	buffer := new(bytes.Buffer)
   184  
   185  	funcs := map[string]interface{}{
   186  		"bindtype":      bindType[lang],
   187  		"bindtopictype": bindTopicType[lang],
   188  		"namedtype":     namedType[lang],
   189  		"formatmethod":  formatMethod,
   190  		"formatevent":   formatEvent,
   191  		"capitalise":    capitalise,
   192  		"decapitalise":  decapitalise,
   193  	}
   194  	tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
   195  	if err := tmpl.Execute(buffer, data); err != nil {
   196  		return "", err
   197  	}
   198  	// For Go bindings pass the code through gofmt to clean it up
   199  	if lang == LangGo {
   200  		code, err := format.Source(buffer.Bytes())
   201  		if err != nil {
   202  			return "", fmt.Errorf("%v\n%s", err, buffer)
   203  		}
   204  		return string(code), nil
   205  	}
   206  	// For all others just return as is for now
   207  	return buffer.String(), nil
   208  }
   209  
   210  // bindType is a set of type binders that convert Solidity types to some supported
   211  // programming language types.
   212  var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   213  	LangGo:   bindTypeGo,
   214  	LangJava: bindTypeJava,
   215  }
   216  
   217  // bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go one.
   218  func bindBasicTypeGo(kind abi.Type) string {
   219  	switch kind.T {
   220  	case abi.AddressTy:
   221  		return "common.Address"
   222  	case abi.IntTy, abi.UintTy:
   223  		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
   224  		switch parts[2] {
   225  		case "8", "16", "32", "64":
   226  			return fmt.Sprintf("%sint%s", parts[1], parts[2])
   227  		}
   228  		return "*big.Int"
   229  	case abi.FixedBytesTy:
   230  		return fmt.Sprintf("[%d]byte", kind.Size)
   231  	case abi.BytesTy:
   232  		return "[]byte"
   233  	case abi.FunctionTy:
   234  		return "[24]byte"
   235  	default:
   236  		// string, bool types
   237  		return kind.String()
   238  	}
   239  }
   240  
   241  // bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
   242  // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
   243  // mapped will use an upscaled type (e.g. BigDecimal).
   244  func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   245  	switch kind.T {
   246  	case abi.TupleTy:
   247  		return structs[kind.String()].Name
   248  	case abi.ArrayTy:
   249  		return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
   250  	case abi.SliceTy:
   251  		return "[]" + bindTypeGo(*kind.Elem, structs)
   252  	default:
   253  		return bindBasicTypeGo(kind)
   254  	}
   255  }
   256  
   257  // bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java one.
   258  func bindBasicTypeJava(kind abi.Type) string {
   259  	switch kind.T {
   260  	case abi.AddressTy:
   261  		return "Address"
   262  	case abi.IntTy, abi.UintTy:
   263  		// Note that uint and int (without digits) are also matched,
   264  		// these are size 256, and will translate to BigInt (the default).
   265  		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
   266  		if len(parts) != 3 {
   267  			return kind.String()
   268  		}
   269  		// All unsigned integers should be translated to BigInt since gomobile doesn't
   270  		// support them.
   271  		if parts[1] == "u" {
   272  			return "BigInt"
   273  		}
   274  
   275  		namedSize := map[string]string{
   276  			"8":  "byte",
   277  			"16": "short",
   278  			"32": "int",
   279  			"64": "long",
   280  		}[parts[2]]
   281  
   282  		// default to BigInt
   283  		if namedSize == "" {
   284  			namedSize = "BigInt"
   285  		}
   286  		return namedSize
   287  	case abi.FixedBytesTy, abi.BytesTy:
   288  		return "byte[]"
   289  	case abi.BoolTy:
   290  		return "boolean"
   291  	case abi.StringTy:
   292  		return "String"
   293  	case abi.FunctionTy:
   294  		return "byte[24]"
   295  	default:
   296  		return kind.String()
   297  	}
   298  }
   299  
   300  // pluralizeJavaType explicitly converts multidimensional types to predefined
   301  // type in go side.
   302  func pluralizeJavaType(typ string) string {
   303  	switch typ {
   304  	case "boolean":
   305  		return "Bools"
   306  	case "String":
   307  		return "Strings"
   308  	case "Address":
   309  		return "Addresses"
   310  	case "byte[]":
   311  		return "Binaries"
   312  	case "BigInt":
   313  		return "BigInts"
   314  	}
   315  	return typ + "[]"
   316  }
   317  
   318  // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
   319  // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
   320  // mapped will use an upscaled type (e.g. BigDecimal).
   321  func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
   322  	switch kind.T {
   323  	case abi.TupleTy:
   324  		return structs[kind.String()].Name
   325  	case abi.ArrayTy, abi.SliceTy:
   326  		return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
   327  	default:
   328  		return bindBasicTypeJava(kind)
   329  	}
   330  }
   331  
   332  // bindTopicType is a set of type binders that convert Solidity types to some
   333  // supported programming language topic types.
   334  var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   335  	LangGo:   bindTopicTypeGo,
   336  	LangJava: bindTopicTypeJava,
   337  }
   338  
   339  // bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
   340  // funcionality as for simple types, but dynamic types get converted to hashes.
   341  func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   342  	bound := bindTypeGo(kind, structs)
   343  	if bound == "string" || bound == "[]byte" {
   344  		bound = "common.Hash"
   345  	}
   346  	return bound
   347  }
   348  
   349  // bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same
   350  // funcionality as for simple types, but dynamic types get converted to hashes.
   351  func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
   352  	bound := bindTypeJava(kind, structs)
   353  	if bound == "String" || bound == "byte[]" {
   354  		bound = "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  	LangJava: bindStructTypeJava,
   364  }
   365  
   366  // bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
   367  // in the given map.
   368  // Notably, this function will resolve and record nested struct recursively.
   369  func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   370  	switch kind.T {
   371  	case abi.TupleTy:
   372  		if s, exist := structs[kind.String()]; exist {
   373  			return s.Name
   374  		}
   375  		var fields []*tmplField
   376  		for i, elem := range kind.TupleElems {
   377  			field := bindStructTypeGo(*elem, structs)
   378  			fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
   379  		}
   380  		name := fmt.Sprintf("Struct%d", len(structs))
   381  		structs[kind.String()] = &tmplStruct{
   382  			Name:   name,
   383  			Fields: fields,
   384  		}
   385  		return name
   386  	case abi.ArrayTy:
   387  		return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
   388  	case abi.SliceTy:
   389  		return "[]" + bindStructTypeGo(*kind.Elem, structs)
   390  	default:
   391  		return bindBasicTypeGo(kind)
   392  	}
   393  }
   394  
   395  // bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping
   396  // in the given map.
   397  // Notably, this function will resolve and record nested struct recursively.
   398  func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
   399  	switch kind.T {
   400  	case abi.TupleTy:
   401  		if s, exist := structs[kind.String()]; exist {
   402  			return s.Name
   403  		}
   404  		var fields []*tmplField
   405  		for i, elem := range kind.TupleElems {
   406  			field := bindStructTypeJava(*elem, structs)
   407  			fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
   408  		}
   409  		name := fmt.Sprintf("Class%d", len(structs))
   410  		structs[kind.String()] = &tmplStruct{
   411  			Name:   name,
   412  			Fields: fields,
   413  		}
   414  		return name
   415  	case abi.ArrayTy, abi.SliceTy:
   416  		return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs))
   417  	default:
   418  		return bindBasicTypeJava(kind)
   419  	}
   420  }
   421  
   422  // namedType is a set of functions that transform language specific types to
   423  // named versions that my be used inside method names.
   424  var namedType = map[Lang]func(string, abi.Type) string{
   425  	LangGo:   func(string, abi.Type) string { panic("this shouldn't be needed") },
   426  	LangJava: namedTypeJava,
   427  }
   428  
   429  // namedTypeJava converts some primitive data types to named variants that can
   430  // be used as parts of method names.
   431  func namedTypeJava(javaKind string, solKind abi.Type) string {
   432  	switch javaKind {
   433  	case "byte[]":
   434  		return "Binary"
   435  	case "boolean":
   436  		return "Bool"
   437  	default:
   438  		parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
   439  		if len(parts) != 4 {
   440  			return javaKind
   441  		}
   442  		switch parts[2] {
   443  		case "8", "16", "32", "64":
   444  			if parts[3] == "" {
   445  				return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
   446  			}
   447  			return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
   448  
   449  		default:
   450  			return javaKind
   451  		}
   452  	}
   453  }
   454  
   455  // methodNormalizer is a name transformer that modifies Solidity method names to
   456  // conform to target language naming concentions.
   457  var methodNormalizer = map[Lang]func(string) string{
   458  	LangGo:   abi.ToCamelCase,
   459  	LangJava: decapitalise,
   460  }
   461  
   462  // capitalise makes a camel-case string which starts with an upper case character.
   463  func capitalise(input string) string {
   464  	return abi.ToCamelCase(input)
   465  }
   466  
   467  // decapitalise makes a camel-case string which starts with a lower case character.
   468  func decapitalise(input string) string {
   469  	if len(input) == 0 {
   470  		return input
   471  	}
   472  
   473  	goForm := abi.ToCamelCase(input)
   474  	return strings.ToLower(goForm[:1]) + goForm[1:]
   475  }
   476  
   477  // structured checks whether a list of ABI data types has enough information to
   478  // operate through a proper Go struct or if flat returns are needed.
   479  func structured(args abi.Arguments) bool {
   480  	if len(args) < 2 {
   481  		return false
   482  	}
   483  	exists := make(map[string]bool)
   484  	for _, out := range args {
   485  		// If the name is anonymous, we can't organize into a struct
   486  		if out.Name == "" {
   487  			return false
   488  		}
   489  		// If the field name is empty when normalized or collides (var, Var, _var, _Var),
   490  		// we can't organize into a struct
   491  		field := capitalise(out.Name)
   492  		if field == "" || exists[field] {
   493  			return false
   494  		}
   495  		exists[field] = true
   496  	}
   497  	return true
   498  }
   499  
   500  // resolveArgName converts a raw argument representation into a user friendly format.
   501  func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string {
   502  	var (
   503  		prefix   string
   504  		embedded string
   505  		typ      = &arg.Type
   506  	)
   507  loop:
   508  	for {
   509  		switch typ.T {
   510  		case abi.SliceTy:
   511  			prefix += "[]"
   512  		case abi.ArrayTy:
   513  			prefix += fmt.Sprintf("[%d]", typ.Size)
   514  		default:
   515  			embedded = typ.String()
   516  			break loop
   517  		}
   518  		typ = typ.Elem
   519  	}
   520  	if s, exist := structs[embedded]; exist {
   521  		return prefix + s.Name
   522  	} else {
   523  		return arg.Type.String()
   524  	}
   525  }
   526  
   527  // formatMethod transforms raw method representation into a user friendly one.
   528  func formatMethod(method abi.Method, structs map[string]*tmplStruct) string {
   529  	inputs := make([]string, len(method.Inputs))
   530  	for i, input := range method.Inputs {
   531  		inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
   532  	}
   533  	outputs := make([]string, len(method.Outputs))
   534  	for i, output := range method.Outputs {
   535  		outputs[i] = resolveArgName(output, structs)
   536  		if len(output.Name) > 0 {
   537  			outputs[i] += fmt.Sprintf(" %v", output.Name)
   538  		}
   539  	}
   540  	constant := ""
   541  	if method.Const {
   542  		constant = "constant "
   543  	}
   544  	return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
   545  }
   546  
   547  // formatEvent transforms raw event representation into a user friendly one.
   548  func formatEvent(event abi.Event, structs map[string]*tmplStruct) string {
   549  	inputs := make([]string, len(event.Inputs))
   550  	for i, input := range event.Inputs {
   551  		if input.Indexed {
   552  			inputs[i] = fmt.Sprintf("%v indexed %v", resolveArgName(input, structs), input.Name)
   553  		} else {
   554  			inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
   555  		}
   556  	}
   557  	return fmt.Sprintf("event %v(%v)", event.Name, strings.Join(inputs, ", "))
   558  }