161 lines
5.1 KiB
Go
161 lines
5.1 KiB
Go
package abispec
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/big"
|
|
"reflect"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
"github.com/ethereum/go-ethereum/common"
|
|
)
|
|
|
|
var methodPattern = regexp.MustCompile(`(^[a-zA-Z].*)\((.*)\)`)
|
|
|
|
var maxSafeInteger = big.NewInt(int64(9007199254740991))
|
|
|
|
const transferInputs = `[{"type":"address"},{"type":"uint256"}]`
|
|
const transferFunctionName = "transfer"
|
|
|
|
var transferInDef = fmt.Sprintf(`[{ "name" : "%s", "type": "function", "inputs": %s}]`, transferFunctionName, transferInputs)
|
|
var transferAbi, _ = abi.JSON(strings.NewReader(transferInDef))
|
|
|
|
func EncodeTransfer(to string, value string) (string, error) {
|
|
amount, success := big.NewInt(0).SetString(value, 0)
|
|
if !success {
|
|
return "", fmt.Errorf("failed to convert %s to big.Int", value)
|
|
}
|
|
address := common.HexToAddress(to)
|
|
result, err := transferAbi.Pack(transferFunctionName, address, amount)
|
|
if err != nil {
|
|
return "", fmt.Errorf("pack failed: %v", err)
|
|
}
|
|
return "0x" + hex.EncodeToString(result), nil
|
|
}
|
|
|
|
func Encode(method string, paramsJSON string) (string, error) {
|
|
matches := methodPattern.FindStringSubmatch(method)
|
|
if len(matches) != 3 {
|
|
return "", fmt.Errorf("unrecognized method: %s", method)
|
|
}
|
|
methodName := matches[1]
|
|
paramTypesString := strings.TrimSpace(matches[2])
|
|
|
|
// value of inputs looks like: `[{ "type": "uint32" },{ "type": "bool" }]`
|
|
inputs := "["
|
|
var params []interface{}
|
|
if len(paramTypesString) > 0 {
|
|
var paramsRaw []json.RawMessage
|
|
if err := json.Unmarshal([]byte(paramsJSON), ¶msRaw); err != nil {
|
|
return "", fmt.Errorf("unable to unmarshal params: %v", err)
|
|
}
|
|
types := strings.Split(paramTypesString, ",")
|
|
if len(paramsRaw) != len(types) {
|
|
return "", fmt.Errorf("num of param type should equal to num of param value, actual value: %d, %d", len(types), len(paramsRaw))
|
|
}
|
|
|
|
for i, typeName := range types {
|
|
if i != 0 {
|
|
inputs += ","
|
|
}
|
|
inputs += fmt.Sprintf(`{"type":"%s"}`, typeName)
|
|
|
|
param, err := toGoTypeValue(typeName, paramsRaw[i])
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
params = append(params, param.Interface())
|
|
}
|
|
}
|
|
inputs += "]"
|
|
|
|
inDef := fmt.Sprintf(`[{ "name" : "%s", "type": "function", "inputs": %s}]`, methodName, inputs)
|
|
inAbi, err := abi.JSON(strings.NewReader(inDef))
|
|
if err != nil {
|
|
return "", fmt.Errorf("invalid ABI definition %s, %v", inDef, err)
|
|
}
|
|
var result []byte
|
|
result, err = inAbi.Pack(methodName, params...)
|
|
|
|
if err != nil {
|
|
return "", fmt.Errorf("Pack failed: %v", err)
|
|
}
|
|
|
|
return "0x" + hex.EncodeToString(result), nil
|
|
}
|
|
|
|
// override result to make it looks like what status-mobile need
|
|
func overrideResult(out []interface{}) []interface{} {
|
|
for i, v := range out {
|
|
outType := reflect.TypeOf(v)
|
|
switch outType.String() {
|
|
case "[]uint8":
|
|
out[i] = "0x" + common.Bytes2Hex(v.([]uint8))
|
|
case bigIntType:
|
|
vv := v.(*big.Int)
|
|
if vv.Cmp(maxSafeInteger) == 1 {
|
|
out[i] = vv.String()
|
|
}
|
|
}
|
|
|
|
if outType.Kind() == reflect.Array && outType.Elem().Kind() == reflect.Array && outType.Elem().Elem().Kind() == reflect.Uint8 { //case e.g. [2][3]uint8
|
|
val := reflect.ValueOf(v)
|
|
rowNum := val.Len()
|
|
colNum := val.Index(0).Len()
|
|
var ss = make([]string, rowNum)
|
|
for i := 0; i < rowNum; i++ {
|
|
bytes := make([]uint8, colNum)
|
|
for j := 0; j < colNum; j++ {
|
|
bytes[j] = uint8(val.Index(i).Index(j).Uint())
|
|
}
|
|
ss[i] = common.Bytes2Hex(bytes)
|
|
}
|
|
out[i] = ss
|
|
} else if outType.String() != "common.Address" && outType.Kind() == reflect.Array && outType.Elem().Kind() == reflect.Uint8 {
|
|
val := reflect.ValueOf(v)
|
|
len := val.Len()
|
|
bytes := make([]uint8, len)
|
|
for i := 0; i < len; i++ {
|
|
bytes[i] = uint8(val.Index(i).Uint())
|
|
}
|
|
out[i] = common.Bytes2Hex(bytes)
|
|
}
|
|
|
|
}
|
|
return out
|
|
}
|
|
|
|
// bytesString e.g. 0x000000000000000000000000000000000000000000000000000000005bc741cd00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000013b86dbf1a83c9e6a492914a0ee39e8a5b7eb60700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147000000000000000000000000000000000000
|
|
// types e.g. []string{"uint256","bytes","address","uint256","uint256"}
|
|
func Decode(bytesString string, types []string) ([]interface{}, error) {
|
|
outputs := "["
|
|
for i, typeName := range types {
|
|
if i != 0 {
|
|
outputs += ","
|
|
}
|
|
outputs += fmt.Sprintf(`{"type":"%s"}`, typeName)
|
|
}
|
|
outputs += "]"
|
|
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, outputs)
|
|
abi, err := abi.JSON(strings.NewReader(def))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid ABI definition %s: %v", def, err)
|
|
}
|
|
|
|
bytesString = strings.TrimPrefix(bytesString, "0x")
|
|
|
|
bytes, err := hex.DecodeString(bytesString)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid hex %s: %v", bytesString, err)
|
|
}
|
|
out, err := abi.Unpack("method", bytes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unpack failed: %v", err)
|
|
}
|
|
|
|
return overrideResult(out), nil
|
|
}
|