@@ -32,11 +32,20 @@ import (
3232 "golang.org/x/tools/imports"
3333)
3434
35+ // Lang is a target programming language selector to generate bindings for.
36+ type Lang int
37+
38+ const (
39+ LangGo Lang = iota
40+ LangJava
41+ LangObjC
42+ )
43+
3544// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
3645// to be used as is in client code, but rather as an intermediate struct which
3746// enforces compile time type safety and naming convention opposed to having to
3847// manually maintain hard coded strings that break on runtime.
39- func Bind (types []string , abis []string , bytecodes []string , pkg string ) (string , error ) {
48+ func Bind (types []string , abis []string , bytecodes []string , pkg string , lang Lang ) (string , error ) {
4049 // Process each individual contract requested binding
4150 contracts := make (map [string ]* tmplContract )
4251
@@ -62,7 +71,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
6271 for _ , original := range evmABI .Methods {
6372 // Normalize the method for capital cases and non-anonymous inputs/outputs
6473 normalized := original
65- normalized .Name = capitalise (original .Name )
74+ normalized .Name = methodNormalizer [ lang ] (original .Name )
6675
6776 normalized .Inputs = make ([]abi.Argument , len (original .Inputs ))
6877 copy (normalized .Inputs , original .Inputs )
@@ -78,7 +87,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
7887 normalized .Outputs [j ].Name = capitalise (output .Name )
7988 }
8089 }
81- // Append the methos to the call or transact lists
90+ // Append the methods to the call or transact lists
8291 if original .Const {
8392 calls [original .Name ] = & tmplMethod {Original : original , Normalized : normalized , Structured : structured (original )}
8493 } else {
@@ -87,7 +96,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
8796 }
8897 contracts [types [i ]] = & tmplContract {
8998 Type : capitalise (types [i ]),
90- InputABI : strippedABI ,
99+ InputABI : strings . Replace ( strippedABI , " \" " , " \\ \" " , - 1 ) ,
91100 InputBin : strings .TrimSpace (bytecodes [i ]),
92101 Constructor : evmABI .Constructor ,
93102 Calls : calls ,
@@ -102,24 +111,38 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
102111 buffer := new (bytes.Buffer )
103112
104113 funcs := map [string ]interface {}{
105- "bindtype" : bindType ,
114+ "bindtype" : bindType [lang ],
115+ "namedtype" : namedType [lang ],
116+ "capitalise" : capitalise ,
117+ "decapitalise" : decapitalise ,
106118 }
107- tmpl := template .Must (template .New ("" ).Funcs (funcs ).Parse (tmplSource ))
119+ tmpl := template .Must (template .New ("" ).Funcs (funcs ).Parse (tmplSource [ lang ] ))
108120 if err := tmpl .Execute (buffer , data ); err != nil {
109121 return "" , err
110122 }
111- // Pass the code through goimports to clean it up and double check
112- code , err := imports .Process ("" , buffer .Bytes (), nil )
113- if err != nil {
114- return "" , fmt .Errorf ("%v\n %s" , err , buffer )
123+ // For Go bindings pass the code through goimports to clean it up and double check
124+ if lang == LangGo {
125+ code , err := imports .Process ("" , buffer .Bytes (), nil )
126+ if err != nil {
127+ return "" , fmt .Errorf ("%v\n %s" , err , buffer )
128+ }
129+ return string (code ), nil
115130 }
116- return string (code ), nil
131+ // For all others just return as is for now
132+ return string (buffer .Bytes ()), nil
117133}
118134
119- // bindType converts a Solidity type to a Go one. Since there is no clear mapping
135+ // bindType is a set of type binders that convert Solidity types to some supported
136+ // programming language.
137+ var bindType = map [Lang ]func (kind abi.Type ) string {
138+ LangGo : bindTypeGo ,
139+ LangJava : bindTypeJava ,
140+ }
141+
142+ // bindTypeGo converts a Solidity type to a Go one. Since there is no clear mapping
120143// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
121144// mapped will use an upscaled type (e.g. *big.Int).
122- func bindType (kind abi.Type ) string {
145+ func bindTypeGo (kind abi.Type ) string {
123146 stringKind := kind .String ()
124147
125148 switch {
@@ -160,11 +183,137 @@ func bindType(kind abi.Type) string {
160183 }
161184}
162185
186+ // bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
187+ // from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
188+ // mapped will use an upscaled type (e.g. BigDecimal).
189+ func bindTypeJava (kind abi.Type ) string {
190+ stringKind := kind .String ()
191+
192+ switch {
193+ case strings .HasPrefix (stringKind , "address" ):
194+ parts := regexp .MustCompile ("address(\\ [[0-9]*\\ ])?" ).FindStringSubmatch (stringKind )
195+ if len (parts ) != 2 {
196+ return stringKind
197+ }
198+ if parts [1 ] == "" {
199+ return fmt .Sprintf ("Address" )
200+ }
201+ return fmt .Sprintf ("Addresses" )
202+
203+ case strings .HasPrefix (stringKind , "bytes" ):
204+ parts := regexp .MustCompile ("bytes([0-9]*)(\\ [[0-9]*\\ ])?" ).FindStringSubmatch (stringKind )
205+ if len (parts ) != 3 {
206+ return stringKind
207+ }
208+ if parts [2 ] != "" {
209+ return "byte[][]"
210+ }
211+ return "byte[]"
212+
213+ case strings .HasPrefix (stringKind , "int" ) || strings .HasPrefix (stringKind , "uint" ):
214+ parts := regexp .MustCompile ("(u)?int([0-9]*)(\\ [[0-9]*\\ ])?" ).FindStringSubmatch (stringKind )
215+ if len (parts ) != 4 {
216+ return stringKind
217+ }
218+ switch parts [2 ] {
219+ case "8" , "16" , "32" , "64" :
220+ if parts [1 ] == "" {
221+ if parts [3 ] == "" {
222+ return fmt .Sprintf ("int%s" , parts [2 ])
223+ }
224+ return fmt .Sprintf ("int%s[]" , parts [2 ])
225+ }
226+ }
227+ if parts [3 ] == "" {
228+ return fmt .Sprintf ("BigInt" )
229+ }
230+ return fmt .Sprintf ("BigInts" )
231+
232+ case strings .HasPrefix (stringKind , "bool" ):
233+ parts := regexp .MustCompile ("bool(\\ [[0-9]*\\ ])?" ).FindStringSubmatch (stringKind )
234+ if len (parts ) != 2 {
235+ return stringKind
236+ }
237+ if parts [1 ] == "" {
238+ return fmt .Sprintf ("bool" )
239+ }
240+ return fmt .Sprintf ("bool[]" )
241+
242+ case strings .HasPrefix (stringKind , "string" ):
243+ parts := regexp .MustCompile ("string(\\ [[0-9]*\\ ])?" ).FindStringSubmatch (stringKind )
244+ if len (parts ) != 2 {
245+ return stringKind
246+ }
247+ if parts [1 ] == "" {
248+ return fmt .Sprintf ("String" )
249+ }
250+ return fmt .Sprintf ("String[]" )
251+
252+ default :
253+ return stringKind
254+ }
255+ }
256+
257+ // namedType is a set of functions that transform language specific types to
258+ // named versions that my be used inside method names.
259+ var namedType = map [Lang ]func (string , abi.Type ) string {
260+ LangGo : func (string , abi.Type ) string { panic ("this shouldn't be needed" ) },
261+ LangJava : namedTypeJava ,
262+ }
263+
264+ // namedTypeJava converts some primitive data types to named variants that can
265+ // be used as parts of method names.
266+ func namedTypeJava (javaKind string , solKind abi.Type ) string {
267+ switch javaKind {
268+ case "byte[]" :
269+ return "Binary"
270+ case "byte[][]" :
271+ return "Binaries"
272+ case "string" :
273+ return "String"
274+ case "string[]" :
275+ return "Strings"
276+ case "bool" :
277+ return "Bool"
278+ case "bool[]" :
279+ return "Bools"
280+ case "BigInt" :
281+ parts := regexp .MustCompile ("(u)?int([0-9]*)(\\ [[0-9]*\\ ])?" ).FindStringSubmatch (solKind .String ())
282+ if len (parts ) != 4 {
283+ return javaKind
284+ }
285+ switch parts [2 ] {
286+ case "8" , "16" , "32" , "64" :
287+ if parts [3 ] == "" {
288+ return capitalise (fmt .Sprintf ("%sint%s" , parts [1 ], parts [2 ]))
289+ }
290+ return capitalise (fmt .Sprintf ("%sint%ss" , parts [1 ], parts [2 ]))
291+
292+ default :
293+ return javaKind
294+ }
295+ default :
296+ return javaKind
297+ }
298+ }
299+
300+ // methodNormalizer is a name transformer that modifies Solidity method names to
301+ // conform to target language naming concentions.
302+ var methodNormalizer = map [Lang ]func (string ) string {
303+ LangGo : capitalise ,
304+ LangJava : decapitalise ,
305+ }
306+
163307// capitalise makes the first character of a string upper case.
164308func capitalise (input string ) string {
165309 return strings .ToUpper (input [:1 ]) + input [1 :]
166310}
167311
312+ // decapitalise makes the first character of a string lower case.
313+ func decapitalise (input string ) string {
314+ return strings .ToLower (input [:1 ]) + input [1 :]
315+ }
316+
168317// structured checks whether a method has enough information to return a proper
169318// Go struct ot if flat returns are needed.
170319func structured (method abi.Method ) bool {
0 commit comments