github.com/status-im/status-go@v1.1.0/abi-spec/core.go (about) 1 package abispec 2 3 import ( 4 "encoding/hex" 5 "encoding/json" 6 "fmt" 7 "math/big" 8 "reflect" 9 "regexp" 10 "strings" 11 12 "github.com/ethereum/go-ethereum/accounts/abi" 13 "github.com/ethereum/go-ethereum/common" 14 ) 15 16 var methodPattern = regexp.MustCompile(`(^[a-zA-Z].*)\((.*)\)`) 17 18 var maxSafeInteger = big.NewInt(int64(9007199254740991)) 19 20 const transferInputs = `[{"type":"address"},{"type":"uint256"}]` 21 const transferFunctionName = "transfer" 22 23 var transferInDef = fmt.Sprintf(`[{ "name" : "%s", "type": "function", "inputs": %s}]`, transferFunctionName, transferInputs) 24 var transferAbi, _ = abi.JSON(strings.NewReader(transferInDef)) 25 26 func EncodeTransfer(to string, value string) (string, error) { 27 amount, success := big.NewInt(0).SetString(value, 0) 28 if !success { 29 return "", fmt.Errorf("failed to convert %s to big.Int", value) 30 } 31 address := common.HexToAddress(to) 32 result, err := transferAbi.Pack(transferFunctionName, address, amount) 33 if err != nil { 34 return "", fmt.Errorf("pack failed: %v", err) 35 } 36 return "0x" + hex.EncodeToString(result), nil 37 } 38 39 func Encode(method string, paramsJSON string) (string, error) { 40 matches := methodPattern.FindStringSubmatch(method) 41 if len(matches) != 3 { 42 return "", fmt.Errorf("unrecognized method: %s", method) 43 } 44 methodName := matches[1] 45 paramTypesString := strings.TrimSpace(matches[2]) 46 47 // value of inputs looks like: `[{ "type": "uint32" },{ "type": "bool" }]` 48 inputs := "[" 49 var params []interface{} 50 if len(paramTypesString) > 0 { 51 var paramsRaw []json.RawMessage 52 if err := json.Unmarshal([]byte(paramsJSON), ¶msRaw); err != nil { 53 return "", fmt.Errorf("unable to unmarshal params: %v", err) 54 } 55 types := strings.Split(paramTypesString, ",") 56 if len(paramsRaw) != len(types) { 57 return "", fmt.Errorf("num of param type should equal to num of param value, actual value: %d, %d", len(types), len(paramsRaw)) 58 } 59 60 for i, typeName := range types { 61 if i != 0 { 62 inputs += "," 63 } 64 inputs += fmt.Sprintf(`{"type":"%s"}`, typeName) 65 66 param, err := toGoTypeValue(typeName, paramsRaw[i]) 67 if err != nil { 68 return "", err 69 } 70 params = append(params, param.Interface()) 71 } 72 } 73 inputs += "]" 74 75 inDef := fmt.Sprintf(`[{ "name" : "%s", "type": "function", "inputs": %s}]`, methodName, inputs) 76 inAbi, err := abi.JSON(strings.NewReader(inDef)) 77 if err != nil { 78 return "", fmt.Errorf("invalid ABI definition %s, %v", inDef, err) 79 } 80 var result []byte 81 result, err = inAbi.Pack(methodName, params...) 82 83 if err != nil { 84 return "", fmt.Errorf("Pack failed: %v", err) 85 } 86 87 return "0x" + hex.EncodeToString(result), nil 88 } 89 90 // override result to make it looks like what status-mobile need 91 func overrideResult(out []interface{}) []interface{} { 92 for i, v := range out { 93 outType := reflect.TypeOf(v) 94 switch outType.String() { 95 case "[]uint8": 96 out[i] = "0x" + common.Bytes2Hex(v.([]uint8)) 97 case bigIntType: 98 vv := v.(*big.Int) 99 if vv.Cmp(maxSafeInteger) == 1 { 100 out[i] = vv.String() 101 } 102 } 103 104 if outType.Kind() == reflect.Array && outType.Elem().Kind() == reflect.Array && outType.Elem().Elem().Kind() == reflect.Uint8 { //case e.g. [2][3]uint8 105 val := reflect.ValueOf(v) 106 rowNum := val.Len() 107 colNum := val.Index(0).Len() 108 var ss = make([]string, rowNum) 109 for i := 0; i < rowNum; i++ { 110 bytes := make([]uint8, colNum) 111 for j := 0; j < colNum; j++ { 112 bytes[j] = uint8(val.Index(i).Index(j).Uint()) 113 } 114 ss[i] = common.Bytes2Hex(bytes) 115 } 116 out[i] = ss 117 } else if outType.String() != "common.Address" && outType.Kind() == reflect.Array && outType.Elem().Kind() == reflect.Uint8 { 118 val := reflect.ValueOf(v) 119 len := val.Len() 120 bytes := make([]uint8, len) 121 for i := 0; i < len; i++ { 122 bytes[i] = uint8(val.Index(i).Uint()) 123 } 124 out[i] = common.Bytes2Hex(bytes) 125 } 126 127 } 128 return out 129 } 130 131 // bytesString e.g. 0x000000000000000000000000000000000000000000000000000000005bc741cd00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000013b86dbf1a83c9e6a492914a0ee39e8a5b7eb60700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147000000000000000000000000000000000000 132 // types e.g. []string{"uint256","bytes","address","uint256","uint256"} 133 func Decode(bytesString string, types []string) ([]interface{}, error) { 134 outputs := "[" 135 for i, typeName := range types { 136 if i != 0 { 137 outputs += "," 138 } 139 outputs += fmt.Sprintf(`{"type":"%s"}`, typeName) 140 } 141 outputs += "]" 142 def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, outputs) 143 abi, err := abi.JSON(strings.NewReader(def)) 144 if err != nil { 145 return nil, fmt.Errorf("invalid ABI definition %s: %v", def, err) 146 } 147 148 bytesString = strings.TrimPrefix(bytesString, "0x") 149 150 bytes, err := hex.DecodeString(bytesString) 151 if err != nil { 152 return nil, fmt.Errorf("invalid hex %s: %v", bytesString, err) 153 } 154 out, err := abi.Unpack("method", bytes) 155 if err != nil { 156 return nil, fmt.Errorf("unpack failed: %v", err) 157 } 158 159 return overrideResult(out), nil 160 }