180 lines
5.9 KiB
Solidity
180 lines
5.9 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.8.7;
|
|
|
|
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
|
|
error MustHaveCollectionRole(uint8 role);
|
|
error MustHaveTokenRole(uint256 tokenId, uint8 role);
|
|
error MustHaveAtLeastOneOwner();
|
|
error RoleAlreadySet();
|
|
|
|
contract FleekAccessControl is Initializable {
|
|
/**
|
|
* @dev All available collection roles.
|
|
*/
|
|
enum CollectionRoles {
|
|
Owner,
|
|
Verifier
|
|
}
|
|
|
|
/**
|
|
* @dev All available token roles.
|
|
*/
|
|
enum TokenRoles {
|
|
Controller
|
|
}
|
|
|
|
/**
|
|
* @dev Emitted when a token role is changed.
|
|
*/
|
|
event TokenRoleChanged(
|
|
uint256 indexed tokenId,
|
|
TokenRoles indexed role,
|
|
address indexed toAddress,
|
|
bool status,
|
|
address byAddress
|
|
);
|
|
|
|
/**
|
|
* @dev Emitted when token roles version is increased and all token roles are cleared.
|
|
*/
|
|
event TokenRolesCleared(uint256 indexed tokenId, address byAddress);
|
|
|
|
/**
|
|
* @dev Emitted when a collection role is changed.
|
|
*/
|
|
event CollectionRoleChanged(
|
|
CollectionRoles indexed role,
|
|
address indexed toAddress,
|
|
bool status,
|
|
address byAddress
|
|
);
|
|
|
|
/**
|
|
* @dev _collectionRolesCounter[role] is the number of addresses that have the role.
|
|
* This is prevent Owner role to go to 0.
|
|
*/
|
|
mapping(CollectionRoles => uint256) private _collectionRolesCounter;
|
|
|
|
/**
|
|
* @dev _collectionRoles[role][address] is the mapping of addresses that have the role.
|
|
*/
|
|
mapping(CollectionRoles => mapping(address => bool)) private _collectionRoles;
|
|
|
|
/**
|
|
* @dev _tokenRolesVersion[tokenId] is the version of the token roles.
|
|
* The version is incremented every time the token roles are cleared.
|
|
* Should be incremented every token transfer.
|
|
*/
|
|
mapping(uint256 => uint256) private _tokenRolesVersion;
|
|
|
|
/**
|
|
* @dev _tokenRoles[tokenId][version][role][address] is the mapping of addresses that have the role.
|
|
*/
|
|
mapping(uint256 => mapping(uint256 => mapping(TokenRoles => mapping(address => bool)))) private _tokenRoles;
|
|
|
|
/**
|
|
* @dev Initializes the contract by granting the `Owner` role to the deployer.
|
|
*/
|
|
function __FleekAccessControl_init() internal onlyInitializing {
|
|
_grantCollectionRole(CollectionRoles.Owner, msg.sender);
|
|
_grantCollectionRole(CollectionRoles.Verifier, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev Checks if the `msg.sender` has a certain role.
|
|
*/
|
|
function _requireCollectionRole(CollectionRoles role) internal view {
|
|
if (!hasCollectionRole(role, msg.sender)) revert MustHaveCollectionRole(uint8(role));
|
|
}
|
|
|
|
/**
|
|
* @dev Checks if the `msg.sender` has the `Token` role for a certain `tokenId`.
|
|
*/
|
|
function _requireTokenRole(uint256 tokenId, TokenRoles role) internal view {
|
|
if (!hasTokenRole(tokenId, role, msg.sender)) revert MustHaveTokenRole(tokenId, uint8(role));
|
|
}
|
|
|
|
/**
|
|
* @dev Returns `True` if a certain address has the collection role.
|
|
*/
|
|
function hasCollectionRole(CollectionRoles role, address account) public view returns (bool) {
|
|
return _collectionRoles[role][account];
|
|
}
|
|
|
|
/**
|
|
* @dev Returns `True` if a certain address has the token role.
|
|
*/
|
|
function hasTokenRole(uint256 tokenId, TokenRoles role, address account) public view returns (bool) {
|
|
uint256 currentVersion = _tokenRolesVersion[tokenId];
|
|
return _tokenRoles[tokenId][currentVersion][role][account];
|
|
}
|
|
|
|
/**
|
|
* @dev Grants the collection role to an address.
|
|
*/
|
|
function _grantCollectionRole(CollectionRoles role, address account) internal {
|
|
if (hasCollectionRole(role, account)) revert RoleAlreadySet();
|
|
|
|
_collectionRoles[role][account] = true;
|
|
_collectionRolesCounter[role] += 1;
|
|
|
|
emit CollectionRoleChanged(role, account, true, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev Revokes the collection role of an address.
|
|
*/
|
|
function _revokeCollectionRole(CollectionRoles role, address account) internal {
|
|
if (!hasCollectionRole(role, account)) revert RoleAlreadySet();
|
|
if (role == CollectionRoles.Owner && _collectionRolesCounter[role] == 1) revert MustHaveAtLeastOneOwner();
|
|
|
|
_collectionRoles[role][account] = false;
|
|
_collectionRolesCounter[role] -= 1;
|
|
|
|
emit CollectionRoleChanged(role, account, false, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev Grants the token role to an address.
|
|
*/
|
|
function _grantTokenRole(uint256 tokenId, TokenRoles role, address account) internal {
|
|
if (hasTokenRole(tokenId, role, account)) revert RoleAlreadySet();
|
|
|
|
uint256 currentVersion = _tokenRolesVersion[tokenId];
|
|
_tokenRoles[tokenId][currentVersion][role][account] = true;
|
|
|
|
emit TokenRoleChanged(tokenId, role, account, true, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev Revokes the token role of an address.
|
|
*/
|
|
function _revokeTokenRole(uint256 tokenId, TokenRoles role, address account) internal {
|
|
if (!hasTokenRole(tokenId, role, account)) revert RoleAlreadySet();
|
|
|
|
uint256 currentVersion = _tokenRolesVersion[tokenId];
|
|
_tokenRoles[tokenId][currentVersion][role][account] = false;
|
|
|
|
emit TokenRoleChanged(tokenId, role, account, false, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
* variables without shifting down storage in the inheritance chain.
|
|
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
*/
|
|
function _clearTokenRoles(uint256 tokenId) internal {
|
|
_tokenRolesVersion[tokenId] += 1;
|
|
emit TokenRolesCleared(tokenId, msg.sender);
|
|
}
|
|
|
|
/**
|
|
* @dev This empty reserved space is put in place to allow future versions to add new
|
|
* variables without shifting down storage in the inheritance chain.
|
|
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
|
|
*/
|
|
uint256[49] private __gap;
|
|
}
|