पाठ 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.