0
# Access Control
1
2
OpenZeppelin provides flexible access control mechanisms including simple ownership patterns and sophisticated role-based access control. These contracts enable secure management of administrative functions and permissions in smart contracts.
3
4
## Capabilities
5
6
### Ownable - Simple Ownership
7
8
Basic access control mechanism where there is an account (an owner) that can be granted exclusive access to specific functions.
9
10
```solidity { .api }
11
/**
12
* Contract module which provides a basic access control mechanism, where there is an account (an owner)
13
* that can be granted exclusive access to specific functions
14
*/
15
abstract contract Ownable is Context {
16
/**
17
* Returns the address of the current owner
18
*/
19
function owner() public view virtual returns (address);
20
21
/**
22
* Leaves the contract without owner. It will not be possible to call onlyOwner functions anymore
23
* Can only be called by the current owner
24
*/
25
function renounceOwnership() public virtual onlyOwner;
26
27
/**
28
* Transfers ownership of the contract to a new account (newOwner)
29
* Can only be called by the current owner
30
*/
31
function transferOwnership(address newOwner) public virtual onlyOwner;
32
33
/**
34
* Throws if called by any account other than the owner
35
*/
36
modifier onlyOwner();
37
38
/**
39
* Emitted when ownership is transferred
40
*/
41
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
42
}
43
```
44
45
**Usage Example:**
46
47
```solidity
48
import "@openzeppelin/contracts/access/Ownable.sol";
49
50
contract MyContract is Ownable {
51
uint256 private _value;
52
53
function setValue(uint256 newValue) public onlyOwner {
54
_value = newValue;
55
}
56
57
function getValue() public view returns (uint256) {
58
return _value;
59
}
60
}
61
```
62
63
### AccessControl - Role-Based Access Control
64
65
Role-based access control mechanism with hierarchical roles where each role can have multiple members and each role can have an admin role that can grant and revoke that role.
66
67
```solidity { .api }
68
/**
69
* Contract module that allows children to implement role-based access control mechanisms
70
*/
71
abstract contract AccessControl is Context, IAccessControl, ERC165 {
72
/**
73
* Default admin role for all roles. Only accounts with this role will be able to grant or revoke other roles
74
*/
75
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
76
77
/**
78
* Returns true if account has been granted role
79
*/
80
function hasRole(bytes32 role, address account) public view virtual override returns (bool);
81
82
/**
83
* Returns the number of accounts that have role
84
*/
85
function getRoleMemberCount(bytes32 role) public view returns (uint256);
86
87
/**
88
* Returns one of the accounts that have role
89
*/
90
function getRoleMember(bytes32 role, uint256 index) public view returns (address);
91
92
/**
93
* Returns the admin role that controls role
94
*/
95
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32);
96
97
/**
98
* Grants role to account
99
* If account had not been already granted role, emits a RoleGranted event
100
*/
101
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role));
102
103
/**
104
* Revokes role from account
105
* If account had been granted role, emits a RoleRevoked event
106
*/
107
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role));
108
109
/**
110
* Revokes role from the calling account
111
*/
112
function renounceRole(bytes32 role, address account) public virtual override;
113
114
/**
115
* Modifier that checks that an account has a specific role
116
*/
117
modifier onlyRole(bytes32 role);
118
119
/**
120
* Emitted when newAdminRole is set as role's admin role, replacing previousAdminRole
121
*/
122
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
123
124
/**
125
* Emitted when account is granted role
126
*/
127
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
128
129
/**
130
* Emitted when account is revoked role
131
*/
132
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
133
}
134
```
135
136
**Usage Example:**
137
138
```solidity
139
import "@openzeppelin/contracts/access/AccessControl.sol";
140
141
contract MyContract is AccessControl {
142
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
143
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
144
145
constructor() {
146
// Grant the contract deployer the default admin role
147
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
148
_grantRole(MINTER_ROLE, msg.sender);
149
}
150
151
function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
152
// Mint tokens
153
}
154
155
function burn(uint256 amount) public onlyRole(BURNER_ROLE) {
156
// Burn tokens
157
}
158
}
159
```
160
161
### TimelockController - Delayed Execution
162
163
A timelock controller contract that acts as a timelocked admin. It is meant to be used with other contracts through composition.
164
165
```solidity { .api }
166
/**
167
* Contract module which acts as a timelocked controller
168
*/
169
contract TimelockController is AccessControl {
170
bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
171
bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
172
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
173
174
/**
175
* Returns whether an id corresponds to a registered operation
176
*/
177
function isOperation(bytes32 id) public view virtual returns (bool registered);
178
179
/**
180
* Returns whether an operation is pending or not
181
*/
182
function isOperationPending(bytes32 id) public view virtual returns (bool pending);
183
184
/**
185
* Returns whether an operation is ready or not
186
*/
187
function isOperationReady(bytes32 id) public view virtual returns (bool ready);
188
189
/**
190
* Returns whether an operation is done or not
191
*/
192
function isOperationDone(bytes32 id) public view virtual returns (bool done);
193
194
/**
195
* Returns the timestamp at which an operation becomes ready
196
*/
197
function getTimestamp(bytes32 id) public view virtual returns (uint256 timestamp);
198
199
/**
200
* Returns the minimum delay for operations
201
*/
202
function getMinDelay() public view virtual returns (uint256 duration);
203
204
/**
205
* Returns the identifier of an operation containing a single transaction
206
*/
207
function hashOperation(
208
address target,
209
uint256 value,
210
bytes calldata data,
211
bytes32 predecessor,
212
bytes32 salt
213
) public pure virtual returns (bytes32 hash);
214
215
/**
216
* Returns the identifier of an operation containing a batch of transactions
217
*/
218
function hashOperationBatch(
219
address[] calldata targets,
220
uint256[] calldata values,
221
bytes[] calldata datas,
222
bytes32 predecessor,
223
bytes32 salt
224
) public pure virtual returns (bytes32 hash);
225
226
/**
227
* Schedule an operation containing a single transaction
228
*/
229
function schedule(
230
address target,
231
uint256 value,
232
bytes calldata data,
233
bytes32 predecessor,
234
bytes32 salt,
235
uint256 delay
236
) public virtual onlyRole(PROPOSER_ROLE);
237
238
/**
239
* Schedule an operation containing a batch of transactions
240
*/
241
function scheduleBatch(
242
address[] calldata targets,
243
uint256[] calldata values,
244
bytes[] calldata datas,
245
bytes32 predecessor,
246
bytes32 salt,
247
uint256 delay
248
) public virtual onlyRole(PROPOSER_ROLE);
249
250
/**
251
* Cancel an operation
252
*/
253
function cancel(bytes32 id) public virtual onlyRole(PROPOSER_ROLE);
254
255
/**
256
* Execute an (ready) operation containing a single transaction
257
*/
258
function execute(
259
address target,
260
uint256 value,
261
bytes calldata data,
262
bytes32 predecessor,
263
bytes32 salt
264
) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE);
265
266
/**
267
* Execute an (ready) operation containing a batch of transactions
268
*/
269
function executeBatch(
270
address[] calldata targets,
271
uint256[] calldata values,
272
bytes[] calldata datas,
273
bytes32 predecessor,
274
bytes32 salt
275
) public payable virtual onlyRoleOrOpenRole(EXECUTOR_ROLE);
276
277
/**
278
* Changes the minimum timelock duration for future operations
279
*/
280
function updateDelay(uint256 newDelay) external virtual;
281
282
event CallScheduled(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data, bytes32 predecessor, uint256 delay);
283
event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data);
284
event Cancelled(bytes32 indexed id);
285
event MinDelayChange(uint256 oldDuration, uint256 newDuration);
286
}
287
```
288
289
## Access Control Patterns
290
291
### Combining Ownership and Roles
292
293
```solidity
294
import "@openzeppelin/contracts/access/Ownable.sol";
295
import "@openzeppelin/contracts/access/AccessControl.sol";
296
297
contract MyContract is Ownable, AccessControl {
298
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
299
300
constructor() {
301
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
302
}
303
304
// Only owner can add operators
305
function addOperator(address operator) public onlyOwner {
306
grantRole(OPERATOR_ROLE, operator);
307
}
308
309
// Only operators can perform this action
310
function performOperation() public onlyRole(OPERATOR_ROLE) {
311
// Operation logic
312
}
313
314
// Override required by Solidity for multiple inheritance
315
function supportsInterface(bytes4 interfaceId)
316
public view override(AccessControl) returns (bool) {
317
return super.supportsInterface(interfaceId);
318
}
319
}
320
```
321
322
### Multi-Signature with AccessControl
323
324
```solidity
325
import "@openzeppelin/contracts/access/AccessControl.sol";
326
327
contract MultiSigContract is AccessControl {
328
bytes32 public constant SIGNER_ROLE = keccak256("SIGNER_ROLE");
329
330
uint256 public constant REQUIRED_SIGNATURES = 2;
331
mapping(bytes32 => uint256) public proposalSignatures;
332
mapping(bytes32 => mapping(address => bool)) public hasSignedProposal;
333
334
constructor(address[] memory signers) {
335
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
336
for (uint i = 0; i < signers.length; i++) {
337
_grantRole(SIGNER_ROLE, signers[i]);
338
}
339
}
340
341
function signProposal(bytes32 proposalId) public onlyRole(SIGNER_ROLE) {
342
require(!hasSignedProposal[proposalId][msg.sender], "Already signed");
343
hasSignedProposal[proposalId][msg.sender] = true;
344
proposalSignatures[proposalId]++;
345
}
346
347
function executeProposal(bytes32 proposalId) public {
348
require(proposalSignatures[proposalId] >= REQUIRED_SIGNATURES, "Insufficient signatures");
349
// Execute proposal logic
350
}
351
}
352
```
353
354
### Timelock Integration
355
356
```solidity
357
import "@openzeppelin/contracts/access/TimelockController.sol";
358
import "@openzeppelin/contracts/access/Ownable.sol";
359
360
contract GovernedContract is Ownable {
361
TimelockController public timelock;
362
363
constructor(address timelockAddress) {
364
timelock = TimelockController(payable(timelockAddress));
365
// Transfer ownership to timelock
366
transferOwnership(timelockAddress);
367
}
368
369
function sensitiveOperation(uint256 newValue) public onlyOwner {
370
// This can only be called by the timelock after delay
371
}
372
}
373
```