Move abi-spec to status-go
This commit is contained in:
parent
bcdb14bd48
commit
e49236a83c
|
@ -0,0 +1,161 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(bytesString, "0x") {
|
||||||
|
bytesString = bytesString[2:]
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
package abispec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMethodPattern(t *testing.T) {
|
||||||
|
results := methodPattern.FindStringSubmatch("sam(bytes,bool,uint256[])")
|
||||||
|
require.Equal(t, "sam(bytes,bool,uint256[])", results[0])
|
||||||
|
require.Equal(t, "sam", results[1])
|
||||||
|
require.Equal(t, "bytes,bool,uint256[]", results[2])
|
||||||
|
|
||||||
|
results = methodPattern.FindStringSubmatch("s(bytes,bool,uint256[])")
|
||||||
|
require.Equal(t, "s(bytes,bool,uint256[])", results[0])
|
||||||
|
require.Equal(t, "s", results[1])
|
||||||
|
require.Equal(t, "bytes,bool,uint256[]", results[2])
|
||||||
|
|
||||||
|
results = methodPattern.FindStringSubmatch("s1(bytes,bool,uint256[])")
|
||||||
|
require.Equal(t, "s1(bytes,bool,uint256[])", results[0])
|
||||||
|
require.Equal(t, "s1", results[1])
|
||||||
|
require.Equal(t, "bytes,bool,uint256[]", results[2])
|
||||||
|
|
||||||
|
results = methodPattern.FindStringSubmatch("1s(bytes,bool,uint256[])")
|
||||||
|
require.Len(t, results, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeTransfer(t *testing.T) {
|
||||||
|
result, err := EncodeTransfer("0x6f5f90fb1dD8E406F233442935F689bA7D5701b2", "10000")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0xa9059cbb0000000000000000000000006f5f90fb1dd8e406f233442935f689ba7d5701b20000000000000000000000000000000000000000000000000000000000002710", result)
|
||||||
|
|
||||||
|
result, err = EncodeTransfer("0x6f5f90fb1dD8E406F233442935F689bA7D5701b2", "0x2710")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0xa9059cbb0000000000000000000000006f5f90fb1dd8e406f233442935f689ba7d5701b20000000000000000000000000000000000000000000000000000000000002710", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncode(t *testing.T) {
|
||||||
|
result, err := Encode("baz(uint32,bool)", "[69,true]")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001", result)
|
||||||
|
|
||||||
|
result, err = Encode("baz(uint256,bool)", "[69,true]")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0x72ed38b600000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001", result)
|
||||||
|
|
||||||
|
result, err = Encode("bar(bytes3[2])", `[["abc","def"]]`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000", result)
|
||||||
|
|
||||||
|
result, err = Encode("sam(bytes)", `["dave"]`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0x05e73fb9000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046461766500000000000000000000000000000000000000000000000000000000", result)
|
||||||
|
|
||||||
|
result, err = Encode("sam(bytes,bool,uint256[])", `["dave",true,[1,2,3]]`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", result)
|
||||||
|
|
||||||
|
result, err = Encode("f(bytes10)", `["1234567890"]`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0x6350865e3132333435363738393000000000000000000000000000000000000000000000", result)
|
||||||
|
|
||||||
|
result, err = Encode("f(uint256,uint32[],bytes10,bytes)", `[291,[1110,1929],"1234567890","Hello, world!"]`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0x8be6524600000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000", result)
|
||||||
|
|
||||||
|
result, err = Encode("getExpectedRate(address,address,uint256)", `["0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",1000]`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0x809a9e55000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000000000000000003e8", result)
|
||||||
|
|
||||||
|
result, err = Encode("f(uint256)", `["115792089237316195423570985008687907853269984665640564039457584007913129639935"]`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "0xb3de648bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", result)
|
||||||
|
|
||||||
|
//TODO following case would fail cause go-ethereum does not support type uint
|
||||||
|
//result, err = Encode("g(uint[][],string[])", `[[[1,2],[3]],["one","two","three"]]`)
|
||||||
|
//require.NoError(t, err)
|
||||||
|
//require.Equal(t, "0xad6a3446000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000036f6e650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000374776f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000057468726565000000000000000000000000000000000000000000000000000000", result)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecode(t *testing.T) {
|
||||||
|
out, err := Decode("0x000000000000000000000000000000000000000000000000000000005bc741cd00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000013b86dbf1a83c9e6a492914a0ee39e8a5b7eb60700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147000000000000000000000000000000000000",
|
||||||
|
[]string{"uint256", "bytes", "address", "uint256", "uint256"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
bytes, err := json.Marshal(out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `[1539785165,"0x516d533152484e4a57414b356e426f6f57454d34654d644268707a35666e325764557473457357754a4b79356147","0x13b86dbf1a83c9e6a492914a0ee39e8a5b7eb607",0,0]`, string(bytes))
|
||||||
|
|
||||||
|
out, err = Decode("0x0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000d7621dc58210001",
|
||||||
|
[]string{"uint256", "uint256"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
bytes, err = json.Marshal(out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `["1000000000000000000","970000000000000001"]`, string(bytes))
|
||||||
|
|
||||||
|
out, err = Decode("0x000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000000000000000003e8",
|
||||||
|
[]string{"address", "address", "uint256"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
bytes, err = json.Marshal(out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `["0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee","0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",1000]`, string(bytes))
|
||||||
|
|
||||||
|
out, err = Decode("0x00000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
[]string{"uint32", "bool"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
bytes, err = json.Marshal(out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `[69,true]`, string(bytes))
|
||||||
|
|
||||||
|
out, err = Decode("0x61626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000",
|
||||||
|
[]string{"bytes3[2]"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
bytes, err = json.Marshal(out[0])
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `["616263","646566"]`, string(bytes))
|
||||||
|
|
||||||
|
out, err = Decode("0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
[]string{"string", "bool", "uint256[]"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
bytes, err = json.Marshal(out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `["dave",true,[1,2,3]]`, string(bytes))
|
||||||
|
|
||||||
|
out, err = Decode("0x00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000",
|
||||||
|
[]string{"uint256", "uint32[]", "bytes10", "bytes"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
bytes, err = json.Marshal(out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `[291,[1110,1929],"31323334353637383930","0x48656c6c6f2c20776f726c6421"]`, string(bytes))
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
package abispec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const bigIntType = "*big.Int"
|
||||||
|
|
||||||
|
var zero = big.NewInt(0)
|
||||||
|
|
||||||
|
var arrayTypePattern = regexp.MustCompile(`(\[([\d]*)\])`)
|
||||||
|
|
||||||
|
var bytesType = reflect.TypeOf([]byte{})
|
||||||
|
|
||||||
|
var typeMap = map[string]reflect.Type{
|
||||||
|
"uint8": reflect.TypeOf(uint8(0)),
|
||||||
|
"int8": reflect.TypeOf(int8(0)),
|
||||||
|
"uint16": reflect.TypeOf(uint16(0)),
|
||||||
|
"int16": reflect.TypeOf(int16(0)),
|
||||||
|
"uint32": reflect.TypeOf(uint32(0)),
|
||||||
|
"int32": reflect.TypeOf(int32(0)),
|
||||||
|
"uint64": reflect.TypeOf(uint64(0)),
|
||||||
|
"int64": reflect.TypeOf(int64(0)),
|
||||||
|
"bytes": bytesType,
|
||||||
|
"bytes1": reflect.TypeOf([1]byte{}),
|
||||||
|
"bytes2": reflect.TypeOf([2]byte{}),
|
||||||
|
"bytes3": reflect.TypeOf([3]byte{}),
|
||||||
|
"bytes4": reflect.TypeOf([4]byte{}),
|
||||||
|
"bytes5": reflect.TypeOf([5]byte{}),
|
||||||
|
"bytes6": reflect.TypeOf([6]byte{}),
|
||||||
|
"bytes7": reflect.TypeOf([7]byte{}),
|
||||||
|
"bytes8": reflect.TypeOf([8]byte{}),
|
||||||
|
"bytes9": reflect.TypeOf([9]byte{}),
|
||||||
|
"bytes10": reflect.TypeOf([10]byte{}),
|
||||||
|
"bytes11": reflect.TypeOf([11]byte{}),
|
||||||
|
"bytes12": reflect.TypeOf([12]byte{}),
|
||||||
|
"bytes13": reflect.TypeOf([13]byte{}),
|
||||||
|
"bytes14": reflect.TypeOf([14]byte{}),
|
||||||
|
"bytes15": reflect.TypeOf([15]byte{}),
|
||||||
|
"bytes16": reflect.TypeOf([16]byte{}),
|
||||||
|
"bytes17": reflect.TypeOf([17]byte{}),
|
||||||
|
"bytes18": reflect.TypeOf([18]byte{}),
|
||||||
|
"bytes19": reflect.TypeOf([19]byte{}),
|
||||||
|
"bytes20": reflect.TypeOf([20]byte{}),
|
||||||
|
"bytes21": reflect.TypeOf([21]byte{}),
|
||||||
|
"bytes22": reflect.TypeOf([22]byte{}),
|
||||||
|
"bytes23": reflect.TypeOf([23]byte{}),
|
||||||
|
"bytes24": reflect.TypeOf([24]byte{}),
|
||||||
|
"bytes25": reflect.TypeOf([25]byte{}),
|
||||||
|
"bytes26": reflect.TypeOf([26]byte{}),
|
||||||
|
"bytes27": reflect.TypeOf([27]byte{}),
|
||||||
|
"bytes28": reflect.TypeOf([28]byte{}),
|
||||||
|
"bytes29": reflect.TypeOf([29]byte{}),
|
||||||
|
"bytes30": reflect.TypeOf([30]byte{}),
|
||||||
|
"bytes31": reflect.TypeOf([31]byte{}),
|
||||||
|
"bytes32": reflect.TypeOf([32]byte{}),
|
||||||
|
"address": reflect.TypeOf(common.Address{}),
|
||||||
|
"bool": reflect.TypeOf(false),
|
||||||
|
"string": reflect.TypeOf(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
func toGoType(solidityType string) (reflect.Type, error) {
|
||||||
|
if t, ok := typeMap[solidityType]; ok {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if arrayTypePattern.MatchString(solidityType) { // type of array
|
||||||
|
index := arrayTypePattern.FindStringIndex(solidityType)[0]
|
||||||
|
arrayType, err := toGoType(solidityType[0:index])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
matches := arrayTypePattern.FindAllStringSubmatch(solidityType, -1)
|
||||||
|
for i := 0; i <= len(matches)-1; i++ {
|
||||||
|
sizeStr := matches[i][2]
|
||||||
|
if sizeStr == "" {
|
||||||
|
arrayType = reflect.SliceOf(arrayType)
|
||||||
|
} else {
|
||||||
|
length, err := strconv.Atoi(sizeStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
arrayType = reflect.ArrayOf(length, arrayType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arrayType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// uint and int are aliases for uint256 and int256, respectively.
|
||||||
|
// source: https://docs.soliditylang.org/en/v0.8.11/types.html
|
||||||
|
//TODO should we support type: uint ?? currently, go-ethereum doesn't support type uint
|
||||||
|
if strings.HasPrefix(solidityType, "uint") || strings.HasPrefix(solidityType, "int") {
|
||||||
|
return reflect.TypeOf(zero), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("unsupported type: %s", solidityType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toGoTypeValue(solidityType string, raw json.RawMessage) (*reflect.Value, error) {
|
||||||
|
goType, err := toGoType(solidityType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
value := reflect.New(goType)
|
||||||
|
|
||||||
|
if goType == bytesType { // to support case like: Encode("sam(bytes)", `["dave"]`)
|
||||||
|
var s string
|
||||||
|
err = json.Unmarshal(raw, &s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bytes := []byte(s)
|
||||||
|
value.Elem().SetBytes(bytes)
|
||||||
|
return &value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(raw, value.Interface())
|
||||||
|
if err != nil {
|
||||||
|
if goType.String() == bigIntType {
|
||||||
|
var s string
|
||||||
|
err = json.Unmarshal(raw, &s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v, success := big.NewInt(0).SetString(s, 0)
|
||||||
|
if !success {
|
||||||
|
return nil, fmt.Errorf("convert to go type value failed, value: %s", s)
|
||||||
|
}
|
||||||
|
value = reflect.ValueOf(v)
|
||||||
|
|
||||||
|
} else if goType.Kind() == reflect.Array { // to support case like: Encode("f(bytes10)", `["1234567890"]`)
|
||||||
|
elemKind := goType.Elem().Kind()
|
||||||
|
if elemKind == reflect.Uint8 {
|
||||||
|
var s string
|
||||||
|
err = json.Unmarshal(raw, &s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bytes := []byte(s)
|
||||||
|
for i, b := range bytes {
|
||||||
|
value.Elem().Index(i).Set(reflect.ValueOf(b))
|
||||||
|
}
|
||||||
|
return &value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if elemKind == reflect.Array { // to support case like: Encode("bar(bytes3[2])", `[["abc","def"]]`)
|
||||||
|
var ss []string
|
||||||
|
err = json.Unmarshal(raw, &ss)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes [][]byte
|
||||||
|
for _, s := range ss {
|
||||||
|
bytes = append(bytes, []byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert []byte to []int
|
||||||
|
// 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.
|
||||||
|
var ints = make([][]int, len(bytes))
|
||||||
|
for i, r := range bytes {
|
||||||
|
ints[i] = make([]int, len(r))
|
||||||
|
for j, b := range r {
|
||||||
|
ints[i][j] = int(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(ints)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(jsonString, value.Interface()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &value, err
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package abispec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToGoTypeValue(t *testing.T) {
|
||||||
|
var raw json.RawMessage
|
||||||
|
err := json.Unmarshal([]byte(`"dave"`), &raw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
val, err := toGoTypeValue("bytes", raw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("dave"), val.Elem().Bytes())
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(`true`), &raw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
val, err = toGoTypeValue("bool", raw)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, val.Elem().Bool())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToGoType(t *testing.T) {
|
||||||
|
var raws []json.RawMessage
|
||||||
|
err := json.Unmarshal([]byte("[8]"), &raws)
|
||||||
|
require.NoError(t, err)
|
||||||
|
value, err := toGoTypeValue("uint8", raws[0])
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, uint8(8), *value.Interface().(*uint8))
|
||||||
|
|
||||||
|
goType, err := toGoType("uint256[][3][]")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "[][3][]*big.Int", goType.String())
|
||||||
|
|
||||||
|
goType, err = toGoType("uint256[][][3]")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "[3][][]*big.Int", goType.String())
|
||||||
|
|
||||||
|
goType, err = toGoType("uint256[3][][]")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "[][][3]*big.Int", goType.String())
|
||||||
|
|
||||||
|
goType, err = toGoType("bytes3[2]")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "[2][3]uint8", goType.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArrayTypePattern(t *testing.T) {
|
||||||
|
require.True(t, arrayTypePattern.MatchString(`uint8[]`))
|
||||||
|
require.False(t, arrayTypePattern.MatchString(`uint8`))
|
||||||
|
|
||||||
|
s := "uint8[][2][1][]"
|
||||||
|
matches := arrayTypePattern.FindAllStringSubmatch(s, -1)
|
||||||
|
require.Equal(t, 3, len(matches[0]))
|
||||||
|
require.Equal(t, "", matches[0][2])
|
||||||
|
require.Equal(t, "2", matches[1][2])
|
||||||
|
|
||||||
|
index := arrayTypePattern.FindStringIndex(s)[0]
|
||||||
|
require.Equal(t, 5, index)
|
||||||
|
require.Equal(t, "uint8", s[0:index])
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package abispec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HexToNumber(hex string) string {
|
||||||
|
num, success := big.NewInt(0).SetString(hex, 16)
|
||||||
|
if success {
|
||||||
|
return num.String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func NumberToHex(numString string) string {
|
||||||
|
num, success := big.NewInt(0).SetString(numString, 0)
|
||||||
|
if success {
|
||||||
|
return fmt.Sprintf("%x", num)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package abispec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHexToNumber(t *testing.T) {
|
||||||
|
//hex number is less than 53 bits, it returns a number
|
||||||
|
num := HexToNumber("9")
|
||||||
|
bytes, err := json.Marshal(num)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `"9"`, string(bytes))
|
||||||
|
|
||||||
|
num = HexToNumber("99999999")
|
||||||
|
bytes, err = json.Marshal(num)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `"2576980377"`, string(bytes))
|
||||||
|
|
||||||
|
num = HexToNumber("1fffffffffffff")
|
||||||
|
bytes, err = json.Marshal(num)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `"9007199254740991"`, string(bytes))
|
||||||
|
|
||||||
|
num = HexToNumber("9999999999999999")
|
||||||
|
bytes, err = json.Marshal(num)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, `"11068046444225730969"`, string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNumberToHex(t *testing.T) {
|
||||||
|
require.Equal(t, "20000000000002", NumberToHex("9007199254740994"))
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/status-im/zxcvbn-go"
|
"github.com/status-im/zxcvbn-go"
|
||||||
"github.com/status-im/zxcvbn-go/scoring"
|
"github.com/status-im/zxcvbn-go/scoring"
|
||||||
|
|
||||||
|
abi_spec "github.com/status-im/status-go/abi-spec"
|
||||||
"github.com/status-im/status-go/api"
|
"github.com/status-im/status-go/api"
|
||||||
"github.com/status-im/status-go/api/multiformat"
|
"github.com/status-im/status-go/api/multiformat"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
@ -937,3 +938,51 @@ func InputConnectionStringForBootstrapping(cs, configJSON string) string {
|
||||||
err := server.StartUpPairingClient(statusBackend.GetMultiaccountDB(), cs, configJSON)
|
err := server.StartUpPairingClient(statusBackend.GetMultiaccountDB(), cs, configJSON)
|
||||||
return makeJSONResponse(err)
|
return makeJSONResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EncodeTransfer(to string, value string) string {
|
||||||
|
result, err := abi_spec.EncodeTransfer(to, value)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to encode transfer", "to", to, "value", value, "error", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeFunctionCall(method string, paramsJSON string) string {
|
||||||
|
result, err := abi_spec.Encode(method, paramsJSON)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to encode function call", "method", method, "paramsJSON", paramsJSON, "error", err)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeParameters(decodeParamJSON string) string {
|
||||||
|
decodeParam := struct {
|
||||||
|
BytesString string `json:"bytesString"`
|
||||||
|
Types []string `json:"types"`
|
||||||
|
}{}
|
||||||
|
err := json.Unmarshal([]byte(decodeParamJSON), &decodeParam)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to unmarshal json when decoding parameters", "decodeParamJSON", decodeParamJSON, "error", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
result, err := abi_spec.Decode(decodeParam.BytesString, decodeParam.Types)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to decode parameters", "decodeParamJSON", decodeParamJSON, "error", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
bytes, err := json.Marshal(result)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to marshal result", "result", result, "decodeParamJSON", decodeParamJSON, "error", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HexToNumber(hex string) string {
|
||||||
|
return abi_spec.HexToNumber(hex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NumberToHex(numString string) string {
|
||||||
|
return abi_spec.NumberToHex(numString)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue