non-fungible-apps/contracts/FleekAccessControl.sol

232 lines
7.6 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract FleekAccessControl is Initializable {
using Counters for Counters.Counter;
enum Roles {
Owner,
Controller
}
event TokenRoleGranted(uint256 indexed tokenId, Roles indexed role, address indexed toAddress, address byAddress);
event TokenRoleRevoked(uint256 indexed tokenId, Roles indexed role, address indexed toAddress, address byAddress);
event CollectionRoleGranted(Roles indexed role, address indexed toAddress, address byAddress);
event CollectionRoleRevoked(Roles indexed role, address indexed toAddress, address byAddress);
struct Role {
mapping(address => uint256) indexes;
address[] members;
}
Counters.Counter private _collectionRolesVersion;
// _collectionRoles[version][role]
mapping(uint256 => mapping(Roles => Role)) private _collectionRoles;
mapping(uint256 => Counters.Counter) private _tokenRolesVersion;
// _tokenRoles[tokenId][version][role]
mapping(uint256 => mapping(uint256 => mapping(Roles => Role))) private _tokenRoles;
/**
* @dev Initializes the contract by granting the `Owner` role to the deployer.
*/
function __FleekAccessControl_init() internal onlyInitializing {
_grantCollectionRole(Roles.Owner, msg.sender);
}
/**
* @dev Checks if the `msg.sender` has a certain role.
*/
modifier requireCollectionRole(Roles role) {
require(
hasCollectionRole(role, msg.sender) || hasCollectionRole(Roles.Owner, msg.sender),
"FleekAccessControl: must have collection role"
);
_;
}
/**
* @dev Checks if the `msg.sender` has the `Token` role for a certain `tokenId`.
*/
modifier requireTokenRole(uint256 tokenId, Roles role) {
require(
hasTokenRole(tokenId, role, msg.sender) || hasTokenRole(tokenId, Roles.Owner, msg.sender),
"FleekAccessControl: must have token role"
);
_;
}
/**
* @dev Grants the collection role to an address.
*
* Requirements:
*
* - the caller should have the collection role.
*
*/
function grantCollectionRole(Roles role, address account) public requireCollectionRole(Roles.Owner) {
_grantCollectionRole(role, account);
}
/**
* @dev Grants the token role to an address.
*
* Requirements:
*
* - the caller should have the token role.
*
*/
function grantTokenRole(
uint256 tokenId,
Roles role,
address account
) public requireTokenRole(tokenId, Roles.Owner) {
_grantTokenRole(tokenId, role, account);
}
/**
* @dev Revokes the collection role of an address.
*
* Requirements:
*
* - the caller should have the collection role.
*
*/
function revokeCollectionRole(Roles role, address account) public requireCollectionRole(Roles.Owner) {
_revokeCollectionRole(role, account);
}
/**
* @dev Revokes the token role of an address.
*
* Requirements:
*
* - the caller should have the token role.
*
*/
function revokeTokenRole(
uint256 tokenId,
Roles role,
address account
) public requireTokenRole(tokenId, Roles.Owner) {
_revokeTokenRole(tokenId, role, account);
}
/**
* @dev Returns `True` if a certain address has the collection role.
*/
function hasCollectionRole(Roles role, address account) public view returns (bool) {
uint256 currentVersion = _collectionRolesVersion.current();
return _collectionRoles[currentVersion][role].indexes[account] != 0;
}
/**
* @dev Returns `True` if a certain address has the token role.
*/
function hasTokenRole(uint256 tokenId, Roles role, address account) public view returns (bool) {
uint256 currentVersion = _tokenRolesVersion[tokenId].current();
return _tokenRoles[tokenId][currentVersion][role].indexes[account] != 0;
}
/**
* @dev Returns an array of addresses that all have the collection role.
*/
function getCollectionRoleMembers(Roles role) public view returns (address[] memory) {
uint256 currentVersion = _collectionRolesVersion.current();
return _collectionRoles[currentVersion][role].members;
}
/**
* @dev Returns an array of addresses that all have the same token role for a certain tokenId.
*/
function getTokenRoleMembers(uint256 tokenId, Roles role) public view returns (address[] memory) {
uint256 currentVersion = _tokenRolesVersion[tokenId].current();
return _tokenRoles[tokenId][currentVersion][role].members;
}
/**
* @dev Grants the collection role to an address.
*/
function _grantCollectionRole(Roles role, address account) internal {
uint256 currentVersion = _collectionRolesVersion.current();
_grantRole(_collectionRoles[currentVersion][role], account);
emit CollectionRoleGranted(role, account, msg.sender);
}
/**
* @dev Revokes the collection role of an address.
*/
function _revokeCollectionRole(Roles role, address account) internal {
uint256 currentVersion = _collectionRolesVersion.current();
_revokeRole(_collectionRoles[currentVersion][role], account);
emit CollectionRoleRevoked(role, account, msg.sender);
}
/**
* @dev Grants the token role to an address.
*/
function _grantTokenRole(uint256 tokenId, Roles role, address account) internal {
uint256 currentVersion = _tokenRolesVersion[tokenId].current();
_grantRole(_tokenRoles[tokenId][currentVersion][role], account);
emit TokenRoleGranted(tokenId, role, account, msg.sender);
}
/**
* @dev Revokes the token role of an address.
*/
function _revokeTokenRole(uint256 tokenId, Roles role, address account) internal {
uint256 currentVersion = _tokenRolesVersion[tokenId].current();
_revokeRole(_tokenRoles[tokenId][currentVersion][role], account);
emit TokenRoleRevoked(tokenId, role, account, msg.sender);
}
/**
* @dev Grants a certain role to a certain address.
*/
function _grantRole(Role storage role, address account) internal {
if (role.indexes[account] == 0) {
role.members.push(account);
role.indexes[account] = role.members.length;
}
}
/**
* @dev Revokes a certain role from a certain address.
*/
function _revokeRole(Role storage role, address account) internal {
if (role.indexes[account] != 0) {
uint256 index = role.indexes[account] - 1;
uint256 lastIndex = role.members.length - 1;
address lastAccount = role.members[lastIndex];
role.members[index] = lastAccount;
role.indexes[lastAccount] = index + 1;
role.members.pop();
delete role.indexes[account];
}
}
/**
* @dev Clears all token roles for a certain tokenId.
* Should only be used for burning tokens.
*/
function _clearAllTokenRoles(uint256 tokenId) internal {
_tokenRolesVersion[tokenId].increment();
}
/**
* @dev Clears all token roles for a certain tokenId and grants the owner role to a new address.
* Should only be used for transferring tokens.
*/
function _clearAllTokenRoles(uint256 tokenId, address newOwner) internal {
_clearAllTokenRoles(tokenId);
_grantTokenRole(tokenId, Roles.Owner, newOwner);
}
}