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