Choose and implement the right access control pattern for your smart contract based on complexity and requirements.
## ROLE
You are a smart contract developer specializing in secure access control implementations for various governance models.
## CONTEXT
I need to implement access control for my smart contract.
## REQUIREMENTS
- Number of roles needed: ${{ROLES}}
- Admin structure: ${{ADMIN_STRUCTURE}}
- Upgrade capability: ${{UPGRADEABLE}}
- Enumeration needed: ${{ENUMERATION}}
## TASK
Design access control system:
**1. PATTERN COMPARISON**
| Pattern | Complexity | Flexibility | Gas Cost | Best For |
|---------|------------|-------------|----------|----------|
| Ownable | Low | Low | Low | Single admin |
| Ownable2Step | Low | Low | Medium | Safer transfer |
| AccessControl | Medium | High | Medium | Multi-role |
| AccessControlEnumerable | High | High | High | Role listing |
| Timelock | High | Medium | High | Governance |
**2. OWNABLE PATTERN**
```solidity
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyContract is Ownable {
constructor(address initialOwner) Ownable(initialOwner) {}
function adminFunction() public onlyOwner {
// Only owner can call
}
}
```
**3. OWNABLE2STEP (SAFER)**
```solidity
import "@openzeppelin/contracts/access/Ownable2Step.sol";
contract MyContract is Ownable2Step {
constructor(address initialOwner) Ownable(initialOwner) {}
// Requires acceptOwnership() call from new owner
// Prevents accidental transfers to wrong address
}
```
**4. ROLE-BASED ACCESS CONTROL**
```solidity
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MyContract is AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
constructor(address admin) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(MINTER_ROLE, admin);
}
function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
// Mint logic
}
function pause() public onlyRole(PAUSER_ROLE) {
// Pause logic
}
}
```
**5. ROLE HIERARCHY**
```solidity
contract HierarchicalAccess is AccessControl {
bytes32 public constant ADMIN = keccak256("ADMIN");
bytes32 public constant OPERATOR = keccak256("OPERATOR");
constructor() {
// ADMIN can grant/revoke OPERATOR role
_setRoleAdmin(OPERATOR, ADMIN);
_grantRole(ADMIN, msg.sender);
}
}
```
**6. ENUMERABLE ROLES**
```solidity
import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
contract MyContract is AccessControlEnumerable {
// Can query all members of a role
function getAllOperators() public view returns (address[] memory) {
uint256 count = getRoleMemberCount(OPERATOR_ROLE);
address[] memory operators = new address[](count);
for (uint256 i = 0; i < count; i++) {
operators[i] = getRoleMember(OPERATOR_ROLE, i);
}
return operators;
}
}
```
**7. TIMELOCK CONTROL**
```solidity
import "@openzeppelin/contracts/governance/TimelockController.sol";
// Deploy timelock
TimelockController timelock = new TimelockController(
minDelay, // Minimum delay for operations
proposers, // Addresses that can propose
executors, // Addresses that can execute
admin // Admin address
);
// Contract uses timelock as owner
contract MyContract is Ownable {
constructor(address timelockAddress) Ownable(timelockAddress) {}
}
```
**8. CUSTOM MODIFIERS**
```solidity
contract CustomAccess {
modifier onlyAuthorized() {
require(
isOwner(msg.sender) || isOperator(msg.sender),
"Not authorized"
);
_;
}
modifier whenNotPaused() {
require(!paused, "Contract paused");
_;
}
}
```
**9. RECOMMENDATION**
Based on your requirements:
- Pattern: [recommendation]
- Roles needed: [list]
- Implementation: [approach]
**10. SECURITY CHECKLIST**
- [ ] Admin role properly assigned
- [ ] Role hierarchy correct
- [ ] No privilege escalation
- [ ] Transfer procedures tested
- [ ] Emergency procedures definedOr press ⌘C to copy
Replace these placeholders with your own content before using the prompt.
[{ROLES][{ADMIN_STRUCTURE][{UPGRADEABLE][{ENUMERATION]{
constructor(address initialOwner) Ownable(initialOwner) {}{
// Only owner can call
}{
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
constructor(address admin) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(MINTER_ROLE, admin);
}{
// Mint logic
}{
// Pause logic
}{
bytes32 public constant ADMIN = keccak256("ADMIN");
bytes32 public constant OPERATOR = keccak256("OPERATOR");
constructor() {
// ADMIN can grant/revoke OPERATOR role
_setRoleAdmin(OPERATOR, ADMIN);
_grantRole(ADMIN, msg.sender);
}{
// Can query all members of a role
function getAllOperators() public view returns (address[] memory) {
uint256 count = getRoleMemberCount(OPERATOR_ROLE);
address[] memory operators = new address[](count);
for (uint256 i = 0; i < count; i++) {
operators[i] = getRoleMember(OPERATOR_ROLE, i);
}{
constructor(address timelockAddress) Ownable(timelockAddress) {}{
modifier onlyAuthorized() {
require(
isOwner(msg.sender) || isOperator(msg.sender),
"Not authorized"
);
_;
}{
require(!paused, "Contract paused");
_;
}