$npx -y skills add OpenZeppelin/openzeppelin-skills --skill develop-secure-contractsDevelop secure smart contracts using OpenZeppelin Contracts libraries. Use when users need to integrate OpenZeppelin library components — including token standards (ERC20, ERC721, ERC1155), access control (Ownable, AccessControl, AccessManager), security primitives (Pausable, Ree
| 1 | # Develop Secure Smart Contracts with OpenZeppelin |
| 2 | |
| 3 | ## Core Workflow |
| 4 | |
| 5 | ### Understand the Request Before Responding |
| 6 | |
| 7 | For conceptual questions ("How does Ownable work?"), explain without generating code. For implementation requests, proceed with the workflow below. |
| 8 | |
| 9 | ### CRITICAL: Always Read the Project First |
| 10 | |
| 11 | Before generating code or suggesting changes: |
| 12 | |
| 13 | 1. **Search the user's project** for existing contracts (`Glob` for `**/*.sol`, `**/*.cairo`, `**/*.rs`, `**/*.move`, etc.) |
| 14 | 2. **Read the relevant contract files** to understand what already exists |
| 15 | 3. **Default to integration, not replacement** — when users say "add pausability" or "make it upgradeable", they mean modify their existing code, not generate something new. Only replace if explicitly requested ("start fresh", "replace this"). |
| 16 | |
| 17 | If a file cannot be read, surface the failure explicitly — report the path attempted and the reason. Ask whether the path is correct. Never silently fall back to a generic response as if the file does not exist. |
| 18 | |
| 19 | ### Fundamental Rule: Prefer Library Components Over Custom Code |
| 20 | |
| 21 | Before writing ANY logic, search the OpenZeppelin library for an existing component: |
| 22 | |
| 23 | 1. **Exact match exists?** Import and use it directly — inherit, implement its trait, compose with it. Done. |
| 24 | 2. **Close match exists?** Import and extend it — override only functions the library marks as overridable (virtual, hooks, configurable parameters). |
| 25 | 3. **No match exists?** Only then write custom logic. Confirm by browsing the library's directory structure first. |
| 26 | |
| 27 | **NEVER copy or embed library source code into the user's contract.** Always import from the dependency so the project receives security updates. Never hand-write what the library already provides: |
| 28 | - Never write a custom `paused` modifier when `Pausable` or `ERC20Pausable` exists |
| 29 | - Never write `require(msg.sender == owner)` when `Ownable` exists |
| 30 | - Never implement ERC165 logic when the library's base contracts already handle it |
| 31 | |
| 32 | ### Methodology |
| 33 | |
| 34 | The primary workflow is **pattern discovery from library source code**: |
| 35 | |
| 36 | 1. Inspect what the user's project already imports |
| 37 | 2. Read the dependency source and docs in the project's installed packages |
| 38 | 3. Identify what functions, modifiers, hooks, and storage the dependency requires |
| 39 | 4. Apply those requirements to the user's contract |
| 40 | |
| 41 | See [Pattern Discovery and Integration](#pattern-discovery-and-integration) below for the full step-by-step procedure. |
| 42 | |
| 43 | ### CLI Generators as Reference |
| 44 | |
| 45 | Use `npx @openzeppelin/contracts-cli` to generate reference implementations for pattern discovery: |
| 46 | generate a baseline to a file, generate with a feature enabled to another file, diff them, and apply the changes to the user's code. The CLI output is the canonical correct integration — use it as the source of truth for what imports, inheritance, storage, and overrides a feature requires. |
| 47 | |
| 48 | See [CLI Generators](#cli-generators) for details on the generate-compare-apply workflow. |
| 49 | |
| 50 | If no CLI command exists for what's needed, use the generic pattern discovery methodology from [Pattern Discovery and Integration](#pattern-discovery-and-integration). The absence of a CLI command does not mean the library lacks support — it only means there is no generator. |
| 51 | |
| 52 | ## Pattern Discovery and Integration |
| 53 | |
| 54 | Procedural guide for discovering and applying OpenZeppelin contract integration patterns |
| 55 | by reading dependency source code. Works for any ecosystem and any library version. |
| 56 | |
| 57 | **Prerequisite:** Always follow the library-first decision tree above |
| 58 | (prefer library components over custom code, never copy/embed source). |
| 59 | |
| 60 | ### Step 1: Identify Dependencies and Search the Library |
| 61 | |
| 62 | 1. Search the project for contract files: `Glob` for `**/*.sol`, `**/*.cairo`, `**/*.rs`, |
| 63 | `**/*.move`, or the relevant extension from the lookup table below. |
| 64 | 2. Read import/use statements in existing contracts to identify which OpenZeppelin components |
| 65 | are already in use. |
| 66 | 3. Locate the installed dependency in the project's dependency tree: |
| 67 | - Solidity: `node_modules/@openzeppelin/contracts/` (Hardhat/npm) or |
| 68 | `lib/openzeppelin-contracts/` (Foundry/forge) |
| 69 | - Cairo: resolve from `Scarb.toml` dependencies — source cached by Scarb |
| 70 | - Stylus: resolve from `Cargo.toml` — source in `target/` or the cargo registry cache |
| 71 | (`~/.cargo/registry/src/`) |
| 72 | - Stell |