@@ -7,13 +7,23 @@ import (
7
7
"io"
8
8
"net/http"
9
9
"reflect"
10
+ "strings"
10
11
11
12
"github.com/gin-gonic/gin"
12
13
"github.com/stackup-wallet/stackup-bundler/pkg/errors"
13
14
"golang.org/x/text/cases"
14
15
"golang.org/x/text/language"
15
16
)
16
17
18
+ var (
19
+ optionalTypePrefix = "optional_"
20
+ )
21
+
22
+ func formatConversionErrMsg (i int , call * reflect.Value ) string {
23
+ s , _ := strings .CutPrefix (call .Type ().In (i ).Name (), optionalTypePrefix )
24
+ return fmt .Sprintf ("Param [%d] can't be converted to %s" , i , s )
25
+ }
26
+
17
27
func jsonrpcError (c * gin.Context , code int , message string , data any , id any ) {
18
28
c .JSON (http .StatusOK , gin.H {
19
29
"jsonrpc" : "2.0" ,
@@ -39,6 +49,27 @@ func parseRequestId(data map[string]any) (any, bool) {
39
49
return nil , false
40
50
}
41
51
52
+ // hasOptionalInput checks if the API method has defined an optional final input:
53
+ // 1. The input must start with the "optional_" prefix in its name.
54
+ // 2. The input must be of kind Map.
55
+ func hasOptionalInput (numIn int , call * reflect.Value ) bool {
56
+ return numIn > 0 &&
57
+ strings .HasPrefix (call .Type ().In (numIn - 1 ).Name (), optionalTypePrefix ) &&
58
+ call .Type ().In (numIn - 1 ).Kind () == reflect .Map
59
+ }
60
+
61
+ // hasValidParamLength checks if the number of parameters in the request is correct:
62
+ // 1. Ok if the number of params equals number of method inputs.
63
+ // 2. Ok if optional input is defined and number of params is one less the number of method inputs.
64
+ func hasValidParamLength (numParams , numIn int , hasOptional bool ) bool {
65
+ return numParams == numIn || (hasOptional && numParams == numIn - 1 )
66
+ }
67
+
68
+ // isOptionalParamUndefined checks if the optional input has been left unset in the request.
69
+ func isOptionalParamUndefined (numParams , numIn int , hasOptional bool ) bool {
70
+ return hasOptional && numParams == numIn - 1
71
+ }
72
+
42
73
// Controller returns a custom Gin middleware that handles incoming JSON-RPC requests via HTTP. It maps the
43
74
// RPC method name to struct methods on the given api. For example, if the RPC request has the method field
44
75
// set to "namespace_methodName" then the controller will make a call to api.Namespace_methodName with the
@@ -99,12 +130,19 @@ func Controller(api interface{}) gin.HandlerFunc {
99
130
return
100
131
}
101
132
102
- if call .Type ().NumIn () != len (params ) {
133
+ numIn := call .Type ().NumIn ()
134
+ numParams := len (params )
135
+ hasOptional := hasOptionalInput (numIn , & call )
136
+ if ! hasValidParamLength (numParams , numIn , hasOptional ) {
103
137
jsonrpcError (c , - 32602 , "Invalid params" , "Invalid number of params" , & id )
104
138
return
105
139
}
140
+ if isOptionalParamUndefined (numParams , numIn , hasOptional ) {
141
+ params = append (params , map [string ]any {})
142
+ numParams ++
143
+ }
106
144
107
- args := make ([]reflect.Value , len ( params ) )
145
+ args := make ([]reflect.Value , numParams )
108
146
for i , arg := range params {
109
147
switch call .Type ().In (i ).Kind () {
110
148
case reflect .Float32 :
@@ -114,7 +152,7 @@ func Controller(api interface{}) gin.HandlerFunc {
114
152
c ,
115
153
- 32602 ,
116
154
"Invalid params" ,
117
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
155
+ formatConversionErrMsg ( i , & call ),
118
156
& id ,
119
157
)
120
158
return
@@ -128,7 +166,7 @@ func Controller(api interface{}) gin.HandlerFunc {
128
166
c ,
129
167
- 32602 ,
130
168
"Invalid params" ,
131
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
169
+ formatConversionErrMsg ( i , & call ),
132
170
& id ,
133
171
)
134
172
return
@@ -150,7 +188,7 @@ func Controller(api interface{}) gin.HandlerFunc {
150
188
c ,
151
189
- 32602 ,
152
190
"Invalid params" ,
153
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
191
+ formatConversionErrMsg ( i , & call ),
154
192
& id ,
155
193
)
156
194
return
@@ -171,7 +209,7 @@ func Controller(api interface{}) gin.HandlerFunc {
171
209
c ,
172
210
- 32602 ,
173
211
"Invalid params" ,
174
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
212
+ formatConversionErrMsg ( i , & call ),
175
213
& id ,
176
214
)
177
215
return
@@ -192,7 +230,7 @@ func Controller(api interface{}) gin.HandlerFunc {
192
230
c ,
193
231
- 32602 ,
194
232
"Invalid params" ,
195
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
233
+ formatConversionErrMsg ( i , & call ),
196
234
& id ,
197
235
)
198
236
return
@@ -213,7 +251,7 @@ func Controller(api interface{}) gin.HandlerFunc {
213
251
c ,
214
252
- 32602 ,
215
253
"Invalid params" ,
216
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
254
+ formatConversionErrMsg ( i , & call ),
217
255
& id ,
218
256
)
219
257
return
@@ -234,7 +272,7 @@ func Controller(api interface{}) gin.HandlerFunc {
234
272
c ,
235
273
- 32602 ,
236
274
"Invalid params" ,
237
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
275
+ formatConversionErrMsg ( i , & call ),
238
276
& id ,
239
277
)
240
278
return
@@ -251,7 +289,7 @@ func Controller(api interface{}) gin.HandlerFunc {
251
289
c ,
252
290
- 32602 ,
253
291
"Invalid params" ,
254
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
292
+ formatConversionErrMsg ( i , & call ),
255
293
& id ,
256
294
)
257
295
return
@@ -265,7 +303,7 @@ func Controller(api interface{}) gin.HandlerFunc {
265
303
c ,
266
304
- 32602 ,
267
305
"Invalid params" ,
268
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
306
+ formatConversionErrMsg ( i , & call ),
269
307
& id ,
270
308
)
271
309
return
@@ -279,7 +317,7 @@ func Controller(api interface{}) gin.HandlerFunc {
279
317
c ,
280
318
- 32602 ,
281
319
"Invalid params" ,
282
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
320
+ formatConversionErrMsg ( i , & call ),
283
321
& id ,
284
322
)
285
323
return
@@ -300,7 +338,7 @@ func Controller(api interface{}) gin.HandlerFunc {
300
338
c ,
301
339
- 32602 ,
302
340
"Invalid params" ,
303
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
341
+ formatConversionErrMsg ( i , & call ),
304
342
& id ,
305
343
)
306
344
return
@@ -321,7 +359,7 @@ func Controller(api interface{}) gin.HandlerFunc {
321
359
c ,
322
360
- 32602 ,
323
361
"Invalid params" ,
324
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
362
+ formatConversionErrMsg ( i , & call ),
325
363
& id ,
326
364
)
327
365
return
@@ -342,7 +380,7 @@ func Controller(api interface{}) gin.HandlerFunc {
342
380
c ,
343
381
- 32602 ,
344
382
"Invalid params" ,
345
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
383
+ formatConversionErrMsg ( i , & call ),
346
384
& id ,
347
385
)
348
386
return
@@ -363,7 +401,7 @@ func Controller(api interface{}) gin.HandlerFunc {
363
401
c ,
364
402
- 32602 ,
365
403
"Invalid params" ,
366
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
404
+ formatConversionErrMsg ( i , & call ),
367
405
& id ,
368
406
)
369
407
return
@@ -384,7 +422,7 @@ func Controller(api interface{}) gin.HandlerFunc {
384
422
c ,
385
423
- 32602 ,
386
424
"Invalid params" ,
387
- fmt . Sprintf ( "Param [%d] can't be converted to %v" , i , call . Type (). In ( i ). String () ),
425
+ formatConversionErrMsg ( i , & call ),
388
426
& id ,
389
427
)
390
428
return
0 commit comments