@@ -23,6 +23,7 @@ import (
23
23
"math"
24
24
"math/big"
25
25
"reflect"
26
+ "strconv"
26
27
27
28
"github.com/ethereum/go-ethereum/common"
28
29
)
@@ -188,6 +189,46 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
188
189
return refSlice .Interface (), nil
189
190
}
190
191
192
+ // forEachUnpack iteratively unpack elements.
193
+ func forEachUnpackAsString (t Type , output []byte , start , size int ) (interface {}, error ) {
194
+ if size < 0 {
195
+ return nil , fmt .Errorf ("cannot marshal input to array, size is negative (%d)" , size )
196
+ }
197
+ if start + 32 * size > len (output ) {
198
+ return nil , fmt .Errorf ("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)" , len (output ), start + 32 * size )
199
+ }
200
+
201
+ // this value will become our slice or our array, depending on the type
202
+ var refSlice reflect.Value
203
+
204
+ if t .T == SliceTy {
205
+ // declare our slice
206
+ refSlice = reflect .MakeSlice (t .GetType (), size , size )
207
+ } else if t .T == ArrayTy {
208
+ // declare our array
209
+ refSlice = reflect .New (t .GetType ()).Elem ()
210
+ } else {
211
+ return nil , errors .New ("abi: invalid type in array/slice unpacking stage" )
212
+ }
213
+
214
+ // Arrays have packed elements, resulting in longer unpack steps.
215
+ // Slices have just 32 bytes per element (pointing to the contents).
216
+ elemSize := getTypeSize (* t .Elem )
217
+
218
+ for i , j := start , 0 ; j < size ; i , j = i + elemSize , j + 1 {
219
+ inter , err := toString (i , * t .Elem , output )
220
+ if err != nil {
221
+ return nil , err
222
+ }
223
+
224
+ // append the item to our reflect slice
225
+ refSlice .Index (j ).Set (reflect .ValueOf (inter ))
226
+ }
227
+
228
+ // return the interface
229
+ return refSlice .Interface (), nil
230
+ }
231
+
191
232
func forTupleUnpack (t Type , output []byte ) (interface {}, error ) {
192
233
retval := reflect .New (t .GetType ()).Elem ()
193
234
virtualArgs := 0
@@ -218,6 +259,36 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
218
259
return retval .Interface (), nil
219
260
}
220
261
262
+ func forTupleUnpackAsString (t Type , output []byte ) (interface {}, error ) {
263
+ retval := reflect .New (t .GetType ()).Elem ()
264
+ virtualArgs := 0
265
+ for index , elem := range t .TupleElems {
266
+ marshalledValue , err := toString ((index + virtualArgs )* 32 , * elem , output )
267
+ if err != nil {
268
+ return nil , err
269
+ }
270
+ if elem .T == ArrayTy && ! isDynamicType (* elem ) {
271
+ // If we have a static array, like [3]uint256, these are coded as
272
+ // just like uint256,uint256,uint256.
273
+ // This means that we need to add two 'virtual' arguments when
274
+ // we count the index from now on.
275
+ //
276
+ // Array values nested multiple levels deep are also encoded inline:
277
+ // [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
278
+ //
279
+ // Calculate the full array size to get the correct offset for the next argument.
280
+ // Decrement it by 1, as the normal index increment is still applied.
281
+ virtualArgs += getTypeSize (* elem )/ 32 - 1
282
+ } else if elem .T == TupleTy && ! isDynamicType (* elem ) {
283
+ // If we have a static tuple, like (uint256, bool, uint256), these are
284
+ // coded as just like uint256,bool,uint256
285
+ virtualArgs += getTypeSize (* elem )/ 32 - 1
286
+ }
287
+ retval .Field (index ).Set (reflect .ValueOf (marshalledValue ))
288
+ }
289
+ return retval .Interface (), nil
290
+ }
291
+
221
292
// toGoType parses the output bytes and recursively assigns the value of these bytes
222
293
// into a go type with accordance with the ABI spec.
223
294
func toGoType (index int , t Type , output []byte ) (interface {}, error ) {
@@ -283,6 +354,85 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
283
354
}
284
355
}
285
356
357
+ // toString parses the output bytes and recursively assigns the value of these bytes into string.
358
+ func toString (index int , t Type , output []byte ) (interface {}, error ) {
359
+ if index + 32 > len (output ) {
360
+ return nil , fmt .Errorf ("abi: cannot marshal in to go type: length insufficient %d require %d" , len (output ), index + 32 )
361
+ }
362
+
363
+ var (
364
+ returnOutput []byte
365
+ begin , length int
366
+ err error
367
+ )
368
+
369
+ // if we require a length prefix, find the beginning word and size returned.
370
+ if t .requiresLengthPrefix () {
371
+ begin , length , err = lengthPrefixPointsTo (index , output )
372
+ if err != nil {
373
+ return nil , err
374
+ }
375
+ } else {
376
+ returnOutput = output [index : index + 32 ]
377
+ }
378
+
379
+ switch t .T {
380
+ case TupleTy :
381
+ if isDynamicType (t ) {
382
+ begin , err := tuplePointsTo (index , output )
383
+ if err != nil {
384
+ return nil , err
385
+ }
386
+ return forTupleUnpackAsString (t , output [begin :])
387
+ }
388
+ return forTupleUnpackAsString (t , output [index :])
389
+ case SliceTy :
390
+ return forEachUnpackAsString (t , output [begin :], 0 , length )
391
+ case ArrayTy :
392
+ if isDynamicType (* t .Elem ) {
393
+ offset := binary .BigEndian .Uint64 (returnOutput [len (returnOutput )- 8 :])
394
+ if offset > uint64 (len (output )) {
395
+ return nil , fmt .Errorf ("abi: toGoType offset greater than output length: offset: %d, len(output): %d" , offset , len (output ))
396
+ }
397
+ return forEachUnpackAsString (t , output [offset :], 0 , t .Size )
398
+ }
399
+ return forEachUnpackAsString (t , output [index :], 0 , t .Size )
400
+ case StringTy : // variable arrays are written at the end of the return bytes
401
+ return string (output [begin : begin + length ]), nil
402
+ case IntTy , UintTy :
403
+ return ReadInteger (t , returnOutput )
404
+ case BoolTy :
405
+ var b bool
406
+ b , err = readBool (returnOutput )
407
+ if err != nil {
408
+ return nil , fmt .Errorf ("abi: cannot convert value as bool: %v" , returnOutput )
409
+ }
410
+ return strconv .FormatBool (b ), nil
411
+ case AddressTy :
412
+ return string (common .BytesToAddress (returnOutput ).Bytes ()), nil
413
+ case HashTy :
414
+ return string (common .BytesToHash (returnOutput ).Bytes ()), nil
415
+ case BytesTy :
416
+ return string (output [begin : begin + length ]), nil
417
+ case FixedBytesTy :
418
+ var b interface {}
419
+ b , err = ReadFixedBytes (t , returnOutput )
420
+ if err != nil {
421
+ return nil , fmt .Errorf ("abi: cannot convert value as fixed bytes array: %v" , returnOutput )
422
+ }
423
+ return string (b .([]byte )), nil
424
+ case FunctionTy :
425
+ var f interface {}
426
+ f , err = ReadFixedBytes (t , returnOutput )
427
+ if err != nil {
428
+ return nil , fmt .Errorf ("abi: cannot convert value as function: %v" , returnOutput )
429
+ }
430
+ return string (f .([]byte )), nil
431
+ default :
432
+ return nil , fmt .Errorf ("abi: unknown type %v" , t .T )
433
+ }
434
+ }
435
+
286
436
// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
287
437
func lengthPrefixPointsTo (index int , output []byte ) (start int , length int , err error ) {
288
438
bigOffsetEnd := new (big.Int ).SetBytes (output [index : index + 32 ])
0 commit comments