-
Notifications
You must be signed in to change notification settings - Fork 687
/
Copy pathBaseAccount.sol
160 lines (143 loc) · 5.96 KB
/
BaseAccount.sol
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
160
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-empty-blocks */
/* solhint-disable no-inline-assembly */
import "../interfaces/IAccount.sol";
import "../interfaces/IEntryPoint.sol";
import "../utils/Exec.sol";
import "./UserOperationLib.sol";
/**
* Basic account implementation.
* This contract provides the basic logic for implementing the IAccount interface - validateUserOp
* Specific account implementation should inherit it and provide the account-specific logic.
*/
abstract contract BaseAccount is IAccount {
using UserOperationLib for PackedUserOperation;
struct Call {
address target;
uint256 value;
bytes data;
}
error ExecuteError(uint256 index, bytes error);
/**
* Return the account nonce.
* This method returns the next sequential nonce.
* For a nonce of a specific key, use `entrypoint.getNonce(account, key)`
*/
function getNonce() public view virtual returns (uint256) {
return entryPoint().getNonce(address(this), 0);
}
/**
* Return the entryPoint used by this account.
* Subclass should return the current entryPoint used by this account.
*/
function entryPoint() public view virtual returns (IEntryPoint);
/**
* execute a single call from the account.
*/
function execute(address target, uint256 value, bytes calldata data) virtual external {
_requireForExecute();
bool ok = Exec.call(target, value, data, gasleft());
if (!ok) {
Exec.revertWithReturnData();
}
}
/**
* execute a batch of calls.
* revert on the first call that fails.
* If the batch reverts, and it contains more than a single call, then wrap the revert with ExecuteError,
* to mark the failing call index.
*/
function executeBatch(Call[] calldata calls) external {
_requireForExecute();
for (uint256 i = 0; i < calls.length; i++) {
Call calldata call = calls[i];
bool ok = Exec.call(call.target, call.value, call.data, gasleft());
if (!ok) {
if (calls.length == 1) {
Exec.revertWithReturnData();
} else {
revert ExecuteError(i, Exec.getReturnData(0));
}
}
}
}
/// @inheritdoc IAccount
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) external virtual override returns (uint256 validationData) {
_requireFromEntryPoint();
validationData = _validateSignature(userOp, userOpHash);
_validateNonce(userOp.nonce);
_payPrefund(missingAccountFunds);
}
/**
* Ensure the request comes from the known entrypoint.
*/
function _requireFromEntryPoint() internal view virtual {
require(
msg.sender == address(entryPoint()),
"account: not from EntryPoint"
);
}
function _requireForExecute() internal view virtual {
_requireFromEntryPoint();
}
/**
* Validate the signature is valid for this message.
* @param userOp - Validate the userOp.signature field.
* @param userOpHash - Convenient field: the hash of the request, to check the signature against.
* (also hashes the entrypoint and chain id)
* @return validationData - Signature and time-range of this operation.
* <20-byte> aggregatorOrSigFail - 0 for valid signature, 1 to mark signature failure,
* otherwise, an address of an aggregator contract.
* <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite"
* <6-byte> validAfter - first timestamp this operation is valid
* If the account doesn't use time-range, it is enough to return
* SIG_VALIDATION_FAILED value (1) for signature failure.
* Note that the validation code cannot use block.timestamp (or block.number) directly.
*/
function _validateSignature(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal virtual returns (uint256 validationData);
/**
* Validate the nonce of the UserOperation.
* This method may validate the nonce requirement of this account.
* e.g.
* To limit the nonce to use sequenced UserOps only (no "out of order" UserOps):
* `require(nonce < type(uint64).max)`
* For a hypothetical account that *requires* the nonce to be out-of-order:
* `require(nonce & type(uint64).max == 0)`
*
* The actual nonce uniqueness is managed by the EntryPoint, and thus no other
* action is needed by the account itself.
*
* @param nonce to validate
*
* solhint-disable-next-line no-empty-blocks
*/
function _validateNonce(uint256 nonce) internal view virtual {
}
/**
* Sends to the entrypoint (msg.sender) the missing funds for this transaction.
* SubClass MAY override this method for better funds management
* (e.g. send to the entryPoint more than the minimum required, so that in future transactions
* it will not be required to send again).
* @param missingAccountFunds - The minimum value this method should send the entrypoint.
* This value MAY be zero, in case there is enough deposit,
* or the userOp has a paymaster.
*/
function _payPrefund(uint256 missingAccountFunds) internal virtual {
if (missingAccountFunds != 0) {
(bool success,) = payable(msg.sender).call{
value: missingAccountFunds
}("");
(success);
//ignore failure (its EntryPoint's job to verify, not account.)
}
}
}