-
Notifications
You must be signed in to change notification settings - Fork 124
/
parse.go
120 lines (101 loc) · 2.57 KB
/
parse.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package userop
import (
"encoding/hex"
"errors"
"fmt"
"math/big"
"reflect"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/go-playground/validator/v10"
"github.com/mitchellh/mapstructure"
)
var (
validate = validator.New()
onlyOnce = sync.Once{}
ErrBadUserOperationData = errors.New("cannot decode UserOperation")
)
func exactFieldMatch(mapKey, fieldName string) bool {
return mapKey == fieldName
}
func decodeOpTypes(
f reflect.Kind,
t reflect.Kind,
data interface{}) (interface{}, error) {
// String to common.Address conversion
if f == reflect.String && t == reflect.Array {
return common.HexToAddress(data.(string)), nil
}
// String to big.Int conversion
if f == reflect.String && t == reflect.Struct {
n := new(big.Int)
n, ok := n.SetString(data.(string), 0)
if !ok {
return nil, errors.New("bigInt conversion failed")
}
return n, nil
}
// Float64 to big.Int conversion
if f == reflect.Float64 && t == reflect.Struct {
n, ok := data.(float64)
if !ok {
return nil, errors.New("bigInt conversion failed")
}
return big.NewInt(int64(n)), nil
}
// String to []byte conversion
if f == reflect.String && t == reflect.Slice {
byteStr := data.(string)
if len(byteStr) < 2 || byteStr[:2] != "0x" {
return nil, errors.New("not byte string")
}
b, err := hex.DecodeString(byteStr[2:])
if err != nil {
return nil, err
}
return b, nil
}
return data, nil
}
func validateAddressType(field reflect.Value) interface{} {
value, ok := field.Interface().(common.Address)
if !ok || value == common.HexToAddress("0x") {
return nil
}
return field
}
func validateBigIntType(field reflect.Value) interface{} {
value, ok := field.Interface().(big.Int)
if !ok || value.Cmp(big.NewInt(0)) == -1 {
return nil
}
return field
}
// New decodes a map into a UserOperation object and validates all the fields are correctly typed.
func New(data map[string]any) (*UserOperation, error) {
var op UserOperation
// Convert map to struct
config := &mapstructure.DecoderConfig{
DecodeHook: decodeOpTypes,
Result: &op,
ErrorUnset: true,
MatchName: exactFieldMatch,
}
decoder, err := mapstructure.NewDecoder(config)
if err != nil {
return nil, err
}
if err := decoder.Decode(data); err != nil {
return nil, fmt.Errorf("%w: %w", ErrBadUserOperationData, err)
}
// Validate struct
onlyOnce.Do(func() {
validate.RegisterCustomTypeFunc(validateAddressType, common.Address{})
validate.RegisterCustomTypeFunc(validateBigIntType, big.Int{})
})
err = validate.Struct(op)
if err != nil {
return nil, err
}
return &op, nil
}