11// SPDX-License-Identifier: UNLICENSED
22pragma solidity ^ 0.8.0 ;
33
4+ /* solhint-disable avoid-low-level-calls */
5+ /* solhint-disable no-inline-assembly */
6+ /* solhint-disable reason-string */
7+
8+ // Base
9+ import "../smart-wallet/utils/BaseAccount.sol " ;
10+
11+ // Extensions
12+ import "../extension/Multicall.sol " ;
13+ import "../dynamic-contracts/extension/Initializable.sol " ;
14+ import "../dynamic-contracts/extension/ContractMetadata.sol " ;
15+ import "../openzeppelin-presets/token/ERC721/utils/ERC721Holder.sol " ;
16+ import "../openzeppelin-presets/token/ERC1155/utils/ERC1155Holder.sol " ;
17+ import "../eip/ERC1271.sol " ;
18+
19+ // Utils
20+ import "../openzeppelin-presets/utils/cryptography/ECDSA.sol " ;
21+ import "../smart-wallet/utils/BaseAccountFactory.sol " ;
22+
423import "./erc6551-utils/ERC6551AccountLib.sol " ;
524import "./erc6551-utils/IERC6551Account.sol " ;
625
726import "../eip/interface/IERC721.sol " ;
8- import "../smart-wallet/non-upgradeable/Account .sol " ;
27+ import "../openzeppelin-presets/utils/cryptography/EIP712 .sol " ;
928
10- contract TokenBoundAccount is Account , IERC6551Account {
29+ contract TokenBoundAccount is
30+ Initializable ,
31+ ERC1271 ,
32+ Multicall ,
33+ BaseAccount ,
34+ ContractMetadata ,
35+ ERC721Holder ,
36+ ERC1155Holder ,
37+ IERC6551Account ,
38+ EIP712
39+ {
1140 using ECDSA for bytes32 ;
1241
1342 /*///////////////////////////////////////////////////////////////
@@ -17,7 +46,17 @@ contract TokenBoundAccount is Account, IERC6551Account {
1746 event TokenBoundAccountCreated (address indexed account , bytes indexed data );
1847
1948 /*///////////////////////////////////////////////////////////////
20- Constructor
49+ State
50+ //////////////////////////////////////////////////////////////*/
51+
52+ /// @notice EIP 4337 factory for this contract.
53+ address public immutable factory;
54+
55+ /// @notice EIP 4337 Entrypoint contract.
56+ IEntryPoint private immutable entrypointContract;
57+
58+ /*///////////////////////////////////////////////////////////////
59+ Constructor, Initializer, Modifiers
2160 //////////////////////////////////////////////////////////////*/
2261
2362 /**
@@ -27,14 +66,22 @@ contract TokenBoundAccount is Account, IERC6551Account {
2766 * @param _factory - The factory contract address to issue token Bound accounts
2867 *
2968 */
30- constructor (IEntryPoint _entrypoint , address _factory ) Account (_entrypoint, _factory ) {
69+ constructor (IEntryPoint _entrypoint , address _factory ) EIP712 ( " TokenBoundAccount " , " 1 " ) {
3170 _disableInitializers ();
71+ factory = _factory;
72+ entrypointContract = _entrypoint;
3273 }
3374
34- receive () external payable override (IERC6551Account , Account) {}
75+ // solhint-disable-next-line no-empty-blocks
76+ receive () external payable virtual {}
77+
78+ /// @notice Initializes the smart contract wallet.
79+ function initialize (address _defaultAdmin , bytes calldata _data ) public virtual initializer {
80+ emit TokenBoundAccountCreated (_defaultAdmin, _data);
81+ }
3582
3683 /// @notice Returns whether a signer is authorized to perform transactions using the wallet.
37- function isValidSigner (address _signer , UserOperation calldata ) public view override returns (bool ) {
84+ function isValidSigner (address _signer , UserOperation calldata ) public view returns (bool ) {
3885 return (owner () == _signer);
3986 }
4087
@@ -70,7 +117,7 @@ contract TokenBoundAccount is Account, IERC6551Account {
70117 }
71118
72119 /// @notice Withdraw funds for this account from Entrypoint.
73- function withdrawDepositTo (address payable withdrawAddress , uint256 amount ) public virtual override {
120+ function withdrawDepositTo (address payable withdrawAddress , uint256 amount ) public virtual {
74121 require (owner () == msg .sender , "Account: not NFT owner " );
75122 entryPoint ().withdrawTo (withdrawAddress, amount);
76123 }
@@ -91,6 +138,50 @@ contract TokenBoundAccount is Account, IERC6551Account {
91138 return getNonce ();
92139 }
93140
141+ /// @notice See {IERC165-supportsInterface}.
142+ function supportsInterface (bytes4 interfaceId ) public view virtual override (ERC1155Receiver ) returns (bool ) {
143+ return
144+ interfaceId == type (IERC1155Receiver ).interfaceId ||
145+ interfaceId == type (IERC721Receiver ).interfaceId ||
146+ super .supportsInterface (interfaceId);
147+ }
148+
149+ /// @notice Returns the EIP 4337 entrypoint contract.
150+ function entryPoint () public view virtual override returns (IEntryPoint) {
151+ return entrypointContract;
152+ }
153+
154+ /// @notice Returns the balance of the account in Entrypoint.
155+ function getDeposit () public view virtual returns (uint256 ) {
156+ return entryPoint ().balanceOf (address (this ));
157+ }
158+
159+ /// @notice Executes a transaction (called directly from an admin, or by entryPoint)
160+ function execute (
161+ address _target ,
162+ uint256 _value ,
163+ bytes calldata _calldata
164+ ) external virtual onlyAdminOrEntrypoint {
165+ _call (_target, _value, _calldata);
166+ }
167+
168+ /// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint)
169+ function executeBatch (
170+ address [] calldata _target ,
171+ uint256 [] calldata _value ,
172+ bytes [] calldata _calldata
173+ ) external virtual onlyAdminOrEntrypoint {
174+ require (_target.length == _calldata.length && _target.length == _value.length , "Account: wrong array lengths. " );
175+ for (uint256 i = 0 ; i < _target.length ; i++ ) {
176+ _call (_target[i], _value[i], _calldata[i]);
177+ }
178+ }
179+
180+ /// @notice Deposit funds for this account in Entrypoint.
181+ function addDeposit () public payable virtual {
182+ entryPoint ().depositTo { value: msg .value }(address (this ));
183+ }
184+
94185 /*///////////////////////////////////////////////////////////////
95186 Internal Functions
96187 //////////////////////////////////////////////////////////////*/
@@ -99,7 +190,7 @@ contract TokenBoundAccount is Account, IERC6551Account {
99190 address _target ,
100191 uint256 value ,
101192 bytes memory _calldata
102- ) internal virtual override returns (bytes memory result ) {
193+ ) internal virtual returns (bytes memory result ) {
103194 bool success;
104195 (success, result) = _target.call { value: value }(_calldata);
105196 if (! success) {
@@ -109,12 +200,65 @@ contract TokenBoundAccount is Account, IERC6551Account {
109200 }
110201 }
111202
203+ /// @notice Validates the signature of a user operation.
204+ function _validateSignature (UserOperation calldata userOp , bytes32 userOpHash )
205+ internal
206+ virtual
207+ override
208+ returns (uint256 validationData )
209+ {
210+ bytes32 hash = userOpHash.toEthSignedMessageHash ();
211+ address signer = hash.recover (userOp.signature);
212+
213+ if (! isValidSigner (signer, userOp)) return SIG_VALIDATION_FAILED;
214+ return 0 ;
215+ }
216+
217+ function getFunctionSignature (bytes calldata data ) internal pure returns (bytes4 functionSelector ) {
218+ require (data.length >= 4 , "Data too short " );
219+ return bytes4 (data[:4 ]);
220+ }
221+
222+ function decodeExecuteCalldata (bytes calldata data ) internal pure returns (address _target , uint256 _value ) {
223+ require (data.length >= 4 + 32 + 32 , "Data too short " );
224+
225+ // Decode the address, which is bytes 4 to 35
226+ _target = abi.decode (data[4 :36 ], (address ));
227+
228+ // Decode the value, which is bytes 36 to 68
229+ _value = abi.decode (data[36 :68 ], (uint256 ));
230+ }
231+
232+ function decodeExecuteBatchCalldata (bytes calldata data )
233+ internal
234+ pure
235+ returns (
236+ address [] memory _targets ,
237+ uint256 [] memory _values ,
238+ bytes [] memory _callData
239+ )
240+ {
241+ require (data.length >= 4 + 32 + 32 + 32 , "Data too short " );
242+
243+ (_targets, _values, _callData) = abi.decode (data[4 :], (address [], uint256 [], bytes []));
244+ }
245+
246+ /// @dev Returns whether contract metadata can be set in the given execution context.
247+ function _canSetContractURI () internal view virtual override returns (bool ) {
248+ return msg .sender == owner ();
249+ }
250+
112251 /*///////////////////////////////////////////////////////////////
113252 Modifiers
114253 //////////////////////////////////////////////////////////////*/
115254
116- modifier onlyAdminOrEntrypoint () override {
255+ modifier onlyAdminOrEntrypoint () {
117256 require (msg .sender == address (entryPoint ()) || msg .sender == owner (), "Account: not admin or EntryPoint. " );
118257 _;
119258 }
259+
260+ modifier onlyAdmin () {
261+ require (msg .sender == owner (), "Account: not admin. " );
262+ _;
263+ }
120264}
0 commit comments