protocols/openzeppelin-patterns
OpenZeppelin Contract Patterns
ethereumguide🏛️ Officialconfidence highhealth 100%
v1.0.0·Updated 3/20/2026
Installation
npm install @openzeppelin/contracts
npm install @openzeppelin/contracts-upgradeable # Upgradeable version
ERC-20 Token
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, Ownable {
constructor(uint256 initialSupply) ERC20("MyToken", "MTK") Ownable(msg.sender) {
_mint(msg.sender, initialSupply * 10 ** decimals());
}
function mint(address to, uint256 amount) public onlyOwner { _mint(to, amount); }
}
ERC-721 NFT
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MyNFT is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
function mint(address to, string memory tokenURI) public onlyOwner returns (uint256) {
_tokenIds.increment();
uint256 newId = _tokenIds.current();
_safeMint(to, newId);
_setTokenURI(newId, tokenURI);
return newId;
}
}
Access Control (Multi-Role)
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MyContract is AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
}
function mint(address to) public onlyRole(MINTER_ROLE) { ... }
}
Upgradeable Contracts (UUPS)
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract MyContractV1 is UUPSUpgradeable, OwnableUpgradeable {
function initialize() public initializer {
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address) internal override onlyOwner {}
}
// Deploy: upgrades.deployProxy(MyContractV1, [], { kind: 'uups' })
// Upgrade: upgrades.upgradeProxy(proxyAddress, MyContractV2)
ReentrancyGuard (Reentrancy Protection)
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract SafeVault is ReentrancyGuard {
function withdraw(uint256 amount) external nonReentrant {
// CEI pattern: Check → Effect → Interact
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // Effect: modify state first
payable(msg.sender).transfer(amount); // Interact last
}
}
Pausable
import "@openzeppelin/contracts/utils/Pausable.sol";
contract MyContract is Pausable, Ownable {
function pause() public onlyOwner { _pause(); }
function unpause() public onlyOwner { _unpause(); }
function transfer() public whenNotPaused { ... }
}
Common Pitfalls
- Upgradeable contracts cannot have a constructor, use
initialize()instead with theinitializermodifier - Upgradeable contracts cannot change storage layout, only append new variables
- Use
_disableInitializers()to prevent implementation contracts from being initialized directly - AccessControl is more flexible than Ownable, prefer it for multi-person team projects