github.com/ava-labs/subnet-evm@v0.6.4/accounts/abi/bind/bind.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2016 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 // Package bind generates Ethereum contract Go bindings. 28 // 29 // Detailed usage document and tutorial available on the go-ethereum Wiki page: 30 // https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts 31 package bind 32 33 import ( 34 "bytes" 35 "fmt" 36 "go/format" 37 "regexp" 38 "strings" 39 "text/template" 40 "unicode" 41 42 "github.com/ava-labs/subnet-evm/accounts/abi" 43 "github.com/ethereum/go-ethereum/log" 44 ) 45 46 // BindHook is a callback function that can be used to customize the binding. 47 type BindHook func(lang Lang, pkg string, types []string, contracts map[string]*TmplContract, structs map[string]*TmplStruct) (data interface{}, templateSource string, err error) 48 49 // Lang is a target programming language selector to generate bindings for. 50 type Lang int 51 52 const ( 53 LangGo Lang = iota 54 ) 55 56 func IsKeyWord(arg string) bool { 57 switch arg { 58 case "break": 59 case "case": 60 case "chan": 61 case "const": 62 case "continue": 63 case "default": 64 case "defer": 65 case "else": 66 case "fallthrough": 67 case "for": 68 case "func": 69 case "go": 70 case "goto": 71 case "if": 72 case "import": 73 case "interface": 74 case "iota": 75 case "map": 76 case "make": 77 case "new": 78 case "package": 79 case "range": 80 case "return": 81 case "select": 82 case "struct": 83 case "switch": 84 case "type": 85 case "var": 86 default: 87 return false 88 } 89 90 return true 91 } 92 93 // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant 94 // to be used as is in client code, but rather as an intermediate struct which 95 // enforces compile time type safety and naming convention opposed to having to 96 // manually maintain hard coded strings that break on runtime. 97 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) { 98 return BindHelper(types, abis, bytecodes, fsigs, pkg, lang, libs, aliases, nil) 99 } 100 101 func BindHelper(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string, bindHook BindHook) (string, error) { 102 var ( 103 // contracts is the map of each individual contract requested binding 104 contracts = make(map[string]*TmplContract) 105 106 // structs is the map of all redeclared structs shared by passed contracts. 107 structs = make(map[string]*TmplStruct) 108 109 // isLib is the map used to flag each encountered library as such 110 isLib = make(map[string]struct{}) 111 ) 112 for i := 0; i < len(types); i++ { 113 // Parse the actual ABI to generate the binding for 114 evmABI, err := abi.JSON(strings.NewReader(abis[i])) 115 if err != nil { 116 return "", err 117 } 118 // Strip any whitespace from the JSON ABI 119 strippedABI := strings.Map(func(r rune) rune { 120 if unicode.IsSpace(r) { 121 return -1 122 } 123 return r 124 }, abis[i]) 125 126 // Extract the call and transact methods; events, struct definitions; and sort them alphabetically 127 var ( 128 calls = make(map[string]*TmplMethod) 129 transacts = make(map[string]*TmplMethod) 130 events = make(map[string]*tmplEvent) 131 fallback *TmplMethod 132 receive *TmplMethod 133 134 // identifiers are used to detect duplicated identifiers of functions 135 // and events. For all calls, transacts and events, abigen will generate 136 // corresponding bindings. However we have to ensure there is no 137 // identifier collisions in the bindings of these categories. 138 callIdentifiers = make(map[string]bool) 139 transactIdentifiers = make(map[string]bool) 140 eventIdentifiers = make(map[string]bool) 141 ) 142 143 for _, input := range evmABI.Constructor.Inputs { 144 if hasStruct(input.Type) { 145 bindStructType[lang](input.Type, structs) 146 } 147 } 148 149 for _, original := range evmABI.Methods { 150 // Normalize the method for capital cases and non-anonymous inputs/outputs 151 normalized := original 152 normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) 153 // Ensure there is no duplicated identifier 154 var identifiers = callIdentifiers 155 if !original.IsConstant() { 156 identifiers = transactIdentifiers 157 } 158 // Name shouldn't start with a digit. It will make the generated code invalid. 159 if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) { 160 normalizedName = fmt.Sprintf("M%s", normalizedName) 161 normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool { 162 _, ok := identifiers[name] 163 return ok 164 }) 165 } 166 if identifiers[normalizedName] { 167 return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) 168 } 169 identifiers[normalizedName] = true 170 171 normalized.Name = normalizedName 172 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 173 copy(normalized.Inputs, original.Inputs) 174 for j, input := range normalized.Inputs { 175 if input.Name == "" || IsKeyWord(input.Name) { 176 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 177 } 178 if hasStruct(input.Type) { 179 bindStructType[lang](input.Type, structs) 180 } 181 } 182 normalized.Outputs = make([]abi.Argument, len(original.Outputs)) 183 copy(normalized.Outputs, original.Outputs) 184 for j, output := range normalized.Outputs { 185 if output.Name != "" { 186 normalized.Outputs[j].Name = capitalise(output.Name) 187 } 188 if hasStruct(output.Type) { 189 bindStructType[lang](output.Type, structs) 190 } 191 } 192 // Append the methods to the call or transact lists 193 if original.IsConstant() { 194 calls[original.Name] = &TmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 195 } else { 196 transacts[original.Name] = &TmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 197 } 198 } 199 for _, original := range evmABI.Events { 200 // Skip anonymous events as they don't support explicit filtering 201 if original.Anonymous { 202 continue 203 } 204 // Normalize the event for capital cases and non-anonymous outputs 205 normalized := original 206 207 // Ensure there is no duplicated identifier 208 normalizedName := methodNormalizer[lang](alias(aliases, original.Name)) 209 // Name shouldn't start with a digit. It will make the generated code invalid. 210 if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) { 211 normalizedName = fmt.Sprintf("E%s", normalizedName) 212 normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool { 213 _, ok := eventIdentifiers[name] 214 return ok 215 }) 216 } 217 if eventIdentifiers[normalizedName] { 218 return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) 219 } 220 eventIdentifiers[normalizedName] = true 221 normalized.Name = normalizedName 222 223 used := make(map[string]bool) 224 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 225 copy(normalized.Inputs, original.Inputs) 226 for j, input := range normalized.Inputs { 227 if input.Name == "" || IsKeyWord(input.Name) { 228 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 229 } 230 // Event is a bit special, we need to define event struct in binding, 231 // ensure there is no camel-case-style name conflict. 232 for index := 0; ; index++ { 233 if !used[capitalise(normalized.Inputs[j].Name)] { 234 used[capitalise(normalized.Inputs[j].Name)] = true 235 break 236 } 237 normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index) 238 } 239 if hasStruct(input.Type) { 240 bindStructType[lang](input.Type, structs) 241 } 242 } 243 // Append the event to the accumulator list 244 events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} 245 } 246 // Add two special fallback functions if they exist 247 if evmABI.HasFallback() { 248 fallback = &TmplMethod{Original: evmABI.Fallback} 249 } 250 if evmABI.HasReceive() { 251 receive = &TmplMethod{Original: evmABI.Receive} 252 } 253 contracts[types[i]] = &TmplContract{ 254 Type: capitalise(types[i]), 255 InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""), 256 InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"), 257 Constructor: evmABI.Constructor, 258 Calls: calls, 259 Transacts: transacts, 260 Fallback: fallback, 261 Receive: receive, 262 Events: events, 263 Libraries: make(map[string]string), 264 } 265 // Function 4-byte signatures are stored in the same sequence 266 // as types, if available. 267 if len(fsigs) > i { 268 contracts[types[i]].FuncSigs = fsigs[i] 269 } 270 // Parse library references. 271 for pattern, name := range libs { 272 matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin)) 273 if err != nil { 274 log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err) 275 } 276 if matched { 277 contracts[types[i]].Libraries[pattern] = name 278 // keep track that this type is a library 279 if _, ok := isLib[name]; !ok { 280 isLib[name] = struct{}{} 281 } 282 } 283 } 284 } 285 // Check if that type has already been identified as a library 286 for i := 0; i < len(types); i++ { 287 _, ok := isLib[types[i]] 288 contracts[types[i]].Library = ok 289 } 290 291 var ( 292 data interface{} 293 templateSource string 294 ) 295 296 // Generate the contract template data according to hook 297 if bindHook != nil { 298 var err error 299 data, templateSource, err = bindHook(lang, pkg, types, contracts, structs) 300 if err != nil { 301 return "", err 302 } 303 } else { // default to generate contract binding 304 templateSource = tmplSource[lang] 305 data = &tmplData{ 306 Package: pkg, 307 Contracts: contracts, 308 Libraries: libs, 309 Structs: structs, 310 } 311 } 312 buffer := new(bytes.Buffer) 313 314 funcs := map[string]interface{}{ 315 "bindtype": bindType[lang], 316 "add": add, 317 "bindtypenew": bindTypeNew[lang], 318 "bindtopictype": bindTopicType[lang], 319 "namedtype": namedType[lang], 320 "capitalise": capitalise, 321 "decapitalise": decapitalise, 322 "mkList": mkList, 323 } 324 325 // render the template 326 tmpl := template.Must(template.New("").Funcs(funcs).Parse(templateSource)) 327 if err := tmpl.Execute(buffer, data); err != nil { 328 return "", err 329 } 330 // For Go bindings pass the code through gofmt to clean it up 331 if lang == LangGo { 332 code, err := format.Source(buffer.Bytes()) 333 if err != nil { 334 return "", fmt.Errorf("%v\n%s", err, buffer) 335 } 336 return string(code), nil 337 } 338 // For all others just return as is for now 339 return buffer.String(), nil 340 } 341 342 // bindType is a set of type binders that convert Solidity types to some supported 343 // programming language types. 344 var bindType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{ 345 LangGo: bindTypeGo, 346 } 347 348 var bindTypeNew = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{ 349 LangGo: bindTypeNewGo, 350 } 351 352 // bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones. 353 func bindBasicTypeGo(kind abi.Type) string { 354 switch kind.T { 355 case abi.AddressTy: 356 return "common.Address" 357 case abi.IntTy, abi.UintTy: 358 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) 359 switch parts[2] { 360 case "8", "16", "32", "64": 361 return fmt.Sprintf("%sint%s", parts[1], parts[2]) 362 } 363 return "*big.Int" 364 case abi.FixedBytesTy: 365 return fmt.Sprintf("[%d]byte", kind.Size) 366 case abi.BytesTy: 367 return "[]byte" 368 case abi.FunctionTy: 369 return "[24]byte" 370 default: 371 // string, bool types 372 return kind.String() 373 } 374 } 375 376 // bindTypeNewGo converts new types to Go ones. 377 func bindTypeNewGo(kind abi.Type, structs map[string]*TmplStruct) string { 378 switch kind.T { 379 case abi.TupleTy: 380 return structs[kind.TupleRawName+kind.String()].Name + "{}" 381 case abi.ArrayTy: 382 return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) + "{}" 383 case abi.SliceTy: 384 return "nil" 385 case abi.AddressTy: 386 return "common.Address{}" 387 case abi.IntTy, abi.UintTy: 388 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) 389 switch parts[2] { 390 case "8", "16", "32", "64": 391 return "0" 392 } 393 return "new(big.Int)" 394 case abi.FixedBytesTy: 395 return fmt.Sprintf("[%d]byte", kind.Size) + "{}" 396 case abi.BytesTy: 397 return "[]byte{}" 398 case abi.FunctionTy: 399 return "[24]byte{}" 400 case abi.BoolTy: 401 return "false" 402 case abi.StringTy: 403 return `""` 404 default: 405 return "nil" 406 } 407 } 408 409 // bindTypeGo converts solidity types to Go ones. Since there is no clear mapping 410 // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly 411 // mapped will use an upscaled type (e.g. BigDecimal). 412 func bindTypeGo(kind abi.Type, structs map[string]*TmplStruct) string { 413 switch kind.T { 414 case abi.TupleTy: 415 return structs[kind.TupleRawName+kind.String()].Name 416 case abi.ArrayTy: 417 return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) 418 case abi.SliceTy: 419 return "[]" + bindTypeGo(*kind.Elem, structs) 420 default: 421 return bindBasicTypeGo(kind) 422 } 423 } 424 425 // bindTopicType is a set of type binders that convert Solidity types to some 426 // supported programming language topic types. 427 var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{ 428 LangGo: bindTopicTypeGo, 429 } 430 431 // bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same 432 // functionality as for simple types, but dynamic types get converted to hashes. 433 func bindTopicTypeGo(kind abi.Type, structs map[string]*TmplStruct) string { 434 bound := bindTypeGo(kind, structs) 435 436 // todo(rjl493456442) according solidity documentation, indexed event 437 // parameters that are not value types i.e. arrays and structs are not 438 // stored directly but instead a keccak256-hash of an encoding is stored. 439 // 440 // We only convert stringS and bytes to hash, still need to deal with 441 // array(both fixed-size and dynamic-size) and struct. 442 if bound == "string" || bound == "[]byte" { 443 bound = "common.Hash" 444 } 445 return bound 446 } 447 448 // bindStructType is a set of type binders that convert Solidity tuple types to some supported 449 // programming language struct definition. 450 var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*TmplStruct) string{ 451 LangGo: bindStructTypeGo, 452 } 453 454 // bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping 455 // in the given map. 456 // Notably, this function will resolve and record nested struct recursively. 457 func bindStructTypeGo(kind abi.Type, structs map[string]*TmplStruct) string { 458 switch kind.T { 459 case abi.TupleTy: 460 // We compose a raw struct name and a canonical parameter expression 461 // together here. The reason is before solidity v0.5.11, kind.TupleRawName 462 // is empty, so we use canonical parameter expression to distinguish 463 // different struct definition. From the consideration of backward 464 // compatibility, we concat these two together so that if kind.TupleRawName 465 // is not empty, it can have unique id. 466 id := kind.TupleRawName + kind.String() 467 if s, exist := structs[id]; exist { 468 return s.Name 469 } 470 var ( 471 names = make(map[string]bool) 472 fields []*tmplField 473 ) 474 for i, elem := range kind.TupleElems { 475 name := capitalise(kind.TupleRawNames[i]) 476 name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] }) 477 names[name] = true 478 fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem}) 479 } 480 name := kind.TupleRawName 481 if name == "" { 482 name = fmt.Sprintf("Struct%d", len(structs)) 483 } 484 name = capitalise(name) 485 486 structs[id] = &TmplStruct{ 487 Name: name, 488 Fields: fields, 489 } 490 return name 491 case abi.ArrayTy: 492 return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs) 493 case abi.SliceTy: 494 return "[]" + bindStructTypeGo(*kind.Elem, structs) 495 default: 496 return bindBasicTypeGo(kind) 497 } 498 } 499 500 // namedType is a set of functions that transform language specific types to 501 // named versions that may be used inside method names. 502 var namedType = map[Lang]func(string, abi.Type) string{ 503 LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") }, 504 } 505 506 // alias returns an alias of the given string based on the aliasing rules 507 // or returns itself if no rule is matched. 508 func alias(aliases map[string]string, n string) string { 509 if alias, exist := aliases[n]; exist { 510 return alias 511 } 512 return n 513 } 514 515 // methodNormalizer is a name transformer that modifies Solidity method names to 516 // conform to target language naming conventions. 517 var methodNormalizer = map[Lang]func(string) string{ 518 LangGo: abi.ToCamelCase, 519 } 520 521 // capitalise makes a camel-case string which starts with an upper case character. 522 var capitalise = abi.ToCamelCase 523 524 // decapitalise makes a camel-case string which starts with a lower case character. 525 func decapitalise(input string) string { 526 if len(input) == 0 { 527 return input 528 } 529 530 goForm := abi.ToCamelCase(input) 531 return strings.ToLower(goForm[:1]) + goForm[1:] 532 } 533 534 // structured checks whether a list of ABI data types has enough information to 535 // operate through a proper Go struct or if flat returns are needed. 536 func structured(args abi.Arguments) bool { 537 if len(args) < 2 { 538 return false 539 } 540 exists := make(map[string]bool) 541 for _, out := range args { 542 // If the name is anonymous, we can't organize into a struct 543 if out.Name == "" { 544 return false 545 } 546 // If the field name is empty when normalized or collides (var, Var, _var, _Var), 547 // we can't organize into a struct 548 field := capitalise(out.Name) 549 if field == "" || exists[field] { 550 return false 551 } 552 exists[field] = true 553 } 554 return true 555 } 556 557 // hasStruct returns an indicator whether the given type is struct, struct slice 558 // or struct array. 559 func hasStruct(t abi.Type) bool { 560 switch t.T { 561 case abi.SliceTy: 562 return hasStruct(*t.Elem) 563 case abi.ArrayTy: 564 return hasStruct(*t.Elem) 565 case abi.TupleTy: 566 return true 567 default: 568 return false 569 } 570 } 571 572 func mkList(args ...interface{}) []interface{} { 573 return args 574 } 575 576 func add(a, b int) int { 577 return a + b 578 }