/
SimpleAccount.sol
179 lines (151 loc) · 5.35 KB
/
SimpleAccount.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.12;
/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-inline-assembly */
/* solhint-disable reason-string */
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "../core/BaseAccount.sol";
import {RsaVerify} from "../RsaVerify.sol";
/**
* minimal account.
* this is sample minimal account.
* has execute, eth handling methods
* has a single signer that can send requests through the entryPoint.
*/
contract SimpleAccount is BaseAccount, UUPSUpgradeable, Initializable {
using RsaVerify for bytes32;
//filler member, to push the nonce and owner to the same slot
// the "Initializeble" class takes 2 bytes in the first slot
bytes28 private _filler;
//explicit sizes of nonce, to fit a single storage cell with "owner"
uint96 private _nonce;
bytes public modulus;
bytes internal constant exponent =
hex"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010001";
IEntryPoint private immutable _entryPoint;
event SimpleAccountInitialized(
IEntryPoint indexed entryPoint,
bytes modulus
);
modifier onlyOwner() {
_onlyOwner();
_;
}
/// @inheritdoc BaseAccount
function nonce() public view virtual override returns (uint256) {
return _nonce;
}
/// @inheritdoc BaseAccount
function entryPoint() public view virtual override returns (IEntryPoint) {
return _entryPoint;
}
// solhint-disable-next-line no-empty-blocks
receive() external payable {}
constructor(IEntryPoint anEntryPoint) {
_entryPoint = anEntryPoint;
_disableInitializers();
}
function _onlyOwner() internal view {
//directly through the account itself (which gets redirected through execute())
require(msg.sender == address(this), "only owner");
}
/**
* execute a transaction (called directly from owner, or by entryPoint)
*/
function execute(
address dest,
uint256 value,
bytes calldata func
) external {
_requireFromEntryPoint();
_call(dest, value, func);
}
/**
* execute a sequence of transactions
*/
function executeBatch(
address[] calldata dest,
bytes[] calldata func
) external {
_requireFromEntryPoint();
require(dest.length == func.length, "wrong array lengths");
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], 0, func[i]);
}
}
/**
* @dev The _entryPoint member is immutable, to reduce gas consumption. To upgrade EntryPoint,
* a new implementation of SimpleAccount must be deployed with the new EntryPoint address, then upgrading
* the implementation by calling `upgradeTo()`
*/
function initialize(bytes memory anModulus) public virtual initializer {
_initialize(anModulus);
}
function _initialize(bytes memory anModulus) internal virtual {
modulus = anModulus;
emit SimpleAccountInitialized(_entryPoint, anModulus);
}
/// implement template method of BaseAccount
function _validateAndUpdateNonce(
UserOperation calldata userOp
) internal override {
require(_nonce++ == userOp.nonce, "account: invalid nonce");
}
/// implement template method of BaseAccount
function _validateSignature(
UserOperation calldata userOp,
bytes32 userOpHash
) internal virtual override returns (uint256 validationData) {
bytes32 hash = sha256(abi.encode(userOpHash));
uint256 ret = _verifySha256(hash, userOp.signature, exponent, modulus);
if (ret != 0) return SIG_VALIDATION_FAILED;
return 0;
}
function _call(address target, uint256 value, bytes memory data) internal {
(bool success, bytes memory result) = target.call{value: value}(data);
if (!success) {
assembly {
revert(add(result, 32), mload(result))
}
}
}
/**
* check current account deposit in the entryPoint
*/
function getDeposit() public view returns (uint256) {
return entryPoint().balanceOf(address(this));
}
/**
* deposit more funds for this account in the entryPoint
*/
function addDeposit() public payable {
entryPoint().depositTo{value: msg.value}(address(this));
}
/**
* withdraw value from the account's deposit
* @param withdrawAddress target to send to
* @param amount to withdraw
*/
function withdrawDepositTo(
address payable withdrawAddress,
uint256 amount
) public onlyOwner {
entryPoint().withdrawTo(withdrawAddress, amount);
}
function _authorizeUpgrade(
address newImplementation
) internal view override {
(newImplementation);
_onlyOwner();
}
// verify
function _verifySha256(
bytes32 _hash,
bytes memory _signature,
bytes memory _exponent,
bytes memory _modulus
) public view returns (uint256) {
return _hash.pkcs1Sha256Verify(_signature, _exponent, _modulus);
}
}