# OpenZeppelin and Access Control — Blockchain & Smart Contracts (Solidity)

Source: https://www.geekswithgeeks.com/en/solidity/d-openzeppelin

> 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.

```solidity
// 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.

**Quiz:** Why should production admin roles usually be held by a multisig with a timelock?

- [ ] It is cheaper
- [ ] It speeds up transactions
- [x] 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.
