draft-AccountERC7579Hooked.sol 4.4 KB

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