```yaml
---
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](https://github.com/OpenZeppelin/openzeppelin-skills) 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:
  ```solidity
  // ✅ 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`)
