bind.go 20 KB

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