-
Notifications
You must be signed in to change notification settings - Fork 12.4k
Add AccessManagerEnumerable #6053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add AccessManagerEnumerable #6053
Conversation
|
WalkthroughIntroduces a new abstract contract Suggested labels
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
contracts/access/manager/extensions/AccessManagerEnumerable.sol(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: ernestognw
Repo: OpenZeppelin/openzeppelin-contracts PR: 5904
File: contracts/crosschain/README.adoc:1-1
Timestamp: 2025-08-28T15:48:30.716Z
Learning: ernestognw prefers "Cross chain" without hyphenation rather than "Cross-chain" in documentation titles.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: tests
- GitHub Check: tests-foundry
- GitHub Check: tests-upgradeable
- GitHub Check: coverage
- GitHub Check: slither
- GitHub Check: halmos
- GitHub Check: Redirect rules - solidity-contracts
- GitHub Check: Header rules - solidity-contracts
- GitHub Check: Pages changed - solidity-contracts
🔇 Additional comments (2)
contracts/access/manager/extensions/AccessManagerEnumerable.sol (2)
25-31: The implementation is correct—all role revocation paths call_revokeRole.Verification confirms:
revokeRole()→ calls_revokeRole()✓renounceRole()→ calls_revokeRole()✓cancel()→ handles scheduled operation cancellation, not role revocation- No role expiration mechanism exists in the codebase
- The enumerable override properly guards set removal with the return value from
super._revokeRole()No desynchronization risk exists.
Likely an incorrect or invalid review comment.
12-23: Clarify intent: should enumerable set track scheduled or only effective role grants?The concern is partially valid. Analysis confirms:
getRoleMembers()includes accounts with scheduled grants (wheresince > now) that are not yet effectivehasRole()correctly returnsfalsefor these same accounts until the grant becomes effective_revokeRole()is properly synchronized and removes accounts from the enumerable setThis creates an API inconsistency:
getRoleMembers()andhasRole()disagree on whether an account "has" a role during thegrantDelaywindow. This appears intentional but should be clarified:
- Is
getRoleMembers()meant to enumerate all grant holders (scheduled or effective)?- Should the docs explicitly explain this distinction?
- Or should the enumerable set only track effective members?
contracts/access/manager/extensions/AccessManagerEnumerable.sol
Outdated
Show resolved
Hide resolved
contracts/access/manager/extensions/AccessManagerEnumerable.sol
Outdated
Show resolved
Hide resolved
|
Related to #6091 |
contracts/access/manager/extensions/AccessManagerEnumerable.sol
Outdated
Show resolved
Hide resolved
contracts/access/manager/extensions/IAccessManagerEnumerable.sol
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we interrested by having an enumeration of all the roles (that have at least one member) ?
Its nice to be able to list the memebers of a role, but you first need to know which role exist, don't you ?
| bytes4 selector, | ||
| uint64 oldRoleId, | ||
| uint64 newRoleId | ||
| ) internal virtual { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this should be internal virtual:
- I see little reason for anyone to override it (they could override
_setTargetFunctionRoledirectly) - if its called directly, it would possibly mess up stuff.
I would be in favor of either:
- inline it in side the
_setTargetFunctionRoleoverride (my prefered option) - (alternativelly) make it a private function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the comment:
* Developers who wish to explicitly track {ADMIN_ROLE} can override this function. For example: * * ```solidity * function _updateRoleTargetFunction(address target, bytes4 selector, uint64 oldRoleId, uint64 newRoleId) internal virtual override { * if (oldRoleId != 0) { * _roleTargetFunctions[oldRoleId][target].remove(selector); * } * if (newRoleId != 0) { * _roleTargetFunctions[newRoleId][target].add(selector); * } * } * ```
assumes access to the _roleTargetFunctions storage mapping, which is private and not accessible in overrides
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally, ADMIN_ROLE is equal to 0, so the proposed override would exactly match the current behavior of the function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@james-toussaint WDYT ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Your preferred option was what Ernest had in the first place before my comment, please have a look. I believe Natspec has not been properly updated though ("proposed override would exactly match the current behavior").
- Private
_roleTargetFunctions: good point 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Yes, the proposed override was wrong because the mapping is private anywa
Another option would be:
function _setTargetFunctionRole(address target, bytes4 selector, uint64 roleId) internal virtual override {
uint64 oldRoleId = getTargetFunctionRole(target, selector);
super._setTargetFunctionRole(target, selector, roleId);
_updateRoleTargetFunction(target, selector, oldRoleId, roleId);
if (oldRoleId != ADMIN_ROLE) _removeTargetFunction(target, selector, oldRoleId);
if (roleId != ADMIN_ROLE) _addTargetFunction(target, selector, roleId);
}
function _removeTargetFunction(address target, bytes4 selector, uint64 roleId) internal virtual {
_roleTargetFunctions[roleId][target].remove(selector);
}
function _addTargetFunction(address target, bytes4 selector, uint64 roleId) internal virtual {
_roleTargetFunctions[roleId][target].add(selector);
}So that users can override _setTargetFunctionRole and do:
function _setTargetFunctionRole(address target, bytes4 selector, uint64 roleId) internal virtual override {
uint64 oldRoleId = getTargetFunctionRole(target, selector);
super._setTargetFunctionRole(target, selector, roleId);
if(oldRoleId == ADMIN_ROLE) _removeTargetFunction(target, selector, oldRoleId);
if(roleId == ADMIN_ROLE) _addTargetFunction(target, selector, roleId);
}But @Amxx's argument is that having these functions internal virtual will really mess things up if someone uses them directly. I somewhat agree
| Object.assign(this, await loadFixture(fixture)); | ||
| }); | ||
|
|
||
| shouldBehaveLikeAccessManagerEnumerable(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think here we should also run all the other "shouldBehaliveLikeXxx" to prove that the enumerable extension doesn't break any of the other expected behaviors.
|
Given that we could not reach consensus on what exactly to enumerate (include roleIds that have more than one member ?), and that the main user of this contract mentionned they would NOT use an implementation if we provided one, it was decided that we should probably not provide that as part of the library, and commit to supporting it. Still, because it works, and it might be valuable to some users, we should keep this code available to our users through the docs, similarly to ERC4626Fees. A follow up PR should fix the testing that we have in commit 0b981fd |
|
I changed the title of the new docs section, otherwise it would collide with other section that has the same name under LGTM |
Testing how an AccessManagerEnumerable would look like
PR Checklist
npx changeset add)