github.com/0xsequence/ethkit@v1.25.0/ethcoder/solidity_pack.go (about)

     1  package ethcoder
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"reflect"
     7  	"regexp"
     8  	"strconv"
     9  
    10  	"github.com/0xsequence/ethkit/go-ethereum/common"
    11  	"github.com/0xsequence/ethkit/go-ethereum/common/hexutil"
    12  	"github.com/0xsequence/ethkit/go-ethereum/common/math"
    13  )
    14  
    15  // a port of ethers/utils/solidity.ts
    16  
    17  func SolidityPack(argTypes []string, argValues []interface{}) ([]byte, error) {
    18  	if len(argTypes) != len(argValues) {
    19  		return nil, fmt.Errorf("invalid arguments - types and values do not match")
    20  	}
    21  	pack := []byte{}
    22  	for i := 0; i < len(argTypes); i++ {
    23  		b, err := solidityArgumentPack(argTypes[i], argValues[i], false)
    24  		if err != nil {
    25  			return nil, err
    26  		}
    27  		pack = append(pack, b...)
    28  	}
    29  	return pack, nil
    30  }
    31  
    32  func SolidityPackHex(argTypes []string, argValues []interface{}) (string, error) {
    33  	b, err := SolidityPack(argTypes, argValues)
    34  	if err != nil {
    35  		return "", err
    36  	}
    37  	h := hexutil.Encode(b)
    38  	return h, nil
    39  }
    40  
    41  func solidityArgumentPackHex(typ string, val interface{}, isArray bool) (string, error) {
    42  	b, err := solidityArgumentPack(typ, val, isArray)
    43  	if err != nil {
    44  		return "", err
    45  	}
    46  	h := hexutil.Encode(b)
    47  	return h, nil
    48  }
    49  
    50  func solidityArgumentPack(typ string, val interface{}, isArray bool) ([]byte, error) {
    51  	switch typ {
    52  	case "address":
    53  		v, ok := val.(common.Address)
    54  		if !ok {
    55  			return nil, fmt.Errorf("not an common.Address")
    56  		}
    57  		b := v.Bytes()
    58  		if isArray {
    59  			return PadZeros(b, 32)
    60  		}
    61  		return b, nil
    62  
    63  	case "string":
    64  		v, ok := val.(string)
    65  		if !ok {
    66  			return nil, fmt.Errorf("not a string")
    67  		}
    68  		h := hexutil.Encode([]byte(v))
    69  		b, err := hexutil.Decode(h)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		return b, nil
    74  
    75  	case "bytes":
    76  		b, ok := val.([]byte)
    77  		if !ok {
    78  			return nil, fmt.Errorf("not a []byte")
    79  		}
    80  		return b, nil
    81  
    82  	case "bool":
    83  		v, ok := val.(bool)
    84  		if !ok {
    85  			return nil, fmt.Errorf("not a bool")
    86  		}
    87  		var b []byte
    88  		if v {
    89  			b = []byte{1}
    90  		} else {
    91  			b = []byte{0}
    92  		}
    93  		if isArray {
    94  			return PadZeros(b, 32)
    95  		}
    96  		return b, nil
    97  	}
    98  
    99  	// numbers
   100  	if match := regexArgNumber.FindStringSubmatch(typ); len(match) > 0 {
   101  		size, err := strconv.ParseInt(match[2], 10, 64)
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		if (size%8 != 0) || size == 0 || size > 256 {
   106  			return nil, fmt.Errorf("invalid number type '%s'", typ)
   107  		}
   108  		if isArray {
   109  			size = 256
   110  		}
   111  
   112  		num := big.NewInt(0)
   113  		switch v := val.(type) {
   114  		case *big.Int:
   115  			num = v
   116  		case uint8:
   117  			num.SetUint64(uint64(v))
   118  		case uint16:
   119  			num.SetUint64(uint64(v))
   120  		case uint32:
   121  			num.SetUint64(uint64(v))
   122  		case uint64:
   123  			num.SetUint64(v)
   124  		case int8:
   125  			num.SetInt64(int64(v))
   126  		case int16:
   127  			num.SetInt64(int64(v))
   128  		case int32:
   129  			num.SetInt64(int64(v))
   130  		case int64:
   131  			num.SetInt64(v)
   132  		default:
   133  			return nil, fmt.Errorf("expecting *big.Int or (u)intX value for type '%s'", typ)
   134  		}
   135  
   136  		b := math.PaddedBigBytes(num, int(size/8))
   137  		return b, nil
   138  	}
   139  
   140  	// bytes
   141  	if match := regexArgBytes.FindStringSubmatch(typ); len(match) > 0 {
   142  		size, err := strconv.ParseInt(match[1], 10, 64)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		if size == 0 || size > 32 {
   147  			return nil, fmt.Errorf("invalid number type '%s'", typ)
   148  		}
   149  
   150  		if isArray {
   151  			// if (isArray) { return arrayify((value + Zeros).substring(0, 66)); }
   152  			return nil, fmt.Errorf("unsupported, file ticket.")
   153  		}
   154  
   155  		rv := reflect.ValueOf(val)
   156  		if rv.Type().Kind() != reflect.Array && rv.Type().Kind() != reflect.Slice {
   157  			return nil, fmt.Errorf("not an array")
   158  		}
   159  		if rv.Type().Elem().Kind() != reflect.Uint8 {
   160  			return nil, fmt.Errorf("not a byte array")
   161  		}
   162  		if rv.Len() != int(size) {
   163  			return nil, fmt.Errorf("not a [%d]byte", size)
   164  		}
   165  
   166  		v := make([]byte, size, size)
   167  		var ok bool
   168  		for i := 0; i < int(size); i++ {
   169  			v[i], ok = rv.Index(i).Interface().(byte)
   170  			if !ok {
   171  				return nil, fmt.Errorf("unable to set byte")
   172  			}
   173  		}
   174  		return v, nil
   175  	}
   176  
   177  	// arrays
   178  	if match := regexArgArray.FindStringSubmatch(typ); len(match) > 0 {
   179  		baseTyp := match[1]
   180  		if match[2] == "" {
   181  			match[2] = "0"
   182  		}
   183  		count, err := strconv.ParseInt(match[2], 10, 64)
   184  		if err != nil {
   185  			return nil, err
   186  		}
   187  
   188  		rv := reflect.ValueOf(val)
   189  		if rv.Type().Kind() != reflect.Array && rv.Type().Kind() != reflect.Slice {
   190  			return nil, fmt.Errorf("not an array")
   191  		}
   192  		size := rv.Len()
   193  		if count > 0 && size != int(count) {
   194  			return nil, fmt.Errorf("array size does not match required size of %d", count)
   195  		}
   196  
   197  		buf := []byte{}
   198  		for i := 0; i < size; i++ {
   199  			b, err := solidityArgumentPack(baseTyp, rv.Index(i).Interface(), true)
   200  			if err != nil {
   201  				return nil, err
   202  			}
   203  			buf = append(buf, b...)
   204  		}
   205  
   206  		return buf, nil
   207  	}
   208  
   209  	return nil, fmt.Errorf("unknown type '%s'", typ)
   210  }
   211  
   212  func PadZeros(array []byte, totalLength int) ([]byte, error) {
   213  	if len(array) > totalLength {
   214  		return nil, fmt.Errorf("array is larger than total expected length")
   215  	}
   216  
   217  	buf := make([]byte, totalLength)
   218  	i := totalLength - 1
   219  	for j := len(array) - 1; j >= 0; j-- {
   220  		buf[i] = array[j]
   221  		i--
   222  	}
   223  	return buf, nil
   224  }
   225  
   226  var (
   227  	regexArgBytes  = regexp.MustCompile(`^bytes([0-9]+)$`)
   228  	regexArgNumber = regexp.MustCompile(`^(u?int)([0-9]*)$`)
   229  	regexArgArray  = regexp.MustCompile(`^(.*)\[([0-9]*)\]$`)
   230  )