Secure Smart Contract library providing battle-tested implementations of industry-standard Solidity contracts including ERC20, ERC721, ERC1155 tokens, access control mechanisms, proxy patterns, and governance systems for Ethereum and EVM-compatible blockchains.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
OpenZeppelin Contracts provides flexible access control mechanisms through role-based and ownership-based patterns, enabling secure function access management in smart contracts.
Flexible role-based access control mechanism where roles are identified by bytes32 identifiers.
abstract contract AccessControl is Context, IAccessControl, ERC165 {
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool);
function hasRole(bytes32 role, address account) public view virtual returns (bool);
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32);
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role));
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role));
function renounceRole(bytes32 role, address account) public virtual;
}
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;
}Events:
RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole)RoleGranted(bytes32 indexed role, address indexed account, address indexed sender)RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender)Modifiers:
onlyRole(bytes32 role) - Restricts access to accounts with specific roleUsage Example:
contract MyContract is AccessControl {
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MANAGER_ROLE, msg.sender);
}
function managerOnlyFunction() public onlyRole(MANAGER_ROLE) {
// Only accounts with MANAGER_ROLE can call this
}
function operatorFunction() public onlyRole(OPERATOR_ROLE) {
// Only accounts with OPERATOR_ROLE can call this
}
}Extension of AccessControl that allows enumeration of role members.
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool);
function getRoleMember(bytes32 role, uint256 index) public view virtual returns (address);
function getRoleMemberCount(bytes32 role) public view virtual returns (uint256);
}
interface IAccessControlEnumerable is IAccessControl {
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}Usage Example:
contract MyEnumerableContract is AccessControlEnumerable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
function getAllMinters() public view returns (address[] memory) {
uint256 count = getRoleMemberCount(MINTER_ROLE);
address[] memory minters = new address[](count);
for (uint256 i = 0; i < count; i++) {
minters[i] = getRoleMember(MINTER_ROLE, i);
}
return minters;
}
}Extension for cross-chain access control scenarios.
abstract contract AccessControlCrossChain is AccessControl, CrossChainEnabled {
function hasRole(bytes32 role, address account) public view virtual override returns (bool);
}Simple ownership access control with a single owner.
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor();
function owner() public view virtual returns (address);
function renounceOwnership() public virtual onlyOwner;
function transferOwnership(address newOwner) public virtual onlyOwner;
}Events:
OwnershipTransferred(address indexed previousOwner, address indexed newOwner)Modifiers:
onlyOwner() - Restricts access to the contract ownerUsage Example:
contract MyOwnableContract is Ownable {
uint256 public value;
function setValue(uint256 _value) public onlyOwner {
value = _value;
}
function emergencyStop() public onlyOwner {
// Only owner can call emergency functions
}
}Two-step ownership transfer for enhanced security.
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function pendingOwner() public view virtual returns (address);
function transferOwnership(address newOwner) public virtual override onlyOwner;
function acceptOwnership() external;
}Events:
OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner)Usage Example:
contract SecureContract is Ownable2Step {
function initiateOwnershipTransfer(address newOwner) public onlyOwner {
transferOwnership(newOwner);
// newOwner must call acceptOwnership() to complete the transfer
}
}You can combine different access control patterns for more sophisticated permission systems:
contract HybridAccessControl is AccessControl, Ownable {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant USER_ROLE = keccak256("USER_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
}
// Only owner or admin role can call
function sensitiveFunction() public {
require(
owner() == msg.sender || hasRole(ADMIN_ROLE, msg.sender),
"Not authorized"
);
// Function logic
}
// Only owner can grant admin role
function grantAdminRole(address account) public onlyOwner {
grantRole(ADMIN_ROLE, account);
}
}Create role hierarchies by setting role admins:
contract HierarchicalRoles is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
// Set role hierarchy
_setRoleAdmin(MANAGER_ROLE, ADMIN_ROLE);
_setRoleAdmin(OPERATOR_ROLE, MANAGER_ROLE);
}
function adminFunction() public onlyRole(ADMIN_ROLE) {
// Only admins can call
}
function managerFunction() public onlyRole(MANAGER_ROLE) {
// Only managers can call
}
function operatorFunction() public onlyRole(OPERATOR_ROLE) {
// Only operators can call
}
}// Example test patterns
contract AccessControlTest {
function testOnlyOwnerFunctions() public {
// Test that only owner can call owner-only functions
vm.prank(owner);
contract.ownerOnlyFunction();
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(user);
contract.ownerOnlyFunction();
}
function testRoleBasedAccess() public {
// Test role-based access
vm.prank(admin);
contract.grantRole(MANAGER_ROLE, manager);
vm.prank(manager);
contract.managerFunction();
vm.expectRevert();
vm.prank(user);
contract.managerFunction();
}
}Install with Tessl CLI
npx tessl i tessl/npm-openzeppelin-solidity