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 )