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), &paramsRaw); 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  }