191 lines
5.2 KiB
Go
191 lines
5.2 KiB
Go
|
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
|
||
|
}
|