-
Notifications
You must be signed in to change notification settings - Fork 1
/
types.go
159 lines (132 loc) · 6.03 KB
/
types.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package account_abstraction
import (
"encoding/json"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/holiman/uint256"
"github.com/pkg/errors"
)
// EntryPointContract defines the address of the built-in AA entry point contract.
var EntryPointContract = common.HexToAddress("0x000000000000000000000000000000000000AAEC")
var entrypointABI, _ = IEntryPointMetaData.GetAbi()
var (
userOperationPackedJJSONABI = "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"hashInitCode\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"hashCallData\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"hashPaymasterAndData\",\"type\":\"bytes32\"}],\"name\":\"pack\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"entrypoint\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"}],\"name\":\"packHash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
userOperationPackedABI = parseABI(userOperationPackedJJSONABI)
)
type ABIItem interface {
Unpack(data []byte) (interface{}, error)
}
// ReturnInfo is the return value of the entry point.
type ReturnInfo struct {
PreOpGas *uint256.Int `json:"preOpGas"`
Prefund *uint256.Int `json:"prefund"`
SigFailed bool `json:"sigFailed"`
ValidAfter uint64 `json:"validAfter"` // Using uint64 since Go doesn't have an uint48 type.
ValidUntil uint64 `json:"validUntil"` // Using uint64 to accommodate uint48.
PaymasterContext []byte `json:"paymasterContext"`
}
// StakeInfo is the stake information of an account.
type StakeInfo struct {
Stake *uint256.Int `json:"stake"`
UnstakeDelaySec *uint256.Int `json:"unstakeDelaySec"`
}
// ValidationResult is the result of the validation.
type ValidationResult struct {
ReturnInfo *ReturnInfo `json:"returnInfo"`
SenderInfo *StakeInfo `json:"senderInfo"`
FactoryInfo *StakeInfo `json:"factoryInfo"`
PaymasterInfo *StakeInfo `json:"paymasterInfo"`
}
func DecodeValidationResult(data []byte) (*ValidationResult, error) {
// failed to DecodeError as ValidationResult, try to DecodeError as FailedOp
validationResultABI := entrypointABI.Errors["ValidationResult"]
return DecodeError[ValidationResult](&validationResultABI, data)
}
// FailedOp is the failed operation error returned by aa entrypoint.
type FailedOp struct {
OpIndex *uint256.Int `json:"opIndex"`
Reason string `json:"reason"`
}
func DecodeFailedOpError(data []byte) error {
// failed to DecodeError as ValidationResult, try to DecodeError as FailedOp
failedOpABI := entrypointABI.Errors["FailedOp"]
failedOp, err := DecodeError[FailedOp](&failedOpABI, data)
if err != nil {
// DecodeError fail means it's not a FailedOp error
return errors.New("unknown error")
}
// return fail reason
return errors.New(failedOp.Reason)
}
// ExecutionResult is the result of the aa operation execution.
type ExecutionResult struct {
PreOpGas *uint256.Int `json:"preOpGas"`
Paid *uint256.Int `json:"paid"`
ValidAfter uint64 `json:"validAfter"` // Using uint64 since Go doesn't have a uint48 type.
ValidUntil uint64 `json:"validUntil"` // Using uint64 to accommodate uint48.
TargetSuccess bool `json:"targetSuccess"`
TargetResult []byte `json:"targetResult"`
}
func DecodeExecutionResult(data []byte) (*ExecutionResult, error) {
// failed to DecodeError as ValidationResult, try to DecodeError as FailedOp
executionResultABI := entrypointABI.Errors["ExecutionResult"]
return DecodeError[ExecutionResult](&executionResultABI, data)
}
func (i *UserOperation) Hash(chainId *big.Int) common.Hash {
initCodeHash := crypto.Keccak256Hash(i.InitCode)
callDataHash := crypto.Keccak256Hash(i.CallData)
paymasterAndDataHash := crypto.Keccak256Hash(i.PaymasterAndData)
packed, _ := userOperationPackedABI.Methods["pack"].Inputs.Pack(
i.Sender,
i.Nonce,
initCodeHash,
callDataHash,
i.CallGasLimit,
i.VerificationGasLimit,
i.PreVerificationGas,
i.MaxFeePerGas,
i.MaxPriorityFeePerGas,
paymasterAndDataHash)
userOpHash := crypto.Keccak256Hash(packed)
hashPacked, _ := userOperationPackedABI.Methods["packHash"].Inputs.Pack(
userOpHash,
EntryPointContract,
chainId,
)
return crypto.Keccak256Hash(hashPacked)
}
func DecodeResponse(methodName string, data []byte) ([]interface{}, error) {
method, ok := entrypointABI.Methods[methodName]
if !ok {
return nil, errors.New("method not found")
}
return method.Outputs.Unpack(data)
}
func DecodeError[V any](decodeErrorABI *abi.Error, data []byte) (*V, error) {
res, err := decodeErrorABI.Unpack(data)
if err != nil {
return nil, err
}
if casted, ok := res.(*V); ok {
return casted, nil
}
return InterfaceToStruct[V](res)
}
// InterfaceToStruct converts interface to struct,
// use json here for convenience, optimize later
func InterfaceToStruct[T any](input interface{}) (*T, error) {
raw, _ := json.Marshal(input)
var output T
err := json.Unmarshal(raw, &output)
return &output, err
}
func PackCallData(ops []*UserOperation, beneficiary common.Address) ([]byte, error) {
return entrypointABI.Pack("handleOps", ops, beneficiary)
}
func parseABI(json string) *abi.ABI {
parsedABI, _ := abi.JSON(strings.NewReader(json))
return &parsedABI
}