github.com/status-im/status-go@v1.1.0/abi-spec/types.go (about)

     1  package abispec
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"math/big"
     7  	"reflect"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/ethereum/go-ethereum/common"
    13  )
    14  
    15  const bigIntType = "*big.Int"
    16  
    17  var zero = big.NewInt(0)
    18  
    19  var arrayTypePattern = regexp.MustCompile(`(\[([\d]*)\])`)
    20  
    21  var bytesType = reflect.TypeOf([]byte{})
    22  
    23  var typeMap = map[string]reflect.Type{
    24  	"uint8":   reflect.TypeOf(uint8(0)),
    25  	"int8":    reflect.TypeOf(int8(0)),
    26  	"uint16":  reflect.TypeOf(uint16(0)),
    27  	"int16":   reflect.TypeOf(int16(0)),
    28  	"uint32":  reflect.TypeOf(uint32(0)),
    29  	"int32":   reflect.TypeOf(int32(0)),
    30  	"uint64":  reflect.TypeOf(uint64(0)),
    31  	"int64":   reflect.TypeOf(int64(0)),
    32  	"bytes":   bytesType,
    33  	"bytes1":  reflect.TypeOf([1]byte{}),
    34  	"bytes2":  reflect.TypeOf([2]byte{}),
    35  	"bytes3":  reflect.TypeOf([3]byte{}),
    36  	"bytes4":  reflect.TypeOf([4]byte{}),
    37  	"bytes5":  reflect.TypeOf([5]byte{}),
    38  	"bytes6":  reflect.TypeOf([6]byte{}),
    39  	"bytes7":  reflect.TypeOf([7]byte{}),
    40  	"bytes8":  reflect.TypeOf([8]byte{}),
    41  	"bytes9":  reflect.TypeOf([9]byte{}),
    42  	"bytes10": reflect.TypeOf([10]byte{}),
    43  	"bytes11": reflect.TypeOf([11]byte{}),
    44  	"bytes12": reflect.TypeOf([12]byte{}),
    45  	"bytes13": reflect.TypeOf([13]byte{}),
    46  	"bytes14": reflect.TypeOf([14]byte{}),
    47  	"bytes15": reflect.TypeOf([15]byte{}),
    48  	"bytes16": reflect.TypeOf([16]byte{}),
    49  	"bytes17": reflect.TypeOf([17]byte{}),
    50  	"bytes18": reflect.TypeOf([18]byte{}),
    51  	"bytes19": reflect.TypeOf([19]byte{}),
    52  	"bytes20": reflect.TypeOf([20]byte{}),
    53  	"bytes21": reflect.TypeOf([21]byte{}),
    54  	"bytes22": reflect.TypeOf([22]byte{}),
    55  	"bytes23": reflect.TypeOf([23]byte{}),
    56  	"bytes24": reflect.TypeOf([24]byte{}),
    57  	"bytes25": reflect.TypeOf([25]byte{}),
    58  	"bytes26": reflect.TypeOf([26]byte{}),
    59  	"bytes27": reflect.TypeOf([27]byte{}),
    60  	"bytes28": reflect.TypeOf([28]byte{}),
    61  	"bytes29": reflect.TypeOf([29]byte{}),
    62  	"bytes30": reflect.TypeOf([30]byte{}),
    63  	"bytes31": reflect.TypeOf([31]byte{}),
    64  	"bytes32": reflect.TypeOf([32]byte{}),
    65  	"address": reflect.TypeOf(common.Address{}),
    66  	"bool":    reflect.TypeOf(false),
    67  	"string":  reflect.TypeOf(""),
    68  }
    69  
    70  func toGoType(solidityType string) (reflect.Type, error) {
    71  	if t, ok := typeMap[solidityType]; ok {
    72  		return t, nil
    73  	}
    74  
    75  	if arrayTypePattern.MatchString(solidityType) { // type of array
    76  		index := arrayTypePattern.FindStringIndex(solidityType)[0]
    77  		arrayType, err := toGoType(solidityType[0:index])
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  		matches := arrayTypePattern.FindAllStringSubmatch(solidityType, -1)
    82  		for i := 0; i <= len(matches)-1; i++ {
    83  			sizeStr := matches[i][2]
    84  			if sizeStr == "" {
    85  				arrayType = reflect.SliceOf(arrayType)
    86  			} else {
    87  				length, err := strconv.Atoi(sizeStr)
    88  				if err != nil {
    89  					return nil, err
    90  				}
    91  				arrayType = reflect.ArrayOf(length, arrayType)
    92  			}
    93  		}
    94  		return arrayType, nil
    95  	}
    96  
    97  	// uint and int are aliases for uint256 and int256, respectively.
    98  	// source: https://docs.soliditylang.org/en/v0.8.11/types.html
    99  	//TODO should we support type: uint ?? currently, go-ethereum doesn't support type uint
   100  	if strings.HasPrefix(solidityType, "uint") || strings.HasPrefix(solidityType, "int") {
   101  		return reflect.TypeOf(zero), nil
   102  	}
   103  
   104  	return nil, fmt.Errorf("unsupported type: %s", solidityType)
   105  }
   106  
   107  func toGoTypeValue(solidityType string, raw json.RawMessage) (*reflect.Value, error) {
   108  	goType, err := toGoType(solidityType)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	value := reflect.New(goType)
   114  
   115  	if goType == bytesType { // to support case like: Encode("sam(bytes)", `["dave"]`)
   116  		var s string
   117  		err = json.Unmarshal(raw, &s)
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  		bytes := []byte(s)
   122  		value.Elem().SetBytes(bytes)
   123  		return &value, nil
   124  	}
   125  
   126  	err = json.Unmarshal(raw, value.Interface())
   127  	if err != nil {
   128  		if goType.String() == bigIntType {
   129  			var s string
   130  			err = json.Unmarshal(raw, &s)
   131  			if err != nil {
   132  				return nil, err
   133  			}
   134  			v, success := big.NewInt(0).SetString(s, 0)
   135  			if !success {
   136  				return nil, fmt.Errorf("convert to go type value failed, value: %s", s)
   137  			}
   138  			value = reflect.ValueOf(v)
   139  
   140  		} else if goType.Kind() == reflect.Array { // to support case like: Encode("f(bytes10)", `["1234567890"]`)
   141  			elemKind := goType.Elem().Kind()
   142  			if elemKind == reflect.Uint8 {
   143  				var s string
   144  				err = json.Unmarshal(raw, &s)
   145  				if err != nil {
   146  					return nil, err
   147  				}
   148  				bytes := []byte(s)
   149  				for i, b := range bytes {
   150  					value.Elem().Index(i).Set(reflect.ValueOf(b))
   151  				}
   152  				return &value, nil
   153  			}
   154  
   155  			if elemKind == reflect.Array { // to support case like: Encode("bar(bytes3[2])", `[["abc","def"]]`)
   156  				var ss []string
   157  				err = json.Unmarshal(raw, &ss)
   158  				if err != nil {
   159  					return nil, err
   160  				}
   161  
   162  				var bytes [][]byte
   163  				for _, s := range ss {
   164  					bytes = append(bytes, []byte(s))
   165  				}
   166  
   167  				// convert []byte to []int
   168  				// note: Array and slice values encode as JSON arrays, except that []byte encodes as a base64-encoded string, and a nil slice encodes as the null JSON object.
   169  				var ints = make([][]int, len(bytes))
   170  				for i, r := range bytes {
   171  					ints[i] = make([]int, len(r))
   172  					for j, b := range r {
   173  						ints[i][j] = int(b)
   174  					}
   175  				}
   176  
   177  				jsonString, err := json.Marshal(ints)
   178  				if err != nil {
   179  					return nil, err
   180  				}
   181  				if err = json.Unmarshal(jsonString, value.Interface()); err != nil {
   182  					return nil, err
   183  				}
   184  			}
   185  
   186  		}
   187  	}
   188  
   189  	return &value, err
   190  }