Commit 0502cc31 by github-actions

Merge upstream openzeppelin-contracts into upstream-patched

parents 1a6bf792 29ffe6f4
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
* Rename `UpgradeableProxy` to `ERC1967Proxy`. ([#2547](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2547)) * Rename `UpgradeableProxy` to `ERC1967Proxy`. ([#2547](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2547))
* `ERC777`: Optimize the gas costs of the constructor. ([#2551](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2551)) * `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)) * `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))
### How to upgrade from 3.x ### How to upgrade from 3.x
......
...@@ -3,6 +3,18 @@ ...@@ -3,6 +3,18 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import "../utils/Context.sol"; import "../utils/Context.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
/** /**
* @dev Contract module that allows children to implement role-based access * @dev Contract module that allows children to implement role-based access
...@@ -42,7 +54,7 @@ import "../utils/Context.sol"; ...@@ -42,7 +54,7 @@ import "../utils/Context.sol";
* grant and revoke this role. Extra precautions should be taken to secure * grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. * accounts that have been granted it.
*/ */
abstract contract AccessControl is Context { abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData { struct RoleData {
mapping (address => bool) members; mapping (address => bool) members;
bytes32 adminRole; bytes32 adminRole;
...@@ -80,9 +92,17 @@ abstract contract AccessControl is Context { ...@@ -80,9 +92,17 @@ abstract contract AccessControl is Context {
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/** /**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId
|| super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`. * @dev Returns `true` if `account` has been granted `role`.
*/ */
function hasRole(bytes32 role, address account) public view returns (bool) { function hasRole(bytes32 role, address account) public view override returns (bool) {
return _roles[role].members[account]; return _roles[role].members[account];
} }
...@@ -92,7 +112,7 @@ abstract contract AccessControl is Context { ...@@ -92,7 +112,7 @@ abstract contract AccessControl is Context {
* *
* To change a role's admin, use {_setRoleAdmin}. * To change a role's admin, use {_setRoleAdmin}.
*/ */
function getRoleAdmin(bytes32 role) public view returns (bytes32) { function getRoleAdmin(bytes32 role) public view override returns (bytes32) {
return _roles[role].adminRole; return _roles[role].adminRole;
} }
...@@ -106,7 +126,7 @@ abstract contract AccessControl is Context { ...@@ -106,7 +126,7 @@ abstract contract AccessControl is Context {
* *
* - the caller must have ``role``'s admin role. * - the caller must have ``role``'s admin role.
*/ */
function grantRole(bytes32 role, address account) public virtual { function grantRole(bytes32 role, address account) public virtual override {
require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant"); require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account); _grantRole(role, account);
...@@ -121,7 +141,7 @@ abstract contract AccessControl is Context { ...@@ -121,7 +141,7 @@ abstract contract AccessControl is Context {
* *
* - the caller must have ``role``'s admin role. * - the caller must have ``role``'s admin role.
*/ */
function revokeRole(bytes32 role, address account) public virtual { function revokeRole(bytes32 role, address account) public virtual override {
require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke"); require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account); _revokeRole(role, account);
...@@ -141,7 +161,7 @@ abstract contract AccessControl is Context { ...@@ -141,7 +161,7 @@ abstract contract AccessControl is Context {
* *
* - the caller must be `account`. * - the caller must be `account`.
*/ */
function renounceRole(bytes32 role, address account) public virtual { function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self"); require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account); _revokeRole(role, account);
......
...@@ -6,14 +6,30 @@ import "./AccessControl.sol"; ...@@ -6,14 +6,30 @@ import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol"; import "../utils/structs/EnumerableSet.sol";
/** /**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable {
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role. * @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/ */
abstract contract AccessControlEnumerable is AccessControl { abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.AddressSet;
mapping (bytes32 => EnumerableSet.AddressSet) private _roleMembers; mapping (bytes32 => EnumerableSet.AddressSet) private _roleMembers;
/** /**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId
|| super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a * @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive. * value between 0 and {getRoleMemberCount}, non-inclusive.
* *
...@@ -25,7 +41,7 @@ abstract contract AccessControlEnumerable is AccessControl { ...@@ -25,7 +41,7 @@ abstract contract AccessControlEnumerable is AccessControl {
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] * https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information. * for more information.
*/ */
function getRoleMember(bytes32 role, uint256 index) public view returns (address) { function getRoleMember(bytes32 role, uint256 index) public view override returns (address) {
return _roleMembers[role].at(index); return _roleMembers[role].at(index);
} }
...@@ -33,7 +49,7 @@ abstract contract AccessControlEnumerable is AccessControl { ...@@ -33,7 +49,7 @@ abstract contract AccessControlEnumerable is AccessControl {
* @dev Returns the number of accounts that have `role`. Can be used * @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role. * together with {getRoleMember} to enumerate all bearers of a role.
*/ */
function getRoleMemberCount(bytes32 role) public view returns (uint256) { function getRoleMemberCount(bytes32 role) public view override returns (uint256) {
return _roleMembers[role].length(); return _roleMembers[role].length();
} }
......
...@@ -89,6 +89,13 @@ contract ERC1155PresetMinterPauser is Context, AccessControlEnumerable, ERC1155B ...@@ -89,6 +89,13 @@ contract ERC1155PresetMinterPauser is Context, AccessControlEnumerable, ERC1155B
_unpause(); _unpause();
} }
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC1155) returns (bool) {
return super.supportsInterface(interfaceId);
}
function _beforeTokenTransfer( function _beforeTokenTransfer(
address operator, address operator,
address from, address from,
......
...@@ -110,7 +110,7 @@ contract ERC721PresetMinterPauserAutoId is Context, AccessControlEnumerable, ERC ...@@ -110,7 +110,7 @@ contract ERC721PresetMinterPauserAutoId is Context, AccessControlEnumerable, ERC
/** /**
* @dev See {IERC165-supportsInterface}. * @dev See {IERC165-supportsInterface}.
*/ */
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) { function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControlEnumerable, ERC721, ERC721Enumerable) returns (bool) {
return super.supportsInterface(interfaceId); return super.supportsInterface(interfaceId);
} }
} }
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai'); const { expect } = require('chai');
const { shouldSupportInterfaces } = require('../utils/introspection/SupportsInterface.behavior');
const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000'; const DEFAULT_ADMIN_ROLE = '0x0000000000000000000000000000000000000000000000000000000000000000';
const ROLE = web3.utils.soliditySha3('ROLE'); const ROLE = web3.utils.soliditySha3('ROLE');
const OTHER_ROLE = web3.utils.soliditySha3('OTHER_ROLE'); const OTHER_ROLE = web3.utils.soliditySha3('OTHER_ROLE');
function shouldBehaveLikeAccessControl (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) { function shouldBehaveLikeAccessControl (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) {
shouldSupportInterfaces(['AccessControl']);
describe('default admin', function () { describe('default admin', function () {
it('deployer has default admin role', async function () { it('deployer has default admin role', async function () {
expect(await this.accessControl.hasRole(DEFAULT_ADMIN_ROLE, admin)).to.equal(true); expect(await this.accessControl.hasRole(DEFAULT_ADMIN_ROLE, admin)).to.equal(true);
...@@ -156,6 +160,8 @@ function shouldBehaveLikeAccessControl (errorPrefix, admin, authorized, other, o ...@@ -156,6 +160,8 @@ function shouldBehaveLikeAccessControl (errorPrefix, admin, authorized, other, o
} }
function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) { function shouldBehaveLikeAccessControlEnumerable (errorPrefix, admin, authorized, other, otherAdmin, otherAuthorized) {
shouldSupportInterfaces(['AccessControlEnumerable']);
describe('enumerating', function () { describe('enumerating', function () {
it('role bearers can be enumerated', async function () { it('role bearers can be enumerated', async function () {
await this.accessControl.grantRole(ROLE, authorized, { from: admin }); await this.accessControl.grantRole(ROLE, authorized, { from: admin });
......
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior');
const { expect } = require('chai'); const { expect } = require('chai');
...@@ -24,6 +25,8 @@ contract('ERC1155PresetMinterPauser', function (accounts) { ...@@ -24,6 +25,8 @@ contract('ERC1155PresetMinterPauser', function (accounts) {
this.token = await ERC1155PresetMinterPauser.new(uri, { from: deployer }); this.token = await ERC1155PresetMinterPauser.new(uri, { from: deployer });
}); });
shouldSupportInterfaces(['ERC1155', 'AccessControl', 'AccessControlEnumerable']);
it('deployer has the default admin role', async function () { it('deployer has the default admin role', async function () {
expect(await this.token.getRoleMemberCount(DEFAULT_ADMIN_ROLE)).to.be.bignumber.equal('1'); expect(await this.token.getRoleMemberCount(DEFAULT_ADMIN_ROLE)).to.be.bignumber.equal('1');
expect(await this.token.getRoleMember(DEFAULT_ADMIN_ROLE, 0)).to.equal(deployer); expect(await this.token.getRoleMember(DEFAULT_ADMIN_ROLE, 0)).to.equal(deployer);
......
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { ZERO_ADDRESS } = constants; const { ZERO_ADDRESS } = constants;
const { shouldSupportInterfaces } = require('../../../utils/introspection/SupportsInterface.behavior');
const { expect } = require('chai'); const { expect } = require('chai');
...@@ -19,6 +20,8 @@ contract('ERC721PresetMinterPauserAutoId', function (accounts) { ...@@ -19,6 +20,8 @@ contract('ERC721PresetMinterPauserAutoId', function (accounts) {
this.token = await ERC721PresetMinterPauserAutoId.new(name, symbol, baseURI, { from: deployer }); this.token = await ERC721PresetMinterPauserAutoId.new(name, symbol, baseURI, { from: deployer });
}); });
shouldSupportInterfaces(['ERC721', 'ERC721Enumerable', 'AccessControl', 'AccessControlEnumerable']);
it('token has correct name', async function () { it('token has correct name', async function () {
expect(await this.token.name()).to.equal(name); expect(await this.token.name()).to.equal(name);
}); });
......
...@@ -39,6 +39,17 @@ const INTERFACES = { ...@@ -39,6 +39,17 @@ const INTERFACES = {
'onERC1155Received(address,address,uint256,uint256,bytes)', 'onERC1155Received(address,address,uint256,uint256,bytes)',
'onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)', 'onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)',
], ],
AccessControl: [
'hasRole(bytes32,address)',
'getRoleAdmin(bytes32)',
'grantRole(bytes32,address)',
'revokeRole(bytes32,address)',
'renounceRole(bytes32,address)',
],
AccessControlEnumerable: [
'getRoleMember(bytes32,uint256)',
'getRoleMemberCount(bytes32)',
],
}; };
const INTERFACE_IDS = {}; const INTERFACE_IDS = {};
...@@ -54,7 +65,7 @@ for (const k of Object.getOwnPropertyNames(INTERFACES)) { ...@@ -54,7 +65,7 @@ for (const k of Object.getOwnPropertyNames(INTERFACES)) {
function shouldSupportInterfaces (interfaces = []) { function shouldSupportInterfaces (interfaces = []) {
describe('Contract interface', function () { describe('Contract interface', function () {
beforeEach(function () { beforeEach(function () {
this.contractUnderTest = this.mock || this.token || this.holder; this.contractUnderTest = this.mock || this.token || this.holder || this.accessControl;
}); });
for (const k of interfaces) { for (const k of interfaces) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment