github.com/ethereum/go-ethereum@v1.16.1/accounts/abi/abigen/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 abigen generates Ethereum contract Go bindings. 18 // 19 // Detailed usage document and tutorial available on the go-ethereum Wiki page: 20 // https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings 21 package abigen 22 23 import ( 24 "bytes" 25 "fmt" 26 "go/format" 27 "regexp" 28 "strings" 29 "text/template" 30 "unicode" 31 32 "github.com/ethereum/go-ethereum/accounts/abi" 33 "github.com/ethereum/go-ethereum/log" 34 ) 35 36 func isKeyWord(arg string) bool { 37 switch arg { 38 case "break": 39 case "case": 40 case "chan": 41 case "const": 42 case "continue": 43 case "default": 44 case "defer": 45 case "else": 46 case "fallthrough": 47 case "for": 48 case "func": 49 case "go": 50 case "goto": 51 case "if": 52 case "import": 53 case "interface": 54 case "iota": 55 case "map": 56 case "make": 57 case "new": 58 case "package": 59 case "range": 60 case "return": 61 case "select": 62 case "struct": 63 case "switch": 64 case "type": 65 case "var": 66 default: 67 return false 68 } 69 70 return true 71 } 72 73 // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant 74 // to be used as is in client code, but rather as an intermediate struct which 75 // enforces compile time type safety and naming convention as opposed to having to 76 // manually maintain hard coded strings that break on runtime. 77 func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, libs map[string]string, aliases map[string]string) (string, error) { 78 var ( 79 // contracts is the map of each individual contract requested binding 80 contracts = make(map[string]*tmplContract) 81 82 // structs is the map of all redeclared structs shared by passed contracts. 83 structs = make(map[string]*tmplStruct) 84 85 // isLib is the map used to flag each encountered library as such 86 isLib = make(map[string]struct{}) 87 ) 88 for i := 0; i < len(types); i++ { 89 // Parse the actual ABI to generate the binding for 90 evmABI, err := abi.JSON(strings.NewReader(abis[i])) 91 if err != nil { 92 return "", err 93 } 94 // Strip any whitespace from the JSON ABI 95 strippedABI := strings.Map(func(r rune) rune { 96 if unicode.IsSpace(r) { 97 return -1 98 } 99 return r 100 }, abis[i]) 101 102 // Extract the call and transact methods; events, struct definitions; and sort them alphabetically 103 var ( 104 calls = make(map[string]*tmplMethod) 105 transacts = make(map[string]*tmplMethod) 106 events = make(map[string]*tmplEvent) 107 fallback *tmplMethod 108 receive *tmplMethod 109 110 // identifiers are used to detect duplicated identifiers of functions 111 // and events. For all calls, transacts and events, abigen will generate 112 // corresponding bindings. However we have to ensure there is no 113 // identifier collisions in the bindings of these categories. 114 callIdentifiers = make(map[string]bool) 115 transactIdentifiers = make(map[string]bool) 116 eventIdentifiers = make(map[string]bool) 117 ) 118 119 for _, input := range evmABI.Constructor.Inputs { 120 if hasStruct(input.Type) { 121 bindStructType(input.Type, structs) 122 } 123 } 124 125 for _, original := range evmABI.Methods { 126 // Normalize the method for capital cases and non-anonymous inputs/outputs 127 normalized := original 128 normalizedName := abi.ToCamelCase(alias(aliases, original.Name)) 129 // Ensure there is no duplicated identifier 130 var identifiers = callIdentifiers 131 if !original.IsConstant() { 132 identifiers = transactIdentifiers 133 } 134 // Name shouldn't start with a digit. It will make the generated code invalid. 135 if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) { 136 normalizedName = fmt.Sprintf("M%s", normalizedName) 137 normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool { 138 _, ok := identifiers[name] 139 return ok 140 }) 141 } 142 if identifiers[normalizedName] { 143 return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) 144 } 145 identifiers[normalizedName] = true 146 147 normalized.Name = normalizedName 148 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 149 copy(normalized.Inputs, original.Inputs) 150 for j, input := range normalized.Inputs { 151 if input.Name == "" || isKeyWord(input.Name) { 152 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 153 } 154 if hasStruct(input.Type) { 155 bindStructType(input.Type, structs) 156 } 157 } 158 normalized.Outputs = make([]abi.Argument, len(original.Outputs)) 159 copy(normalized.Outputs, original.Outputs) 160 for j, output := range normalized.Outputs { 161 if output.Name != "" { 162 normalized.Outputs[j].Name = abi.ToCamelCase(output.Name) 163 } 164 if hasStruct(output.Type) { 165 bindStructType(output.Type, structs) 166 } 167 } 168 // Append the methods to the call or transact lists 169 if original.IsConstant() { 170 calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 171 } else { 172 transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)} 173 } 174 } 175 for _, original := range evmABI.Events { 176 // Skip anonymous events as they don't support explicit filtering 177 if original.Anonymous { 178 continue 179 } 180 // Normalize the event for capital cases and non-anonymous outputs 181 normalized := original 182 183 // Ensure there is no duplicated identifier 184 normalizedName := abi.ToCamelCase(alias(aliases, original.Name)) 185 // Name shouldn't start with a digit. It will make the generated code invalid. 186 if len(normalizedName) > 0 && unicode.IsDigit(rune(normalizedName[0])) { 187 normalizedName = fmt.Sprintf("E%s", normalizedName) 188 normalizedName = abi.ResolveNameConflict(normalizedName, func(name string) bool { 189 _, ok := eventIdentifiers[name] 190 return ok 191 }) 192 } 193 if eventIdentifiers[normalizedName] { 194 return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName) 195 } 196 eventIdentifiers[normalizedName] = true 197 normalized.Name = normalizedName 198 199 used := make(map[string]bool) 200 normalized.Inputs = make([]abi.Argument, len(original.Inputs)) 201 copy(normalized.Inputs, original.Inputs) 202 for j, input := range normalized.Inputs { 203 if input.Name == "" || isKeyWord(input.Name) { 204 normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) 205 } 206 // Event is a bit special, we need to define event struct in binding, 207 // ensure there is no camel-case-style name conflict. 208 for index := 0; ; index++ { 209 if !used[abi.ToCamelCase(normalized.Inputs[j].Name)] { 210 used[abi.ToCamelCase(normalized.Inputs[j].Name)] = true 211 break 212 } 213 normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index) 214 } 215 if hasStruct(input.Type) { 216 bindStructType(input.Type, structs) 217 } 218 } 219 // Append the event to the accumulator list 220 events[original.Name] = &tmplEvent{Original: original, Normalized: normalized} 221 } 222 // Add two special fallback functions if they exist 223 if evmABI.HasFallback() { 224 fallback = &tmplMethod{Original: evmABI.Fallback} 225 } 226 if evmABI.HasReceive() { 227 receive = &tmplMethod{Original: evmABI.Receive} 228 } 229 230 contracts[types[i]] = &tmplContract{ 231 Type: abi.ToCamelCase(types[i]), 232 InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""), 233 InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"), 234 Constructor: evmABI.Constructor, 235 Calls: calls, 236 Transacts: transacts, 237 Fallback: fallback, 238 Receive: receive, 239 Events: events, 240 Libraries: make(map[string]string), 241 } 242 243 // Function 4-byte signatures are stored in the same sequence 244 // as types, if available. 245 if len(fsigs) > i { 246 contracts[types[i]].FuncSigs = fsigs[i] 247 } 248 // Parse library references. 249 for pattern, name := range libs { 250 matched, err := regexp.MatchString("__\\$"+pattern+"\\$__", contracts[types[i]].InputBin) 251 if err != nil { 252 log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err) 253 } 254 if matched { 255 contracts[types[i]].Libraries[pattern] = name 256 // keep track that this type is a library 257 if _, ok := isLib[name]; !ok { 258 isLib[name] = struct{}{} 259 } 260 } 261 } 262 } 263 // Check if that type has already been identified as a library 264 for i := 0; i < len(types); i++ { 265 _, ok := isLib[types[i]] 266 contracts[types[i]].Library = ok 267 } 268 269 // Generate the contract template data content and render it 270 data := &tmplData{ 271 Package: pkg, 272 Contracts: contracts, 273 Libraries: libs, 274 Structs: structs, 275 } 276 buffer := new(bytes.Buffer) 277 278 funcs := map[string]interface{}{ 279 "bindtype": bindType, 280 "bindtopictype": bindTopicType, 281 "capitalise": abi.ToCamelCase, 282 "decapitalise": decapitalise, 283 } 284 tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource)) 285 if err := tmpl.Execute(buffer, data); err != nil { 286 return "", err 287 } 288 // Pass the code through gofmt to clean it up 289 code, err := format.Source(buffer.Bytes()) 290 if err != nil { 291 return "", fmt.Errorf("%v\n%s", err, buffer) 292 } 293 return string(code), nil 294 } 295 296 // bindBasicType converts basic solidity types(except array, slice and tuple) to Go ones. 297 func bindBasicType(kind abi.Type) string { 298 switch kind.T { 299 case abi.AddressTy: 300 return "common.Address" 301 case abi.IntTy, abi.UintTy: 302 parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String()) 303 switch parts[2] { 304 case "8", "16", "32", "64": 305 return fmt.Sprintf("%sint%s", parts[1], parts[2]) 306 } 307 return "*big.Int" 308 case abi.FixedBytesTy: 309 return fmt.Sprintf("[%d]byte", kind.Size) 310 case abi.BytesTy: 311 return "[]byte" 312 case abi.FunctionTy: 313 return "[24]byte" 314 default: 315 // string, bool types 316 return kind.String() 317 } 318 } 319 320 // bindType converts solidity types to Go ones. Since there is no clear mapping 321 // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly 322 // mapped will use an upscaled type (e.g. BigDecimal). 323 func bindType(kind abi.Type, structs map[string]*tmplStruct) string { 324 switch kind.T { 325 case abi.TupleTy: 326 return structs[kind.TupleRawName+kind.String()].Name 327 case abi.ArrayTy: 328 return fmt.Sprintf("[%d]", kind.Size) + bindType(*kind.Elem, structs) 329 case abi.SliceTy: 330 return "[]" + bindType(*kind.Elem, structs) 331 default: 332 return bindBasicType(kind) 333 } 334 } 335 336 // bindTopicType converts a Solidity topic type to a Go one. It is almost the same 337 // functionality as for simple types, but dynamic types get converted to hashes. 338 func bindTopicType(kind abi.Type, structs map[string]*tmplStruct) string { 339 bound := bindType(kind, structs) 340 341 // todo(rjl493456442) according solidity documentation, indexed event 342 // parameters that are not value types i.e. arrays and structs are not 343 // stored directly but instead a keccak256-hash of an encoding is stored. 344 // 345 // We only convert strings and bytes to hash, still need to deal with 346 // array(both fixed-size and dynamic-size) and struct. 347 if bound == "string" || bound == "[]byte" { 348 bound = "common.Hash" 349 } 350 return bound 351 } 352 353 // bindStructType converts a Solidity tuple type to a Go one and records the mapping 354 // in the given map. Notably, this function will resolve and record nested struct 355 // recursively. 356 func bindStructType(kind abi.Type, structs map[string]*tmplStruct) string { 357 switch kind.T { 358 case abi.TupleTy: 359 // We compose a raw struct name and a canonical parameter expression 360 // together here. The reason is before solidity v0.5.11, kind.TupleRawName 361 // is empty, so we use canonical parameter expression to distinguish 362 // different struct definition. From the consideration of backward 363 // compatibility, we concat these two together so that if kind.TupleRawName 364 // is not empty, it can have unique id. 365 id := kind.TupleRawName + kind.String() 366 if s, exist := structs[id]; exist { 367 return s.Name 368 } 369 var ( 370 names = make(map[string]bool) 371 fields []*tmplField 372 ) 373 for i, elem := range kind.TupleElems { 374 name := abi.ToCamelCase(kind.TupleRawNames[i]) 375 name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] }) 376 names[name] = true 377 fields = append(fields, &tmplField{ 378 Type: bindStructType(*elem, structs), 379 Name: name, 380 SolKind: *elem, 381 }) 382 } 383 name := kind.TupleRawName 384 if name == "" { 385 name = fmt.Sprintf("Struct%d", len(structs)) 386 } 387 name = abi.ToCamelCase(name) 388 389 structs[id] = &tmplStruct{ 390 Name: name, 391 Fields: fields, 392 } 393 return name 394 case abi.ArrayTy: 395 return fmt.Sprintf("[%d]", kind.Size) + bindStructType(*kind.Elem, structs) 396 case abi.SliceTy: 397 return "[]" + bindStructType(*kind.Elem, structs) 398 default: 399 return bindBasicType(kind) 400 } 401 } 402 403 // alias returns an alias of the given string based on the aliasing rules 404 // or returns itself if no rule is matched. 405 func alias(aliases map[string]string, n string) string { 406 if alias, exist := aliases[n]; exist { 407 return alias 408 } 409 return n 410 } 411 412 // decapitalise makes a camel-case string which starts with a lower case character. 413 func decapitalise(input string) string { 414 if len(input) == 0 { 415 return input 416 } 417 goForm := abi.ToCamelCase(input) 418 return strings.ToLower(goForm[:1]) + goForm[1:] 419 } 420 421 // structured checks whether a list of ABI data types has enough information to 422 // operate through a proper Go struct or if flat returns are needed. 423 func structured(args abi.Arguments) bool { 424 if len(args) < 2 { 425 return false 426 } 427 exists := make(map[string]bool) 428 for _, out := range args { 429 // If the name is anonymous, we can't organize into a struct 430 if out.Name == "" { 431 return false 432 } 433 // If the field name is empty when normalized or collides (var, Var, _var, _Var), 434 // we can't organize into a struct 435 field := abi.ToCamelCase(out.Name) 436 if field == "" || exists[field] { 437 return false 438 } 439 exists[field] = true 440 } 441 return true 442 } 443 444 // hasStruct returns an indicator whether the given type is struct, struct slice 445 // or struct array. 446 func hasStruct(t abi.Type) bool { 447 switch t.T { 448 case abi.SliceTy: 449 return hasStruct(*t.Elem) 450 case abi.ArrayTy: 451 return hasStruct(*t.Elem) 452 case abi.TupleTy: 453 return true 454 default: 455 return false 456 } 457 }