bind.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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. "fmt"
  24. "go/format"
  25. "regexp"
  26. "strings"
  27. "text/template"
  28. "unicode"
  29. "github.com/ethereum/go-ethereum/accounts/abi"
  30. )
  31. // Lang is a target programming language selector to generate bindings for.
  32. type Lang int
  33. const (
  34. LangGo Lang = iota
  35. LangJava
  36. )
  37. // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
  38. // to be used as is in client code, but rather as an intermediate struct which
  39. // enforces compile time type safety and naming convention opposed to having to
  40. // manually maintain hard coded strings that break on runtime.
  41. func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) {
  42. // Process each individual contract requested binding
  43. contracts := make(map[string]*tmplContract)
  44. for i := 0; i < len(types); i++ {
  45. // Parse the actual ABI to generate the binding for
  46. evmABI, err := abi.JSON(strings.NewReader(abis[i]))
  47. if err != nil {
  48. return "", err
  49. }
  50. // Strip any whitespace from the JSON ABI
  51. strippedABI := strings.Map(func(r rune) rune {
  52. if unicode.IsSpace(r) {
  53. return -1
  54. }
  55. return r
  56. }, abis[i])
  57. // Extract the call and transact methods; events; and sort them alphabetically
  58. var (
  59. calls = make(map[string]*tmplMethod)
  60. transacts = make(map[string]*tmplMethod)
  61. events = make(map[string]*tmplEvent)
  62. )
  63. for _, original := range evmABI.Methods {
  64. // Normalize the method for capital cases and non-anonymous inputs/outputs
  65. normalized := original
  66. normalized.Name = methodNormalizer[lang](original.Name)
  67. normalized.Inputs = make([]abi.Argument, len(original.Inputs))
  68. copy(normalized.Inputs, original.Inputs)
  69. for j, input := range normalized.Inputs {
  70. if input.Name == "" {
  71. normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
  72. }
  73. }
  74. normalized.Outputs = make([]abi.Argument, len(original.Outputs))
  75. copy(normalized.Outputs, original.Outputs)
  76. for j, output := range normalized.Outputs {
  77. if output.Name != "" {
  78. normalized.Outputs[j].Name = capitalise(output.Name)
  79. }
  80. }
  81. // Append the methods to the call or transact lists
  82. if original.Const {
  83. calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
  84. } else {
  85. transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
  86. }
  87. }
  88. for _, original := range evmABI.Events {
  89. // Skip anonymous events as they don't support explicit filtering
  90. if original.Anonymous {
  91. continue
  92. }
  93. // Normalize the event for capital cases and non-anonymous outputs
  94. normalized := original
  95. normalized.Name = methodNormalizer[lang](original.Name)
  96. normalized.Inputs = make([]abi.Argument, len(original.Inputs))
  97. copy(normalized.Inputs, original.Inputs)
  98. for j, input := range normalized.Inputs {
  99. // Indexed fields are input, non-indexed ones are outputs
  100. if input.Indexed {
  101. if input.Name == "" {
  102. normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
  103. }
  104. }
  105. }
  106. // Append the event to the accumulator list
  107. events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
  108. }
  109. contracts[types[i]] = &tmplContract{
  110. Type: capitalise(types[i]),
  111. InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
  112. InputBin: strings.TrimSpace(bytecodes[i]),
  113. Constructor: evmABI.Constructor,
  114. Calls: calls,
  115. Transacts: transacts,
  116. Events: events,
  117. }
  118. }
  119. // Generate the contract template data content and render it
  120. data := &tmplData{
  121. Package: pkg,
  122. Contracts: contracts,
  123. }
  124. buffer := new(bytes.Buffer)
  125. funcs := map[string]interface{}{
  126. "bindtype": bindType[lang],
  127. "bindtopictype": bindTopicType[lang],
  128. "namedtype": namedType[lang],
  129. "capitalise": capitalise,
  130. "decapitalise": decapitalise,
  131. }
  132. tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
  133. if err := tmpl.Execute(buffer, data); err != nil {
  134. return "", err
  135. }
  136. // For Go bindings pass the code through gofmt to clean it up
  137. if lang == LangGo {
  138. code, err := format.Source(buffer.Bytes())
  139. if err != nil {
  140. return "", fmt.Errorf("%v\n%s", err, buffer)
  141. }
  142. return string(code), nil
  143. }
  144. // For all others just return as is for now
  145. return buffer.String(), nil
  146. }
  147. // bindType is a set of type binders that convert Solidity types to some supported
  148. // programming language types.
  149. var bindType = map[Lang]func(kind abi.Type) string{
  150. LangGo: bindTypeGo,
  151. LangJava: bindTypeJava,
  152. }
  153. // bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go one.
  154. func bindBasicTypeGo(kind abi.Type) string {
  155. switch kind.T {
  156. case abi.AddressTy:
  157. return "common.Address"
  158. case abi.IntTy, abi.UintTy:
  159. parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
  160. switch parts[2] {
  161. case "8", "16", "32", "64":
  162. return fmt.Sprintf("%sint%s", parts[1], parts[2])
  163. }
  164. return "*big.Int"
  165. case abi.FixedBytesTy:
  166. return fmt.Sprintf("[%d]byte", kind.Size)
  167. case abi.BytesTy:
  168. return "[]byte"
  169. case abi.FunctionTy:
  170. // todo(rjl493456442)
  171. return ""
  172. default:
  173. // string, bool types
  174. return kind.String()
  175. }
  176. }
  177. // bindTypeGo converts solidity types to Go ones. Since there is no clear mapping
  178. // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
  179. // mapped will use an upscaled type (e.g. BigDecimal).
  180. func bindTypeGo(kind abi.Type) string {
  181. // todo(rjl493456442) tuple
  182. switch kind.T {
  183. case abi.ArrayTy:
  184. return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem)
  185. case abi.SliceTy:
  186. return "[]" + bindTypeGo(*kind.Elem)
  187. default:
  188. return bindBasicTypeGo(kind)
  189. }
  190. }
  191. // bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java one.
  192. func bindBasicTypeJava(kind abi.Type) string {
  193. switch kind.T {
  194. case abi.AddressTy:
  195. return "Address"
  196. case abi.IntTy, abi.UintTy:
  197. // Note that uint and int (without digits) are also matched,
  198. // these are size 256, and will translate to BigInt (the default).
  199. parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
  200. if len(parts) != 3 {
  201. return kind.String()
  202. }
  203. // All unsigned integers should be translated to BigInt since gomobile doesn't
  204. // support them.
  205. if parts[1] == "u" {
  206. return "BigInt"
  207. }
  208. namedSize := map[string]string{
  209. "8": "byte",
  210. "16": "short",
  211. "32": "int",
  212. "64": "long",
  213. }[parts[2]]
  214. // default to BigInt
  215. if namedSize == "" {
  216. namedSize = "BigInt"
  217. }
  218. return namedSize
  219. case abi.FixedBytesTy, abi.BytesTy:
  220. return "byte[]"
  221. case abi.BoolTy:
  222. return "boolean"
  223. case abi.StringTy:
  224. return "String"
  225. case abi.FunctionTy:
  226. // todo(rjl493456442)
  227. return ""
  228. default:
  229. return kind.String()
  230. }
  231. }
  232. // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
  233. // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
  234. // mapped will use an upscaled type (e.g. BigDecimal).
  235. func bindTypeJava(kind abi.Type) string {
  236. switch kind.T {
  237. case abi.ArrayTy, abi.SliceTy:
  238. // Explicitly convert multidimensional types to predefined type in go side.
  239. inner := bindTypeJava(*kind.Elem)
  240. switch inner {
  241. case "boolean":
  242. return "Bools"
  243. case "String":
  244. return "Strings"
  245. case "Address":
  246. return "Addresses"
  247. case "byte[]":
  248. return "Binaries"
  249. case "BigInt":
  250. return "BigInts"
  251. }
  252. return inner + "[]"
  253. default:
  254. return bindBasicTypeJava(kind)
  255. }
  256. }
  257. // bindTopicType is a set of type binders that convert Solidity types to some
  258. // supported programming language topic types.
  259. var bindTopicType = map[Lang]func(kind abi.Type) string{
  260. LangGo: bindTopicTypeGo,
  261. LangJava: bindTopicTypeJava,
  262. }
  263. // bindTypeGo converts a Solidity topic type to a Go one. It is almost the same
  264. // funcionality as for simple types, but dynamic types get converted to hashes.
  265. func bindTopicTypeGo(kind abi.Type) string {
  266. bound := bindTypeGo(kind)
  267. if bound == "string" || bound == "[]byte" {
  268. bound = "common.Hash"
  269. }
  270. return bound
  271. }
  272. // bindTypeGo converts a Solidity topic type to a Java one. It is almost the same
  273. // funcionality as for simple types, but dynamic types get converted to hashes.
  274. func bindTopicTypeJava(kind abi.Type) string {
  275. bound := bindTypeJava(kind)
  276. if bound == "String" || bound == "byte[]" {
  277. bound = "Hash"
  278. }
  279. return bound
  280. }
  281. // namedType is a set of functions that transform language specific types to
  282. // named versions that my be used inside method names.
  283. var namedType = map[Lang]func(string, abi.Type) string{
  284. LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
  285. LangJava: namedTypeJava,
  286. }
  287. // namedTypeJava converts some primitive data types to named variants that can
  288. // be used as parts of method names.
  289. func namedTypeJava(javaKind string, solKind abi.Type) string {
  290. switch javaKind {
  291. case "byte[]":
  292. return "Binary"
  293. case "boolean":
  294. return "Bool"
  295. default:
  296. parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
  297. if len(parts) != 4 {
  298. return javaKind
  299. }
  300. switch parts[2] {
  301. case "8", "16", "32", "64":
  302. if parts[3] == "" {
  303. return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
  304. }
  305. return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
  306. default:
  307. return javaKind
  308. }
  309. }
  310. }
  311. // methodNormalizer is a name transformer that modifies Solidity method names to
  312. // conform to target language naming concentions.
  313. var methodNormalizer = map[Lang]func(string) string{
  314. LangGo: abi.ToCamelCase,
  315. LangJava: decapitalise,
  316. }
  317. // capitalise makes a camel-case string which starts with an upper case character.
  318. func capitalise(input string) string {
  319. return abi.ToCamelCase(input)
  320. }
  321. // decapitalise makes a camel-case string which starts with a lower case character.
  322. func decapitalise(input string) string {
  323. if len(input) == 0 {
  324. return input
  325. }
  326. goForm := abi.ToCamelCase(input)
  327. return strings.ToLower(goForm[:1]) + goForm[1:]
  328. }
  329. // structured checks whether a list of ABI data types has enough information to
  330. // operate through a proper Go struct or if flat returns are needed.
  331. func structured(args abi.Arguments) bool {
  332. if len(args) < 2 {
  333. return false
  334. }
  335. exists := make(map[string]bool)
  336. for _, out := range args {
  337. // If the name is anonymous, we can't organize into a struct
  338. if out.Name == "" {
  339. return false
  340. }
  341. // If the field name is empty when normalized or collides (var, Var, _var, _Var),
  342. // we can't organize into a struct
  343. field := capitalise(out.Name)
  344. if field == "" || exists[field] {
  345. return false
  346. }
  347. exists[field] = true
  348. }
  349. return true
  350. }