-
Notifications
You must be signed in to change notification settings - Fork 126
/
userop.go
122 lines (111 loc) · 4.59 KB
/
userop.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
// Package userop provides an ERC-4337 pseudo-transaction object called a UserOperation
// which is used to execute actions through a smart contract account.
// This isn't to be mistaken for a regular transaction type.
package userop
import (
"encoding/json"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/shopspring/decimal"
)
// UserOperation represents an EIP-4337 style transaction for a smart contract account.
type UserOperation struct {
Sender common.Address `json:"sender"`
Nonce decimal.Decimal `json:"nonce"`
InitCode []byte `json:"initCode"`
CallData []byte `json:"callData"`
CallGasLimit decimal.Decimal `json:"callGasLimit"`
VerificationGasLimit decimal.Decimal `json:"verificationGasLimit"`
PreVerificationGas decimal.Decimal `json:"preVerificationGas"`
MaxFeePerGas decimal.Decimal `json:"maxFeePerGas"`
MaxPriorityFeePerGas decimal.Decimal `json:"maxPriorityFeePerGas"`
PaymasterAndData []byte `json:"paymasterAndData"`
Signature []byte `json:"signature,omitempty"`
}
// GetFactory returns the address portion of InitCode if applicable.
// Otherwise, it returns the zero address.
func (op *UserOperation) GetFactory() common.Address {
if len(op.InitCode) < common.AddressLength {
return common.HexToAddress("0x")
}
return common.BytesToAddress(op.InitCode[:common.AddressLength])
}
// GetFactoryData returns the data portion of InitCode if applicable.
// Otherwise, it returns an empty byte array.
func (op *UserOperation) GetFactoryData() []byte {
if len(op.InitCode) < common.AddressLength {
return []byte{}
}
return op.InitCode[common.AddressLength:]
}
// UserOpHash returns the hash of the userOp + entryPoint address + chainID.
func (op *UserOperation) UserOpHash(entryPoint common.Address, chainID *big.Int) (common.Hash, error) {
args := abi.Arguments{
{Name: "sender", Type: address},
{Name: "nonce", Type: uint256},
{Name: "hashInitCode", Type: bytes32},
{Name: "hashCallData", Type: bytes32},
{Name: "callGasLimit", Type: uint256},
{Name: "verificationGasLimit", Type: uint256},
{Name: "preVerificationGas", Type: uint256},
{Name: "maxFeePerGas", Type: uint256},
{Name: "maxPriorityFeePerGas", Type: uint256},
{Name: "hashPaymasterAndData", Type: bytes32},
}
packed, err := args.Pack(
op.Sender,
op.Nonce.BigInt(),
crypto.Keccak256Hash(op.InitCode),
crypto.Keccak256Hash(op.CallData),
op.CallGasLimit.BigInt(),
op.VerificationGasLimit.BigInt(),
op.PreVerificationGas.BigInt(),
op.MaxFeePerGas.BigInt(),
op.MaxPriorityFeePerGas.BigInt(),
crypto.Keccak256Hash(op.PaymasterAndData),
)
if err != nil { // This should never happen
return common.Hash{}, fmt.Errorf("failed to pack UserOperation: %w", err)
}
return crypto.Keccak256Hash(
crypto.Keccak256(packed),
common.LeftPadBytes(entryPoint.Bytes(), 32),
common.LeftPadBytes(chainID.Bytes(), 32),
), nil
}
// MarshalJSON returns a JSON encoding of the UserOperation.
func (op UserOperation) MarshalJSON() ([]byte, error) {
ic := "0x"
if fa := op.GetFactory(); fa != common.HexToAddress("0x") {
ic = fmt.Sprintf("%s%s", fa, common.Bytes2Hex(op.GetFactoryData()))
}
return json.Marshal(&struct {
Sender string `json:"sender"`
Nonce string `json:"nonce"`
InitCode string `json:"initCode"`
CallData string `json:"callData"`
CallGasLimit string `json:"callGasLimit"`
VerificationGasLimit string `json:"verificationGasLimit"`
PreVerificationGas string `json:"preVerificationGas"`
MaxFeePerGas string `json:"maxFeePerGas"`
MaxPriorityFeePerGas string `json:"maxPriorityFeePerGas"`
PaymasterAndData string `json:"paymasterAndData"`
Signature string `json:"signature"`
}{
Sender: op.Sender.String(),
Nonce: hexutil.EncodeBig(op.Nonce.BigInt()),
InitCode: ic,
CallData: hexutil.Encode(op.CallData),
CallGasLimit: hexutil.EncodeBig(op.CallGasLimit.BigInt()),
VerificationGasLimit: hexutil.EncodeBig(op.VerificationGasLimit.BigInt()),
PreVerificationGas: hexutil.EncodeBig(op.PreVerificationGas.BigInt()),
MaxFeePerGas: hexutil.EncodeBig(op.MaxFeePerGas.BigInt()),
MaxPriorityFeePerGas: hexutil.EncodeBig(op.MaxPriorityFeePerGas.BigInt()),
PaymasterAndData: hexutil.Encode(op.PaymasterAndData),
Signature: hexutil.Encode(op.Signature),
})
}