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