github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/chain/accounts/abi/bind/bind.go (about)

     1  package bind
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"go/format"
     8  	"regexp"
     9  	"strings"
    10  	"text/template"
    11  	"unicode"
    12  
    13  	"github.com/neatio-net/neatio/chain/accounts/abi"
    14  	"github.com/neatio-net/neatio/chain/log"
    15  )
    16  
    17  type Lang int
    18  
    19  const (
    20  	LangGo Lang = iota
    21  	LangJava
    22  	LangObjC
    23  )
    24  
    25  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) {
    26  	var (
    27  		contracts = make(map[string]*tmplContract)
    28  
    29  		structs = make(map[string]*tmplStruct)
    30  
    31  		isLib = make(map[string]struct{})
    32  	)
    33  	for i := 0; i < len(types); i++ {
    34  
    35  		evmABI, err := abi.JSON(strings.NewReader(abis[i]))
    36  		if err != nil {
    37  			return "", err
    38  		}
    39  
    40  		strippedABI := strings.Map(func(r rune) rune {
    41  			if unicode.IsSpace(r) {
    42  				return -1
    43  			}
    44  			return r
    45  		}, abis[i])
    46  
    47  		var (
    48  			calls     = make(map[string]*tmplMethod)
    49  			transacts = make(map[string]*tmplMethod)
    50  			events    = make(map[string]*tmplEvent)
    51  
    52  			callIdentifiers     = make(map[string]bool)
    53  			transactIdentifiers = make(map[string]bool)
    54  			eventIdentifiers    = make(map[string]bool)
    55  		)
    56  		for _, original := range evmABI.Methods {
    57  
    58  			normalized := original
    59  			normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
    60  
    61  			var identifiers = callIdentifiers
    62  			if !original.Const {
    63  				identifiers = transactIdentifiers
    64  			}
    65  			if identifiers[normalizedName] {
    66  				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
    67  			}
    68  			identifiers[normalizedName] = true
    69  			normalized.Name = normalizedName
    70  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
    71  			copy(normalized.Inputs, original.Inputs)
    72  			for j, input := range normalized.Inputs {
    73  				if input.Name == "" {
    74  					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
    75  				}
    76  				if hasStruct(input.Type) {
    77  					bindStructType[lang](input.Type, structs)
    78  				}
    79  			}
    80  			normalized.Outputs = make([]abi.Argument, len(original.Outputs))
    81  			copy(normalized.Outputs, original.Outputs)
    82  			for j, output := range normalized.Outputs {
    83  				if output.Name != "" {
    84  					normalized.Outputs[j].Name = capitalise(output.Name)
    85  				}
    86  				if hasStruct(output.Type) {
    87  					bindStructType[lang](output.Type, structs)
    88  				}
    89  			}
    90  
    91  			if original.Const {
    92  				calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
    93  			} else {
    94  				transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
    95  			}
    96  		}
    97  		for _, original := range evmABI.Events {
    98  
    99  			if original.Anonymous {
   100  				continue
   101  			}
   102  
   103  			normalized := original
   104  
   105  			normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
   106  			if eventIdentifiers[normalizedName] {
   107  				return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
   108  			}
   109  			eventIdentifiers[normalizedName] = true
   110  			normalized.Name = normalizedName
   111  
   112  			normalized.Inputs = make([]abi.Argument, len(original.Inputs))
   113  			copy(normalized.Inputs, original.Inputs)
   114  			for j, input := range normalized.Inputs {
   115  				if input.Name == "" {
   116  					normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
   117  				}
   118  				if hasStruct(input.Type) {
   119  					bindStructType[lang](input.Type, structs)
   120  				}
   121  			}
   122  
   123  			events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
   124  		}
   125  
   126  		if len(structs) > 0 && lang == LangJava {
   127  			return "", errors.New("java binding for tuple arguments is not supported yet")
   128  		}
   129  
   130  		contracts[types[i]] = &tmplContract{
   131  			Type:        capitalise(types[i]),
   132  			InputABI:    strings.Replace(strippedABI, "\"", "\\\"", -1),
   133  			InputBin:    strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
   134  			Constructor: evmABI.Constructor,
   135  			Calls:       calls,
   136  			Transacts:   transacts,
   137  			Events:      events,
   138  			Libraries:   make(map[string]string),
   139  		}
   140  
   141  		if len(fsigs) > i {
   142  			contracts[types[i]].FuncSigs = fsigs[i]
   143  		}
   144  
   145  		for pattern, name := range libs {
   146  			matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
   147  			if err != nil {
   148  				log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
   149  			}
   150  			if matched {
   151  				contracts[types[i]].Libraries[pattern] = name
   152  
   153  				if _, ok := isLib[name]; !ok {
   154  					isLib[name] = struct{}{}
   155  				}
   156  			}
   157  		}
   158  	}
   159  
   160  	for i := 0; i < len(types); i++ {
   161  		_, ok := isLib[types[i]]
   162  		contracts[types[i]].Library = ok
   163  	}
   164  
   165  	data := &tmplData{
   166  		Package:   pkg,
   167  		Contracts: contracts,
   168  		Libraries: libs,
   169  		Structs:   structs,
   170  	}
   171  	buffer := new(bytes.Buffer)
   172  
   173  	funcs := map[string]interface{}{
   174  		"bindtype":      bindType[lang],
   175  		"bindtopictype": bindTopicType[lang],
   176  		"namedtype":     namedType[lang],
   177  		"formatmethod":  formatMethod,
   178  		"formatevent":   formatEvent,
   179  		"capitalise":    capitalise,
   180  		"decapitalise":  decapitalise,
   181  	}
   182  	tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
   183  	if err := tmpl.Execute(buffer, data); err != nil {
   184  		return "", err
   185  	}
   186  
   187  	if lang == LangGo {
   188  		code, err := format.Source(buffer.Bytes())
   189  		if err != nil {
   190  			return "", fmt.Errorf("%v\n%s", err, buffer)
   191  		}
   192  		return string(code), nil
   193  	}
   194  
   195  	return buffer.String(), nil
   196  }
   197  
   198  var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   199  	LangGo:   bindTypeGo,
   200  	LangJava: bindTypeJava,
   201  }
   202  
   203  func bindBasicTypeGo(kind abi.Type) string {
   204  	switch kind.T {
   205  	case abi.AddressTy:
   206  		return "common.Address"
   207  	case abi.IntTy, abi.UintTy:
   208  		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
   209  		switch parts[2] {
   210  		case "8", "16", "32", "64":
   211  			return fmt.Sprintf("%sint%s", parts[1], parts[2])
   212  		}
   213  		return "*big.Int"
   214  	case abi.FixedBytesTy:
   215  		return fmt.Sprintf("[%d]byte", kind.Size)
   216  	case abi.BytesTy:
   217  		return "[]byte"
   218  	case abi.FunctionTy:
   219  		return "[24]byte"
   220  	default:
   221  
   222  		return kind.String()
   223  	}
   224  }
   225  
   226  func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   227  	switch kind.T {
   228  	case abi.TupleTy:
   229  		return structs[kind.TupleRawName+kind.String()].Name
   230  	case abi.ArrayTy:
   231  		return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
   232  	case abi.SliceTy:
   233  		return "[]" + bindTypeGo(*kind.Elem, structs)
   234  	default:
   235  		return bindBasicTypeGo(kind)
   236  	}
   237  }
   238  
   239  func bindBasicTypeJava(kind abi.Type) string {
   240  	switch kind.T {
   241  	case abi.AddressTy:
   242  		return "Address"
   243  	case abi.IntTy, abi.UintTy:
   244  
   245  		parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
   246  		if len(parts) != 3 {
   247  			return kind.String()
   248  		}
   249  
   250  		if parts[1] == "u" {
   251  			return "BigInt"
   252  		}
   253  
   254  		namedSize := map[string]string{
   255  			"8":  "byte",
   256  			"16": "short",
   257  			"32": "int",
   258  			"64": "long",
   259  		}[parts[2]]
   260  
   261  		if namedSize == "" {
   262  			namedSize = "BigInt"
   263  		}
   264  		return namedSize
   265  	case abi.FixedBytesTy, abi.BytesTy:
   266  		return "byte[]"
   267  	case abi.BoolTy:
   268  		return "boolean"
   269  	case abi.StringTy:
   270  		return "String"
   271  	case abi.FunctionTy:
   272  		return "byte[24]"
   273  	default:
   274  		return kind.String()
   275  	}
   276  }
   277  
   278  func pluralizeJavaType(typ string) string {
   279  	switch typ {
   280  	case "boolean":
   281  		return "Bools"
   282  	case "String":
   283  		return "Strings"
   284  	case "Address":
   285  		return "Addresses"
   286  	case "byte[]":
   287  		return "Binaries"
   288  	case "BigInt":
   289  		return "BigInts"
   290  	}
   291  	return typ + "[]"
   292  }
   293  
   294  func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
   295  	switch kind.T {
   296  	case abi.TupleTy:
   297  		return structs[kind.TupleRawName+kind.String()].Name
   298  	case abi.ArrayTy, abi.SliceTy:
   299  		return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
   300  	default:
   301  		return bindBasicTypeJava(kind)
   302  	}
   303  }
   304  
   305  var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   306  	LangGo:   bindTopicTypeGo,
   307  	LangJava: bindTopicTypeJava,
   308  }
   309  
   310  func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   311  	bound := bindTypeGo(kind, structs)
   312  
   313  	if bound == "string" || bound == "[]byte" {
   314  		bound = "common.Hash"
   315  	}
   316  	return bound
   317  }
   318  
   319  func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
   320  	bound := bindTypeJava(kind, structs)
   321  
   322  	if bound == "String" || bound == "byte[]" {
   323  		bound = "Hash"
   324  	}
   325  	return bound
   326  }
   327  
   328  var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
   329  	LangGo:   bindStructTypeGo,
   330  	LangJava: bindStructTypeJava,
   331  }
   332  
   333  func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
   334  	switch kind.T {
   335  	case abi.TupleTy:
   336  
   337  		id := kind.TupleRawName + kind.String()
   338  		if s, exist := structs[id]; exist {
   339  			return s.Name
   340  		}
   341  		var fields []*tmplField
   342  		for i, elem := range kind.TupleElems {
   343  			field := bindStructTypeGo(*elem, structs)
   344  			fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
   345  		}
   346  		name := kind.TupleRawName
   347  		if name == "" {
   348  			name = fmt.Sprintf("Struct%d", len(structs))
   349  		}
   350  		structs[id] = &tmplStruct{
   351  			Name:   name,
   352  			Fields: fields,
   353  		}
   354  		return name
   355  	case abi.ArrayTy:
   356  		return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
   357  	case abi.SliceTy:
   358  		return "[]" + bindStructTypeGo(*kind.Elem, structs)
   359  	default:
   360  		return bindBasicTypeGo(kind)
   361  	}
   362  }
   363  
   364  func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
   365  	switch kind.T {
   366  	case abi.TupleTy:
   367  
   368  		id := kind.TupleRawName + kind.String()
   369  		if s, exist := structs[id]; exist {
   370  			return s.Name
   371  		}
   372  		var fields []*tmplField
   373  		for i, elem := range kind.TupleElems {
   374  			field := bindStructTypeJava(*elem, structs)
   375  			fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
   376  		}
   377  		name := kind.TupleRawName
   378  		if name == "" {
   379  			name = fmt.Sprintf("Class%d", len(structs))
   380  		}
   381  		structs[id] = &tmplStruct{
   382  			Name:   name,
   383  			Fields: fields,
   384  		}
   385  		return name
   386  	case abi.ArrayTy, abi.SliceTy:
   387  		return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs))
   388  	default:
   389  		return bindBasicTypeJava(kind)
   390  	}
   391  }
   392  
   393  var namedType = map[Lang]func(string, abi.Type) string{
   394  	LangGo:   func(string, abi.Type) string { panic("this shouldn't be needed") },
   395  	LangJava: namedTypeJava,
   396  }
   397  
   398  func namedTypeJava(javaKind string, solKind abi.Type) string {
   399  	switch javaKind {
   400  	case "byte[]":
   401  		return "Binary"
   402  	case "boolean":
   403  		return "Bool"
   404  	default:
   405  		parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
   406  		if len(parts) != 4 {
   407  			return javaKind
   408  		}
   409  		switch parts[2] {
   410  		case "8", "16", "32", "64":
   411  			if parts[3] == "" {
   412  				return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
   413  			}
   414  			return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
   415  
   416  		default:
   417  			return javaKind
   418  		}
   419  	}
   420  }
   421  
   422  func alias(aliases map[string]string, n string) string {
   423  	if alias, exist := aliases[n]; exist {
   424  		return alias
   425  	}
   426  	return n
   427  }
   428  
   429  var methodNormalizer = map[Lang]func(string) string{
   430  	LangGo:   abi.ToCamelCase,
   431  	LangJava: decapitalise,
   432  }
   433  
   434  func capitalise(input string) string {
   435  	return abi.ToCamelCase(input)
   436  }
   437  
   438  func decapitalise(input string) string {
   439  	if len(input) == 0 {
   440  		return input
   441  	}
   442  
   443  	goForm := abi.ToCamelCase(input)
   444  	return strings.ToLower(goForm[:1]) + goForm[1:]
   445  }
   446  
   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  
   454  		if out.Name == "" {
   455  			return false
   456  		}
   457  
   458  		field := capitalise(out.Name)
   459  		if field == "" || exists[field] {
   460  			return false
   461  		}
   462  		exists[field] = true
   463  	}
   464  	return true
   465  }
   466  
   467  func hasStruct(t abi.Type) bool {
   468  	switch t.T {
   469  	case abi.SliceTy:
   470  		return hasStruct(*t.Elem)
   471  	case abi.ArrayTy:
   472  		return hasStruct(*t.Elem)
   473  	case abi.TupleTy:
   474  		return true
   475  	default:
   476  		return false
   477  	}
   478  }
   479  
   480  func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string {
   481  	var (
   482  		prefix   string
   483  		embedded string
   484  		typ      = &arg.Type
   485  	)
   486  loop:
   487  	for {
   488  		switch typ.T {
   489  		case abi.SliceTy:
   490  			prefix += "[]"
   491  		case abi.ArrayTy:
   492  			prefix += fmt.Sprintf("[%d]", typ.Size)
   493  		default:
   494  			embedded = typ.TupleRawName + typ.String()
   495  			break loop
   496  		}
   497  		typ = typ.Elem
   498  	}
   499  	if s, exist := structs[embedded]; exist {
   500  		return prefix + s.Name
   501  	} else {
   502  		return arg.Type.String()
   503  	}
   504  }
   505  
   506  func formatMethod(method abi.Method, structs map[string]*tmplStruct) string {
   507  	inputs := make([]string, len(method.Inputs))
   508  	for i, input := range method.Inputs {
   509  		inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
   510  	}
   511  	outputs := make([]string, len(method.Outputs))
   512  	for i, output := range method.Outputs {
   513  		outputs[i] = resolveArgName(output, structs)
   514  		if len(output.Name) > 0 {
   515  			outputs[i] += fmt.Sprintf(" %v", output.Name)
   516  		}
   517  	}
   518  	constant := ""
   519  	if method.Const {
   520  		constant = "constant "
   521  	}
   522  	return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.RawName, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
   523  }
   524  
   525  func formatEvent(event abi.Event, structs map[string]*tmplStruct) string {
   526  	inputs := make([]string, len(event.Inputs))
   527  	for i, input := range event.Inputs {
   528  		if input.Indexed {
   529  			inputs[i] = fmt.Sprintf("%v indexed %v", resolveArgName(input, structs), input.Name)
   530  		} else {
   531  			inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
   532  		}
   533  	}
   534  	return fmt.Sprintf("event %v(%v)", event.RawName, strings.Join(inputs, ", "))
   535  }