iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

OpenZeppelin

OpenZeppelin Contracts is the audited Solidity library every serious dapp builds on. Use its ERC-20, ERC-721, AccessControl, Pausable, ReentrancyGuard, and proxy patterns instead of hand-rolling primitives. Hand-rolled crypto economics are bug factories; OpenZeppelin is the floor.

A safe ERC-20 with roles, pause, and upgradeable proxy

EXAMPLE
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

// ============================================================
// 1) A safe ERC-20 with mint, burn, pause, and role gating
// ============================================================
import { ERC20 }       from '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import { ERC20Burnable } from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';
import { ERC20Pausable } from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol';
import { AccessControl } from '@openzeppelin/contracts/access/AccessControl.sol';

contract ShopToken is ERC20, ERC20Burnable, ERC20Pausable, AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE');
    bytes32 public constant PAUSER_ROLE = keccak256('PAUSER_ROLE');

    constructor(address admin) ERC20('Shop Token', 'SHOP') {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE, admin);
        _grantRole(PAUSER_ROLE, admin);
    }

    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }

    function pause() external onlyRole(PAUSER_ROLE) { _pause(); }
    function unpause() external onlyRole(PAUSER_ROLE) { _unpause(); }

    // Required override — both bases hook _update
    function _update(address from, address to, uint256 value)
        internal override(ERC20, ERC20Pausable) {
        super._update(from, to, value);
    }
}

// ============================================================
// 2) Reentrancy guard — for ANY function that calls external
// ============================================================
import { ReentrancyGuard } from '@openzeppelin/contracts/utils/ReentrancyGuard.sol';

contract Vault is ReentrancyGuard {
    mapping(address => uint256) public balanceOf;

    function deposit() external payable {
        balanceOf[msg.sender] += msg.value;
    }

    // nonReentrant blocks the classic re-entrancy attack
    function withdraw(uint256 amount) external nonReentrant {
        require(balanceOf[msg.sender] >= amount, 'insufficient');
        balanceOf[msg.sender] -= amount;          // EFFECTS before INTERACTION
        (bool ok, ) = msg.sender.call{ value: amount }('');
        require(ok, 'send failed');
    }
}

// ============================================================
// 3) Upgradeable contract via the UUPS proxy pattern
// ============================================================
// npm i @openzeppelin/contracts-upgradeable
import { Initializable }   from '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import { UUPSUpgradeable } from '@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol';
import { OwnableUpgradeable } from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';

contract Registry is Initializable, OwnableUpgradeable, UUPSUpgradeable {
    mapping(bytes32 => address) public addrs;

    // Use initialize instead of constructor — proxies have no constructor state.
    function initialize(address admin) public initializer {
        __Ownable_init(admin);
        __UUPSUpgradeable_init();
    }

    function set(bytes32 key, address value) external onlyOwner {
        addrs[key] = value;
    }

    function _authorizeUpgrade(address) internal override onlyOwner {}
}

// Deploy with the OpenZeppelin Upgrades plugin (Hardhat / Foundry):
// const r = await upgrades.deployProxy(Registry, [admin.address], { kind: 'uups' });
// later:
// const r2 = await upgrades.upgradeProxy(r.address, RegistryV2);

// ============================================================
// 4) Patterns that come with OZ for free
// ============================================================
// SafeERC20         wraps non-conforming ERC-20s (USDT, etc.) so safeTransfer
//                   reverts on failure instead of returning false
// MerkleProof       Merkle airdrop / allowlist verification
// EIP712            structured signatures (gasless approvals, permit)
// AccessControl     role-based admin, optional admin-of-admin chains
// TimelockController  N-of-M signers + min delay for sensitive ops

// ============================================================
// 5) Audit + governance pattern
// ============================================================
// - Deploy via a multisig (Gnosis Safe) as admin
// - Wrap admin actions in a Timelock with > 24 hour delay
// - Public bounty program before adding new roles to the contract
// - Verify source on Etherscan / Sourcify so users can read it

Why it matters

Default to OpenZeppelin for every primitive — ERC-20, ERC-721, AccessControl, ReentrancyGuard, UUPS proxies. They have been audited, fuzzed, and battle-tested across billions of dollars of TVL. The mythical performance or "elegance" win of a hand-rolled version is never worth the audit budget you skip; ship boring, audited primitives and put creativity into the unique parts of your protocol.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Battle-tested implementations: ERC-20/721/1155, AccessControl, Ownable,
// ReentrancyGuard, PausableUpgradeable, ERC-2535 diamonds.
// Default to these instead of writing your own primitives.
Try it Yourself »

Discussion

Loading…