draft-AccountERC7579Hooked.sol 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.26;
  3. import {IERC7579Hook, MODULE_TYPE_HOOK} from "../../interfaces/draft-IERC7579.sol";
  4. import {ERC7579Utils, Mode} from "../../account/utils/draft-ERC7579Utils.sol";
  5. import {AccountERC7579} from "./draft-AccountERC7579.sol";
  6. /**
  7. * @dev Extension of {AccountERC7579} with support for a single hook module (type 4).
  8. *
  9. * If installed, this extension will call the hook module's {IERC7579Hook-preCheck} before executing any operation
  10. * with {_execute} (including {execute} and {executeFromExecutor} by default) and {IERC7579Hook-postCheck} thereafter.
  11. *
  12. * NOTE: Hook modules break the check-effect-interaction pattern. In particular, the {IERC7579Hook-preCheck} hook can
  13. * lead to potentially dangerous reentrancy. Using the `withHook()` modifier is safe if no effect is performed
  14. * before the preHook or after the postHook. That is the case on all functions here, but it may not be the case if
  15. * functions that have this modifier are overridden. Developers should be extremely careful when implementing hook
  16. * modules or further overriding functions that involve hooks.
  17. */
  18. abstract contract AccountERC7579Hooked is AccountERC7579 {
  19. address private _hook;
  20. /// @dev A hook module is already present. This contract only supports one hook module.
  21. error ERC7579HookModuleAlreadyPresent(address hook);
  22. /**
  23. * @dev Calls {IERC7579Hook-preCheck} before executing the modified function and {IERC7579Hook-postCheck}
  24. * thereafter.
  25. */
  26. modifier withHook() {
  27. address hook_ = hook();
  28. bytes memory hookData;
  29. // slither-disable-next-line reentrancy-no-eth
  30. if (hook_ != address(0)) hookData = IERC7579Hook(hook_).preCheck(msg.sender, msg.value, msg.data);
  31. _;
  32. if (hook_ != address(0)) IERC7579Hook(hook_).postCheck(hookData);
  33. }
  34. /// @inheritdoc AccountERC7579
  35. function accountId() public view virtual override returns (string memory) {
  36. // vendorname.accountname.semver
  37. return "@openzeppelin/community-contracts.AccountERC7579Hooked.v0.0.0";
  38. }
  39. /// @dev Returns the hook module address if installed, or `address(0)` otherwise.
  40. function hook() public view virtual returns (address) {
  41. return _hook;
  42. }
  43. /// @dev Supports hook modules. See {AccountERC7579-supportsModule}
  44. function supportsModule(uint256 moduleTypeId) public view virtual override returns (bool) {
  45. return moduleTypeId == MODULE_TYPE_HOOK || super.supportsModule(moduleTypeId);
  46. }
  47. /// @inheritdoc AccountERC7579
  48. function isModuleInstalled(
  49. uint256 moduleTypeId,
  50. address module,
  51. bytes calldata data
  52. ) public view virtual override returns (bool) {
  53. return
  54. (moduleTypeId == MODULE_TYPE_HOOK && module == hook()) ||
  55. super.isModuleInstalled(moduleTypeId, module, data);
  56. }
  57. /// @dev Installs a module with support for hook modules. See {AccountERC7579-_installModule}
  58. function _installModule(
  59. uint256 moduleTypeId,
  60. address module,
  61. bytes memory initData
  62. ) internal virtual override withHook {
  63. if (moduleTypeId == MODULE_TYPE_HOOK) {
  64. require(_hook == address(0), ERC7579HookModuleAlreadyPresent(_hook));
  65. _hook = module;
  66. }
  67. super._installModule(moduleTypeId, module, initData);
  68. }
  69. /// @dev Uninstalls a module with support for hook modules. See {AccountERC7579-_uninstallModule}
  70. function _uninstallModule(
  71. uint256 moduleTypeId,
  72. address module,
  73. bytes memory deInitData
  74. ) internal virtual override withHook {
  75. if (moduleTypeId == MODULE_TYPE_HOOK) {
  76. require(_hook == module, ERC7579Utils.ERC7579UninstalledModule(moduleTypeId, module));
  77. _hook = address(0);
  78. }
  79. super._uninstallModule(moduleTypeId, module, deInitData);
  80. }
  81. /// @dev Hooked version of {AccountERC7579-_execute}.
  82. function _execute(
  83. Mode mode,
  84. bytes calldata executionCalldata
  85. ) internal virtual override withHook returns (bytes[] memory) {
  86. return super._execute(mode, executionCalldata);
  87. }
  88. /// @dev Hooked version of {AccountERC7579-_fallback}.
  89. function _fallback() internal virtual override withHook returns (bytes memory) {
  90. return super._fallback();
  91. }
  92. }