@@ -32,11 +32,20 @@ import (
32
32
"golang.org/x/tools/imports"
33
33
)
34
34
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
+
35
44
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
36
45
// to be used as is in client code, but rather as an intermediate struct which
37
46
// enforces compile time type safety and naming convention opposed to having to
38
47
// 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 ) {
40
49
// Process each individual contract requested binding
41
50
contracts := make (map [string ]* tmplContract )
42
51
@@ -62,7 +71,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
62
71
for _ , original := range evmABI .Methods {
63
72
// Normalize the method for capital cases and non-anonymous inputs/outputs
64
73
normalized := original
65
- normalized .Name = capitalise (original .Name )
74
+ normalized .Name = methodNormalizer [ lang ] (original .Name )
66
75
67
76
normalized .Inputs = make ([]abi.Argument , len (original .Inputs ))
68
77
copy (normalized .Inputs , original .Inputs )
@@ -78,7 +87,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
78
87
normalized .Outputs [j ].Name = capitalise (output .Name )
79
88
}
80
89
}
81
- // Append the methos to the call or transact lists
90
+ // Append the methods to the call or transact lists
82
91
if original .Const {
83
92
calls [original .Name ] = & tmplMethod {Original : original , Normalized : normalized , Structured : structured (original )}
84
93
} else {
@@ -87,7 +96,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
87
96
}
88
97
contracts [types [i ]] = & tmplContract {
89
98
Type : capitalise (types [i ]),
90
- InputABI : strippedABI ,
99
+ InputABI : strings . Replace ( strippedABI , " \" " , " \\ \" " , - 1 ) ,
91
100
InputBin : strings .TrimSpace (bytecodes [i ]),
92
101
Constructor : evmABI .Constructor ,
93
102
Calls : calls ,
@@ -102,24 +111,38 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string) (string
102
111
buffer := new (bytes.Buffer )
103
112
104
113
funcs := map [string ]interface {}{
105
- "bindtype" : bindType ,
114
+ "bindtype" : bindType [lang ],
115
+ "namedtype" : namedType [lang ],
116
+ "capitalise" : capitalise ,
117
+ "decapitalise" : decapitalise ,
106
118
}
107
- tmpl := template .Must (template .New ("" ).Funcs (funcs ).Parse (tmplSource ))
119
+ tmpl := template .Must (template .New ("" ).Funcs (funcs ).Parse (tmplSource [ lang ] ))
108
120
if err := tmpl .Execute (buffer , data ); err != nil {
109
121
return "" , err
110
122
}
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
115
130
}
116
- return string (code ), nil
131
+ // For all others just return as is for now
132
+ return string (buffer .Bytes ()), nil
117
133
}
118
134
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
120
143
// from all Solidity types to Go ones (e.g. uint17), those that cannot be exactly
121
144
// mapped will use an upscaled type (e.g. *big.Int).
122
- func bindType (kind abi.Type ) string {
145
+ func bindTypeGo (kind abi.Type ) string {
123
146
stringKind := kind .String ()
124
147
125
148
switch {
@@ -160,11 +183,137 @@ func bindType(kind abi.Type) string {
160
183
}
161
184
}
162
185
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
+
163
307
// capitalise makes the first character of a string upper case.
164
308
func capitalise (input string ) string {
165
309
return strings .ToUpper (input [:1 ]) + input [1 :]
166
310
}
167
311
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
+
168
317
// structured checks whether a method has enough information to return a proper
169
318
// Go struct ot if flat returns are needed.
170
319
func structured (method abi.Method ) bool {
0 commit comments