Skip to content

Add AccountERC7579Guarded extension - #6647

Draft
james-toussaint wants to merge 5 commits into
OpenZeppelin:masterfrom
james-toussaint:feat/account-erc7579-guarded
Draft

Add AccountERC7579Guarded extension#6647
james-toussaint wants to merge 5 commits into
OpenZeppelin:masterfrom
james-toussaint:feat/account-erc7579-guarded

Conversation

@james-toussaint

Copy link
Copy Markdown
Contributor

Fixes #6628.

PR Checklist

  • Tests
  • Documentation
  • Changeset entry (run npx changeset add)

Amxx and others added 5 commits July 22, 2026 20:29
PR OpenZeppelin#6390 special-cased hook-module uninstallation so that a bugged or
malicious hook could not block its own removal by reverting in
preCheck/postCheck. This reverts that behavior, giving the hook module
full control over its lifecycle: uninstalling a hook module now goes
through the standard withHook flow, so the hook can prevent its own
uninstallation by reverting.

OpenZeppelin#6390 was never released, so its pending changeset is dropped and no new
changeset is required.
PR OpenZeppelin#6142 (released in 5.6.0) swallowed reverts from a module's
onUninstall hook so a buggy or malicious module could not block its own
removal. This reverts that behavior: onUninstall is called without
catching reverts, so a module can now prevent its own uninstallation by
reverting.

A forced uninstallation that bypasses the onUninstall hook can still be
performed through a delegate call via execute; document this in the
_uninstallModule NatSpec.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 27, 2026 15:27
@changeset-bot

changeset-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f861252

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
openzeppelin-solidity Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the ERC-7579 account framework by introducing a guarded “escape hatch” for forced module removal, while also reverting earlier behavior that prevented modules (including hook modules) from blocking their own uninstallation.

Changes:

  • Make AccountERC7579 propagate onUninstall reverts and factor storage-only removal into a new _removeModule primitive.
  • Simplify AccountERC7579Hooked uninstallation logic (restore plain withHook behavior) and override _removeModule to support hook-module removal without hook wrapping.
  • Add AccountERC7579Guarded with a guardian-gated forceUninstall that bypasses hooks and onUninstall.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/account/extensions/AccountERC7579.behavior.js Updates behavior tests to expect uninstall to revert when onUninstall reverts; adjusts hook-uninstall expectations accordingly.
contracts/account/extensions/draft-AccountERC7579.sol Introduces _removeModule and changes _uninstallModule to call onUninstall without swallowing reverts.
contracts/account/extensions/draft-AccountERC7579Hooked.sol Removes the special-cased “ignore hook failures during hook uninstall” logic; adds a hook-aware _removeModule override.
contracts/account/extensions/draft-AccountERC7579Guarded.sol New extension exposing a guardian-gated forceUninstall escape hatch built on _removeModule.
.changeset/lazy-hooks-return.md Patch changeset documenting the uninstall revert behavior change in AccountERC7579.
.changeset/guarded-accounts-recover.md Minor changeset documenting the new AccountERC7579Guarded extension.
.changeset/chatty-dryers-joke.md Removes an obsolete changeset related to the previously reverted hooked-uninstall behavior.
Comments suppressed due to low confidence (1)

contracts/account/extensions/draft-AccountERC7579Guarded.sol:85

  • _setGuardian currently allows overwriting an existing guardian and setting it to address(0), which contradicts the contract-level rationale (guardian set once, delay cannot be bypassed) and can brick forceUninstall. Enforce the set-once + nonzero invariant.
    function _setGuardian(address newGuardian) internal virtual {
        _guardian = newGuardian;
        emit UninstallGuardianSet(newGuardian);
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -0,0 +1,91 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (account/extensions/draft-AccountERC7579Guarded.sol)
Comment on lines +50 to +52
/// @dev The caller is not the configured guardian.
error AccountUnauthorizedGuardian(address caller);

*
* Recovering from a bricked hook then goes through the guardian's own delay:
* ```solidity
* bytes memory call = abi.encodeCall(AccountERC7579Guarded.forceUninstall, (MODULE_TYPE_HOOK, hook, ""));
Comment on lines +64 to +71
function forceUninstall(
uint256 moduleTypeId,
address module,
bytes calldata deInitData
) public virtual onlyGuardian {
_removeModule(moduleTypeId, module, deInitData);
emit ModuleUninstalled(moduleTypeId, module);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants