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 }