@@ -23,6 +23,7 @@ import (
2323 "math"
2424 "math/big"
2525 "reflect"
26+ "strconv"
2627
2728 "github.com/ethereum/go-ethereum/common"
2829)
@@ -188,6 +189,46 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
188189 return refSlice .Interface (), nil
189190}
190191
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+
191232func forTupleUnpack (t Type , output []byte ) (interface {}, error ) {
192233 retval := reflect .New (t .GetType ()).Elem ()
193234 virtualArgs := 0
@@ -218,6 +259,36 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
218259 return retval .Interface (), nil
219260}
220261
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+
221292// toGoType parses the output bytes and recursively assigns the value of these bytes
222293// into a go type with accordance with the ABI spec.
223294func toGoType (index int , t Type , output []byte ) (interface {}, error ) {
@@ -283,6 +354,85 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
283354 }
284355}
285356
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+
286436// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
287437func lengthPrefixPointsTo (index int , output []byte ) (start int , length int , err error ) {
288438 bigOffsetEnd := new (big.Int ).SetBytes (output [index : index + 32 ])
0 commit comments