पाठ 9 / 25
OpenZeppelin and Access Control
Reuse audited components and control who can do what.
Do not reinvent security-critical code
OpenZeppelin Contracts is the most widely used library of audited Solidity components: token standards (ERC-20, ERC-721, ERC-1155), access control, security utilities (ReentrancyGuard, Pausable), cryptography (ECDSA, MerkleProof, EIP-712), math and safe-cast helpers, and upgradeable variants. Install it as a dependency (npm or a Foundry library) and inherit or import what you need, rather than copying code. Version 5 changed several APIs: for example, Ownable requires the initial owner in its constructor, and token hooks were consolidated into an internal _update function. Access control choices: Ownable gives one owner with onlyOwner functions and ownership transfer (prefer Ownable2Step, which requires the new owner to accept, avoiding transfers to a wrong address); AccessControl provides role-based permissions (MINTER_ROLE, PAUSER_ROLE) with admin roles that grant and revoke them. In production, the owner or admin should be a multisig (such as Safe), often behind a timelock (TimelockController) so users can see and react to privileged changes before they take effect.
Role-based access control with OpenZeppelin
Separate roles for minting and pausing, administered by a multisig.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
contract Membership is AccessControl, Pausable {
bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
mapping(address => uint64) public expiresAt;
event Issued(address indexed member, uint64 expiresAt);
constructor(address adminMultisig, address issuer) {
_grantRole(DEFAULT_ADMIN_ROLE, adminMultisig); // can grant and revoke roles
_grantRole(ISSUER_ROLE, issuer);
_grantRole(PAUSER_ROLE, adminMultisig);
}
function issue(address member, uint64 durationSeconds) external onlyRole(ISSUER_ROLE) whenNotPaused {
uint64 expiry = uint64(block.timestamp) + durationSeconds;
expiresAt[member] = expiry;
emit Issued(member, expiry);
}
function isActive(address member) external view returns (bool) {
return expiresAt[member] > block.timestamp;
}
function pause() external onlyRole(PAUSER_ROLE) { _pause(); }
function unpause() external onlyRole(PAUSER_ROLE) { _unpause(); }
}Check the OpenZeppelin version
Import paths and APIs differ between OpenZeppelin 4.x and 5.x (for example, Pausable and ReentrancyGuard moved from security/ to utils/). Match examples and documentation to the version in your project.
त्वरित जाँच: Why should production admin roles usually be held by a multisig with a timelock?
- It is cheaper
- It speeds up transactions
- No single compromised key can act alone, and users get time to react to privileged changes
- It is required by the EVM
Answer
No single compromised key can act alone, and users get time to react to privileged changes — Multisigs remove single points of failure, and timelocks give transparency and reaction time.