Skip to content
---
name: openzeppelin
description: "Develop smart contracts using OpenZeppelin Contracts library. Use when the user wants to create or modify Solidity contracts that use OpenZeppelin โ€” including token standards (ERC20, ERC721, ERC1155), access control (Ownable, AccessControl), security primitives (Pausable, ReentrancyGuard), or any OZ extension. Covers library-first integration, pattern discovery from installed source, and version-safe development."
---

Develop Smart Contracts with OpenZeppelin

Adapted from OpenZeppelin's official skill for Scaffold-ETH 2 Solidity projects.

Prerequisites

Check if ./packages/nextjs/scaffold.config.ts exists directly in the current working directory (do not search subdirectories). If it doesn't exist, this is not a Scaffold-ETH 2 project. Follow the instructions at https://docs.scaffoldeth.io/SKILL.md to scaffold it first. If it exists, continue directly with building.

Detect the Solidity framework flavor:

  • If packages/hardhat exists โ†’ Hardhat flavor
  • If packages/foundry exists โ†’ Foundry flavor

Core Principle: Prefer Library Components Over Custom Code

Before writing ANY contract logic, search the installed OpenZeppelin library for an existing component:

  1. Exact match exists? Import and use it directly โ€” inherit, override only what's needed
  2. Close match exists? Import and extend โ€” override only functions the library marks as virtual
  3. No match? Only then write custom logic. Confirm by browsing the library's directory first
Never hand-write what the library already provides:
  • Don't write require(totalSupply() + amount <= cap) when ERC20Capped exists
  • Don't write require(msg.sender == owner) when Ownable exists
  • Don't implement token transfer hooks manually when the library's base contracts handle it
  • Don't copy library source into your contract โ€” always import from the dependency so the project gets security updates

Pattern Discovery: Read the Installed Source

APIs, override points, and import syntax change between major versions. Don't assume patterns from memory โ€” always verify by reading the installed source.

Step 1: Identify the installed version and browse the library

Find the OpenZeppelin version and locate the installed source:

  • Hardhat: check packages/hardhat/package.json for @openzeppelin/contracts version, then browse packages/hardhat/node_modules/@openzeppelin/contracts/
  • Foundry: check packages/foundry/lib/openzeppelin-contracts/ and the remappings in packages/foundry/foundry.toml

Browse the library's directory structure to discover available components. Key directories inside the OZ contracts root:

  • token/{ERC20,ERC721,ERC1155}/ โ€” token standards and their base implementations
  • token/{ERC20,ERC721}/extensions/ โ€” Capped, Burnable, Pausable, Permit, Votes, Enumerable, etc.
  • access/ โ€” Ownable, AccessControl, AccessManager
  • utils/ โ€” ReentrancyGuard, Pausable, math, structs

Step 2: Read the source file for the component you need

Look at:

  • Import syntax: how the library's own files import each other โ€” mirror that style. OZ v5+ uses named imports:
    // โœ… import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
    // โŒ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
  • virtual functions: only these can be overridden. Functions that were virtual in one version may not be in another
  • Constructor parameters: what must be passed during deployment
  • NatSpec comments: NOTE: This function is not virtual, {X} should be overridden instead โ€” follow these

Step 3: Extract the minimal integration pattern

From the source, identify only what's needed:

  • Imports to add
  • Inheritance chain
  • Constructor parameters and chaining
  • Required overrides (when inheriting multiple contracts that define the same virtual function, you must override it and call super)
  • New functions to expose

Step 4: Apply to the user's contract

Integrate into existing code. Check for conflicts: duplicate access control, incompatible inheritance, missing overrides. The Solidity compiler will error if two parent contracts define the same function and you don't explicitly override it.

SE-2 Integration Notes

  • OpenZeppelin is pre-installed in both flavors โ€” no additional dependency installation needed
  • Contracts: packages/hardhat/contracts/ (Hardhat) or packages/foundry/contracts/ (Foundry)
  • Deploy scripts: packages/hardhat/deploy/ (Hardhat) or packages/foundry/script/ (Foundry)
  • Frontend reads contract ABIs from packages/nextjs/contracts/deployedContracts.ts (auto-generated by yarn deploy)