draft-AccountERC7579.sol 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.4.0) (account/extensions/draft-AccountERC7579.sol)
  3. pragma solidity ^0.8.26;
  4. import {PackedUserOperation} from "../../interfaces/draft-IERC4337.sol";
  5. import {IERC1271} from "../../interfaces/IERC1271.sol";
  6. import {
  7. IERC7579Module,
  8. IERC7579Validator,
  9. IERC7579Execution,
  10. IERC7579AccountConfig,
  11. IERC7579ModuleConfig,
  12. MODULE_TYPE_VALIDATOR,
  13. MODULE_TYPE_EXECUTOR,
  14. MODULE_TYPE_FALLBACK
  15. } from "../../interfaces/draft-IERC7579.sol";
  16. import {ERC7579Utils, Mode, CallType, ExecType} from "../../account/utils/draft-ERC7579Utils.sol";
  17. import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";
  18. import {Bytes} from "../../utils/Bytes.sol";
  19. import {Packing} from "../../utils/Packing.sol";
  20. import {Address} from "../../utils/Address.sol";
  21. import {Calldata} from "../../utils/Calldata.sol";
  22. import {Account} from "../Account.sol";
  23. /**
  24. * @dev Extension of {Account} that implements support for ERC-7579 modules.
  25. *
  26. * To comply with the ERC-1271 support requirement, this contract defers signature validation to
  27. * installed validator modules by calling {IERC7579Validator-isValidSignatureWithSender}.
  28. *
  29. * This contract does not implement validation logic for user operations since this functionality
  30. * is often delegated to self-contained validation modules. Developers must install a validator module
  31. * upon initialization (or any other mechanism to enable execution from the account):
  32. *
  33. * ```solidity
  34. * contract MyAccountERC7579 is AccountERC7579, Initializable {
  35. * function initializeAccount(address validator, bytes calldata validatorData) public initializer {
  36. * _installModule(MODULE_TYPE_VALIDATOR, validator, validatorData);
  37. * }
  38. * }
  39. * ```
  40. *
  41. * [NOTE]
  42. * ====
  43. * * Hook support is not included. See {AccountERC7579Hooked} for a version that hooks to execution.
  44. * * Validator selection, when verifying either ERC-1271 signature or ERC-4337 UserOperation is implemented in
  45. * internal virtual functions {_extractUserOpValidator} and {_extractSignatureValidator}. Both are implemented
  46. * following common practices. However, this part is not standardized in ERC-7579 (or in any follow-up ERC). Some
  47. * accounts may want to override these internal functions.
  48. * * When combined with {ERC7739}, resolution ordering of {isValidSignature} may have an impact ({ERC7739} does not
  49. * call super). Manual resolution might be necessary.
  50. * * Static calls (using callType `0xfe`) are currently NOT supported.
  51. * ====
  52. *
  53. * WARNING: Removing all validator modules will render the account inoperable, as no user operations can be validated thereafter.
  54. */
  55. abstract contract AccountERC7579 is Account, IERC1271, IERC7579Execution, IERC7579AccountConfig, IERC7579ModuleConfig {
  56. using Bytes for *;
  57. using ERC7579Utils for *;
  58. using EnumerableSet for *;
  59. using Packing for bytes32;
  60. EnumerableSet.AddressSet private _validators;
  61. EnumerableSet.AddressSet private _executors;
  62. mapping(bytes4 selector => address) private _fallbacks;
  63. /// @dev The account's {fallback} was called with a selector that doesn't have an installed handler.
  64. error ERC7579MissingFallbackHandler(bytes4 selector);
  65. /// @dev Modifier that checks if the caller is an installed module of the given type.
  66. modifier onlyModule(uint256 moduleTypeId, bytes calldata additionalContext) {
  67. _checkModule(moduleTypeId, msg.sender, additionalContext);
  68. _;
  69. }
  70. /// @dev See {_fallback}.
  71. fallback(bytes calldata) external payable virtual returns (bytes memory) {
  72. return _fallback();
  73. }
  74. /// @inheritdoc IERC7579AccountConfig
  75. function accountId() public view virtual returns (string memory) {
  76. // vendorname.accountname.semver
  77. return "@openzeppelin/community-contracts.AccountERC7579.v0.0.0";
  78. }
  79. /**
  80. * @inheritdoc IERC7579AccountConfig
  81. *
  82. * @dev Supported call types:
  83. * * Single (`0x00`): A single transaction execution.
  84. * * Batch (`0x01`): A batch of transactions execution.
  85. * * Delegate (`0xff`): A delegate call execution.
  86. *
  87. * Supported exec types:
  88. * * Default (`0x00`): Default execution type (revert on failure).
  89. * * Try (`0x01`): Try execution type (emits ERC7579TryExecuteFail on failure).
  90. */
  91. function supportsExecutionMode(bytes32 encodedMode) public view virtual returns (bool) {
  92. (CallType callType, ExecType execType, , ) = Mode.wrap(encodedMode).decodeMode();
  93. return
  94. (callType == ERC7579Utils.CALLTYPE_SINGLE ||
  95. callType == ERC7579Utils.CALLTYPE_BATCH ||
  96. callType == ERC7579Utils.CALLTYPE_DELEGATECALL) &&
  97. (execType == ERC7579Utils.EXECTYPE_DEFAULT || execType == ERC7579Utils.EXECTYPE_TRY);
  98. }
  99. /**
  100. * @inheritdoc IERC7579AccountConfig
  101. *
  102. * @dev Supported module types:
  103. *
  104. * * Validator: A module used during the validation phase to determine if a transaction is valid and
  105. * should be executed on the account.
  106. * * Executor: A module that can execute transactions on behalf of the smart account via a callback.
  107. * * Fallback Handler: A module that can extend the fallback functionality of a smart account.
  108. */
  109. function supportsModule(uint256 moduleTypeId) public view virtual returns (bool) {
  110. return
  111. moduleTypeId == MODULE_TYPE_VALIDATOR ||
  112. moduleTypeId == MODULE_TYPE_EXECUTOR ||
  113. moduleTypeId == MODULE_TYPE_FALLBACK;
  114. }
  115. /// @inheritdoc IERC7579ModuleConfig
  116. function installModule(
  117. uint256 moduleTypeId,
  118. address module,
  119. bytes calldata initData
  120. ) public virtual onlyEntryPointOrSelf {
  121. _installModule(moduleTypeId, module, initData);
  122. }
  123. /// @inheritdoc IERC7579ModuleConfig
  124. function uninstallModule(
  125. uint256 moduleTypeId,
  126. address module,
  127. bytes calldata deInitData
  128. ) public virtual onlyEntryPointOrSelf {
  129. _uninstallModule(moduleTypeId, module, deInitData);
  130. }
  131. /// @inheritdoc IERC7579ModuleConfig
  132. function isModuleInstalled(
  133. uint256 moduleTypeId,
  134. address module,
  135. bytes calldata additionalContext
  136. ) public view virtual returns (bool) {
  137. if (moduleTypeId == MODULE_TYPE_VALIDATOR) return _validators.contains(module);
  138. if (moduleTypeId == MODULE_TYPE_EXECUTOR) return _executors.contains(module);
  139. if (moduleTypeId == MODULE_TYPE_FALLBACK) return _fallbacks[bytes4(additionalContext[0:4])] == module;
  140. return false;
  141. }
  142. /// @inheritdoc IERC7579Execution
  143. function execute(bytes32 mode, bytes calldata executionCalldata) public payable virtual onlyEntryPointOrSelf {
  144. _execute(Mode.wrap(mode), executionCalldata);
  145. }
  146. /// @inheritdoc IERC7579Execution
  147. function executeFromExecutor(
  148. bytes32 mode,
  149. bytes calldata executionCalldata
  150. )
  151. public
  152. payable
  153. virtual
  154. onlyModule(MODULE_TYPE_EXECUTOR, Calldata.emptyBytes())
  155. returns (bytes[] memory returnData)
  156. {
  157. return _execute(Mode.wrap(mode), executionCalldata);
  158. }
  159. /**
  160. * @dev Implement ERC-1271 through IERC7579Validator modules. If module based validation fails, fallback to
  161. * "native" validation by the abstract signer.
  162. *
  163. * NOTE: when combined with {ERC7739}, resolution ordering may have an impact ({ERC7739} does not call super).
  164. * Manual resolution might be necessary.
  165. */
  166. function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4) {
  167. // check signature length is enough for extraction
  168. if (signature.length >= 20) {
  169. (address module, bytes calldata innerSignature) = _extractSignatureValidator(signature);
  170. // if module is not installed, skip
  171. if (isModuleInstalled(MODULE_TYPE_VALIDATOR, module, Calldata.emptyBytes())) {
  172. // try validation, skip any revert
  173. try IERC7579Validator(module).isValidSignatureWithSender(msg.sender, hash, innerSignature) returns (
  174. bytes4 magic
  175. ) {
  176. return magic;
  177. } catch {}
  178. }
  179. }
  180. return bytes4(0xffffffff);
  181. }
  182. /**
  183. * @dev Validates a user operation with {_signableUserOpHash} and returns the validation data
  184. * if the module specified by the first 20 bytes of the nonce key is installed. Falls back to
  185. * {Account-_validateUserOp} otherwise.
  186. *
  187. * See {_extractUserOpValidator} for the module extraction logic.
  188. */
  189. function _validateUserOp(
  190. PackedUserOperation calldata userOp,
  191. bytes32 userOpHash
  192. ) internal virtual override returns (uint256) {
  193. address module = _extractUserOpValidator(userOp);
  194. return
  195. isModuleInstalled(MODULE_TYPE_VALIDATOR, module, Calldata.emptyBytes())
  196. ? IERC7579Validator(module).validateUserOp(userOp, _signableUserOpHash(userOp, userOpHash))
  197. : super._validateUserOp(userOp, userOpHash);
  198. }
  199. /**
  200. * @dev ERC-7579 execution logic. See {supportsExecutionMode} for supported modes.
  201. *
  202. * Reverts if the call type is not supported.
  203. */
  204. function _execute(
  205. Mode mode,
  206. bytes calldata executionCalldata
  207. ) internal virtual returns (bytes[] memory returnData) {
  208. (CallType callType, ExecType execType, , ) = mode.decodeMode();
  209. if (callType == ERC7579Utils.CALLTYPE_SINGLE) return executionCalldata.execSingle(execType);
  210. if (callType == ERC7579Utils.CALLTYPE_BATCH) return executionCalldata.execBatch(execType);
  211. if (callType == ERC7579Utils.CALLTYPE_DELEGATECALL) return executionCalldata.execDelegateCall(execType);
  212. revert ERC7579Utils.ERC7579UnsupportedCallType(callType);
  213. }
  214. /**
  215. * @dev Installs a module of the given type with the given initialization data.
  216. *
  217. * For the fallback module type, the `initData` is expected to be the (packed) concatenation of a 4-byte
  218. * selector and the rest of the data to be sent to the handler when calling {IERC7579Module-onInstall}.
  219. *
  220. * Requirements:
  221. *
  222. * * Module type must be supported. See {supportsModule}. Reverts with {ERC7579Utils-ERC7579UnsupportedModuleType}.
  223. * * Module must be of the given type. Reverts with {ERC7579Utils-ERC7579MismatchedModuleTypeId}.
  224. * * Module must not be already installed. Reverts with {ERC7579Utils-ERC7579AlreadyInstalledModule}.
  225. *
  226. * Emits a {IERC7579ModuleConfig-ModuleInstalled} event.
  227. */
  228. function _installModule(uint256 moduleTypeId, address module, bytes memory initData) internal virtual {
  229. require(supportsModule(moduleTypeId), ERC7579Utils.ERC7579UnsupportedModuleType(moduleTypeId));
  230. require(
  231. IERC7579Module(module).isModuleType(moduleTypeId),
  232. ERC7579Utils.ERC7579MismatchedModuleTypeId(moduleTypeId, module)
  233. );
  234. if (moduleTypeId == MODULE_TYPE_VALIDATOR) {
  235. require(_validators.add(module), ERC7579Utils.ERC7579AlreadyInstalledModule(moduleTypeId, module));
  236. } else if (moduleTypeId == MODULE_TYPE_EXECUTOR) {
  237. require(_executors.add(module), ERC7579Utils.ERC7579AlreadyInstalledModule(moduleTypeId, module));
  238. } else if (moduleTypeId == MODULE_TYPE_FALLBACK) {
  239. bytes4 selector;
  240. (selector, initData) = _decodeFallbackData(initData);
  241. require(
  242. _fallbacks[selector] == address(0),
  243. ERC7579Utils.ERC7579AlreadyInstalledModule(moduleTypeId, module)
  244. );
  245. _fallbacks[selector] = module;
  246. }
  247. IERC7579Module(module).onInstall(initData);
  248. emit ModuleInstalled(moduleTypeId, module);
  249. }
  250. /**
  251. * @dev Uninstalls a module of the given type with the given de-initialization data.
  252. *
  253. * For the fallback module type, the `deInitData` is expected to be the (packed) concatenation of a 4-byte
  254. * selector and the rest of the data to be sent to the handler when calling {IERC7579Module-onUninstall}.
  255. *
  256. * Requirements:
  257. *
  258. * * Module must be already installed. Reverts with {ERC7579Utils-ERC7579UninstalledModule} otherwise.
  259. */
  260. function _uninstallModule(uint256 moduleTypeId, address module, bytes memory deInitData) internal virtual {
  261. require(supportsModule(moduleTypeId), ERC7579Utils.ERC7579UnsupportedModuleType(moduleTypeId));
  262. if (moduleTypeId == MODULE_TYPE_VALIDATOR) {
  263. require(_validators.remove(module), ERC7579Utils.ERC7579UninstalledModule(moduleTypeId, module));
  264. } else if (moduleTypeId == MODULE_TYPE_EXECUTOR) {
  265. require(_executors.remove(module), ERC7579Utils.ERC7579UninstalledModule(moduleTypeId, module));
  266. } else if (moduleTypeId == MODULE_TYPE_FALLBACK) {
  267. bytes4 selector;
  268. (selector, deInitData) = _decodeFallbackData(deInitData);
  269. require(
  270. _fallbackHandler(selector) == module && module != address(0),
  271. ERC7579Utils.ERC7579UninstalledModule(moduleTypeId, module)
  272. );
  273. delete _fallbacks[selector];
  274. }
  275. IERC7579Module(module).onUninstall(deInitData);
  276. emit ModuleUninstalled(moduleTypeId, module);
  277. }
  278. /**
  279. * @dev Fallback function that delegates the call to the installed handler for the given selector.
  280. *
  281. * Reverts with {ERC7579MissingFallbackHandler} if the handler is not installed.
  282. *
  283. * Calls the handler with the original `msg.sender` appended at the end of the calldata following
  284. * the ERC-2771 format.
  285. */
  286. function _fallback() internal virtual returns (bytes memory) {
  287. address handler = _fallbackHandler(msg.sig);
  288. require(handler != address(0), ERC7579MissingFallbackHandler(msg.sig));
  289. // From https://eips.ethereum.org/EIPS/eip-7579#fallback[ERC-7579 specifications]:
  290. // - MUST utilize ERC-2771 to add the original msg.sender to the calldata sent to the fallback handler
  291. // - MUST use call to invoke the fallback handler
  292. (bool success, bytes memory returndata) = handler.call{value: msg.value}(
  293. abi.encodePacked(msg.data, msg.sender)
  294. );
  295. if (success) return returndata;
  296. assembly ("memory-safe") {
  297. revert(add(returndata, 0x20), mload(returndata))
  298. }
  299. }
  300. /// @dev Returns the fallback handler for the given selector. Returns `address(0)` if not installed.
  301. function _fallbackHandler(bytes4 selector) internal view virtual returns (address) {
  302. return _fallbacks[selector];
  303. }
  304. /// @dev Checks if the module is installed. Reverts if the module is not installed.
  305. function _checkModule(
  306. uint256 moduleTypeId,
  307. address module,
  308. bytes calldata additionalContext
  309. ) internal view virtual {
  310. require(
  311. isModuleInstalled(moduleTypeId, module, additionalContext),
  312. ERC7579Utils.ERC7579UninstalledModule(moduleTypeId, module)
  313. );
  314. }
  315. /**
  316. * @dev Extracts the nonce validator from the user operation.
  317. *
  318. * To construct a nonce key, set nonce as follows:
  319. *
  320. * ```
  321. * <module address (20 bytes)> | <key (4 bytes)> | <nonce (8 bytes)>
  322. * ```
  323. * NOTE: The default behavior of this function replicates the behavior of
  324. * https://github.com/rhinestonewtf/safe7579/blob/bb29e8b1a66658790c4169e72608e27d220f79be/src/Safe7579.sol#L266[Safe adapter],
  325. * https://github.com/etherspot/etherspot-prime-contracts/blob/cfcdb48c4172cea0d66038324c0bae3288aa8caa/src/modular-etherspot-wallet/wallet/ModularEtherspotWallet.sol#L227[Etherspot's Prime Account], and
  326. * https://github.com/erc7579/erc7579-implementation/blob/16138d1afd4e9711f6c1425133538837bd7787b5/src/MSAAdvanced.sol#L247[ERC7579 reference implementation].
  327. *
  328. * This is not standardized in ERC-7579 (or in any follow-up ERC). Some accounts may want to override these internal functions.
  329. *
  330. * For example, https://github.com/bcnmy/nexus/blob/54f4e19baaff96081a8843672977caf712ef19f4/contracts/lib/NonceLib.sol#L17[Biconomy's Nexus]
  331. * uses a similar yet incompatible approach (the validator address is also part of the nonce, but not at the same location)
  332. */
  333. function _extractUserOpValidator(PackedUserOperation calldata userOp) internal pure virtual returns (address) {
  334. return address(bytes32(userOp.nonce).extract_32_20(0));
  335. }
  336. /**
  337. * @dev Extracts the signature validator from the signature.
  338. *
  339. * To construct a signature, set the first 20 bytes as the module address and the remaining bytes as the
  340. * signature data:
  341. *
  342. * ```
  343. * <module address (20 bytes)> | <signature data>
  344. * ```
  345. *
  346. * NOTE: The default behavior of this function replicates the behavior of
  347. * https://github.com/rhinestonewtf/safe7579/blob/bb29e8b1a66658790c4169e72608e27d220f79be/src/Safe7579.sol#L350[Safe adapter],
  348. * https://github.com/bcnmy/nexus/blob/54f4e19baaff96081a8843672977caf712ef19f4/contracts/Nexus.sol#L239[Biconomy's Nexus],
  349. * https://github.com/etherspot/etherspot-prime-contracts/blob/cfcdb48c4172cea0d66038324c0bae3288aa8caa/src/modular-etherspot-wallet/wallet/ModularEtherspotWallet.sol#L252[Etherspot's Prime Account], and
  350. * https://github.com/erc7579/erc7579-implementation/blob/16138d1afd4e9711f6c1425133538837bd7787b5/src/MSAAdvanced.sol#L296[ERC7579 reference implementation].
  351. *
  352. * This is not standardized in ERC-7579 (or in any follow-up ERC). Some accounts may want to override these internal functions.
  353. */
  354. function _extractSignatureValidator(
  355. bytes calldata signature
  356. ) internal pure virtual returns (address module, bytes calldata innerSignature) {
  357. return (address(bytes20(signature[0:20])), signature[20:]);
  358. }
  359. /**
  360. * @dev Extract the function selector from initData/deInitData for MODULE_TYPE_FALLBACK
  361. *
  362. * NOTE: If we had calldata here, we could use calldata slice which are cheaper to manipulate and don't require
  363. * actual copy. However, this would require `_installModule` to get a calldata bytes object instead of a memory
  364. * bytes object. This would prevent calling `_installModule` from a contract constructor and would force the use
  365. * of external initializers. That may change in the future, as most accounts will probably be deployed as
  366. * clones/proxy/ERC-7702 delegates and therefore rely on initializers anyway.
  367. */
  368. function _decodeFallbackData(
  369. bytes memory data
  370. ) internal pure virtual returns (bytes4 selector, bytes memory remaining) {
  371. return (bytes4(data), data.slice(4));
  372. }
  373. /// @dev By default, only use the modules for validation of userOp and signature. Disable raw signatures.
  374. function _rawSignatureValidation(
  375. bytes32 /*hash*/,
  376. bytes calldata /*signature*/
  377. ) internal view virtual override returns (bool) {
  378. return false;
  379. }
  380. }