bind.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. "regexp"
  25. "strings"
  26. "text/template"
  27. "unicode"
  28. "github.com/ethereum/go-ethereum/accounts/abi"
  29. "golang.org/x/tools/imports"
  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. LangObjC
  37. )
  38. // Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
  39. // to be used as is in client code, but rather as an intermediate struct which
  40. // enforces compile time type safety and naming convention opposed to having to
  41. // manually maintain hard coded strings that break on runtime.
  42. func Bind(types []string, abis []string, bytecodes []string, pkg string, lang Lang) (string, error) {
  43. // Process each individual contract requested binding
  44. contracts := make(map[string]*tmplContract)
  45. for i := 0; i < len(types); i++ {
  46. // Parse the actual ABI to generate the binding for
  47. evmABI, err := abi.JSON(strings.NewReader(abis[i]))
  48. if err != nil {
  49. return "", err
  50. }
  51. // Strip any whitespace from the JSON ABI
  52. strippedABI := strings.Map(func(r rune) rune {
  53. if unicode.IsSpace(r) {
  54. return -1
  55. }
  56. return r
  57. }, abis[i])
  58. // Extract the call and transact methods, and sort them alphabetically
  59. var (
  60. calls = make(map[string]*tmplMethod)
  61. transacts = make(map[string]*tmplMethod)
  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)}
  84. } else {
  85. transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)}
  86. }
  87. }
  88. contracts[types[i]] = &tmplContract{
  89. Type: capitalise(types[i]),
  90. InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
  91. InputBin: strings.TrimSpace(bytecodes[i]),
  92. Constructor: evmABI.Constructor,
  93. Calls: calls,
  94. Transacts: transacts,
  95. }
  96. }
  97. // Generate the contract template data content and render it
  98. data := &tmplData{
  99. Package: pkg,
  100. Contracts: contracts,
  101. }
  102. buffer := new(bytes.Buffer)
  103. funcs := map[string]interface{}{
  104. "bindtype": bindType[lang],
  105. "namedtype": namedType[lang],
  106. "capitalise": capitalise,
  107. "decapitalise": decapitalise,
  108. }
  109. tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
  110. if err := tmpl.Execute(buffer, data); err != nil {
  111. return "", err
  112. }
  113. // Pass the code through goimports to clean it up and double check
  114. code, err := imports.Process("", buffer.Bytes(), nil)
  115. if err != nil {
  116. return "", fmt.Errorf("%v\n%s", err, buffer)
  117. }
  118. return string(code), nil
  119. }
  120. // bindType is a set of type binders that convert Solidity types to some supported
  121. // programming language.
  122. var bindType = map[Lang]func(kind abi.Type) string{
  123. LangGo: bindTypeGo,
  124. LangJava: bindTypeJava,
  125. }
  126. // bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping
  127. // from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
  128. // mapped will use an upscaled type (e.g. *big.Int).
  129. func bindTypeGo(kind abi.Type) string {
  130. stringKind := kind.String()
  131. switch {
  132. case strings.HasPrefix(stringKind, "address"):
  133. parts := regexp.MustCompile("address(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  134. if len(parts) != 2 {
  135. return stringKind
  136. }
  137. return fmt.Sprintf("%scommon.Address", parts[1])
  138. case strings.HasPrefix(stringKind, "bytes"):
  139. parts := regexp.MustCompile("bytes([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  140. if len(parts) != 3 {
  141. return stringKind
  142. }
  143. return fmt.Sprintf("%s[%s]byte", parts[2], parts[1])
  144. case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
  145. parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  146. if len(parts) != 4 {
  147. return stringKind
  148. }
  149. switch parts[2] {
  150. case "8", "16", "32", "64":
  151. return fmt.Sprintf("%s%sint%s", parts[3], parts[1], parts[2])
  152. }
  153. return fmt.Sprintf("%s*big.Int", parts[3])
  154. case strings.HasPrefix(stringKind, "bool") || strings.HasPrefix(stringKind, "string"):
  155. parts := regexp.MustCompile("([a-z]+)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  156. if len(parts) != 3 {
  157. return stringKind
  158. }
  159. return fmt.Sprintf("%s%s", parts[2], parts[1])
  160. default:
  161. return stringKind
  162. }
  163. }
  164. // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
  165. // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
  166. // mapped will use an upscaled type (e.g. BigDecimal).
  167. func bindTypeJava(kind abi.Type) string {
  168. stringKind := kind.String()
  169. switch {
  170. case strings.HasPrefix(stringKind, "address"):
  171. parts := regexp.MustCompile("address(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  172. if len(parts) != 2 {
  173. return stringKind
  174. }
  175. if parts[1] == "" {
  176. return fmt.Sprintf("Address")
  177. }
  178. return fmt.Sprintf("Addresses")
  179. case strings.HasPrefix(stringKind, "bytes"):
  180. parts := regexp.MustCompile("bytes([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  181. if len(parts) != 3 {
  182. return stringKind
  183. }
  184. if parts[2] != "" {
  185. return "byte[][]"
  186. }
  187. return "byte[]"
  188. case strings.HasPrefix(stringKind, "int") || strings.HasPrefix(stringKind, "uint"):
  189. parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  190. if len(parts) != 4 {
  191. return stringKind
  192. }
  193. switch parts[2] {
  194. case "8", "16", "32", "64":
  195. if parts[1] == "" {
  196. if parts[3] == "" {
  197. return fmt.Sprintf("int%s", parts[2])
  198. }
  199. return fmt.Sprintf("int%s[]", parts[2])
  200. }
  201. }
  202. if parts[3] == "" {
  203. return fmt.Sprintf("BigInt")
  204. }
  205. return fmt.Sprintf("BigInts")
  206. case strings.HasPrefix(stringKind, "bool"):
  207. parts := regexp.MustCompile("bool(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  208. if len(parts) != 2 {
  209. return stringKind
  210. }
  211. if parts[1] == "" {
  212. return fmt.Sprintf("bool")
  213. }
  214. return fmt.Sprintf("bool[]")
  215. case strings.HasPrefix(stringKind, "string"):
  216. parts := regexp.MustCompile("string(\\[[0-9]*\\])?").FindStringSubmatch(stringKind)
  217. if len(parts) != 2 {
  218. return stringKind
  219. }
  220. if parts[1] == "" {
  221. return fmt.Sprintf("String")
  222. }
  223. return fmt.Sprintf("String[]")
  224. default:
  225. return stringKind
  226. }
  227. }
  228. // namedType is a set of functions that transform language specific types to
  229. // named versions that my be used inside method names.
  230. var namedType = map[Lang]func(string, abi.Type) string{
  231. LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
  232. LangJava: namedTypeJava,
  233. }
  234. // namedTypeJava converts some primitive data types to named variants that can
  235. // be used as parts of method names.
  236. func namedTypeJava(javaKind string, solKind abi.Type) string {
  237. switch javaKind {
  238. case "byte[]":
  239. return "Binary"
  240. case "byte[][]":
  241. return "Binaries"
  242. case "string":
  243. return "String"
  244. case "string[]":
  245. return "Strings"
  246. case "bool":
  247. return "Bool"
  248. case "bool[]":
  249. return "Bools"
  250. case "BigInt":
  251. parts := regexp.MustCompile("(u)?int([0-9]*)(\\[[0-9]*\\])?").FindStringSubmatch(solKind.String())
  252. if len(parts) != 4 {
  253. return javaKind
  254. }
  255. switch parts[2] {
  256. case "8", "16", "32", "64":
  257. if parts[3] == "" {
  258. return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
  259. }
  260. return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
  261. default:
  262. return javaKind
  263. }
  264. default:
  265. return javaKind
  266. }
  267. }
  268. // methodNormalizer is a name transformer that modifies Solidity method names to
  269. // conform to target language naming concentions.
  270. var methodNormalizer = map[Lang]func(string) string{
  271. LangGo: capitalise,
  272. LangJava: decapitalise,
  273. }
  274. // capitalise makes the first character of a string upper case.
  275. func capitalise(input string) string {
  276. return strings.ToUpper(input[:1]) + input[1:]
  277. }
  278. // decapitalise makes the first character of a string lower case.
  279. func decapitalise(input string) string {
  280. return strings.ToLower(input[:1]) + input[1:]
  281. }
  282. // structured checks whether a method has enough information to return a proper
  283. // Go struct ot if flat returns are needed.
  284. func structured(method abi.Method) bool {
  285. if len(method.Outputs) < 2 {
  286. return false
  287. }
  288. for _, out := range method.Outputs {
  289. if out.Name == "" {
  290. return false
  291. }
  292. }
  293. return true
  294. }