Skip to content

Instantly share code, notes, and snippets.

@nventuro
Last active May 10, 2022 09:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nventuro/ac42bd936f678ebf55c4c0c4a4ee4c72 to your computer and use it in GitHub Desktop.
Save nventuro/ac42bd936f678ebf55c4c0c4a4ee4c72 to your computer and use it in GitHub Desktop.
Access Control Proposal
pragma solidity ^0.6.0;
import "./IAccessControl.sol";
import "../utils/EnumerableSet.sol";
abstract contract AccessControl is IAccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
struct Role {
EnumerableSet.AddressSet members;
bytes32 admin;
}
mapping (bytes32 => Role) _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x0000000000000000000000000000000000000000;
function hasRole(bytes32 roleId, address account)
public view returns (bool)
{
return _roles[roleId].members.contains(account);
}
function getRoleMembersCount(bytes32 roleId)
external
view
returns (uint256)
{
return _roles[roleId].members.length();
}
function getRoleMember(bytes32 roleId, uint256 index)
external
view
returns (address)
{
return _roles[roleId].members.get(index);
}
function getRoleAdmin(bytes32 roleId)
external
view
returns (bytes32)
{
return _roles[roleId].admin;
}
function grantRole(bytes32 roleId, address account) external {
require(hasRole(_roles[roleId].admin, msg.sender),
"AccessControl: sender must be an admin to grant"
);
_grantRole(roleId, account);
}
function revokeRole(bytes32 roleId, address account) external {
require(hasRole(_roles[roleId].admin, msg.sender),
"AccessControl: sender must be an admin to revoke"
);
_revokeRole(roleId, account);
}
function renounceRole(bytes32 roleId, address account) external {
require(account == msg.sender,
"AccessControl: can only renounce roles for self");
require(hasRole(roleId, msg.sender),
"AccessControl: sender have the role to revoke"
);
_revokeRole(roleId, account);
}
function _grantRole(bytes32 roleId, address account) internal {
bool added = _roles[roleId].members.add(account);
require(added, "AccessControl: account already has role");
}
function _revokeRole(bytes32 roleId, address account) internal {
bool removed = _roles[roleId].members.remove(account);
require(removed, "AccessControl: account does not have role");
}
function _setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) internal {
_roles[roleId].admin = adminRoleId;
}
}
pragma solidity ^0.6.0;
import "../access/AccessControl.sol";
import "../lifecycle/Pausable.sol";
import "../token/ERC20/ERC20.sol";
// Only the deployer of the token can ever mint tokens
contract ERC20SingleMinter is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor () {
_grantRole(MINTER_ROLE, msg.sender);
}
function mint(address recipient, uint256 amount) external {
require(hasRole(msg.sender, MINTER_ROLE);
_mint(recipient, amount);
}
}
// The deployer of the token can grant and revoke the minter role
contract ERC20MultipleMinters is ERC20, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
constructor () {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function mint(address recipient, uint256 amount) external {
require(hasRole(msg.sender, MINTER_ROLE);
_mint(recipient, amount);
}
}
// The deployer of the token can grant and revoke the minter and pauser roles
contract ERC20MintablePausable is ERC20, ERC20Pausable, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
constructor () {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function mint(address recipient, uint256 amount) external {
require(hasRole(msg.sender, MINTER_ROLE);
_mint(recipient, amount);
}
function pause() external {
require(hasRole(msg.sender, PAUSER_ROLE));
_pause();
}
}
// The deployer of the token can grant and revoke the permission to grant and
// revoke minter or pauser roles
contract ERC20MintablePausable is ERC20, ERC20Pausable, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant MINTER_ADMIN_ROLE = keccak256("MINTER_ADMIN_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant PAUSER_ADMIN_ROLE = keccak256("PAUSER_ADMIN_ROLE");
constructor () {
_setRoleAdmin(MINTER_ROLE, MINTER_ADMIN_ROLE);
_setRoleAdmin(PAUSER_ROLE, PAUSER_ADMIN_ROLE);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function mint(address recipient, uint256 amount) external {
require(hasRole(msg.sender, MINTER_ROLE);
_mint(recipient, amount);
}
function pause() external {
require(hasRole(msg.sender, PAUSER_ROLE));
_pause();
}
}
pragma solidity ^0.6.0;
interface IAccessControl {
// Queries
// Returns true if an account has a role
function hasRole(bytes32 roleId, address account) external view returns (bool);
// Returns the number of accounts with a role
function getRoleMembersCount(bytes32 roleId) external view returns (uint256);
// Returns an account with a role at index
function getRoleMember(bytes32 roleId, uint256 index) external view returns (address);
// Returns a role's admin role
function getRoleAdmin(bytes32 roleId) external view returns (bytes32);
// Operations
// Gives a role to an account. Caller must have its admin role
function grantRole(bytes32 roleId, address account) external;
// Revokes a role from an account. Caller must have its admin role
function revokeRole(bytes32 roleId, address account) external;
// Renounces a role. Caller must be `account`
function renounceRole(bytes32 roleId, address account) external;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment