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