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 }