diff --git a/CHANGELOG.md b/CHANGELOG.md index 4406f5bd0..cfdd75051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * `ERC777`: Optimize the gas costs of the constructor. ([#2551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2551)) * `ERC721URIStorage`: Add a new extension that implements the `_setTokenURI` behavior as it was available in 3.4.0. ([#2555](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2555)) * `AccessControl`: Added ERC165 interface detection. ([#2562](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2562)) + * `AccessControlEnumerable`: Fixed `renounceRole` not updated underlying set. ([#2572](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2572)) ### How to upgrade from 3.x diff --git a/contracts/access/AccessControlEnumerable.sol b/contracts/access/AccessControlEnumerable.sol index 8fe8f0332..c1522aa1a 100644 --- a/contracts/access/AccessControlEnumerable.sol +++ b/contracts/access/AccessControlEnumerable.sol @@ -69,6 +69,14 @@ abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessCon _roleMembers[role].remove(account); } + /** + * @dev Overload {renounceRole} to track enumerable memberships + */ + function renounceRole(bytes32 role, address account) public virtual override { + super.renounceRole(role, account); + _roleMembers[role].remove(account); + } + /** * @dev Overload {_setupRole} to track enumerable memberships */ diff --git a/test/access/AccessControl.behavior.js b/test/access/AccessControl.behavior.js index 4cb4f7fc8..4f7fece9b 100644 --- a/test/access/AccessControl.behavior.js +++ b/test/access/AccessControl.behavior.js @@ -179,6 +179,13 @@ function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized expect(bearers).to.have.members([authorized, otherAuthorized]); }); + it('role enumeration should be in sync after renounceRole call', async function () { + expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0'); + await this.accessControl.grantRole(ROLE, admin, { from: admin }); + expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('1'); + await this.accessControl.renounceRole(ROLE, admin, { from: admin }); + expect(await this.accessControl.getRoleMemberCount(ROLE)).to.bignumber.equal('0'); + }); }); }