{"owner":"OpenZeppelin","repo":"openzeppelin-contracts","hasSkills":true,"totalSkillsCount":5,"totalTokensCount":9299,"categories":["anthropic-skill","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":[".claude/skills/add-changeset/SKILL.md",".claude/skills/solidity-style/SKILL.md",".claude/skills/testing/SKILL.md","CLAUDE.md",".claude/skills/library-api-design/SKILL.md"],"skills":{".claude/skills/add-changeset/SKILL.md":"---\nname: add-changeset\ndescription: Add a changelog entry for a PR. Use when the user asks for a changeset, when finishing a PR that changes user-visible contract behavior, or when the changeset-bot has flagged the PR. Skip for NatSpec-only edits, internal refactors with no observable effect, or repo plumbing.\n---\n\n# Add a changeset\n\n## When to add one\n\nAdd a changeset when the PR changes **user-visible contract behavior**: a new function, a new contract or extension, a bug fix that changes outputs, a new event, a new error, a new interface ID, or a tighter requirement.\n\n**Skip** for:\n\n- NatSpec-only changes.\n- Internal refactors with no observable effect.\n- Test/mock-only changes.\n- CI, lint, docgen, dependency bumps that don't change the published `contracts/`.\n\nIf unsure, err on the side of adding one — release reviewers can downgrade to \"skip\" by deleting the file before tagging.\n\n## How\n\n```bash\nnpx changeset add\n```\n\nThe CLI prompts for severity, then opens an editor. Severity:\n\n- `patch` — bug fixes, gas optimizations, no API surface change.\n- `minor` — new contracts, new extensions, new public functions on existing contracts.\n\nThere is no `major` from contributors — major version bumps are coordinated by maintainers.\n\nThe resulting file in `.changeset/` looks like:\n\n```\n---\n'openzeppelin-solidity': minor\n---\n\n`ComponentName`: One-sentence description starting with a backtick-quoted component name.\n```\n\n## Format rules\n\n- **First word inside the backticks** is the affected contract, extension, or library: `ERC20`, `ERC1155Crosschain`, `Memory`, `Governor`, etc.\n- **Then a colon, then one sentence** describing the user-visible change.\n- **Imperative mood** (matching PR titles: \"Add\", \"Fix\", not \"Added\", \"Fixed\"). Trailing period optional.\n- **No bullet points**, no headings, no multi-paragraph prose. The release workflow concatenates these verbatim.\n- **State the change, don't justify it.** Skip \"This mirrors ...\", \"so that ...\", \"closes a gap where ...\", parallels to other functions, or motivation. If the user needs to take action (migration step, breaking rename, tighter requirement they might now hit), state that instead — otherwise the changelog line is just the fact of the change. Rationale belongs in the PR body and commit message, not the changelog.\n\nGood examples (taken from existing entries):\n\n```\n`Memory`: Add a `isReserved(Slice)` function that checks if the memory occupied by the slice is reserved (i.e. before the free memory pointer).\n```\n\n```\n`ERC1155Crosschain`: Add an ERC-1155 extension to embed an ERC-7786 based crosschain bridge directly in the token contract.\n```\n\n```\nAdd ERC-165 detection for the `IERC6909ContentURI`, `IERC6909TokenSupply` and `IERC6909Metadata` interfaces in the `ERC6909ContentURI`, `ERC6909TokenSupply` and `ERC6909Metadata` contracts respectively.\n```\n\n(The last form — no leading backticks — is acceptable when several components are touched at once and a single prefix would be misleading.)\n\n## After committing\n\nThe `changeset-bot` GitHub check posts a comment confirming the changeset was detected. If the bot complains \"No Changeset found\" and the PR should not have one (NatSpec-only, etc.), leave a comment justifying it — a maintainer can apply the `ignore-changeset` label.\n",".claude/skills/solidity-style/SKILL.md":"---\nname: solidity-style\ndescription: Solidity style and safety conventions for contract source files. Use when writing or editing any .sol file under contracts/. Covers errors, events, NatSpec, imports, file structure, assembly, unchecked, casting, numeric literal formatting, and inheritance ordering.\n---\n\n# Solidity style and safety\n\nThe conventions below are not enforced by solhint. For the lint-enforced rules (private state vars, underscore prefixes, library visibility, etc.), see the project root `CLAUDE.md`.\n\n## Errors\n\nPrefer `require(condition, CustomError(args));` in files whose pragma is `^0.8.27` or later (the first version supporting it in the legacy pipeline). Keep `if (!condition) revert CustomError(args);` when:\n\n- The file's pragma is lower. Do not bump pragma just to use `require`.\n- An error argument is anything other than a variable, constant, literal or cast of those. `require` evaluates its arguments even when the condition holds, so a function call, storage or member read costs gas on the success path, and checked arithmetic (e.g. `revert Exceeded(value - max)`) can panic instead of passing.\n- The check is part of an `if`/`else if` chain.\n\nWhen a condition compares against a literal or constant bound, prefer the strict operator: `require(x < 0x5fd4, ...)` rather than `require(x <= 0x5fd3, ...)`. The EVM has `LT`/`GT` but no `LTE`/`GTE`, so `<=` compiles to `ISZERO(GT(...))`. Don't rewrite a comparison against a variable bound this way: `x < max + 1` adds arithmetic that overflows at `type(uint256).max`.\n\nCustom errors follow [ERC-6093](https://eips.ethereum.org/EIPS/eip-6093):\n\n- **Domain prefix**: `ERC<number>` if the error violates an ERC spec; otherwise the component name (`Governor`, `ECDSA`, `Timelock`).\n- **Include offending values** as arguments so callers can decode the failure context.\n- **Never declare the same error name twice** across the library — duplicates cause identifier conflicts when inheriting from multiple contracts.\n\n**Location priority**:\n\n1. Reuse the error from the underlying ERC if already defined there.\n2. Otherwise declare in the interface or library that owns the concept.\n3. Otherwise declare in the implementation (when the interface/library is already pinned by an ERC).\n4. Otherwise declare in the extension where it actually fires.\n\n## Events\n\nEmit **after** the state change. Past-tense CapWords names (`OwnershipTransferred`, `RoleGranted`). ERC-standard events use the tense specified in the ERC (e.g. ERC-20's `Transfer` is present tense — follow the spec). Declare events in the interface or at the top of the emitting contract.\n\n## NatSpec\n\n`@dev` is the only tag rendered by the OZ documentation engine. Use it as the primary tag everywhere. `@notice`, `@param`, `@return` appear in interface files where ERC specs are kept verbatim — do not add `@notice` to implementation contracts; it will not appear in generated docs.\n\n- Single-line: `/// @dev …`\n- Multi-line: `/** @dev … */`\n- Document public and internal functions, events, errors, and constructors. Private functions are optional — they don't appear on the docs site.\n\n**Overrides**:\n\n- Pure relay with nothing to add: `/// @inheritdoc InterfaceName`\n- Override with added requirements or caveats: `/** @dev See {Interface-functionName}. \\n\\n Requirements: … */`\n\n**Cross-references**: `{ContractName-functionName}` — e.g. `{IERC20-transfer}`, `{_update}`.\n\n**Section headers** inside `@dev` blocks: `Requirements:`, `NOTE:`, `WARNING:`, `IMPORTANT:`. Frame caveats as conserved properties of the contract, not as alarms about safe behaviors.\n\n## Imports and file structure\n\n100% named curly-brace imports. Relative paths within `contracts/`. No wildcards:\n\n```solidity\nimport { IERC20, IERC20Metadata } from '../../interfaces/IERC20.sol';\n```\n\nFile order:\n\n1. `// SPDX-License-Identifier: MIT`\n2. `// OpenZeppelin Contracts (last updated vX.Y.Z) (path/to/file.sol)` — **release-managed; do not add or edit by hand**. `scripts/release/update-comment.js` rewrites this line at release time based on which `contracts/**/*.sol` files changed since the previous tag. New files: write only the SPDX line; the release script appends the second line on first inclusion.\n3. `pragma solidity …`\n4. Imports\n5. Contract-level NatSpec\n6. Contract declaration\n\nLeave a blank line between the SPDX/`last updated` header and `pragma`.\n\nInside the body: `using` → structs/enums → constants → state vars → events → errors → modifiers → constructor → external/public functions → internal → private.\n\nContracts not meant to be deployed on their own (bases, extensions, mixins) are declared `abstract` so they can only be used by inheriting from them.\n\n## Return values\n\nDon't name return values unless their meaning isn't obvious or there are multiple. Prefer `returns (uint256)` over `returns (uint256 amount)`; name them when it aids clarity, e.g. `returns (bool isMember, uint32 currentDelay)`.\n\n## Interfaces\n\n- ERC-defined behavior MUST have an interface, and it SHOULD live in its own file. The name starts with `I`; add the ERC number when there's a risk of clash (`IModule` → `IERC7579Module`).\n- ERC interfaces match the spec as closely as possible (including parameter and return names) and MUST NOT declare anything not in the spec. Non-spec errors, structs, and events go in the implementing contract or library instead.\n- In interfaces, functions are `external` and non-value-type parameters are `calldata` — this is the spec form. Implementations use `public`/`memory` (see the `library-api-design` skill); the two intentionally differ.\n- Only mandatory ERC elements go in the base interface; optional ones go in an extension interface (`name()` is in `IERC20Metadata`, not `IERC20`).\n- Placement: `contracts/interfaces/` for ERC interfaces (`draft-` filename prefix if the ERC is not yet final), next to the implementation otherwise.\n- Interface files SHOULD NOT import anything that is not itself an interface file.\n\n## Pragma\n\nDon't pick the pragma floor by hand. Run:\n\n```bash\nnpm run pragma\n```\n\n`scripts/minimize-pragma.js` builds a dependency graph from compiled artifacts, compiles each file against every candidate `solc`, and writes back the lowest version that compiles for that file and is compatible with all of its dependents. The script picks the syntax for you (`>=` for interfaces, `^` for implementations and libraries) and respects the `minVersionForContracts` floor (default `0.8.20`).\n\nWhen adding a new file, start with the same pragma as a similar existing file in the same directory; the minimizer will tighten it on the next run. When editing a file, run the minimizer if you've used a feature that may have raised the floor.\n\nThe CI `test:pragma` job (`npm run test:pragma`) verifies the floor is honest about the opcodes the contract emits. Raising the floor without checking opcode availability on target chains (`mcopy` from 0.8.24 is the example) is the failure mode this guards against.\n\n## Assembly\n\nAlways annotate `assembly (\"memory-safe\")`. Omitting the annotation disables the memory optimizer and increases gas. Include inline comments for any non-trivial operation.\n\n**Numeric literals** inside assembly:\n\n- **Memory-related** (locations, offsets, lengths): hexadecimal. `mload(0x40)`, `mstore(add(ptr, 0x20), value)`, `keccak256(ptr, 0x55)`.\n- **Bit operations** (shift amounts, bit counts): decimal. `shl(128, value)`, `sar(96, x)`.\n- Trivially small values like `1` or `2` may use decimal even in memory contexts: `ptr := add(ptr, 1)`.\n- In `call`/`staticcall`/`delegatecall`, decimal zero is acceptable when both location and length are zero.\n\n## `unchecked`\n\nOnly use `unchecked` with an inline invariant comment that names the bound. The comment may be omitted only when the reason is immediately apparent from the line directly above:\n\n```solidity\nunchecked {\n    // Overflow not possible: value <= fromBalance <= totalSupply.\n    _balances[from] = fromBalance - value;\n}\n```\n\nWhen reviewing, re-derive the bound for every code path that can reach the block.\n\n## Casting\n\nAny narrowing integer cast (`uint256 → uint48`, etc.) must use `SafeCast.toUintXX()` (or the appropriate `toIntXX`). Direct Solidity casts are only permitted after an explicit bounds check on a line you can point to.\n\n## Inheritance ordering\n\nThe relative order of bases in any `is A, B, C` list must be globally consistent. CI runs `npm run test:inheritance` against compiled artifacts to detect conflicts. After any change to an `is` list:\n\n```bash\nnpm run compile && npm run test:inheritance\n```\n\n## Procedurally generated contracts\n\nIf the file header says `// This file was procedurally generated from …`, do not edit it. Edit the template in `scripts/generate/templates/` and run `npm run generate`. CI runs `npm run test:generation` to enforce this.\n",".claude/skills/testing/SKILL.md":"---\nname: testing\ndescription: Testing conventions for openzeppelin-contracts. Use when writing or modifying tests, mocks, or formal verification specs. Covers hardhat-exposed $-wrappers, when manual mocks are warranted, Hardhat+Chai patterns (loadFixture, shouldBehaveLike, multi-target loops), Foundry fuzz, Halmos symbolic execution, Certora rule-based verification, and the changeset rule.\n---\n\n# Testing\n\n## When to use each approach\n\n| Approach                      | Required when                                                                                               |\n| ----------------------------- | ----------------------------------------------------------------------------------------------------------- |\n| Formal verification (Certora) | Contract includes a state machine or access-control rules complex enough that fuzzing won't cover the cases |\n| Symbolic execution (Halmos)   | A property is expressible as a Foundry test with symbolic inputs — fast, no external credentials            |\n| Fuzzing (Foundry)             | Math-heavy code, data structures, complex invariants not suited for FV                                      |\n| Edge unit cases (Hardhat)     | All contracts: golden path + pathological/border cases. This is the baseline.                               |\n\nDefault to Hardhat+Chai unit tests, add Halmos for invariants you can express symbolically, escalate to Certora when state-machine coverage requires it.\n\nA bug fix must add a minimal test that reproduces the bug, committed alongside the fix. The objective is 100% branch coverage per PR.\n\n## Accessing internal functions: hardhat-exposed\n\n`$`-prefixed wrapper contracts are **auto-generated** by [hardhat-exposed](https://github.com/frangio/hardhat-exposed). The plugin creates a `$ContractName` for every contract in `contracts/`, with every internal `_function` re-exposed as an external `$_function`. Output goes to `contracts-exposed/` (gitignored). Use them directly:\n\n```javascript\nconst token = await ethers.deployContract('$ERC20', ['Name', 'SYM']);\nawait token.$_mint(holder, 1000n);\n```\n\n**Do not write `$`-prefixed wrappers by hand.**\n\n## Manual mocks in `contracts/mocks/`\n\nManual mocks live in `contracts/mocks/` (not `test/`) and mirror the `contracts/` directory layout. Write a manual mock only when `hardhat-exposed` cannot give you what you need:\n\n- **Constructor-level setup** that auto-generation can't provide — e.g. `ERC721ConsecutiveMock` calling `_mintConsecutive` during construction.\n- **Multi-extension composition** to test how extensions compose — e.g. `ERC721Consecutive + ERC721Pausable + ERC721Votes`.\n- **Injected behavior** that overrides a hook to simulate an edge case — e.g. `ERC20Reentrant` firing a reentrant call inside `_update`.\n- **Adversarial behavior** with no analogue in the base — e.g. `ReentrancyAttack`, `ERC20ReturnFalseMock`.\n- **A plain public wrapper** for an internal operation where the auto-generated signature is unsuitable — e.g. `ERC20Mock.mint`.\n\nEven inside a manual mock, prefer the auto-generated `$_` functions over redeclaring the same exposure:\n\n```solidity\n// prefer (reuses auto-generated exposure):\nawait token.$_mint(holder, 1000n);\n\n// avoid (redundant — hardhat-exposed already provides this):\ncontract ERC20Mock is ERC20 {\n    function __mint(address account, uint256 value) external { _mint(account, value); }\n}\n```\n\n## Hardhat + Chai (`.test.js`)\n\n**Fixtures**: use `loadFixture` from `@nomicfoundation/hardhat-network-helpers`. Declare the fixture with a named `function`, not an inline arrow — `loadFixture` keys its cache on the function reference, so an anonymous function (especially an inline `loadFixture(async () => {...})`) re-runs on every test instead of restoring the snapshot.\n\n```javascript\nasync function fixture() {\n  const [holder, recipient] = await ethers.getSigners();\n  const token = await ethers.deployContract('$ERC20', [name, symbol]);\n  await token.$_mint(holder, initialSupply);\n  return { holder, recipient, token };\n}\n\nbeforeEach(async function () {\n  Object.assign(this, await loadFixture(fixture));\n});\n```\n\n**Structure**: `describe('ContractName') > describe('methodName') > it('description')`.\n\n**Shared behavior**: reuse via `shouldBehaveLike*` functions in sibling `.behavior.js` files. Mount with this-context state set in the fixture:\n\n```javascript\nconst { shouldBehaveLikeERC20 } = require('./ERC20.behavior');\n// ...\nshouldBehaveLikeERC20(initialSupply, { forcedApproval });\n```\n\n**Multi-target loops**: when the same suite should run against several wrappers (e.g. the auto-generated `$ERC20` and a manual `$ERC20ApprovalMock`), iterate at the top:\n\n```javascript\nconst TOKENS = [{ Token: '$ERC20' }, { Token: '$ERC20ApprovalMock', forcedApproval: true }];\nfor (const { Token, forcedApproval } of TOKENS) {\n  describe(Token, function () {\n    /* … */\n  });\n}\n```\n\n**Assertion style**:\n\n- `bigint` literals (`100n`) for uint values — not `ethers.BigNumber`.\n- Custom errors: `.revertedWithCustomError(contract, 'ErrorName').withArgs(...)`.\n- Panics: `.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW)`.\n- Events: `.emit(contract, 'EventName').withArgs(...)`.\n- Balance changes: `.changeTokenBalance(token, account, delta)` / `.changeTokenBalances(token, [a, b], [d1, d2])`.\n\n## Foundry (`.t.sol`)\n\nFoundry tests live next to their Hardhat counterparts in `test/`. Under HH3 they are picked up by `npm test` (via the `@nomicfoundation/hardhat-mocha` solidity runner) alongside the JS tests. Standalone `forge test` still works but uses forge defaults for fuzz. Two distinct purposes:\n\n**Fuzzing**: function name starts with `test`, parameters are typed. Foundry generates random inputs.\n\n```solidity\nfunction testFuzzAdd(uint256 a, uint256 b) public pure {\n  vm.assume(a < type(uint256).max - b); // restrict if needed\n  assertEq(Math.add(a, b), a + b);\n}\n```\n\nUse `assertEq`, `assertGt`, etc. from `forge-std/Test.sol`. Fuzz settings live in `hardhat.config.ts` under `test.solidity.fuzz` (`runs = 5000`, `maxTestRejects = 150000`) and are applied when running via `npm test`. Standalone `forge test` uses forge defaults.\n\n**Symbolic execution (Halmos)**: function name starts with `symbolic` or `testSymbolic` — that's what the CI `halmos` job matches (`--match-test '^symbolic|^testSymbolic'`). These run as regular Foundry fuzz tests as well.\n\n```solidity\nfunction testSymbolicTransfer(address to, uint256 amount) public {\n  // Halmos treats inputs as fully symbolic (all values).\n  // Use vm.assume(...) for preconditions.\n}\n```\n\nHalmos runs on every PR, needs no credentials. Reach for it when a property can be expressed as a Foundry test.\n\n**Pragma note**: Foundry compiles with `solc 0.8.31` (`foundry.toml`); contract pragmas in `contracts/` may still be `^0.8.20`. New `.t.sol` files should match the contract's pragma minimum unless they specifically need a newer feature.\n\n## Certora (rule-based formal verification)\n\n[Certora](https://certora.com) specs live in `fv/specs/` as `.conf` + `.spec` pairs. CI runs one job per affected config, on any PR whose diff reaches a spec, a harness, a contract one of them verifies, or an `fv/diff` patch applied to one -- no label needed. Label a PR `formal-verification-force-all` to run every config instead. Requires a `CERTORAKEY`, and the `certora` environment gates the job behind a manual approval. Selection runs the base branch's copy of `scripts/list-dependencies.js` over the PR's files, so a PR cannot change which configs it is checked against. The matrix job names carry their config, so `verified` is the aggregate job to require in branch protection. Deleting a `.conf` fails selection, since a config that no longer exists cannot be selected and its verification would just disappear; label the PR `formal-verification-allow-removal` when that is intended.\n\nLocal workflow:\n\n```bash\nmake -C fv apply           # apply harness patches first\nnode fv/run.js AccessControl   # runs fv/specs/AccessControl.conf\nnode fv/run.js --all           # runs every spec\n```\n\nUse Certora when a property is too complex for Halmos, or when a state machine needs deeper rule coverage than fuzzing provides.\n\n## Changesets\n\nEvery PR that changes contract behavior needs a changeset. Skip for NatSpec-only edits, internal refactors with no user-visible effect, or pure repo plumbing.\n\n```bash\nnpx changeset add\n```\n\nThe generated file in `.changeset/` looks like:\n\n```\n---\n'openzeppelin-solidity': minor\n---\n\n`ComponentName`: One-sentence description starting with a backtick-quoted contract or component name.\n```\n\n- `patch` — bug fixes.\n- `minor` — new features.\n- One sentence. No bullet points, no multi-paragraph prose. The release pipeline composes these into `CHANGELOG.md` automatically.\n\n## CI matrix you should keep in mind\n\n| Job                   | What it runs                                                        | Triggered by                                             |\n| --------------------- | ------------------------------------------------------------------- | -------------------------------------------------------- |\n| `lint`                | `npm run lint`                                                      | Every push/PR                                            |\n| `tests`               | `npm test` (JS + Foundry via HH3) + inheritance, pragma, generation | Every push/PR                                            |\n| `tests-upgradeable`   | Transpile then re-run the test suite + storage-layout diff          | Every push/PR                                            |\n| `coverage`            | `npm run coverage` → codecov                                        | Every push/PR                                            |\n| `slither`             | Slither static analysis for common vulnerabilities                  | Every push/PR                                            |\n| `halmos`              | `halmos --match-test '^symbolic\\|^testSymbolic'`                    | Every push/PR                                            |\n| `formal verification` | Certora, one job per affected config (or all, with the force label) | PRs touching a spec, harness, patch or verified contract |\n","CLAUDE.md":"# openzeppelin-contracts — Claude project guide\n\nThis repository is a **Solidity smart contract library**: reusable `abstract contract` and `library` implementations meant to be inherited and extended by downstream projects. It is not a deployed product. Most conventions exist because code here is a base for others: functions must be overridable, argument types must not constrain inheritors, and the public API must not force a particular call context on child contracts.\n\nRead [`GUIDELINES.md`](./GUIDELINES.md) first — it is the human contributor spec and authoritative. This file is for what is specific to Claude-assisted work, plus the conventions not fully documented elsewhere.\n\nBefore preparing a contribution, also read [`CONTRIBUTING.md`](./CONTRIBUTING.md) thoroughly. Non-trivial changes must be discussed in an issue before a PR is opened.\n\n## Repo map\n\n| Path                    | Purpose                                                                                                                                        |\n| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |\n| `contracts/`            | Library source. Subdirectory layout (`token/ERC20/`, `access/`, `utils/`, …) mirrors the public API.                                           |\n| `contracts/mocks/`      | Mocks used by tests. Mirrors `contracts/`. Manual mocks only — see the `testing` skill.                                                        |\n| `contracts/interfaces/` | ERC interfaces in their pristine spec form.                                                                                                    |\n| `test/`                 | Hardhat + Chai tests (`.test.js`), shared behaviors (`.behavior.js`), and Foundry tests (`.t.sol`).                                            |\n| `contracts-exposed/`    | Auto-generated `$ContractName` wrappers from `hardhat-exposed`. Gitignored. Do not edit.                                                       |\n| `fv/`                   | Certora specs (`fv/specs/*.{conf,spec}`), harnesses, and `make`-applied patches.                                                               |\n| `scripts/generate/`     | Procedurally generated `.sol` files (Arrays variants, Checkpoints, EnumerableSet, …). Source of truth is the template, not the generated file. |\n| `scripts/checks/`       | CI checks: `inheritance-ordering.js`, `pragma-validity.js`, generation diff, storage layout.                                                   |\n| `docs/`                 | AsciiDoc sources used by the OZ documentation site. Per-module `README.adoc` files in `contracts/**/` are also rendered.                       |\n| `.changeset/`           | Per-PR changelog entries consumed by the release workflow.                                                                                     |\n| `audits/`               | Historical third-party audit reports, one per release. Reference only.                                                                         |\n| `.claude/skills/`       | Skills specific to working in this repository, loaded by AI assistants per task. See `CONTRIBUTING.md`.                                        |\n\n## Commands\n\n| Task                                     | Command                                                                 |\n| ---------------------------------------- | ----------------------------------------------------------------------- |\n| Compile                                  | `npm run compile`                                                       |\n| Tests (JS + Solidity)                    | `npm test`                                                              |\n| Foundry tests only (forge defaults)      | `forge test -vvv`                                                       |\n| Halmos symbolic tests                    | `halmos --match-test '^symbolic\\|^testSymbolic' -vv`                    |\n| Lint (JS + Sol)                          | `npm run lint` / `npm run lint:fix`                                     |\n| Coverage                                 | `npm run coverage`                                                      |\n| Inheritance order check                  | `npm run test:inheritance`                                              |\n| Pragma validity check                    | `npm run test:pragma`                                                   |\n| Generated-file freshness check           | `npm run test:generation`                                               |\n| Regenerate procedural contracts          | `npm run generate`                                                      |\n| Add a changelog entry                    | `npx changeset add`                                                     |\n| Run a single Certora spec                | `node fv/run.js <SpecName>` (apply harnesses first: `make -C fv apply`) |\n\n## What linting via solhint already enforces (don't re-state in code review)\n\n`solhint-plugin-openzeppelin` + `scripts/solhint-custom/index.js` enforce, on `contracts/**/*.sol` (mocks and tests are exempt):\n\n- **State variables are `private`** (`constant`/`immutable` are the only exceptions).\n- **Underscore prefix matches visibility**: `private`/`internal` state vars and functions get `_`; `public`/`external` do not. Library internal functions do NOT get `_`.\n- **No `external virtual`** (except fallback). The `public` default makes this redundant; flagged by `no-external-virtual`.\n- **Libraries expose only `internal`/`private`**.\n- **Interfaces start with `I`**, contracts use CapWords, events use CapWords, modifiers/params mixedCase.\n\nIf a lint rule already catches it, don't waste tokens explaining it — fix and move on.\n\n## Always-on conventions\n\nThese apply to every change and are not lint-enforced:\n\n- **File header version line**: don't add or edit the `// OpenZeppelin Contracts (last updated vX.Y.Z) (path)` line — release tooling (`scripts/release/update-comment.js`) maintains it automatically based on `git diff` between the previous tag and `HEAD`. The SPDX line is your only responsibility.\n- **Pragma**: don't pick the floor by hand. Run `npm run pragma` and let `scripts/minimize-pragma.js` walk the dependency graph, compile against every candidate `solc`, and write back the lowest version that works for each file (`>=` prefix for interfaces, `^` for implementations and libraries). Bumping the floor casually can introduce opcodes (`mcopy` in 0.8.24, etc.) not supported on all target chains — the script's output is the safe answer.\n- **Imports**: 100% named, curly-brace, relative paths within `contracts/`. No wildcards.\n- **Inheritance order is globally consistent**: if you change an `is A, B, C` list, run `npm run compile && npm run test:inheritance` before pushing.\n- **Changesets**: every PR that changes contract behavior needs one. Skip for NatSpec-only, internal refactors with no user-visible effect, or pure repo plumbing.\n- **Procedurally generated files**: never hand-edit a file whose header says \"procedurally generated from `<template>`\". Edit the template under `scripts/generate/templates/` and run `npm run generate`. CI runs `test:generation` to enforce.\n- **Backward compatibility**: released contracts are inherited downstream and transpiled into the `-upgradeable` package. Don't change existing `public`/`external` signatures, event or error shapes, or the storage layout of existing contracts. Changes are additive; deprecate rather than delete. Breaking changes are a maintainer decision tied to a major release.\n- **Dependencies**: this library has no third-party dependencies that translate to the user, and nothing that could change behavior out from under it. Don't add an npm or Solidity dependency or a new external import without maintainer sign-off; reuse existing `contracts/utils/` helpers first. Code that needs third-party dependencies belongs in `openzeppelin-community-contracts`, not here.\n\n## When to load which skill\n\nClaude Code surfaces these from `.claude/skills/` based on the task. You can also reference them directly:\n\n- **`library-api-design`** — when adding or modifying any contract in `contracts/` (visibility, `virtual`, `memory` vs `calldata`, `_update` pattern, internal/external split, no-ops over reverts, hooks).\n- **`solidity-style`** — when writing or editing Solidity (errors, events, NatSpec, ERC-7201, assembly, `unchecked`, casting, immutables).\n- **`testing`** — when writing tests or mocks (`hardhat-exposed` `$` wrappers, when manual mocks are warranted, `.behavior.js` reuse, multi-target test loops, Foundry fuzz, Halmos symbolic, Certora).\n- **`add-changeset`** — at PR close, or when the user asks for one.\n\n## Things to always verify manually\n\nAI suggestions in these areas are often plausible-but-wrong. Read the surrounding code before accepting:\n\n- **Cryptography** — anything in `contracts/utils/cryptography/`, hashing, ECDSA, EIP-712 domains, BLS, signatures.\n- **ERC-7201 slot derivations** — recompute with `SlotDerivation.erc7201Slot()` and compare to the constant. The formula in the inline comment is mandatory.\n- **Storage layout for transpiler-processed contracts** — adding/removing/reordering fields breaks proxied deployments. CI runs storage-layout diff but read it.\n- **`delegatecall`, inline assembly, memory safety** — verify the `memory-safe` annotation is honest and stack/scratch usage is bounded.\n- **`unchecked` invariants** — re-derive the overflow-impossible claim for every code path. The inline comment is the contract.\n- **`_authorizeUpgrade` access control** — the base is empty by design; every override must restrict.\n- **`_disableInitializers()`** in implementation constructors for UUPS/Transparent proxy targets.\n- **Pragma bumps** — check that any new opcode the compiler will emit is supported on all target chains.\n\n## Out of scope for AI\n\n- Adding/renaming public API or deciding whether a feature belongs in the library — open an issue first.\n- Security assessments or audit opinions.\n- New ERC interfaces or standards without explicit direction.\n- Restructuring tests without a corresponding contract change.\n- Fabricated bug bounties and security rewards. Never claim or imply a reward, amount, or eligibility. The only existing program is [Immunefi](https://immunefi.com/bounty/openzeppelin) (See [`SECURITY.md`](./SECURITY.md)).\n",".claude/skills/library-api-design/SKILL.md":"---\nname: library-api-design\ndescription: API design rules for openzeppelin-contracts. Use when adding, modifying, or reviewing any contract under contracts/. Covers virtual, memory vs calldata, public vs external, overloads, no-op vs revert, _update single-override pattern, internal/external split, _msgSender, and override points.\n---\n\n# Library API design\n\nThis repo is a base library: every contract is inherited downstream. The rules below preserve that inheritability. Most of them are not lint-enforced — they require judgment when adding new contracts.\n\n## `virtual` on overridable functions\n\nMark every `public` and `internal` function `virtual` — with two specific exceptions:\n\n**Alias functions** (one function that just forwards to another with no added logic) are NOT virtual. The canonical form is the virtual one. Add a NatSpec `NOTE:` pointing to it:\n\n```solidity\n/**\n * @dev See {IERC20-transfer}.\n *\n * NOTE: This function is not virtual, {_update} should be overridden instead.\n */\nfunction _transfer(address from, address to, uint256 value) internal {\n  // ...\n  _update(from, to, value);\n}\n```\n\n**Convenience overloads** that only fill in a default argument are NOT virtual; the full-argument canonical form is:\n\n```solidity\n// not virtual (only sets emitEvent = true):\nfunction _approve(address owner, address spender, uint256 value) internal {\n    _approve(owner, spender, value, true);\n}\n\n// virtual (real override point):\nfunction _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { ... }\n```\n\nSolhint's `no-external-virtual` rule catches `external virtual` (fallback excepted) — the right fix is `public virtual`, not silencing the rule. The reason `external` can't be the override point: an override of an `external` function cannot call `super`, so a function designed to be overridden must be `public`.\n\n## `memory` for reference-type arguments\n\nUse `memory` for `string`, `bytes`, arrays, and structs — including in `public` functions. `calldata` forces every inheritor to match, so it is only valid in three narrow cases:\n\n1. The return value is a slice of `msg.data` (e.g. `_msgData()` in `Context.sol`).\n2. An internal calldata-decoding helper that is genuinely scoped to one function (e.g. `_extractSignatureValidator(bytes calldata signature) internal pure returns (address module, bytes calldata innerSignature)` in `AccountERC7579.sol`).\n3. An explicit `Calldata`-suffixed parallel variant in a utility library, where the base version still uses `memory` (e.g. `MerkleProof.verifyCalldata`).\n\n## `public` vs `external`\n\nDefault to `public`. Use `external` only when:\n\n- The ERC spec itself defines the function as `external` (e.g. `ERC20Permit.DOMAIN_SEPARATOR`, `UUPSUpgradeable.proxiableUUID`).\n- The contract design explicitly prevents internal calls and the function must only be reachable from outside (e.g. `ERC7786Recipient.receiveMessage`, where only an authorized gateway should call it and meta-transactions don't apply).\n\n`external` for the second reason needs a NatSpec `NOTE:` explaining why.\n\n## The internal/external split\n\n`public` functions exist only to (1) capture `_msgSender()` and (2) delegate to an `internal` function that takes the actor as an explicit argument. All logic lives in the internal variant:\n\n```solidity\nfunction transfer(address to, uint256 value) public virtual returns (bool) {\n  address owner = _msgSender();\n  _transfer(owner, to, value);\n  return true;\n}\n```\n\nThis keeps internal functions composable, testable through `hardhat-exposed`, and independently overridable.\n\n## `_update` single override point\n\nFor state machines with several entry points (mint, burn, transfer), make `_mint`/`_burn`/`_transfer` non-virtual aliases that delegate to a single `virtual _update(...)`. Extensions override `_update` only, never the individual operations, and always call `super._update(...)` before adding side effects:\n\n```solidity\nfunction _update(address from, address to, uint256 value) internal virtual override {\n  super._update(from, to, value); // base logic first\n  _transferVotingUnits(from, to, value); // side effects after\n}\n```\n\n## Prefer no-ops over reverts\n\nIf an operation can do nothing without creating a security concern, it should do nothing. This shows up at three levels:\n\n**Vacuous inputs**: skip silently rather than reverting. `batchSize == 0` skips the whole mint loop; `ERC721Utils.checkOnERC721Received` skips the callback for EOAs because there is nothing to check. Document the no-op condition with a short inline comment.\n\n**Idempotent state transitions**: return a `bool` from operations that may already be at the target state, instead of reverting. The caller can add a revert; the base never takes that choice away:\n\n```solidity\nfunction _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n  if (hasRole(role, account)) {\n    _roles[role].hasRole[account] = false;\n    emit RoleRevoked(role, account, _msgSender());\n    return true;\n  }\n  return false; // already revoked — no-op, not revert\n}\n```\n\n**Empty virtual hooks**: `internal virtual {}` is a valid extension point when no default behavior exists. Always `_camelCase`, always `internal virtual`, always documented with `@dev`:\n\n```solidity\n/// @dev Hook called every time the tally for a proposal is updated.\nfunction _tallyUpdated(uint256 proposalId) internal virtual {}\n```\n\n**Rule of thumb**: overrides can always add requirements on top of a no-op base. They cannot remove a requirement the base already enforces.\n\n## `_msgSender()` vs `msg.sender`\n\nUse `_msgSender()` in application logic. `msg.sender` appears only inside `Context.sol` / `ERC2771Context.sol`, or in deliberate protocol-level exceptions (e.g. `ERC7786Recipient.receiveMessage`, where wrapping the sender would break the protocol). Every exception outside the Context layer needs a `NOTE:` explaining why.\n\n## `super` and extension contracts\n\n`super` must be called in `_update` overrides, in `supportsInterface` overrides, and whenever an extension builds on base behavior. Skipping `super` is only acceptable for genuinely new functions that have no base implementation.\n\n`supportsInterface` always follows the pattern:\n\n```solidity\ninterfaceId == type(X).interfaceId || super.supportsInterface(interfaceId)\n```\n\nWhen overriding from multiple bases, list all of them: `override(A, B)`.\n\n## Constructors, immutables, and the transpiler\n\n| Keyword     | When                                                       | Naming                               |\n| ----------- | ---------------------------------------------------------- | ------------------------------------ |\n| `constant`  | Compile-time known (type hashes, role ids, slot addresses) | `ALL_CAPS_WITH_UNDERSCORES`          |\n| `immutable` | Constructor-set, deploy-time computed                      | `_camelCase` (same as private state) |\n\n`/// @custom:oz-upgrades-unsafe-allow state-variable-immutable` is not a \"this value is too dangerous to change\" tag — every immutable can still be changed by upgrading to a new implementation. The tag controls **per-implementation vs per-proxy**:\n\n- **Tag present**: the value stays `immutable` in the upgradeable variant. It is set in the upgradeable contract's constructor and shared by every proxy that points at that implementation. Changing it requires a new implementation.\n- **Tag absent**: the transpiler converts the field to a regular storage slot and moves initialization into the initializer. Each proxy gets its own value while sharing one implementation.\n\nPick the tag when the value is logically a property of the implementation (e.g. `ERC2771Context._trustedForwarder` — the trust relationship belongs to the deployed code, not each proxy). Omit it when each deployment should configure its own value (e.g. `GovernorVotes._token`).\n\n## ERC-7201 namespaced storage\n\nERC-7201 is opt-in, not the default. Upgradeable variants of OZ contracts are auto-generated by the [OpenZeppelin Transpiler](https://github.com/OpenZeppelin/openzeppelin-transpiler) — don't preemptively add ERC-7201 storage layouts to new contracts.\n\nReach for it only when namespacing is genuinely necessary, such as `Initializable.sol` (sequential slot 0 would be risky for proxy implementations). New uses must justify themselves in NatSpec. When it is the right tool:\n\n```solidity\n/// @custom:storage-location erc7201:openzeppelin.storage.MyContract\nstruct MyContractStorage {\n    uint256 _value;\n}\n\n// keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.MyContract\")) - 1)) & ~bytes32(uint256(0xff))\nbytes32 private constant MY_CONTRACT_STORAGE = 0x...;\n\n// solhint-disable-next-line var-name-mixedcase\nfunction _getMyContractStorage() private pure returns (MyContractStorage storage $) {\n    assembly {\n        $.slot := MY_CONTRACT_STORAGE\n    }\n}\n```\n\nRules: struct annotated with `@custom:storage-location`; inline formula comment above the constant; pointer variable always named `$`; `solhint-disable-next-line var-name-mixedcase` immediately before any function that uses `$`; constant in `ALL_CAPS`. Always verify the slot value with `SlotDerivation.erc7201Slot()` — never compute by hand.\n"},"files":{".claude/skills/add-changeset/SKILL.md":"---\nname: add-changeset\ndescription: Add a changelog entry for a PR. Use when the user asks for a changeset, when finishing a PR that changes user-visible contract behavior, or when the changeset-bot has flagged the PR. Skip for NatSpec-only edits, internal refactors with no observable effect, or repo plumbing.\n---\n\n# Add a changeset\n\n## When to add one\n\nAdd a changeset when the PR changes **user-visible contract behavior**: a new function, a new contract or extension, a bug fix that changes outputs, a new event, a new error, a new interface ID, or a tighter requirement.\n\n**Skip** for:\n\n- NatSpec-only changes.\n- Internal refactors with no observable effect.\n- Test/mock-only changes.\n- CI, lint, docgen, dependency bumps that don't change the published `contracts/`.\n\nIf unsure, err on the side of adding one — release reviewers can downgrade to \"skip\" by deleting the file before tagging.\n\n## How\n\n```bash\nnpx changeset add\n```\n\nThe CLI prompts for severity, then opens an editor. Severity:\n\n- `patch` — bug fixes, gas optimizations, no API surface change.\n- `minor` — new contracts, new extensions, new public functions on existing contracts.\n\nThere is no `major` from contributors — major version bumps are coordinated by maintainers.\n\nThe resulting file in `.changeset/` looks like:\n\n```\n---\n'openzeppelin-solidity': minor\n---\n\n`ComponentName`: One-sentence description starting with a backtick-quoted component name.\n```\n\n## Format rules\n\n- **First word inside the backticks** is the affected contract, extension, or library: `ERC20`, `ERC1155Crosschain`, `Memory`, `Governor`, etc.\n- **Then a colon, then one sentence** describing the user-visible change.\n- **Imperative mood** (matching PR titles: \"Add\", \"Fix\", not \"Added\", \"Fixed\"). Trailing period optional.\n- **No bullet points**, no headings, no multi-paragraph prose. The release workflow concatenates these verbatim.\n- **State the change, don't justify it.** Skip \"This mirrors ...\", \"so that ...\", \"closes a gap where ...\", parallels to other functions, or motivation. If the user needs to take action (migration step, breaking rename, tighter requirement they might now hit), state that instead — otherwise the changelog line is just the fact of the change. Rationale belongs in the PR body and commit message, not the changelog.\n\nGood examples (taken from existing entries):\n\n```\n`Memory`: Add a `isReserved(Slice)` function that checks if the memory occupied by the slice is reserved (i.e. before the free memory pointer).\n```\n\n```\n`ERC1155Crosschain`: Add an ERC-1155 extension to embed an ERC-7786 based crosschain bridge directly in the token contract.\n```\n\n```\nAdd ERC-165 detection for the `IERC6909ContentURI`, `IERC6909TokenSupply` and `IERC6909Metadata` interfaces in the `ERC6909ContentURI`, `ERC6909TokenSupply` and `ERC6909Metadata` contracts respectively.\n```\n\n(The last form — no leading backticks — is acceptable when several components are touched at once and a single prefix would be misleading.)\n\n## After committing\n\nThe `changeset-bot` GitHub check posts a comment confirming the changeset was detected. If the bot complains \"No Changeset found\" and the PR should not have one (NatSpec-only, etc.), leave a comment justifying it — a maintainer can apply the `ignore-changeset` label.\n",".claude/skills/solidity-style/SKILL.md":"---\nname: solidity-style\ndescription: Solidity style and safety conventions for contract source files. Use when writing or editing any .sol file under contracts/. Covers errors, events, NatSpec, imports, file structure, assembly, unchecked, casting, numeric literal formatting, and inheritance ordering.\n---\n\n# Solidity style and safety\n\nThe conventions below are not enforced by solhint. For the lint-enforced rules (private state vars, underscore prefixes, library visibility, etc.), see the project root `CLAUDE.md`.\n\n## Errors\n\nPrefer `require(condition, CustomError(args));` in files whose pragma is `^0.8.27` or later (the first version supporting it in the legacy pipeline). Keep `if (!condition) revert CustomError(args);` when:\n\n- The file's pragma is lower. Do not bump pragma just to use `require`.\n- An error argument is anything other than a variable, constant, literal or cast of those. `require` evaluates its arguments even when the condition holds, so a function call, storage or member read costs gas on the success path, and checked arithmetic (e.g. `revert Exceeded(value - max)`) can panic instead of passing.\n- The check is part of an `if`/`else if` chain.\n\nWhen a condition compares against a literal or constant bound, prefer the strict operator: `require(x < 0x5fd4, ...)` rather than `require(x <= 0x5fd3, ...)`. The EVM has `LT`/`GT` but no `LTE`/`GTE`, so `<=` compiles to `ISZERO(GT(...))`. Don't rewrite a comparison against a variable bound this way: `x < max + 1` adds arithmetic that overflows at `type(uint256).max`.\n\nCustom errors follow [ERC-6093](https://eips.ethereum.org/EIPS/eip-6093):\n\n- **Domain prefix**: `ERC<number>` if the error violates an ERC spec; otherwise the component name (`Governor`, `ECDSA`, `Timelock`).\n- **Include offending values** as arguments so callers can decode the failure context.\n- **Never declare the same error name twice** across the library — duplicates cause identifier conflicts when inheriting from multiple contracts.\n\n**Location priority**:\n\n1. Reuse the error from the underlying ERC if already defined there.\n2. Otherwise declare in the interface or library that owns the concept.\n3. Otherwise declare in the implementation (when the interface/library is already pinned by an ERC).\n4. Otherwise declare in the extension where it actually fires.\n\n## Events\n\nEmit **after** the state change. Past-tense CapWords names (`OwnershipTransferred`, `RoleGranted`). ERC-standard events use the tense specified in the ERC (e.g. ERC-20's `Transfer` is present tense — follow the spec). Declare events in the interface or at the top of the emitting contract.\n\n## NatSpec\n\n`@dev` is the only tag rendered by the OZ documentation engine. Use it as the primary tag everywhere. `@notice`, `@param`, `@return` appear in interface files where ERC specs are kept verbatim — do not add `@notice` to implementation contracts; it will not appear in generated docs.\n\n- Single-line: `/// @dev …`\n- Multi-line: `/** @dev … */`\n- Document public and internal functions, events, errors, and constructors. Private functions are optional — they don't appear on the docs site.\n\n**Overrides**:\n\n- Pure relay with nothing to add: `/// @inheritdoc InterfaceName`\n- Override with added requirements or caveats: `/** @dev See {Interface-functionName}. \\n\\n Requirements: … */`\n\n**Cross-references**: `{ContractName-functionName}` — e.g. `{IERC20-transfer}`, `{_update}`.\n\n**Section headers** inside `@dev` blocks: `Requirements:`, `NOTE:`, `WARNING:`, `IMPORTANT:`. Frame caveats as conserved properties of the contract, not as alarms about safe behaviors.\n\n## Imports and file structure\n\n100% named curly-brace imports. Relative paths within `contracts/`. No wildcards:\n\n```solidity\nimport { IERC20, IERC20Metadata } from '../../interfaces/IERC20.sol';\n```\n\nFile order:\n\n1. `// SPDX-License-Identifier: MIT`\n2. `// OpenZeppelin Contracts (last updated vX.Y.Z) (path/to/file.sol)` — **release-managed; do not add or edit by hand**. `scripts/release/update-comment.js` rewrites this line at release time based on which `contracts/**/*.sol` files changed since the previous tag. New files: write only the SPDX line; the release script appends the second line on first inclusion.\n3. `pragma solidity …`\n4. Imports\n5. Contract-level NatSpec\n6. Contract declaration\n\nLeave a blank line between the SPDX/`last updated` header and `pragma`.\n\nInside the body: `using` → structs/enums → constants → state vars → events → errors → modifiers → constructor → external/public functions → internal → private.\n\nContracts not meant to be deployed on their own (bases, extensions, mixins) are declared `abstract` so they can only be used by inheriting from them.\n\n## Return values\n\nDon't name return values unless their meaning isn't obvious or there are multiple. Prefer `returns (uint256)` over `returns (uint256 amount)`; name them when it aids clarity, e.g. `returns (bool isMember, uint32 currentDelay)`.\n\n## Interfaces\n\n- ERC-defined behavior MUST have an interface, and it SHOULD live in its own file. The name starts with `I`; add the ERC number when there's a risk of clash (`IModule` → `IERC7579Module`).\n- ERC interfaces match the spec as closely as possible (including parameter and return names) and MUST NOT declare anything not in the spec. Non-spec errors, structs, and events go in the implementing contract or library instead.\n- In interfaces, functions are `external` and non-value-type parameters are `calldata` — this is the spec form. Implementations use `public`/`memory` (see the `library-api-design` skill); the two intentionally differ.\n- Only mandatory ERC elements go in the base interface; optional ones go in an extension interface (`name()` is in `IERC20Metadata`, not `IERC20`).\n- Placement: `contracts/interfaces/` for ERC interfaces (`draft-` filename prefix if the ERC is not yet final), next to the implementation otherwise.\n- Interface files SHOULD NOT import anything that is not itself an interface file.\n\n## Pragma\n\nDon't pick the pragma floor by hand. Run:\n\n```bash\nnpm run pragma\n```\n\n`scripts/minimize-pragma.js` builds a dependency graph from compiled artifacts, compiles each file against every candidate `solc`, and writes back the lowest version that compiles for that file and is compatible with all of its dependents. The script picks the syntax for you (`>=` for interfaces, `^` for implementations and libraries) and respects the `minVersionForContracts` floor (default `0.8.20`).\n\nWhen adding a new file, start with the same pragma as a similar existing file in the same directory; the minimizer will tighten it on the next run. When editing a file, run the minimizer if you've used a feature that may have raised the floor.\n\nThe CI `test:pragma` job (`npm run test:pragma`) verifies the floor is honest about the opcodes the contract emits. Raising the floor without checking opcode availability on target chains (`mcopy` from 0.8.24 is the example) is the failure mode this guards against.\n\n## Assembly\n\nAlways annotate `assembly (\"memory-safe\")`. Omitting the annotation disables the memory optimizer and increases gas. Include inline comments for any non-trivial operation.\n\n**Numeric literals** inside assembly:\n\n- **Memory-related** (locations, offsets, lengths): hexadecimal. `mload(0x40)`, `mstore(add(ptr, 0x20), value)`, `keccak256(ptr, 0x55)`.\n- **Bit operations** (shift amounts, bit counts): decimal. `shl(128, value)`, `sar(96, x)`.\n- Trivially small values like `1` or `2` may use decimal even in memory contexts: `ptr := add(ptr, 1)`.\n- In `call`/`staticcall`/`delegatecall`, decimal zero is acceptable when both location and length are zero.\n\n## `unchecked`\n\nOnly use `unchecked` with an inline invariant comment that names the bound. The comment may be omitted only when the reason is immediately apparent from the line directly above:\n\n```solidity\nunchecked {\n    // Overflow not possible: value <= fromBalance <= totalSupply.\n    _balances[from] = fromBalance - value;\n}\n```\n\nWhen reviewing, re-derive the bound for every code path that can reach the block.\n\n## Casting\n\nAny narrowing integer cast (`uint256 → uint48`, etc.) must use `SafeCast.toUintXX()` (or the appropriate `toIntXX`). Direct Solidity casts are only permitted after an explicit bounds check on a line you can point to.\n\n## Inheritance ordering\n\nThe relative order of bases in any `is A, B, C` list must be globally consistent. CI runs `npm run test:inheritance` against compiled artifacts to detect conflicts. After any change to an `is` list:\n\n```bash\nnpm run compile && npm run test:inheritance\n```\n\n## Procedurally generated contracts\n\nIf the file header says `// This file was procedurally generated from …`, do not edit it. Edit the template in `scripts/generate/templates/` and run `npm run generate`. CI runs `npm run test:generation` to enforce this.\n",".claude/skills/testing/SKILL.md":"---\nname: testing\ndescription: Testing conventions for openzeppelin-contracts. Use when writing or modifying tests, mocks, or formal verification specs. Covers hardhat-exposed $-wrappers, when manual mocks are warranted, Hardhat+Chai patterns (loadFixture, shouldBehaveLike, multi-target loops), Foundry fuzz, Halmos symbolic execution, Certora rule-based verification, and the changeset rule.\n---\n\n# Testing\n\n## When to use each approach\n\n| Approach                      | Required when                                                                                               |\n| ----------------------------- | ----------------------------------------------------------------------------------------------------------- |\n| Formal verification (Certora) | Contract includes a state machine or access-control rules complex enough that fuzzing won't cover the cases |\n| Symbolic execution (Halmos)   | A property is expressible as a Foundry test with symbolic inputs — fast, no external credentials            |\n| Fuzzing (Foundry)             | Math-heavy code, data structures, complex invariants not suited for FV                                      |\n| Edge unit cases (Hardhat)     | All contracts: golden path + pathological/border cases. This is the baseline.                               |\n\nDefault to Hardhat+Chai unit tests, add Halmos for invariants you can express symbolically, escalate to Certora when state-machine coverage requires it.\n\nA bug fix must add a minimal test that reproduces the bug, committed alongside the fix. The objective is 100% branch coverage per PR.\n\n## Accessing internal functions: hardhat-exposed\n\n`$`-prefixed wrapper contracts are **auto-generated** by [hardhat-exposed](https://github.com/frangio/hardhat-exposed). The plugin creates a `$ContractName` for every contract in `contracts/`, with every internal `_function` re-exposed as an external `$_function`. Output goes to `contracts-exposed/` (gitignored). Use them directly:\n\n```javascript\nconst token = await ethers.deployContract('$ERC20', ['Name', 'SYM']);\nawait token.$_mint(holder, 1000n);\n```\n\n**Do not write `$`-prefixed wrappers by hand.**\n\n## Manual mocks in `contracts/mocks/`\n\nManual mocks live in `contracts/mocks/` (not `test/`) and mirror the `contracts/` directory layout. Write a manual mock only when `hardhat-exposed` cannot give you what you need:\n\n- **Constructor-level setup** that auto-generation can't provide — e.g. `ERC721ConsecutiveMock` calling `_mintConsecutive` during construction.\n- **Multi-extension composition** to test how extensions compose — e.g. `ERC721Consecutive + ERC721Pausable + ERC721Votes`.\n- **Injected behavior** that overrides a hook to simulate an edge case — e.g. `ERC20Reentrant` firing a reentrant call inside `_update`.\n- **Adversarial behavior** with no analogue in the base — e.g. `ReentrancyAttack`, `ERC20ReturnFalseMock`.\n- **A plain public wrapper** for an internal operation where the auto-generated signature is unsuitable — e.g. `ERC20Mock.mint`.\n\nEven inside a manual mock, prefer the auto-generated `$_` functions over redeclaring the same exposure:\n\n```solidity\n// prefer (reuses auto-generated exposure):\nawait token.$_mint(holder, 1000n);\n\n// avoid (redundant — hardhat-exposed already provides this):\ncontract ERC20Mock is ERC20 {\n    function __mint(address account, uint256 value) external { _mint(account, value); }\n}\n```\n\n## Hardhat + Chai (`.test.js`)\n\n**Fixtures**: use `loadFixture` from `@nomicfoundation/hardhat-network-helpers`. Declare the fixture with a named `function`, not an inline arrow — `loadFixture` keys its cache on the function reference, so an anonymous function (especially an inline `loadFixture(async () => {...})`) re-runs on every test instead of restoring the snapshot.\n\n```javascript\nasync function fixture() {\n  const [holder, recipient] = await ethers.getSigners();\n  const token = await ethers.deployContract('$ERC20', [name, symbol]);\n  await token.$_mint(holder, initialSupply);\n  return { holder, recipient, token };\n}\n\nbeforeEach(async function () {\n  Object.assign(this, await loadFixture(fixture));\n});\n```\n\n**Structure**: `describe('ContractName') > describe('methodName') > it('description')`.\n\n**Shared behavior**: reuse via `shouldBehaveLike*` functions in sibling `.behavior.js` files. Mount with this-context state set in the fixture:\n\n```javascript\nconst { shouldBehaveLikeERC20 } = require('./ERC20.behavior');\n// ...\nshouldBehaveLikeERC20(initialSupply, { forcedApproval });\n```\n\n**Multi-target loops**: when the same suite should run against several wrappers (e.g. the auto-generated `$ERC20` and a manual `$ERC20ApprovalMock`), iterate at the top:\n\n```javascript\nconst TOKENS = [{ Token: '$ERC20' }, { Token: '$ERC20ApprovalMock', forcedApproval: true }];\nfor (const { Token, forcedApproval } of TOKENS) {\n  describe(Token, function () {\n    /* … */\n  });\n}\n```\n\n**Assertion style**:\n\n- `bigint` literals (`100n`) for uint values — not `ethers.BigNumber`.\n- Custom errors: `.revertedWithCustomError(contract, 'ErrorName').withArgs(...)`.\n- Panics: `.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW)`.\n- Events: `.emit(contract, 'EventName').withArgs(...)`.\n- Balance changes: `.changeTokenBalance(token, account, delta)` / `.changeTokenBalances(token, [a, b], [d1, d2])`.\n\n## Foundry (`.t.sol`)\n\nFoundry tests live next to their Hardhat counterparts in `test/`. Under HH3 they are picked up by `npm test` (via the `@nomicfoundation/hardhat-mocha` solidity runner) alongside the JS tests. Standalone `forge test` still works but uses forge defaults for fuzz. Two distinct purposes:\n\n**Fuzzing**: function name starts with `test`, parameters are typed. Foundry generates random inputs.\n\n```solidity\nfunction testFuzzAdd(uint256 a, uint256 b) public pure {\n  vm.assume(a < type(uint256).max - b); // restrict if needed\n  assertEq(Math.add(a, b), a + b);\n}\n```\n\nUse `assertEq`, `assertGt`, etc. from `forge-std/Test.sol`. Fuzz settings live in `hardhat.config.ts` under `test.solidity.fuzz` (`runs = 5000`, `maxTestRejects = 150000`) and are applied when running via `npm test`. Standalone `forge test` uses forge defaults.\n\n**Symbolic execution (Halmos)**: function name starts with `symbolic` or `testSymbolic` — that's what the CI `halmos` job matches (`--match-test '^symbolic|^testSymbolic'`). These run as regular Foundry fuzz tests as well.\n\n```solidity\nfunction testSymbolicTransfer(address to, uint256 amount) public {\n  // Halmos treats inputs as fully symbolic (all values).\n  // Use vm.assume(...) for preconditions.\n}\n```\n\nHalmos runs on every PR, needs no credentials. Reach for it when a property can be expressed as a Foundry test.\n\n**Pragma note**: Foundry compiles with `solc 0.8.31` (`foundry.toml`); contract pragmas in `contracts/` may still be `^0.8.20`. New `.t.sol` files should match the contract's pragma minimum unless they specifically need a newer feature.\n\n## Certora (rule-based formal verification)\n\n[Certora](https://certora.com) specs live in `fv/specs/` as `.conf` + `.spec` pairs. CI runs one job per affected config, on any PR whose diff reaches a spec, a harness, a contract one of them verifies, or an `fv/diff` patch applied to one -- no label needed. Label a PR `formal-verification-force-all` to run every config instead. Requires a `CERTORAKEY`, and the `certora` environment gates the job behind a manual approval. Selection runs the base branch's copy of `scripts/list-dependencies.js` over the PR's files, so a PR cannot change which configs it is checked against. The matrix job names carry their config, so `verified` is the aggregate job to require in branch protection. Deleting a `.conf` fails selection, since a config that no longer exists cannot be selected and its verification would just disappear; label the PR `formal-verification-allow-removal` when that is intended.\n\nLocal workflow:\n\n```bash\nmake -C fv apply           # apply harness patches first\nnode fv/run.js AccessControl   # runs fv/specs/AccessControl.conf\nnode fv/run.js --all           # runs every spec\n```\n\nUse Certora when a property is too complex for Halmos, or when a state machine needs deeper rule coverage than fuzzing provides.\n\n## Changesets\n\nEvery PR that changes contract behavior needs a changeset. Skip for NatSpec-only edits, internal refactors with no user-visible effect, or pure repo plumbing.\n\n```bash\nnpx changeset add\n```\n\nThe generated file in `.changeset/` looks like:\n\n```\n---\n'openzeppelin-solidity': minor\n---\n\n`ComponentName`: One-sentence description starting with a backtick-quoted contract or component name.\n```\n\n- `patch` — bug fixes.\n- `minor` — new features.\n- One sentence. No bullet points, no multi-paragraph prose. The release pipeline composes these into `CHANGELOG.md` automatically.\n\n## CI matrix you should keep in mind\n\n| Job                   | What it runs                                                        | Triggered by                                             |\n| --------------------- | ------------------------------------------------------------------- | -------------------------------------------------------- |\n| `lint`                | `npm run lint`                                                      | Every push/PR                                            |\n| `tests`               | `npm test` (JS + Foundry via HH3) + inheritance, pragma, generation | Every push/PR                                            |\n| `tests-upgradeable`   | Transpile then re-run the test suite + storage-layout diff          | Every push/PR                                            |\n| `coverage`            | `npm run coverage` → codecov                                        | Every push/PR                                            |\n| `slither`             | Slither static analysis for common vulnerabilities                  | Every push/PR                                            |\n| `halmos`              | `halmos --match-test '^symbolic\\|^testSymbolic'`                    | Every push/PR                                            |\n| `formal verification` | Certora, one job per affected config (or all, with the force label) | PRs touching a spec, harness, patch or verified contract |\n","CLAUDE.md":"# openzeppelin-contracts — Claude project guide\n\nThis repository is a **Solidity smart contract library**: reusable `abstract contract` and `library` implementations meant to be inherited and extended by downstream projects. It is not a deployed product. Most conventions exist because code here is a base for others: functions must be overridable, argument types must not constrain inheritors, and the public API must not force a particular call context on child contracts.\n\nRead [`GUIDELINES.md`](./GUIDELINES.md) first — it is the human contributor spec and authoritative. This file is for what is specific to Claude-assisted work, plus the conventions not fully documented elsewhere.\n\nBefore preparing a contribution, also read [`CONTRIBUTING.md`](./CONTRIBUTING.md) thoroughly. Non-trivial changes must be discussed in an issue before a PR is opened.\n\n## Repo map\n\n| Path                    | Purpose                                                                                                                                        |\n| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |\n| `contracts/`            | Library source. Subdirectory layout (`token/ERC20/`, `access/`, `utils/`, …) mirrors the public API.                                           |\n| `contracts/mocks/`      | Mocks used by tests. Mirrors `contracts/`. Manual mocks only — see the `testing` skill.                                                        |\n| `contracts/interfaces/` | ERC interfaces in their pristine spec form.                                                                                                    |\n| `test/`                 | Hardhat + Chai tests (`.test.js`), shared behaviors (`.behavior.js`), and Foundry tests (`.t.sol`).                                            |\n| `contracts-exposed/`    | Auto-generated `$ContractName` wrappers from `hardhat-exposed`. Gitignored. Do not edit.                                                       |\n| `fv/`                   | Certora specs (`fv/specs/*.{conf,spec}`), harnesses, and `make`-applied patches.                                                               |\n| `scripts/generate/`     | Procedurally generated `.sol` files (Arrays variants, Checkpoints, EnumerableSet, …). Source of truth is the template, not the generated file. |\n| `scripts/checks/`       | CI checks: `inheritance-ordering.js`, `pragma-validity.js`, generation diff, storage layout.                                                   |\n| `docs/`                 | AsciiDoc sources used by the OZ documentation site. Per-module `README.adoc` files in `contracts/**/` are also rendered.                       |\n| `.changeset/`           | Per-PR changelog entries consumed by the release workflow.                                                                                     |\n| `audits/`               | Historical third-party audit reports, one per release. Reference only.                                                                         |\n| `.claude/skills/`       | Skills specific to working in this repository, loaded by AI assistants per task. See `CONTRIBUTING.md`.                                        |\n\n## Commands\n\n| Task                                     | Command                                                                 |\n| ---------------------------------------- | ----------------------------------------------------------------------- |\n| Compile                                  | `npm run compile`                                                       |\n| Tests (JS + Solidity)                    | `npm test`                                                              |\n| Foundry tests only (forge defaults)      | `forge test -vvv`                                                       |\n| Halmos symbolic tests                    | `halmos --match-test '^symbolic\\|^testSymbolic' -vv`                    |\n| Lint (JS + Sol)                          | `npm run lint` / `npm run lint:fix`                                     |\n| Coverage                                 | `npm run coverage`                                                      |\n| Inheritance order check                  | `npm run test:inheritance`                                              |\n| Pragma validity check                    | `npm run test:pragma`                                                   |\n| Generated-file freshness check           | `npm run test:generation`                                               |\n| Regenerate procedural contracts          | `npm run generate`                                                      |\n| Add a changelog entry                    | `npx changeset add`                                                     |\n| Run a single Certora spec                | `node fv/run.js <SpecName>` (apply harnesses first: `make -C fv apply`) |\n\n## What linting via solhint already enforces (don't re-state in code review)\n\n`solhint-plugin-openzeppelin` + `scripts/solhint-custom/index.js` enforce, on `contracts/**/*.sol` (mocks and tests are exempt):\n\n- **State variables are `private`** (`constant`/`immutable` are the only exceptions).\n- **Underscore prefix matches visibility**: `private`/`internal` state vars and functions get `_`; `public`/`external` do not. Library internal functions do NOT get `_`.\n- **No `external virtual`** (except fallback). The `public` default makes this redundant; flagged by `no-external-virtual`.\n- **Libraries expose only `internal`/`private`**.\n- **Interfaces start with `I`**, contracts use CapWords, events use CapWords, modifiers/params mixedCase.\n\nIf a lint rule already catches it, don't waste tokens explaining it — fix and move on.\n\n## Always-on conventions\n\nThese apply to every change and are not lint-enforced:\n\n- **File header version line**: don't add or edit the `// OpenZeppelin Contracts (last updated vX.Y.Z) (path)` line — release tooling (`scripts/release/update-comment.js`) maintains it automatically based on `git diff` between the previous tag and `HEAD`. The SPDX line is your only responsibility.\n- **Pragma**: don't pick the floor by hand. Run `npm run pragma` and let `scripts/minimize-pragma.js` walk the dependency graph, compile against every candidate `solc`, and write back the lowest version that works for each file (`>=` prefix for interfaces, `^` for implementations and libraries). Bumping the floor casually can introduce opcodes (`mcopy` in 0.8.24, etc.) not supported on all target chains — the script's output is the safe answer.\n- **Imports**: 100% named, curly-brace, relative paths within `contracts/`. No wildcards.\n- **Inheritance order is globally consistent**: if you change an `is A, B, C` list, run `npm run compile && npm run test:inheritance` before pushing.\n- **Changesets**: every PR that changes contract behavior needs one. Skip for NatSpec-only, internal refactors with no user-visible effect, or pure repo plumbing.\n- **Procedurally generated files**: never hand-edit a file whose header says \"procedurally generated from `<template>`\". Edit the template under `scripts/generate/templates/` and run `npm run generate`. CI runs `test:generation` to enforce.\n- **Backward compatibility**: released contracts are inherited downstream and transpiled into the `-upgradeable` package. Don't change existing `public`/`external` signatures, event or error shapes, or the storage layout of existing contracts. Changes are additive; deprecate rather than delete. Breaking changes are a maintainer decision tied to a major release.\n- **Dependencies**: this library has no third-party dependencies that translate to the user, and nothing that could change behavior out from under it. Don't add an npm or Solidity dependency or a new external import without maintainer sign-off; reuse existing `contracts/utils/` helpers first. Code that needs third-party dependencies belongs in `openzeppelin-community-contracts`, not here.\n\n## When to load which skill\n\nClaude Code surfaces these from `.claude/skills/` based on the task. You can also reference them directly:\n\n- **`library-api-design`** — when adding or modifying any contract in `contracts/` (visibility, `virtual`, `memory` vs `calldata`, `_update` pattern, internal/external split, no-ops over reverts, hooks).\n- **`solidity-style`** — when writing or editing Solidity (errors, events, NatSpec, ERC-7201, assembly, `unchecked`, casting, immutables).\n- **`testing`** — when writing tests or mocks (`hardhat-exposed` `$` wrappers, when manual mocks are warranted, `.behavior.js` reuse, multi-target test loops, Foundry fuzz, Halmos symbolic, Certora).\n- **`add-changeset`** — at PR close, or when the user asks for one.\n\n## Things to always verify manually\n\nAI suggestions in these areas are often plausible-but-wrong. Read the surrounding code before accepting:\n\n- **Cryptography** — anything in `contracts/utils/cryptography/`, hashing, ECDSA, EIP-712 domains, BLS, signatures.\n- **ERC-7201 slot derivations** — recompute with `SlotDerivation.erc7201Slot()` and compare to the constant. The formula in the inline comment is mandatory.\n- **Storage layout for transpiler-processed contracts** — adding/removing/reordering fields breaks proxied deployments. CI runs storage-layout diff but read it.\n- **`delegatecall`, inline assembly, memory safety** — verify the `memory-safe` annotation is honest and stack/scratch usage is bounded.\n- **`unchecked` invariants** — re-derive the overflow-impossible claim for every code path. The inline comment is the contract.\n- **`_authorizeUpgrade` access control** — the base is empty by design; every override must restrict.\n- **`_disableInitializers()`** in implementation constructors for UUPS/Transparent proxy targets.\n- **Pragma bumps** — check that any new opcode the compiler will emit is supported on all target chains.\n\n## Out of scope for AI\n\n- Adding/renaming public API or deciding whether a feature belongs in the library — open an issue first.\n- Security assessments or audit opinions.\n- New ERC interfaces or standards without explicit direction.\n- Restructuring tests without a corresponding contract change.\n- Fabricated bug bounties and security rewards. Never claim or imply a reward, amount, or eligibility. The only existing program is [Immunefi](https://immunefi.com/bounty/openzeppelin) (See [`SECURITY.md`](./SECURITY.md)).\n",".claude/skills/library-api-design/SKILL.md":"---\nname: library-api-design\ndescription: API design rules for openzeppelin-contracts. Use when adding, modifying, or reviewing any contract under contracts/. Covers virtual, memory vs calldata, public vs external, overloads, no-op vs revert, _update single-override pattern, internal/external split, _msgSender, and override points.\n---\n\n# Library API design\n\nThis repo is a base library: every contract is inherited downstream. The rules below preserve that inheritability. Most of them are not lint-enforced — they require judgment when adding new contracts.\n\n## `virtual` on overridable functions\n\nMark every `public` and `internal` function `virtual` — with two specific exceptions:\n\n**Alias functions** (one function that just forwards to another with no added logic) are NOT virtual. The canonical form is the virtual one. Add a NatSpec `NOTE:` pointing to it:\n\n```solidity\n/**\n * @dev See {IERC20-transfer}.\n *\n * NOTE: This function is not virtual, {_update} should be overridden instead.\n */\nfunction _transfer(address from, address to, uint256 value) internal {\n  // ...\n  _update(from, to, value);\n}\n```\n\n**Convenience overloads** that only fill in a default argument are NOT virtual; the full-argument canonical form is:\n\n```solidity\n// not virtual (only sets emitEvent = true):\nfunction _approve(address owner, address spender, uint256 value) internal {\n    _approve(owner, spender, value, true);\n}\n\n// virtual (real override point):\nfunction _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { ... }\n```\n\nSolhint's `no-external-virtual` rule catches `external virtual` (fallback excepted) — the right fix is `public virtual`, not silencing the rule. The reason `external` can't be the override point: an override of an `external` function cannot call `super`, so a function designed to be overridden must be `public`.\n\n## `memory` for reference-type arguments\n\nUse `memory` for `string`, `bytes`, arrays, and structs — including in `public` functions. `calldata` forces every inheritor to match, so it is only valid in three narrow cases:\n\n1. The return value is a slice of `msg.data` (e.g. `_msgData()` in `Context.sol`).\n2. An internal calldata-decoding helper that is genuinely scoped to one function (e.g. `_extractSignatureValidator(bytes calldata signature) internal pure returns (address module, bytes calldata innerSignature)` in `AccountERC7579.sol`).\n3. An explicit `Calldata`-suffixed parallel variant in a utility library, where the base version still uses `memory` (e.g. `MerkleProof.verifyCalldata`).\n\n## `public` vs `external`\n\nDefault to `public`. Use `external` only when:\n\n- The ERC spec itself defines the function as `external` (e.g. `ERC20Permit.DOMAIN_SEPARATOR`, `UUPSUpgradeable.proxiableUUID`).\n- The contract design explicitly prevents internal calls and the function must only be reachable from outside (e.g. `ERC7786Recipient.receiveMessage`, where only an authorized gateway should call it and meta-transactions don't apply).\n\n`external` for the second reason needs a NatSpec `NOTE:` explaining why.\n\n## The internal/external split\n\n`public` functions exist only to (1) capture `_msgSender()` and (2) delegate to an `internal` function that takes the actor as an explicit argument. All logic lives in the internal variant:\n\n```solidity\nfunction transfer(address to, uint256 value) public virtual returns (bool) {\n  address owner = _msgSender();\n  _transfer(owner, to, value);\n  return true;\n}\n```\n\nThis keeps internal functions composable, testable through `hardhat-exposed`, and independently overridable.\n\n## `_update` single override point\n\nFor state machines with several entry points (mint, burn, transfer), make `_mint`/`_burn`/`_transfer` non-virtual aliases that delegate to a single `virtual _update(...)`. Extensions override `_update` only, never the individual operations, and always call `super._update(...)` before adding side effects:\n\n```solidity\nfunction _update(address from, address to, uint256 value) internal virtual override {\n  super._update(from, to, value); // base logic first\n  _transferVotingUnits(from, to, value); // side effects after\n}\n```\n\n## Prefer no-ops over reverts\n\nIf an operation can do nothing without creating a security concern, it should do nothing. This shows up at three levels:\n\n**Vacuous inputs**: skip silently rather than reverting. `batchSize == 0` skips the whole mint loop; `ERC721Utils.checkOnERC721Received` skips the callback for EOAs because there is nothing to check. Document the no-op condition with a short inline comment.\n\n**Idempotent state transitions**: return a `bool` from operations that may already be at the target state, instead of reverting. The caller can add a revert; the base never takes that choice away:\n\n```solidity\nfunction _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n  if (hasRole(role, account)) {\n    _roles[role].hasRole[account] = false;\n    emit RoleRevoked(role, account, _msgSender());\n    return true;\n  }\n  return false; // already revoked — no-op, not revert\n}\n```\n\n**Empty virtual hooks**: `internal virtual {}` is a valid extension point when no default behavior exists. Always `_camelCase`, always `internal virtual`, always documented with `@dev`:\n\n```solidity\n/// @dev Hook called every time the tally for a proposal is updated.\nfunction _tallyUpdated(uint256 proposalId) internal virtual {}\n```\n\n**Rule of thumb**: overrides can always add requirements on top of a no-op base. They cannot remove a requirement the base already enforces.\n\n## `_msgSender()` vs `msg.sender`\n\nUse `_msgSender()` in application logic. `msg.sender` appears only inside `Context.sol` / `ERC2771Context.sol`, or in deliberate protocol-level exceptions (e.g. `ERC7786Recipient.receiveMessage`, where wrapping the sender would break the protocol). Every exception outside the Context layer needs a `NOTE:` explaining why.\n\n## `super` and extension contracts\n\n`super` must be called in `_update` overrides, in `supportsInterface` overrides, and whenever an extension builds on base behavior. Skipping `super` is only acceptable for genuinely new functions that have no base implementation.\n\n`supportsInterface` always follows the pattern:\n\n```solidity\ninterfaceId == type(X).interfaceId || super.supportsInterface(interfaceId)\n```\n\nWhen overriding from multiple bases, list all of them: `override(A, B)`.\n\n## Constructors, immutables, and the transpiler\n\n| Keyword     | When                                                       | Naming                               |\n| ----------- | ---------------------------------------------------------- | ------------------------------------ |\n| `constant`  | Compile-time known (type hashes, role ids, slot addresses) | `ALL_CAPS_WITH_UNDERSCORES`          |\n| `immutable` | Constructor-set, deploy-time computed                      | `_camelCase` (same as private state) |\n\n`/// @custom:oz-upgrades-unsafe-allow state-variable-immutable` is not a \"this value is too dangerous to change\" tag — every immutable can still be changed by upgrading to a new implementation. The tag controls **per-implementation vs per-proxy**:\n\n- **Tag present**: the value stays `immutable` in the upgradeable variant. It is set in the upgradeable contract's constructor and shared by every proxy that points at that implementation. Changing it requires a new implementation.\n- **Tag absent**: the transpiler converts the field to a regular storage slot and moves initialization into the initializer. Each proxy gets its own value while sharing one implementation.\n\nPick the tag when the value is logically a property of the implementation (e.g. `ERC2771Context._trustedForwarder` — the trust relationship belongs to the deployed code, not each proxy). Omit it when each deployment should configure its own value (e.g. `GovernorVotes._token`).\n\n## ERC-7201 namespaced storage\n\nERC-7201 is opt-in, not the default. Upgradeable variants of OZ contracts are auto-generated by the [OpenZeppelin Transpiler](https://github.com/OpenZeppelin/openzeppelin-transpiler) — don't preemptively add ERC-7201 storage layouts to new contracts.\n\nReach for it only when namespacing is genuinely necessary, such as `Initializable.sol` (sequential slot 0 would be risky for proxy implementations). New uses must justify themselves in NatSpec. When it is the right tool:\n\n```solidity\n/// @custom:storage-location erc7201:openzeppelin.storage.MyContract\nstruct MyContractStorage {\n    uint256 _value;\n}\n\n// keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.MyContract\")) - 1)) & ~bytes32(uint256(0xff))\nbytes32 private constant MY_CONTRACT_STORAGE = 0x...;\n\n// solhint-disable-next-line var-name-mixedcase\nfunction _getMyContractStorage() private pure returns (MyContractStorage storage $) {\n    assembly {\n        $.slot := MY_CONTRACT_STORAGE\n    }\n}\n```\n\nRules: struct annotated with `@custom:storage-location`; inline formula comment above the constant; pointer variable always named `$`; `solhint-disable-next-line var-name-mixedcase` immediately before any function that uses `$`; constant in `ALL_CAPS`. Always verify the slot value with `SlotDerivation.erc7201Slot()` — never compute by hand.\n"},"items":[{"name":"SKILL.md","path":".claude/skills/add-changeset/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/HEAD/.claude/skills/add-changeset/SKILL.md","title":"add-changeset","category":"anthropic-skill","format":"markdown","content":"---\nname: add-changeset\ndescription: Add a changelog entry for a PR. Use when the user asks for a changeset, when finishing a PR that changes user-visible contract behavior, or when the changeset-bot has flagged the PR. Skip for NatSpec-only edits, internal refactors with no observable effect, or repo plumbing.\n---\n\n# Add a changeset\n\n## When to add one\n\nAdd a changeset when the PR changes **user-visible contract behavior**: a new function, a new contract or extension, a bug fix that changes outputs, a new event, a new error, a new interface ID, or a tighter requirement.\n\n**Skip** for:\n\n- NatSpec-only changes.\n- Internal refactors with no observable effect.\n- Test/mock-only changes.\n- CI, lint, docgen, dependency bumps that don't change the published `contracts/`.\n\nIf unsure, err on the side of adding one — release reviewers can downgrade to \"skip\" by deleting the file before tagging.\n\n## How\n\n```bash\nnpx changeset add\n```\n\nThe CLI prompts for severity, then opens an editor. Severity:\n\n- `patch` — bug fixes, gas optimizations, no API surface change.\n- `minor` — new contracts, new extensions, new public functions on existing contracts.\n\nThere is no `major` from contributors — major version bumps are coordinated by maintainers.\n\nThe resulting file in `.changeset/` looks like:\n\n```\n---\n'openzeppelin-solidity': minor\n---\n\n`ComponentName`: One-sentence description starting with a backtick-quoted component name.\n```\n\n## Format rules\n\n- **First word inside the backticks** is the affected contract, extension, or library: `ERC20`, `ERC1155Crosschain`, `Memory`, `Governor`, etc.\n- **Then a colon, then one sentence** describing the user-visible change.\n- **Imperative mood** (matching PR titles: \"Add\", \"Fix\", not \"Added\", \"Fixed\"). Trailing period optional.\n- **No bullet points**, no headings, no multi-paragraph prose. The release workflow concatenates these verbatim.\n- **State the change, don't justify it.** Skip \"This mirrors ...\", \"so that ...\", \"closes a gap where ...\", parallels to other functions, or motivation. If the user needs to take action (migration step, breaking rename, tighter requirement they might now hit), state that instead — otherwise the changelog line is just the fact of the change. Rationale belongs in the PR body and commit message, not the changelog.\n\nGood examples (taken from existing entries):\n\n```\n`Memory`: Add a `isReserved(Slice)` function that checks if the memory occupied by the slice is reserved (i.e. before the free memory pointer).\n```\n\n```\n`ERC1155Crosschain`: Add an ERC-1155 extension to embed an ERC-7786 based crosschain bridge directly in the token contract.\n```\n\n```\nAdd ERC-165 detection for the `IERC6909ContentURI`, `IERC6909TokenSupply` and `IERC6909Metadata` interfaces in the `ERC6909ContentURI`, `ERC6909TokenSupply` and `ERC6909Metadata` contracts respectively.\n```\n\n(The last form — no leading backticks — is acceptable when several components are touched at once and a single prefix would be misleading.)\n\n## After committing\n\nThe `changeset-bot` GitHub check posts a comment confirming the changeset was detected. If the bot complains \"No Changeset found\" and the PR should not have one (NatSpec-only, etc.), leave a comment justifying it — a maintainer can apply the `ignore-changeset` label.\n","frontmatter":{"name":"add-changeset","description":"Add a changelog entry for a PR. Use when the user asks for a changeset, when finishing a PR that changes user-visible contract behavior, or when the changeset-bot has flagged the PR. Skip for NatSpec-only edits, internal refactors with no observable effect, or repo plumbing."},"isInternal":false,"tokens":769,"sizeBytes":3295},{"name":"SKILL.md","path":".claude/skills/library-api-design/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/HEAD/.claude/skills/library-api-design/SKILL.md","title":"library-api-design","category":"anthropic-skill","format":"markdown","content":"---\nname: library-api-design\ndescription: API design rules for openzeppelin-contracts. Use when adding, modifying, or reviewing any contract under contracts/. Covers virtual, memory vs calldata, public vs external, overloads, no-op vs revert, _update single-override pattern, internal/external split, _msgSender, and override points.\n---\n\n# Library API design\n\nThis repo is a base library: every contract is inherited downstream. The rules below preserve that inheritability. Most of them are not lint-enforced — they require judgment when adding new contracts.\n\n## `virtual` on overridable functions\n\nMark every `public` and `internal` function `virtual` — with two specific exceptions:\n\n**Alias functions** (one function that just forwards to another with no added logic) are NOT virtual. The canonical form is the virtual one. Add a NatSpec `NOTE:` pointing to it:\n\n```solidity\n/**\n * @dev See {IERC20-transfer}.\n *\n * NOTE: This function is not virtual, {_update} should be overridden instead.\n */\nfunction _transfer(address from, address to, uint256 value) internal {\n  // ...\n  _update(from, to, value);\n}\n```\n\n**Convenience overloads** that only fill in a default argument are NOT virtual; the full-argument canonical form is:\n\n```solidity\n// not virtual (only sets emitEvent = true):\nfunction _approve(address owner, address spender, uint256 value) internal {\n    _approve(owner, spender, value, true);\n}\n\n// virtual (real override point):\nfunction _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { ... }\n```\n\nSolhint's `no-external-virtual` rule catches `external virtual` (fallback excepted) — the right fix is `public virtual`, not silencing the rule. The reason `external` can't be the override point: an override of an `external` function cannot call `super`, so a function designed to be overridden must be `public`.\n\n## `memory` for reference-type arguments\n\nUse `memory` for `string`, `bytes`, arrays, and structs — including in `public` functions. `calldata` forces every inheritor to match, so it is only valid in three narrow cases:\n\n1. The return value is a slice of `msg.data` (e.g. `_msgData()` in `Context.sol`).\n2. An internal calldata-decoding helper that is genuinely scoped to one function (e.g. `_extractSignatureValidator(bytes calldata signature) internal pure returns (address module, bytes calldata innerSignature)` in `AccountERC7579.sol`).\n3. An explicit `Calldata`-suffixed parallel variant in a utility library, where the base version still uses `memory` (e.g. `MerkleProof.verifyCalldata`).\n\n## `public` vs `external`\n\nDefault to `public`. Use `external` only when:\n\n- The ERC spec itself defines the function as `external` (e.g. `ERC20Permit.DOMAIN_SEPARATOR`, `UUPSUpgradeable.proxiableUUID`).\n- The contract design explicitly prevents internal calls and the function must only be reachable from outside (e.g. `ERC7786Recipient.receiveMessage`, where only an authorized gateway should call it and meta-transactions don't apply).\n\n`external` for the second reason needs a NatSpec `NOTE:` explaining why.\n\n## The internal/external split\n\n`public` functions exist only to (1) capture `_msgSender()` and (2) delegate to an `internal` function that takes the actor as an explicit argument. All logic lives in the internal variant:\n\n```solidity\nfunction transfer(address to, uint256 value) public virtual returns (bool) {\n  address owner = _msgSender();\n  _transfer(owner, to, value);\n  return true;\n}\n```\n\nThis keeps internal functions composable, testable through `hardhat-exposed`, and independently overridable.\n\n## `_update` single override point\n\nFor state machines with several entry points (mint, burn, transfer), make `_mint`/`_burn`/`_transfer` non-virtual aliases that delegate to a single `virtual _update(...)`. Extensions override `_update` only, never the individual operations, and always call `super._update(...)` before adding side effects:\n\n```solidity\nfunction _update(address from, address to, uint256 value) internal virtual override {\n  super._update(from, to, value); // base logic first\n  _transferVotingUnits(from, to, value); // side effects after\n}\n```\n\n## Prefer no-ops over reverts\n\nIf an operation can do nothing without creating a security concern, it should do nothing. This shows up at three levels:\n\n**Vacuous inputs**: skip silently rather than reverting. `batchSize == 0` skips the whole mint loop; `ERC721Utils.checkOnERC721Received` skips the callback for EOAs because there is nothing to check. Document the no-op condition with a short inline comment.\n\n**Idempotent state transitions**: return a `bool` from operations that may already be at the target state, instead of reverting. The caller can add a revert; the base never takes that choice away:\n\n```solidity\nfunction _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n  if (hasRole(role, account)) {\n    _roles[role].hasRole[account] = false;\n    emit RoleRevoked(role, account, _msgSender());\n    return true;\n  }\n  return false; // already revoked — no-op, not revert\n}\n```\n\n**Empty virtual hooks**: `internal virtual {}` is a valid extension point when no default behavior exists. Always `_camelCase`, always `internal virtual`, always documented with `@dev`:\n\n```solidity\n/// @dev Hook called every time the tally for a proposal is updated.\nfunction _tallyUpdated(uint256 proposalId) internal virtual {}\n```\n\n**Rule of thumb**: overrides can always add requirements on top of a no-op base. They cannot remove a requirement the base already enforces.\n\n## `_msgSender()` vs `msg.sender`\n\nUse `_msgSender()` in application logic. `msg.sender` appears only inside `Context.sol` / `ERC2771Context.sol`, or in deliberate protocol-level exceptions (e.g. `ERC7786Recipient.receiveMessage`, where wrapping the sender would break the protocol). Every exception outside the Context layer needs a `NOTE:` explaining why.\n\n## `super` and extension contracts\n\n`super` must be called in `_update` overrides, in `supportsInterface` overrides, and whenever an extension builds on base behavior. Skipping `super` is only acceptable for genuinely new functions that have no base implementation.\n\n`supportsInterface` always follows the pattern:\n\n```solidity\ninterfaceId == type(X).interfaceId || super.supportsInterface(interfaceId)\n```\n\nWhen overriding from multiple bases, list all of them: `override(A, B)`.\n\n## Constructors, immutables, and the transpiler\n\n| Keyword     | When                                                       | Naming                               |\n| ----------- | ---------------------------------------------------------- | ------------------------------------ |\n| `constant`  | Compile-time known (type hashes, role ids, slot addresses) | `ALL_CAPS_WITH_UNDERSCORES`          |\n| `immutable` | Constructor-set, deploy-time computed                      | `_camelCase` (same as private state) |\n\n`/// @custom:oz-upgrades-unsafe-allow state-variable-immutable` is not a \"this value is too dangerous to change\" tag — every immutable can still be changed by upgrading to a new implementation. The tag controls **per-implementation vs per-proxy**:\n\n- **Tag present**: the value stays `immutable` in the upgradeable variant. It is set in the upgradeable contract's constructor and shared by every proxy that points at that implementation. Changing it requires a new implementation.\n- **Tag absent**: the transpiler converts the field to a regular storage slot and moves initialization into the initializer. Each proxy gets its own value while sharing one implementation.\n\nPick the tag when the value is logically a property of the implementation (e.g. `ERC2771Context._trustedForwarder` — the trust relationship belongs to the deployed code, not each proxy). Omit it when each deployment should configure its own value (e.g. `GovernorVotes._token`).\n\n## ERC-7201 namespaced storage\n\nERC-7201 is opt-in, not the default. Upgradeable variants of OZ contracts are auto-generated by the [OpenZeppelin Transpiler](https://github.com/OpenZeppelin/openzeppelin-transpiler) — don't preemptively add ERC-7201 storage layouts to new contracts.\n\nReach for it only when namespacing is genuinely necessary, such as `Initializable.sol` (sequential slot 0 would be risky for proxy implementations). New uses must justify themselves in NatSpec. When it is the right tool:\n\n```solidity\n/// @custom:storage-location erc7201:openzeppelin.storage.MyContract\nstruct MyContractStorage {\n    uint256 _value;\n}\n\n// keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.MyContract\")) - 1)) & ~bytes32(uint256(0xff))\nbytes32 private constant MY_CONTRACT_STORAGE = 0x...;\n\n// solhint-disable-next-line var-name-mixedcase\nfunction _getMyContractStorage() private pure returns (MyContractStorage storage $) {\n    assembly {\n        $.slot := MY_CONTRACT_STORAGE\n    }\n}\n```\n\nRules: struct annotated with `@custom:storage-location`; inline formula comment above the constant; pointer variable always named `$`; `solhint-disable-next-line var-name-mixedcase` immediately before any function that uses `$`; constant in `ALL_CAPS`. Always verify the slot value with `SlotDerivation.erc7201Slot()` — never compute by hand.\n","frontmatter":{"name":"library-api-design","description":"API design rules for openzeppelin-contracts. Use when adding, modifying, or reviewing any contract under contracts/. Covers virtual, memory vs calldata, public vs external, overloads, no-op vs revert, _update single-override pattern, internal/external split, _msgSender, and override points."},"isInternal":false,"tokens":2066,"sizeBytes":9219},{"name":"SKILL.md","path":".claude/skills/solidity-style/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/HEAD/.claude/skills/solidity-style/SKILL.md","title":"solidity-style","category":"anthropic-skill","format":"markdown","content":"---\nname: solidity-style\ndescription: Solidity style and safety conventions for contract source files. Use when writing or editing any .sol file under contracts/. Covers errors, events, NatSpec, imports, file structure, assembly, unchecked, casting, numeric literal formatting, and inheritance ordering.\n---\n\n# Solidity style and safety\n\nThe conventions below are not enforced by solhint. For the lint-enforced rules (private state vars, underscore prefixes, library visibility, etc.), see the project root `CLAUDE.md`.\n\n## Errors\n\nPrefer `require(condition, CustomError(args));` in files whose pragma is `^0.8.27` or later (the first version supporting it in the legacy pipeline). Keep `if (!condition) revert CustomError(args);` when:\n\n- The file's pragma is lower. Do not bump pragma just to use `require`.\n- An error argument is anything other than a variable, constant, literal or cast of those. `require` evaluates its arguments even when the condition holds, so a function call, storage or member read costs gas on the success path, and checked arithmetic (e.g. `revert Exceeded(value - max)`) can panic instead of passing.\n- The check is part of an `if`/`else if` chain.\n\nWhen a condition compares against a literal or constant bound, prefer the strict operator: `require(x < 0x5fd4, ...)` rather than `require(x <= 0x5fd3, ...)`. The EVM has `LT`/`GT` but no `LTE`/`GTE`, so `<=` compiles to `ISZERO(GT(...))`. Don't rewrite a comparison against a variable bound this way: `x < max + 1` adds arithmetic that overflows at `type(uint256).max`.\n\nCustom errors follow [ERC-6093](https://eips.ethereum.org/EIPS/eip-6093):\n\n- **Domain prefix**: `ERC<number>` if the error violates an ERC spec; otherwise the component name (`Governor`, `ECDSA`, `Timelock`).\n- **Include offending values** as arguments so callers can decode the failure context.\n- **Never declare the same error name twice** across the library — duplicates cause identifier conflicts when inheriting from multiple contracts.\n\n**Location priority**:\n\n1. Reuse the error from the underlying ERC if already defined there.\n2. Otherwise declare in the interface or library that owns the concept.\n3. Otherwise declare in the implementation (when the interface/library is already pinned by an ERC).\n4. Otherwise declare in the extension where it actually fires.\n\n## Events\n\nEmit **after** the state change. Past-tense CapWords names (`OwnershipTransferred`, `RoleGranted`). ERC-standard events use the tense specified in the ERC (e.g. ERC-20's `Transfer` is present tense — follow the spec). Declare events in the interface or at the top of the emitting contract.\n\n## NatSpec\n\n`@dev` is the only tag rendered by the OZ documentation engine. Use it as the primary tag everywhere. `@notice`, `@param`, `@return` appear in interface files where ERC specs are kept verbatim — do not add `@notice` to implementation contracts; it will not appear in generated docs.\n\n- Single-line: `/// @dev …`\n- Multi-line: `/** @dev … */`\n- Document public and internal functions, events, errors, and constructors. Private functions are optional — they don't appear on the docs site.\n\n**Overrides**:\n\n- Pure relay with nothing to add: `/// @inheritdoc InterfaceName`\n- Override with added requirements or caveats: `/** @dev See {Interface-functionName}. \\n\\n Requirements: … */`\n\n**Cross-references**: `{ContractName-functionName}` — e.g. `{IERC20-transfer}`, `{_update}`.\n\n**Section headers** inside `@dev` blocks: `Requirements:`, `NOTE:`, `WARNING:`, `IMPORTANT:`. Frame caveats as conserved properties of the contract, not as alarms about safe behaviors.\n\n## Imports and file structure\n\n100% named curly-brace imports. Relative paths within `contracts/`. No wildcards:\n\n```solidity\nimport { IERC20, IERC20Metadata } from '../../interfaces/IERC20.sol';\n```\n\nFile order:\n\n1. `// SPDX-License-Identifier: MIT`\n2. `// OpenZeppelin Contracts (last updated vX.Y.Z) (path/to/file.sol)` — **release-managed; do not add or edit by hand**. `scripts/release/update-comment.js` rewrites this line at release time based on which `contracts/**/*.sol` files changed since the previous tag. New files: write only the SPDX line; the release script appends the second line on first inclusion.\n3. `pragma solidity …`\n4. Imports\n5. Contract-level NatSpec\n6. Contract declaration\n\nLeave a blank line between the SPDX/`last updated` header and `pragma`.\n\nInside the body: `using` → structs/enums → constants → state vars → events → errors → modifiers → constructor → external/public functions → internal → private.\n\nContracts not meant to be deployed on their own (bases, extensions, mixins) are declared `abstract` so they can only be used by inheriting from them.\n\n## Return values\n\nDon't name return values unless their meaning isn't obvious or there are multiple. Prefer `returns (uint256)` over `returns (uint256 amount)`; name them when it aids clarity, e.g. `returns (bool isMember, uint32 currentDelay)`.\n\n## Interfaces\n\n- ERC-defined behavior MUST have an interface, and it SHOULD live in its own file. The name starts with `I`; add the ERC number when there's a risk of clash (`IModule` → `IERC7579Module`).\n- ERC interfaces match the spec as closely as possible (including parameter and return names) and MUST NOT declare anything not in the spec. Non-spec errors, structs, and events go in the implementing contract or library instead.\n- In interfaces, functions are `external` and non-value-type parameters are `calldata` — this is the spec form. Implementations use `public`/`memory` (see the `library-api-design` skill); the two intentionally differ.\n- Only mandatory ERC elements go in the base interface; optional ones go in an extension interface (`name()` is in `IERC20Metadata`, not `IERC20`).\n- Placement: `contracts/interfaces/` for ERC interfaces (`draft-` filename prefix if the ERC is not yet final), next to the implementation otherwise.\n- Interface files SHOULD NOT import anything that is not itself an interface file.\n\n## Pragma\n\nDon't pick the pragma floor by hand. Run:\n\n```bash\nnpm run pragma\n```\n\n`scripts/minimize-pragma.js` builds a dependency graph from compiled artifacts, compiles each file against every candidate `solc`, and writes back the lowest version that compiles for that file and is compatible with all of its dependents. The script picks the syntax for you (`>=` for interfaces, `^` for implementations and libraries) and respects the `minVersionForContracts` floor (default `0.8.20`).\n\nWhen adding a new file, start with the same pragma as a similar existing file in the same directory; the minimizer will tighten it on the next run. When editing a file, run the minimizer if you've used a feature that may have raised the floor.\n\nThe CI `test:pragma` job (`npm run test:pragma`) verifies the floor is honest about the opcodes the contract emits. Raising the floor without checking opcode availability on target chains (`mcopy` from 0.8.24 is the example) is the failure mode this guards against.\n\n## Assembly\n\nAlways annotate `assembly (\"memory-safe\")`. Omitting the annotation disables the memory optimizer and increases gas. Include inline comments for any non-trivial operation.\n\n**Numeric literals** inside assembly:\n\n- **Memory-related** (locations, offsets, lengths): hexadecimal. `mload(0x40)`, `mstore(add(ptr, 0x20), value)`, `keccak256(ptr, 0x55)`.\n- **Bit operations** (shift amounts, bit counts): decimal. `shl(128, value)`, `sar(96, x)`.\n- Trivially small values like `1` or `2` may use decimal even in memory contexts: `ptr := add(ptr, 1)`.\n- In `call`/`staticcall`/`delegatecall`, decimal zero is acceptable when both location and length are zero.\n\n## `unchecked`\n\nOnly use `unchecked` with an inline invariant comment that names the bound. The comment may be omitted only when the reason is immediately apparent from the line directly above:\n\n```solidity\nunchecked {\n    // Overflow not possible: value <= fromBalance <= totalSupply.\n    _balances[from] = fromBalance - value;\n}\n```\n\nWhen reviewing, re-derive the bound for every code path that can reach the block.\n\n## Casting\n\nAny narrowing integer cast (`uint256 → uint48`, etc.) must use `SafeCast.toUintXX()` (or the appropriate `toIntXX`). Direct Solidity casts are only permitted after an explicit bounds check on a line you can point to.\n\n## Inheritance ordering\n\nThe relative order of bases in any `is A, B, C` list must be globally consistent. CI runs `npm run test:inheritance` against compiled artifacts to detect conflicts. After any change to an `is` list:\n\n```bash\nnpm run compile && npm run test:inheritance\n```\n\n## Procedurally generated contracts\n\nIf the file header says `// This file was procedurally generated from …`, do not edit it. Edit the template in `scripts/generate/templates/` and run `npm run generate`. CI runs `npm run test:generation` to enforce this.\n","frontmatter":{"name":"solidity-style","description":"Solidity style and safety conventions for contract source files. Use when writing or editing any .sol file under contracts/. Covers errors, events, NatSpec, imports, file structure, assembly, unchecked, casting, numeric literal formatting, and inheritance ordering."},"isInternal":false,"tokens":2074,"sizeBytes":8875},{"name":"SKILL.md","path":".claude/skills/testing/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/HEAD/.claude/skills/testing/SKILL.md","title":"testing","category":"anthropic-skill","format":"markdown","content":"---\nname: testing\ndescription: Testing conventions for openzeppelin-contracts. Use when writing or modifying tests, mocks, or formal verification specs. Covers hardhat-exposed $-wrappers, when manual mocks are warranted, Hardhat+Chai patterns (loadFixture, shouldBehaveLike, multi-target loops), Foundry fuzz, Halmos symbolic execution, Certora rule-based verification, and the changeset rule.\n---\n\n# Testing\n\n## When to use each approach\n\n| Approach                      | Required when                                                                                               |\n| ----------------------------- | ----------------------------------------------------------------------------------------------------------- |\n| Formal verification (Certora) | Contract includes a state machine or access-control rules complex enough that fuzzing won't cover the cases |\n| Symbolic execution (Halmos)   | A property is expressible as a Foundry test with symbolic inputs — fast, no external credentials            |\n| Fuzzing (Foundry)             | Math-heavy code, data structures, complex invariants not suited for FV                                      |\n| Edge unit cases (Hardhat)     | All contracts: golden path + pathological/border cases. This is the baseline.                               |\n\nDefault to Hardhat+Chai unit tests, add Halmos for invariants you can express symbolically, escalate to Certora when state-machine coverage requires it.\n\nA bug fix must add a minimal test that reproduces the bug, committed alongside the fix. The objective is 100% branch coverage per PR.\n\n## Accessing internal functions: hardhat-exposed\n\n`$`-prefixed wrapper contracts are **auto-generated** by [hardhat-exposed](https://github.com/frangio/hardhat-exposed). The plugin creates a `$ContractName` for every contract in `contracts/`, with every internal `_function` re-exposed as an external `$_function`. Output goes to `contracts-exposed/` (gitignored). Use them directly:\n\n```javascript\nconst token = await ethers.deployContract('$ERC20', ['Name', 'SYM']);\nawait token.$_mint(holder, 1000n);\n```\n\n**Do not write `$`-prefixed wrappers by hand.**\n\n## Manual mocks in `contracts/mocks/`\n\nManual mocks live in `contracts/mocks/` (not `test/`) and mirror the `contracts/` directory layout. Write a manual mock only when `hardhat-exposed` cannot give you what you need:\n\n- **Constructor-level setup** that auto-generation can't provide — e.g. `ERC721ConsecutiveMock` calling `_mintConsecutive` during construction.\n- **Multi-extension composition** to test how extensions compose — e.g. `ERC721Consecutive + ERC721Pausable + ERC721Votes`.\n- **Injected behavior** that overrides a hook to simulate an edge case — e.g. `ERC20Reentrant` firing a reentrant call inside `_update`.\n- **Adversarial behavior** with no analogue in the base — e.g. `ReentrancyAttack`, `ERC20ReturnFalseMock`.\n- **A plain public wrapper** for an internal operation where the auto-generated signature is unsuitable — e.g. `ERC20Mock.mint`.\n\nEven inside a manual mock, prefer the auto-generated `$_` functions over redeclaring the same exposure:\n\n```solidity\n// prefer (reuses auto-generated exposure):\nawait token.$_mint(holder, 1000n);\n\n// avoid (redundant — hardhat-exposed already provides this):\ncontract ERC20Mock is ERC20 {\n    function __mint(address account, uint256 value) external { _mint(account, value); }\n}\n```\n\n## Hardhat + Chai (`.test.js`)\n\n**Fixtures**: use `loadFixture` from `@nomicfoundation/hardhat-network-helpers`. Declare the fixture with a named `function`, not an inline arrow — `loadFixture` keys its cache on the function reference, so an anonymous function (especially an inline `loadFixture(async () => {...})`) re-runs on every test instead of restoring the snapshot.\n\n```javascript\nasync function fixture() {\n  const [holder, recipient] = await ethers.getSigners();\n  const token = await ethers.deployContract('$ERC20', [name, symbol]);\n  await token.$_mint(holder, initialSupply);\n  return { holder, recipient, token };\n}\n\nbeforeEach(async function () {\n  Object.assign(this, await loadFixture(fixture));\n});\n```\n\n**Structure**: `describe('ContractName') > describe('methodName') > it('description')`.\n\n**Shared behavior**: reuse via `shouldBehaveLike*` functions in sibling `.behavior.js` files. Mount with this-context state set in the fixture:\n\n```javascript\nconst { shouldBehaveLikeERC20 } = require('./ERC20.behavior');\n// ...\nshouldBehaveLikeERC20(initialSupply, { forcedApproval });\n```\n\n**Multi-target loops**: when the same suite should run against several wrappers (e.g. the auto-generated `$ERC20` and a manual `$ERC20ApprovalMock`), iterate at the top:\n\n```javascript\nconst TOKENS = [{ Token: '$ERC20' }, { Token: '$ERC20ApprovalMock', forcedApproval: true }];\nfor (const { Token, forcedApproval } of TOKENS) {\n  describe(Token, function () {\n    /* … */\n  });\n}\n```\n\n**Assertion style**:\n\n- `bigint` literals (`100n`) for uint values — not `ethers.BigNumber`.\n- Custom errors: `.revertedWithCustomError(contract, 'ErrorName').withArgs(...)`.\n- Panics: `.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW)`.\n- Events: `.emit(contract, 'EventName').withArgs(...)`.\n- Balance changes: `.changeTokenBalance(token, account, delta)` / `.changeTokenBalances(token, [a, b], [d1, d2])`.\n\n## Foundry (`.t.sol`)\n\nFoundry tests live next to their Hardhat counterparts in `test/`. Under HH3 they are picked up by `npm test` (via the `@nomicfoundation/hardhat-mocha` solidity runner) alongside the JS tests. Standalone `forge test` still works but uses forge defaults for fuzz. Two distinct purposes:\n\n**Fuzzing**: function name starts with `test`, parameters are typed. Foundry generates random inputs.\n\n```solidity\nfunction testFuzzAdd(uint256 a, uint256 b) public pure {\n  vm.assume(a < type(uint256).max - b); // restrict if needed\n  assertEq(Math.add(a, b), a + b);\n}\n```\n\nUse `assertEq`, `assertGt`, etc. from `forge-std/Test.sol`. Fuzz settings live in `hardhat.config.ts` under `test.solidity.fuzz` (`runs = 5000`, `maxTestRejects = 150000`) and are applied when running via `npm test`. Standalone `forge test` uses forge defaults.\n\n**Symbolic execution (Halmos)**: function name starts with `symbolic` or `testSymbolic` — that's what the CI `halmos` job matches (`--match-test '^symbolic|^testSymbolic'`). These run as regular Foundry fuzz tests as well.\n\n```solidity\nfunction testSymbolicTransfer(address to, uint256 amount) public {\n  // Halmos treats inputs as fully symbolic (all values).\n  // Use vm.assume(...) for preconditions.\n}\n```\n\nHalmos runs on every PR, needs no credentials. Reach for it when a property can be expressed as a Foundry test.\n\n**Pragma note**: Foundry compiles with `solc 0.8.31` (`foundry.toml`); contract pragmas in `contracts/` may still be `^0.8.20`. New `.t.sol` files should match the contract's pragma minimum unless they specifically need a newer feature.\n\n## Certora (rule-based formal verification)\n\n[Certora](https://certora.com) specs live in `fv/specs/` as `.conf` + `.spec` pairs. CI runs one job per affected config, on any PR whose diff reaches a spec, a harness, a contract one of them verifies, or an `fv/diff` patch applied to one -- no label needed. Label a PR `formal-verification-force-all` to run every config instead. Requires a `CERTORAKEY`, and the `certora` environment gates the job behind a manual approval. Selection runs the base branch's copy of `scripts/list-dependencies.js` over the PR's files, so a PR cannot change which configs it is checked against. The matrix job names carry their config, so `verified` is the aggregate job to require in branch protection. Deleting a `.conf` fails selection, since a config that no longer exists cannot be selected and its verification would just disappear; label the PR `formal-verification-allow-removal` when that is intended.\n\nLocal workflow:\n\n```bash\nmake -C fv apply           # apply harness patches first\nnode fv/run.js AccessControl   # runs fv/specs/AccessControl.conf\nnode fv/run.js --all           # runs every spec\n```\n\nUse Certora when a property is too complex for Halmos, or when a state machine needs deeper rule coverage than fuzzing provides.\n\n## Changesets\n\nEvery PR that changes contract behavior needs a changeset. Skip for NatSpec-only edits, internal refactors with no user-visible effect, or pure repo plumbing.\n\n```bash\nnpx changeset add\n```\n\nThe generated file in `.changeset/` looks like:\n\n```\n---\n'openzeppelin-solidity': minor\n---\n\n`ComponentName`: One-sentence description starting with a backtick-quoted contract or component name.\n```\n\n- `patch` — bug fixes.\n- `minor` — new features.\n- One sentence. No bullet points, no multi-paragraph prose. The release pipeline composes these into `CHANGELOG.md` automatically.\n\n## CI matrix you should keep in mind\n\n| Job                   | What it runs                                                        | Triggered by                                             |\n| --------------------- | ------------------------------------------------------------------- | -------------------------------------------------------- |\n| `lint`                | `npm run lint`                                                      | Every push/PR                                            |\n| `tests`               | `npm test` (JS + Foundry via HH3) + inheritance, pragma, generation | Every push/PR                                            |\n| `tests-upgradeable`   | Transpile then re-run the test suite + storage-layout diff          | Every push/PR                                            |\n| `coverage`            | `npm run coverage` → codecov                                        | Every push/PR                                            |\n| `slither`             | Slither static analysis for common vulnerabilities                  | Every push/PR                                            |\n| `halmos`              | `halmos --match-test '^symbolic\\|^testSymbolic'`                    | Every push/PR                                            |\n| `formal verification` | Certora, one job per affected config (or all, with the force label) | PRs touching a spec, harness, patch or verified contract |\n","frontmatter":{"name":"testing","description":"Testing conventions for openzeppelin-contracts. Use when writing or modifying tests, mocks, or formal verification specs. Covers hardhat-exposed $-wrappers, when manual mocks are warranted, Hardhat+Chai patterns (loadFixture, shouldBehaveLike, multi-target loops), Foundry fuzz, Halmos symbolic execution, Certora rule-based verification, and the changeset rule."},"isInternal":false,"tokens":2305,"sizeBytes":10237},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# openzeppelin-contracts — Claude project guide\n\nThis repository is a **Solidity smart contract library**: reusable `abstract contract` and `library` implementations meant to be inherited and extended by downstream projects. It is not a deployed product. Most conventions exist because code here is a base for others: functions must be overridable, argument types must not constrain inheritors, and the public API must not force a particular call context on child contracts.\n\nRead [`GUIDELINES.md`](./GUIDELINES.md) first — it is the human contributor spec and authoritative. This file is for what is specific to Claude-assisted work, plus the conventions not fully documented elsewhere.\n\nBefore preparing a contribution, also read [`CONTRIBUTING.md`](./CONTRIBUTING.md) thoroughly. Non-trivial changes must be discussed in an issue before a PR is opened.\n\n## Repo map\n\n| Path                    | Purpose                                                                                                                                        |\n| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |\n| `contracts/`            | Library source. Subdirectory layout (`token/ERC20/`, `access/`, `utils/`, …) mirrors the public API.                                           |\n| `contracts/mocks/`      | Mocks used by tests. Mirrors `contracts/`. Manual mocks only — see the `testing` skill.                                                        |\n| `contracts/interfaces/` | ERC interfaces in their pristine spec form.                                                                                                    |\n| `test/`                 | Hardhat + Chai tests (`.test.js`), shared behaviors (`.behavior.js`), and Foundry tests (`.t.sol`).                                            |\n| `contracts-exposed/`    | Auto-generated `$ContractName` wrappers from `hardhat-exposed`. Gitignored. Do not edit.                                                       |\n| `fv/`                   | Certora specs (`fv/specs/*.{conf,spec}`), harnesses, and `make`-applied patches.                                                               |\n| `scripts/generate/`     | Procedurally generated `.sol` files (Arrays variants, Checkpoints, EnumerableSet, …). Source of truth is the template, not the generated file. |\n| `scripts/checks/`       | CI checks: `inheritance-ordering.js`, `pragma-validity.js`, generation diff, storage layout.                                                   |\n| `docs/`                 | AsciiDoc sources used by the OZ documentation site. Per-module `README.adoc` files in `contracts/**/` are also rendered.                       |\n| `.changeset/`           | Per-PR changelog entries consumed by the release workflow.                                                                                     |\n| `audits/`               | Historical third-party audit reports, one per release. Reference only.                                                                         |\n| `.claude/skills/`       | Skills specific to working in this repository, loaded by AI assistants per task. See `CONTRIBUTING.md`.                                        |\n\n## Commands\n\n| Task                                     | Command                                                                 |\n| ---------------------------------------- | ----------------------------------------------------------------------- |\n| Compile                                  | `npm run compile`                                                       |\n| Tests (JS + Solidity)                    | `npm test`                                                              |\n| Foundry tests only (forge defaults)      | `forge test -vvv`                                                       |\n| Halmos symbolic tests                    | `halmos --match-test '^symbolic\\|^testSymbolic' -vv`                    |\n| Lint (JS + Sol)                          | `npm run lint` / `npm run lint:fix`                                     |\n| Coverage                                 | `npm run coverage`                                                      |\n| Inheritance order check                  | `npm run test:inheritance`                                              |\n| Pragma validity check                    | `npm run test:pragma`                                                   |\n| Generated-file freshness check           | `npm run test:generation`                                               |\n| Regenerate procedural contracts          | `npm run generate`                                                      |\n| Add a changelog entry                    | `npx changeset add`                                                     |\n| Run a single Certora spec                | `node fv/run.js <SpecName>` (apply harnesses first: `make -C fv apply`) |\n\n## What linting via solhint already enforces (don't re-state in code review)\n\n`solhint-plugin-openzeppelin` + `scripts/solhint-custom/index.js` enforce, on `contracts/**/*.sol` (mocks and tests are exempt):\n\n- **State variables are `private`** (`constant`/`immutable` are the only exceptions).\n- **Underscore prefix matches visibility**: `private`/`internal` state vars and functions get `_`; `public`/`external` do not. Library internal functions do NOT get `_`.\n- **No `external virtual`** (except fallback). The `public` default makes this redundant; flagged by `no-external-virtual`.\n- **Libraries expose only `internal`/`private`**.\n- **Interfaces start with `I`**, contracts use CapWords, events use CapWords, modifiers/params mixedCase.\n\nIf a lint rule already catches it, don't waste tokens explaining it — fix and move on.\n\n## Always-on conventions\n\nThese apply to every change and are not lint-enforced:\n\n- **File header version line**: don't add or edit the `// OpenZeppelin Contracts (last updated vX.Y.Z) (path)` line — release tooling (`scripts/release/update-comment.js`) maintains it automatically based on `git diff` between the previous tag and `HEAD`. The SPDX line is your only responsibility.\n- **Pragma**: don't pick the floor by hand. Run `npm run pragma` and let `scripts/minimize-pragma.js` walk the dependency graph, compile against every candidate `solc`, and write back the lowest version that works for each file (`>=` prefix for interfaces, `^` for implementations and libraries). Bumping the floor casually can introduce opcodes (`mcopy` in 0.8.24, etc.) not supported on all target chains — the script's output is the safe answer.\n- **Imports**: 100% named, curly-brace, relative paths within `contracts/`. No wildcards.\n- **Inheritance order is globally consistent**: if you change an `is A, B, C` list, run `npm run compile && npm run test:inheritance` before pushing.\n- **Changesets**: every PR that changes contract behavior needs one. Skip for NatSpec-only, internal refactors with no user-visible effect, or pure repo plumbing.\n- **Procedurally generated files**: never hand-edit a file whose header says \"procedurally generated from `<template>`\". Edit the template under `scripts/generate/templates/` and run `npm run generate`. CI runs `test:generation` to enforce.\n- **Backward compatibility**: released contracts are inherited downstream and transpiled into the `-upgradeable` package. Don't change existing `public`/`external` signatures, event or error shapes, or the storage layout of existing contracts. Changes are additive; deprecate rather than delete. Breaking changes are a maintainer decision tied to a major release.\n- **Dependencies**: this library has no third-party dependencies that translate to the user, and nothing that could change behavior out from under it. Don't add an npm or Solidity dependency or a new external import without maintainer sign-off; reuse existing `contracts/utils/` helpers first. Code that needs third-party dependencies belongs in `openzeppelin-community-contracts`, not here.\n\n## When to load which skill\n\nClaude Code surfaces these from `.claude/skills/` based on the task. You can also reference them directly:\n\n- **`library-api-design`** — when adding or modifying any contract in `contracts/` (visibility, `virtual`, `memory` vs `calldata`, `_update` pattern, internal/external split, no-ops over reverts, hooks).\n- **`solidity-style`** — when writing or editing Solidity (errors, events, NatSpec, ERC-7201, assembly, `unchecked`, casting, immutables).\n- **`testing`** — when writing tests or mocks (`hardhat-exposed` `$` wrappers, when manual mocks are warranted, `.behavior.js` reuse, multi-target test loops, Foundry fuzz, Halmos symbolic, Certora).\n- **`add-changeset`** — at PR close, or when the user asks for one.\n\n## Things to always verify manually\n\nAI suggestions in these areas are often plausible-but-wrong. Read the surrounding code before accepting:\n\n- **Cryptography** — anything in `contracts/utils/cryptography/`, hashing, ECDSA, EIP-712 domains, BLS, signatures.\n- **ERC-7201 slot derivations** — recompute with `SlotDerivation.erc7201Slot()` and compare to the constant. The formula in the inline comment is mandatory.\n- **Storage layout for transpiler-processed contracts** — adding/removing/reordering fields breaks proxied deployments. CI runs storage-layout diff but read it.\n- **`delegatecall`, inline assembly, memory safety** — verify the `memory-safe` annotation is honest and stack/scratch usage is bounded.\n- **`unchecked` invariants** — re-derive the overflow-impossible claim for every code path. The inline comment is the contract.\n- **`_authorizeUpgrade` access control** — the base is empty by design; every override must restrict.\n- **`_disableInitializers()`** in implementation constructors for UUPS/Transparent proxy targets.\n- **Pragma bumps** — check that any new opcode the compiler will emit is supported on all target chains.\n\n## Out of scope for AI\n\n- Adding/renaming public API or deciding whether a feature belongs in the library — open an issue first.\n- Security assessments or audit opinions.\n- New ERC interfaces or standards without explicit direction.\n- Restructuring tests without a corresponding contract change.\n- Fabricated bug bounties and security rewards. Never claim or imply a reward, amount, or eligibility. The only existing program is [Immunefi](https://immunefi.com/bounty/openzeppelin) (See [`SECURITY.md`](./SECURITY.md)).\n","isInternal":false,"tokens":2085,"sizeBytes":10493}],"systemPromptSnippet":"<yakaai_skills repo=\"OpenZeppelin/openzeppelin-contracts\">\n<!-- File: .claude/skills/add-changeset/SKILL.md (Tokens: ~769 | Category: anthropic-skill) -->\n---\nname: add-changeset\ndescription: Add a changelog entry for a PR. Use when the user asks for a changeset, when finishing a PR that changes user-visible contract behavior, or when the changeset-bot has flagged the PR. Skip for NatSpec-only edits, internal refactors with no observable effect, or repo plumbing.\n---\n\n# Add a changeset\n\n## When to add one\n\nAdd a changeset when the PR changes **user-visible contract behavior**: a new function, a new contract or extension, a bug fix that changes outputs, a new event, a new error, a new interface ID, or a tighter requirement.\n\n**Skip** for:\n\n- NatSpec-only changes.\n- Internal refactors with no observable effect.\n- Test/mock-only changes.\n- CI, lint, docgen, dependency bumps that don't change the published `contracts/`.\n\nIf unsure, err on the side of adding one — release reviewers can downgrade to \"skip\" by deleting the file before tagging.\n\n## How\n\n```bash\nnpx changeset add\n```\n\nThe CLI prompts for severity, then opens an editor. Severity:\n\n- `patch` — bug fixes, gas optimizations, no API surface change.\n- `minor` — new contracts, new extensions, new public functions on existing contracts.\n\nThere is no `major` from contributors — major version bumps are coordinated by maintainers.\n\nThe resulting file in `.changeset/` looks like:\n\n```\n---\n'openzeppelin-solidity': minor\n---\n\n`ComponentName`: One-sentence description starting with a backtick-quoted component name.\n```\n\n## Format rules\n\n- **First word inside the backticks** is the affected contract, extension, or library: `ERC20`, `ERC1155Crosschain`, `Memory`, `Governor`, etc.\n- **Then a colon, then one sentence** describing the user-visible change.\n- **Imperative mood** (matching PR titles: \"Add\", \"Fix\", not \"Added\", \"Fixed\"). Trailing period optional.\n- **No bullet points**, no headings, no multi-paragraph prose. The release workflow concatenates these verbatim.\n- **State the change, don't justify it.** Skip \"This mirrors ...\", \"so that ...\", \"closes a gap where ...\", parallels to other functions, or motivation. If the user needs to take action (migration step, breaking rename, tighter requirement they might now hit), state that instead — otherwise the changelog line is just the fact of the change. Rationale belongs in the PR body and commit message, not the changelog.\n\nGood examples (taken from existing entries):\n\n```\n`Memory`: Add a `isReserved(Slice)` function that checks if the memory occupied by the slice is reserved (i.e. before the free memory pointer).\n```\n\n```\n`ERC1155Crosschain`: Add an ERC-1155 extension to embed an ERC-7786 based crosschain bridge directly in the token contract.\n```\n\n```\nAdd ERC-165 detection for the `IERC6909ContentURI`, `IERC6909TokenSupply` and `IERC6909Metadata` interfaces in the `ERC6909ContentURI`, `ERC6909TokenSupply` and `ERC6909Metadata` contracts respectively.\n```\n\n(The last form — no leading backticks — is acceptable when several components are touched at once and a single prefix would be misleading.)\n\n## After committing\n\nThe `changeset-bot` GitHub check posts a comment confirming the changeset was detected. If the bot complains \"No Changeset found\" and the PR should not have one (NatSpec-only, etc.), leave a comment justifying it — a maintainer can apply the `ignore-changeset` label.\n\n<!-- File: .claude/skills/library-api-design/SKILL.md (Tokens: ~2066 | Category: anthropic-skill) -->\n---\nname: library-api-design\ndescription: API design rules for openzeppelin-contracts. Use when adding, modifying, or reviewing any contract under contracts/. Covers virtual, memory vs calldata, public vs external, overloads, no-op vs revert, _update single-override pattern, internal/external split, _msgSender, and override points.\n---\n\n# Library API design\n\nThis repo is a base library: every contract is inherited downstream. The rules below preserve that inheritability. Most of them are not lint-enforced — they require judgment when adding new contracts.\n\n## `virtual` on overridable functions\n\nMark every `public` and `internal` function `virtual` — with two specific exceptions:\n\n**Alias functions** (one function that just forwards to another with no added logic) are NOT virtual. The canonical form is the virtual one. Add a NatSpec `NOTE:` pointing to it:\n\n```solidity\n/**\n * @dev See {IERC20-transfer}.\n *\n * NOTE: This function is not virtual, {_update} should be overridden instead.\n */\nfunction _transfer(address from, address to, uint256 value) internal {\n  // ...\n  _update(from, to, value);\n}\n```\n\n**Convenience overloads** that only fill in a default argument are NOT virtual; the full-argument canonical form is:\n\n```solidity\n// not virtual (only sets emitEvent = true):\nfunction _approve(address owner, address spender, uint256 value) internal {\n    _approve(owner, spender, value, true);\n}\n\n// virtual (real override point):\nfunction _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { ... }\n```\n\nSolhint's `no-external-virtual` rule catches `external virtual` (fallback excepted) — the right fix is `public virtual`, not silencing the rule. The reason `external` can't be the override point: an override of an `external` function cannot call `super`, so a function designed to be overridden must be `public`.\n\n## `memory` for reference-type arguments\n\nUse `memory` for `string`, `bytes`, arrays, and structs — including in `public` functions. `calldata` forces every inheritor to match, so it is only valid in three narrow cases:\n\n1. The return value is a slice of `msg.data` (e.g. `_msgData()` in `Context.sol`).\n2. An internal calldata-decoding helper that is genuinely scoped to one function (e.g. `_extractSignatureValidator(bytes calldata signature) internal pure returns (address module, bytes calldata innerSignature)` in `AccountERC7579.sol`).\n3. An explicit `Calldata`-suffixed parallel variant in a utility library, where the base version still uses `memory` (e.g. `MerkleProof.verifyCalldata`).\n\n## `public` vs `external`\n\nDefault to `public`. Use `external` only when:\n\n- The ERC spec itself defines the function as `external` (e.g. `ERC20Permit.DOMAIN_SEPARATOR`, `UUPSUpgradeable.proxiableUUID`).\n- The contract design explicitly prevents internal calls and the function must only be reachable from outside (e.g. `ERC7786Recipient.receiveMessage`, where only an authorized gateway should call it and meta-transactions don't apply).\n\n`external` for the second reason needs a NatSpec `NOTE:` explaining why.\n\n## The internal/external split\n\n`public` functions exist only to (1) capture `_msgSender()` and (2) delegate to an `internal` function that takes the actor as an explicit argument. All logic lives in the internal variant:\n\n```solidity\nfunction transfer(address to, uint256 value) public virtual returns (bool) {\n  address owner = _msgSender();\n  _transfer(owner, to, value);\n  return true;\n}\n```\n\nThis keeps internal functions composable, testable through `hardhat-exposed`, and independently overridable.\n\n## `_update` single override point\n\nFor state machines with several entry points (mint, burn, transfer), make `_mint`/`_burn`/`_transfer` non-virtual aliases that delegate to a single `virtual _update(...)`. Extensions override `_update` only, never the individual operations, and always call `super._update(...)` before adding side effects:\n\n```solidity\nfunction _update(address from, address to, uint256 value) internal virtual override {\n  super._update(from, to, value); // base logic first\n  _transferVotingUnits(from, to, value); // side effects after\n}\n```\n\n## Prefer no-ops over reverts\n\nIf an operation can do nothing without creating a security concern, it should do nothing. This shows up at three levels:\n\n**Vacuous inputs**: skip silently rather than reverting. `batchSize == 0` skips the whole mint loop; `ERC721Utils.checkOnERC721Received` skips the callback for EOAs because there is nothing to check. Document the no-op condition with a short inline comment.\n\n**Idempotent state transitions**: return a `bool` from operations that may already be at the target state, instead of reverting. The caller can add a revert; the base never takes that choice away:\n\n```solidity\nfunction _revokeRole(bytes32 role, address account) internal virtual returns (bool) {\n  if (hasRole(role, account)) {\n    _roles[role].hasRole[account] = false;\n    emit RoleRevoked(role, account, _msgSender());\n    return true;\n  }\n  return false; // already revoked — no-op, not revert\n}\n```\n\n**Empty virtual hooks**: `internal virtual {}` is a valid extension point when no default behavior exists. Always `_camelCase`, always `internal virtual`, always documented with `@dev`:\n\n```solidity\n/// @dev Hook called every time the tally for a proposal is updated.\nfunction _tallyUpdated(uint256 proposalId) internal virtual {}\n```\n\n**Rule of thumb**: overrides can always add requirements on top of a no-op base. They cannot remove a requirement the base already enforces.\n\n## `_msgSender()` vs `msg.sender`\n\nUse `_msgSender()` in application logic. `msg.sender` appears only inside `Context.sol` / `ERC2771Context.sol`, or in deliberate protocol-level exceptions (e.g. `ERC7786Recipient.receiveMessage`, where wrapping the sender would break the protocol). Every exception outside the Context layer needs a `NOTE:` explaining why.\n\n## `super` and extension contracts\n\n`super` must be called in `_update` overrides, in `supportsInterface` overrides, and whenever an extension builds on base behavior. Skipping `super` is only acceptable for genuinely new functions that have no base implementation.\n\n`supportsInterface` always follows the pattern:\n\n```solidity\ninterfaceId == type(X).interfaceId || super.supportsInterface(interfaceId)\n```\n\nWhen overriding from multiple bases, list all of them: `override(A, B)`.\n\n## Constructors, immutables, and the transpiler\n\n| Keyword     | When                                                       | Naming                               |\n| ----------- | ---------------------------------------------------------- | ------------------------------------ |\n| `constant`  | Compile-time known (type hashes, role ids, slot addresses) | `ALL_CAPS_WITH_UNDERSCORES`          |\n| `immutable` | Constructor-set, deploy-time computed                      | `_camelCase` (same as private state) |\n\n`/// @custom:oz-upgrades-unsafe-allow state-variable-immutable` is not a \"this value is too dangerous to change\" tag — every immutable can still be changed by upgrading to a new implementation. The tag controls **per-implementation vs per-proxy**:\n\n- **Tag present**: the value stays `immutable` in the upgradeable variant. It is set in the upgradeable contract's constructor and shared by every proxy that points at that implementation. Changing it requires a new implementation.\n- **Tag absent**: the transpiler converts the field to a regular storage slot and moves initialization into the initializer. Each proxy gets its own value while sharing one implementation.\n\nPick the tag when the value is logically a property of the implementation (e.g. `ERC2771Context._trustedForwarder` — the trust relationship belongs to the deployed code, not each proxy). Omit it when each deployment should configure its own value (e.g. `GovernorVotes._token`).\n\n## ERC-7201 namespaced storage\n\nERC-7201 is opt-in, not the default. Upgradeable variants of OZ contracts are auto-generated by the [OpenZeppelin Transpiler](https://github.com/OpenZeppelin/openzeppelin-transpiler) — don't preemptively add ERC-7201 storage layouts to new contracts.\n\nReach for it only when namespacing is genuinely necessary, such as `Initializable.sol` (sequential slot 0 would be risky for proxy implementations). New uses must justify themselves in NatSpec. When it is the right tool:\n\n```solidity\n/// @custom:storage-location erc7201:openzeppelin.storage.MyContract\nstruct MyContractStorage {\n    uint256 _value;\n}\n\n// keccak256(abi.encode(uint256(keccak256(\"openzeppelin.storage.MyContract\")) - 1)) & ~bytes32(uint256(0xff))\nbytes32 private constant MY_CONTRACT_STORAGE = 0x...;\n\n// solhint-disable-next-line var-name-mixedcase\nfunction _getMyContractStorage() private pure returns (MyContractStorage storage $) {\n    assembly {\n        $.slot := MY_CONTRACT_STORAGE\n    }\n}\n```\n\nRules: struct annotated with `@custom:storage-location`; inline formula comment above the constant; pointer variable always named `$`; `solhint-disable-next-line var-name-mixedcase` immediately before any function that uses `$`; constant in `ALL_CAPS`. Always verify the slot value with `SlotDerivation.erc7201Slot()` — never compute by hand.\n\n<!-- File: .claude/skills/solidity-style/SKILL.md (Tokens: ~2074 | Category: anthropic-skill) -->\n---\nname: solidity-style\ndescription: Solidity style and safety conventions for contract source files. Use when writing or editing any .sol file under contracts/. Covers errors, events, NatSpec, imports, file structure, assembly, unchecked, casting, numeric literal formatting, and inheritance ordering.\n---\n\n# Solidity style and safety\n\nThe conventions below are not enforced by solhint. For the lint-enforced rules (private state vars, underscore prefixes, library visibility, etc.), see the project root `CLAUDE.md`.\n\n## Errors\n\nPrefer `require(condition, CustomError(args));` in files whose pragma is `^0.8.27` or later (the first version supporting it in the legacy pipeline). Keep `if (!condition) revert CustomError(args);` when:\n\n- The file's pragma is lower. Do not bump pragma just to use `require`.\n- An error argument is anything other than a variable, constant, literal or cast of those. `require` evaluates its arguments even when the condition holds, so a function call, storage or member read costs gas on the success path, and checked arithmetic (e.g. `revert Exceeded(value - max)`) can panic instead of passing.\n- The check is part of an `if`/`else if` chain.\n\nWhen a condition compares against a literal or constant bound, prefer the strict operator: `require(x < 0x5fd4, ...)` rather than `require(x <= 0x5fd3, ...)`. The EVM has `LT`/`GT` but no `LTE`/`GTE`, so `<=` compiles to `ISZERO(GT(...))`. Don't rewrite a comparison against a variable bound this way: `x < max + 1` adds arithmetic that overflows at `type(uint256).max`.\n\nCustom errors follow [ERC-6093](https://eips.ethereum.org/EIPS/eip-6093):\n\n- **Domain prefix**: `ERC<number>` if the error violates an ERC spec; otherwise the component name (`Governor`, `ECDSA`, `Timelock`).\n- **Include offending values** as arguments so callers can decode the failure context.\n- **Never declare the same error name twice** across the library — duplicates cause identifier conflicts when inheriting from multiple contracts.\n\n**Location priority**:\n\n1. Reuse the error from the underlying ERC if already defined there.\n2. Otherwise declare in the interface or library that owns the concept.\n3. Otherwise declare in the implementation (when the interface/library is already pinned by an ERC).\n4. Otherwise declare in the extension where it actually fires.\n\n## Events\n\nEmit **after** the state change. Past-tense CapWords names (`OwnershipTransferred`, `RoleGranted`). ERC-standard events use the tense specified in the ERC (e.g. ERC-20's `Transfer` is present tense — follow the spec). Declare events in the interface or at the top of the emitting contract.\n\n## NatSpec\n\n`@dev` is the only tag rendered by the OZ documentation engine. Use it as the primary tag everywhere. `@notice`, `@param`, `@return` appear in interface files where ERC specs are kept verbatim — do not add `@notice` to implementation contracts; it will not appear in generated docs.\n\n- Single-line: `/// @dev …`\n- Multi-line: `/** @dev … */`\n- Document public and internal functions, events, errors, and constructors. Private functions are optional — they don't appear on the docs site.\n\n**Overrides**:\n\n- Pure relay with nothing to add: `/// @inheritdoc InterfaceName`\n- Override with added requirements or caveats: `/** @dev See {Interface-functionName}. \\n\\n Requirements: … */`\n\n**Cross-references**: `{ContractName-functionName}` — e.g. `{IERC20-transfer}`, `{_update}`.\n\n**Section headers** inside `@dev` blocks: `Requirements:`, `NOTE:`, `WARNING:`, `IMPORTANT:`. Frame caveats as conserved properties of the contract, not as alarms about safe behaviors.\n\n## Imports and file structure\n\n100% named curly-brace imports. Relative paths within `contracts/`. No wildcards:\n\n```solidity\nimport { IERC20, IERC20Metadata } from '../../interfaces/IERC20.sol';\n```\n\nFile order:\n\n1. `// SPDX-License-Identifier: MIT`\n2. `// OpenZeppelin Contracts (last updated vX.Y.Z) (path/to/file.sol)` — **release-managed; do not add or edit by hand**. `scripts/release/update-comment.js` rewrites this line at release time based on which `contracts/**/*.sol` files changed since the previous tag. New files: write only the SPDX line; the release script appends the second line on first inclusion.\n3. `pragma solidity …`\n4. Imports\n5. Contract-level NatSpec\n6. Contract declaration\n\nLeave a blank line between the SPDX/`last updated` header and `pragma`.\n\nInside the body: `using` → structs/enums → constants → state vars → events → errors → modifiers → constructor → external/public functions → internal → private.\n\nContracts not meant to be deployed on their own (bases, extensions, mixins) are declared `abstract` so they can only be used by inheriting from them.\n\n## Return values\n\nDon't name return values unless their meaning isn't obvious or there are multiple. Prefer `returns (uint256)` over `returns (uint256 amount)`; name them when it aids clarity, e.g. `returns (bool isMember, uint32 currentDelay)`.\n\n## Interfaces\n\n- ERC-defined behavior MUST have an interface, and it SHOULD live in its own file. The name starts with `I`; add the ERC number when there's a risk of clash (`IModule` → `IERC7579Module`).\n- ERC interfaces match the spec as closely as possible (including parameter and return names) and MUST NOT declare anything not in the spec. Non-spec errors, structs, and events go in the implementing contract or library instead.\n- In interfaces, functions are `external` and non-value-type parameters are `calldata` — this is the spec form. Implementations use `public`/`memory` (see the `library-api-design` skill); the two intentionally differ.\n- Only mandatory ERC elements go in the base interface; optional ones go in an extension interface (`name()` is in `IERC20Metadata`, not `IERC20`).\n- Placement: `contracts/interfaces/` for ERC interfaces (`draft-` filename prefix if the ERC is not yet final), next to the implementation otherwise.\n- Interface files SHOULD NOT import anything that is not itself an interface file.\n\n## Pragma\n\nDon't pick the pragma floor by hand. Run:\n\n```bash\nnpm run pragma\n```\n\n`scripts/minimize-pragma.js` builds a dependency graph from compiled artifacts, compiles each file against every candidate `solc`, and writes back the lowest version that compiles for that file and is compatible with all of its dependents. The script picks the syntax for you (`>=` for interfaces, `^` for implementations and libraries) and respects the `minVersionForContracts` floor (default `0.8.20`).\n\nWhen adding a new file, start with the same pragma as a similar existing file in the same directory; the minimizer will tighten it on the next run. When editing a file, run the minimizer if you've used a feature that may have raised the floor.\n\nThe CI `test:pragma` job (`npm run test:pragma`) verifies the floor is honest about the opcodes the contract emits. Raising the floor without checking opcode availability on target chains (`mcopy` from 0.8.24 is the example) is the failure mode this guards against.\n\n## Assembly\n\nAlways annotate `assembly (\"memory-safe\")`. Omitting the annotation disables the memory optimizer and increases gas. Include inline comments for any non-trivial operation.\n\n**Numeric literals** inside assembly:\n\n- **Memory-related** (locations, offsets, lengths): hexadecimal. `mload(0x40)`, `mstore(add(ptr, 0x20), value)`, `keccak256(ptr, 0x55)`.\n- **Bit operations** (shift amounts, bit counts): decimal. `shl(128, value)`, `sar(96, x)`.\n- Trivially small values like `1` or `2` may use decimal even in memory contexts: `ptr := add(ptr, 1)`.\n- In `call`/`staticcall`/`delegatecall`, decimal zero is acceptable when both location and length are zero.\n\n## `unchecked`\n\nOnly use `unchecked` with an inline invariant comment that names the bound. The comment may be omitted only when the reason is immediately apparent from the line directly above:\n\n```solidity\nunchecked {\n    // Overflow not possible: value <= fromBalance <= totalSupply.\n    _balances[from] = fromBalance - value;\n}\n```\n\nWhen reviewing, re-derive the bound for every code path that can reach the block.\n\n## Casting\n\nAny narrowing integer cast (`uint256 → uint48`, etc.) must use `SafeCast.toUintXX()` (or the appropriate `toIntXX`). Direct Solidity casts are only permitted after an explicit bounds check on a line you can point to.\n\n## Inheritance ordering\n\nThe relative order of bases in any `is A, B, C` list must be globally consistent. CI runs `npm run test:inheritance` against compiled artifacts to detect conflicts. After any change to an `is` list:\n\n```bash\nnpm run compile && npm run test:inheritance\n```\n\n## Procedurally generated contracts\n\nIf the file header says `// This file was procedurally generated from …`, do not edit it. Edit the template in `scripts/generate/templates/` and run `npm run generate`. CI runs `npm run test:generation` to enforce this.\n\n<!-- File: .claude/skills/testing/SKILL.md (Tokens: ~2305 | Category: anthropic-skill) -->\n---\nname: testing\ndescription: Testing conventions for openzeppelin-contracts. Use when writing or modifying tests, mocks, or formal verification specs. Covers hardhat-exposed $-wrappers, when manual mocks are warranted, Hardhat+Chai patterns (loadFixture, shouldBehaveLike, multi-target loops), Foundry fuzz, Halmos symbolic execution, Certora rule-based verification, and the changeset rule.\n---\n\n# Testing\n\n## When to use each approach\n\n| Approach                      | Required when                                                                                               |\n| ----------------------------- | ----------------------------------------------------------------------------------------------------------- |\n| Formal verification (Certora) | Contract includes a state machine or access-control rules complex enough that fuzzing won't cover the cases |\n| Symbolic execution (Halmos)   | A property is expressible as a Foundry test with symbolic inputs — fast, no external credentials            |\n| Fuzzing (Foundry)             | Math-heavy code, data structures, complex invariants not suited for FV                                      |\n| Edge unit cases (Hardhat)     | All contracts: golden path + pathological/border cases. This is the baseline.                               |\n\nDefault to Hardhat+Chai unit tests, add Halmos for invariants you can express symbolically, escalate to Certora when state-machine coverage requires it.\n\nA bug fix must add a minimal test that reproduces the bug, committed alongside the fix. The objective is 100% branch coverage per PR.\n\n## Accessing internal functions: hardhat-exposed\n\n`$`-prefixed wrapper contracts are **auto-generated** by [hardhat-exposed](https://github.com/frangio/hardhat-exposed). The plugin creates a `$ContractName` for every contract in `contracts/`, with every internal `_function` re-exposed as an external `$_function`. Output goes to `contracts-exposed/` (gitignored). Use them directly:\n\n```javascript\nconst token = await ethers.deployContract('$ERC20', ['Name', 'SYM']);\nawait token.$_mint(holder, 1000n);\n```\n\n**Do not write `$`-prefixed wrappers by hand.**\n\n## Manual mocks in `contracts/mocks/`\n\nManual mocks live in `contracts/mocks/` (not `test/`) and mirror the `contracts/` directory layout. Write a manual mock only when `hardhat-exposed` cannot give you what you need:\n\n- **Constructor-level setup** that auto-generation can't provide — e.g. `ERC721ConsecutiveMock` calling `_mintConsecutive` during construction.\n- **Multi-extension composition** to test how extensions compose — e.g. `ERC721Consecutive + ERC721Pausable + ERC721Votes`.\n- **Injected behavior** that overrides a hook to simulate an edge case — e.g. `ERC20Reentrant` firing a reentrant call inside `_update`.\n- **Adversarial behavior** with no analogue in the base — e.g. `ReentrancyAttack`, `ERC20ReturnFalseMock`.\n- **A plain public wrapper** for an internal operation where the auto-generated signature is unsuitable — e.g. `ERC20Mock.mint`.\n\nEven inside a manual mock, prefer the auto-generated `$_` functions over redeclaring the same exposure:\n\n```solidity\n// prefer (reuses auto-generated exposure):\nawait token.$_mint(holder, 1000n);\n\n// avoid (redundant — hardhat-exposed already provides this):\ncontract ERC20Mock is ERC20 {\n    function __mint(address account, uint256 value) external { _mint(account, value); }\n}\n```\n\n## Hardhat + Chai (`.test.js`)\n\n**Fixtures**: use `loadFixture` from `@nomicfoundation/hardhat-network-helpers`. Declare the fixture with a named `function`, not an inline arrow — `loadFixture` keys its cache on the function reference, so an anonymous function (especially an inline `loadFixture(async () => {...})`) re-runs on every test instead of restoring the snapshot.\n\n```javascript\nasync function fixture() {\n  const [holder, recipient] = await ethers.getSigners();\n  const token = await ethers.deployContract('$ERC20', [name, symbol]);\n  await token.$_mint(holder, initialSupply);\n  return { holder, recipient, token };\n}\n\nbeforeEach(async function () {\n  Object.assign(this, await loadFixture(fixture));\n});\n```\n\n**Structure**: `describe('ContractName') > describe('methodName') > it('description')`.\n\n**Shared behavior**: reuse via `shouldBehaveLike*` functions in sibling `.behavior.js` files. Mount with this-context state set in the fixture:\n\n```javascript\nconst { shouldBehaveLikeERC20 } = require('./ERC20.behavior');\n// ...\nshouldBehaveLikeERC20(initialSupply, { forcedApproval });\n```\n\n**Multi-target loops**: when the same suite should run against several wrappers (e.g. the auto-generated `$ERC20` and a manual `$ERC20ApprovalMock`), iterate at the top:\n\n```javascript\nconst TOKENS = [{ Token: '$ERC20' }, { Token: '$ERC20ApprovalMock', forcedApproval: true }];\nfor (const { Token, forcedApproval } of TOKENS) {\n  describe(Token, function () {\n    /* … */\n  });\n}\n```\n\n**Assertion style**:\n\n- `bigint` literals (`100n`) for uint values — not `ethers.BigNumber`.\n- Custom errors: `.revertedWithCustomError(contract, 'ErrorName').withArgs(...)`.\n- Panics: `.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW)`.\n- Events: `.emit(contract, 'EventName').withArgs(...)`.\n- Balance changes: `.changeTokenBalance(token, account, delta)` / `.changeTokenBalances(token, [a, b], [d1, d2])`.\n\n## Foundry (`.t.sol`)\n\nFoundry tests live next to their Hardhat counterparts in `test/`. Under HH3 they are picked up by `npm test` (via the `@nomicfoundation/hardhat-mocha` solidity runner) alongside the JS tests. Standalone `forge test` still works but uses forge defaults for fuzz. Two distinct purposes:\n\n**Fuzzing**: function name starts with `test`, parameters are typed. Foundry generates random inputs.\n\n```solidity\nfunction testFuzzAdd(uint256 a, uint256 b) public pure {\n  vm.assume(a < type(uint256).max - b); // restrict if needed\n  assertEq(Math.add(a, b), a + b);\n}\n```\n\nUse `assertEq`, `assertGt`, etc. from `forge-std/Test.sol`. Fuzz settings live in `hardhat.config.ts` under `test.solidity.fuzz` (`runs = 5000`, `maxTestRejects = 150000`) and are applied when running via `npm test`. Standalone `forge test` uses forge defaults.\n\n**Symbolic execution (Halmos)**: function name starts with `symbolic` or `testSymbolic` — that's what the CI `halmos` job matches (`--match-test '^symbolic|^testSymbolic'`). These run as regular Foundry fuzz tests as well.\n\n```solidity\nfunction testSymbolicTransfer(address to, uint256 amount) public {\n  // Halmos treats inputs as fully symbolic (all values).\n  // Use vm.assume(...) for preconditions.\n}\n```\n\nHalmos runs on every PR, needs no credentials. Reach for it when a property can be expressed as a Foundry test.\n\n**Pragma note**: Foundry compiles with `solc 0.8.31` (`foundry.toml`); contract pragmas in `contracts/` may still be `^0.8.20`. New `.t.sol` files should match the contract's pragma minimum unless they specifically need a newer feature.\n\n## Certora (rule-based formal verification)\n\n[Certora](https://certora.com) specs live in `fv/specs/` as `.conf` + `.spec` pairs. CI runs one job per affected config, on any PR whose diff reaches a spec, a harness, a contract one of them verifies, or an `fv/diff` patch applied to one -- no label needed. Label a PR `formal-verification-force-all` to run every config instead. Requires a `CERTORAKEY`, and the `certora` environment gates the job behind a manual approval. Selection runs the base branch's copy of `scripts/list-dependencies.js` over the PR's files, so a PR cannot change which configs it is checked against. The matrix job names carry their config, so `verified` is the aggregate job to require in branch protection. Deleting a `.conf` fails selection, since a config that no longer exists cannot be selected and its verification would just disappear; label the PR `formal-verification-allow-removal` when that is intended.\n\nLocal workflow:\n\n```bash\nmake -C fv apply           # apply harness patches first\nnode fv/run.js AccessControl   # runs fv/specs/AccessControl.conf\nnode fv/run.js --all           # runs every spec\n```\n\nUse Certora when a property is too complex for Halmos, or when a state machine needs deeper rule coverage than fuzzing provides.\n\n## Changesets\n\nEvery PR that changes contract behavior needs a changeset. Skip for NatSpec-only edits, internal refactors with no user-visible effect, or pure repo plumbing.\n\n```bash\nnpx changeset add\n```\n\nThe generated file in `.changeset/` looks like:\n\n```\n---\n'openzeppelin-solidity': minor\n---\n\n`ComponentName`: One-sentence description starting with a backtick-quoted contract or component name.\n```\n\n- `patch` — bug fixes.\n- `minor` — new features.\n- One sentence. No bullet points, no multi-paragraph prose. The release pipeline composes these into `CHANGELOG.md` automatically.\n\n## CI matrix you should keep in mind\n\n| Job                   | What it runs                                                        | Triggered by                                             |\n| --------------------- | ------------------------------------------------------------------- | -------------------------------------------------------- |\n| `lint`                | `npm run lint`                                                      | Every push/PR                                            |\n| `tests`               | `npm test` (JS + Foundry via HH3) + inheritance, pragma, generation | Every push/PR                                            |\n| `tests-upgradeable`   | Transpile then re-run the test suite + storage-layout diff          | Every push/PR                                            |\n| `coverage`            | `npm run coverage` → codecov                                        | Every push/PR                                            |\n| `slither`             | Slither static analysis for common vulnerabilities                  | Every push/PR                                            |\n| `halmos`              | `halmos --match-test '^symbolic\\|^testSymbolic'`                    | Every push/PR                                            |\n| `formal verification` | Certora, one job per affected config (or all, with the force label) | PRs touching a spec, harness, patch or verified contract |\n\n<!-- File: CLAUDE.md (Tokens: ~2085 | Category: claude-rule) -->\n# openzeppelin-contracts — Claude project guide\n\nThis repository is a **Solidity smart contract library**: reusable `abstract contract` and `library` implementations meant to be inherited and extended by downstream projects. It is not a deployed product. Most conventions exist because code here is a base for others: functions must be overridable, argument types must not constrain inheritors, and the public API must not force a particular call context on child contracts.\n\nRead [`GUIDELINES.md`](./GUIDELINES.md) first — it is the human contributor spec and authoritative. This file is for what is specific to Claude-assisted work, plus the conventions not fully documented elsewhere.\n\nBefore preparing a contribution, also read [`CONTRIBUTING.md`](./CONTRIBUTING.md) thoroughly. Non-trivial changes must be discussed in an issue before a PR is opened.\n\n## Repo map\n\n| Path                    | Purpose                                                                                                                                        |\n| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |\n| `contracts/`            | Library source. Subdirectory layout (`token/ERC20/`, `access/`, `utils/`, …) mirrors the public API.                                           |\n| `contracts/mocks/`      | Mocks used by tests. Mirrors `contracts/`. Manual mocks only — see the `testing` skill.                                                        |\n| `contracts/interfaces/` | ERC interfaces in their pristine spec form.                                                                                                    |\n| `test/`                 | Hardhat + Chai tests (`.test.js`), shared behaviors (`.behavior.js`), and Foundry tests (`.t.sol`).                                            |\n| `contracts-exposed/`    | Auto-generated `$ContractName` wrappers from `hardhat-exposed`. Gitignored. Do not edit.                                                       |\n| `fv/`                   | Certora specs (`fv/specs/*.{conf,spec}`), harnesses, and `make`-applied patches.                                                               |\n| `scripts/generate/`     | Procedurally generated `.sol` files (Arrays variants, Checkpoints, EnumerableSet, …). Source of truth is the template, not the generated file. |\n| `scripts/checks/`       | CI checks: `inheritance-ordering.js`, `pragma-validity.js`, generation diff, storage layout.                                                   |\n| `docs/`                 | AsciiDoc sources used by the OZ documentation site. Per-module `README.adoc` files in `contracts/**/` are also rendered.                       |\n| `.changeset/`           | Per-PR changelog entries consumed by the release workflow.                                                                                     |\n| `audits/`               | Historical third-party audit reports, one per release. Reference only.                                                                         |\n| `.claude/skills/`       | Skills specific to working in this repository, loaded by AI assistants per task. See `CONTRIBUTING.md`.                                        |\n\n## Commands\n\n| Task                                     | Command                                                                 |\n| ---------------------------------------- | ----------------------------------------------------------------------- |\n| Compile                                  | `npm run compile`                                                       |\n| Tests (JS + Solidity)                    | `npm test`                                                              |\n| Foundry tests only (forge defaults)      | `forge test -vvv`                                                       |\n| Halmos symbolic tests                    | `halmos --match-test '^symbolic\\|^testSymbolic' -vv`                    |\n| Lint (JS + Sol)                          | `npm run lint` / `npm run lint:fix`                                     |\n| Coverage                                 | `npm run coverage`                                                      |\n| Inheritance order check                  | `npm run test:inheritance`                                              |\n| Pragma validity check                    | `npm run test:pragma`                                                   |\n| Generated-file freshness check           | `npm run test:generation`                                               |\n| Regenerate procedural contracts          | `npm run generate`                                                      |\n| Add a changelog entry                    | `npx changeset add`                                                     |\n| Run a single Certora spec                | `node fv/run.js <SpecName>` (apply harnesses first: `make -C fv apply`) |\n\n## What linting via solhint already enforces (don't re-state in code review)\n\n`solhint-plugin-openzeppelin` + `scripts/solhint-custom/index.js` enforce, on `contracts/**/*.sol` (mocks and tests are exempt):\n\n- **State variables are `private`** (`constant`/`immutable` are the only exceptions).\n- **Underscore prefix matches visibility**: `private`/`internal` state vars and functions get `_`; `public`/`external` do not. Library internal functions do NOT get `_`.\n- **No `external virtual`** (except fallback). The `public` default makes this redundant; flagged by `no-external-virtual`.\n- **Libraries expose only `internal`/`private`**.\n- **Interfaces start with `I`**, contracts use CapWords, events use CapWords, modifiers/params mixedCase.\n\nIf a lint rule already catches it, don't waste tokens explaining it — fix and move on.\n\n## Always-on conventions\n\nThese apply to every change and are not lint-enforced:\n\n- **File header version line**: don't add or edit the `// OpenZeppelin Contracts (last updated vX.Y.Z) (path)` line — release tooling (`scripts/release/update-comment.js`) maintains it automatically based on `git diff` between the previous tag and `HEAD`. The SPDX line is your only responsibility.\n- **Pragma**: don't pick the floor by hand. Run `npm run pragma` and let `scripts/minimize-pragma.js` walk the dependency graph, compile against every candidate `solc`, and write back the lowest version that works for each file (`>=` prefix for interfaces, `^` for implementations and libraries). Bumping the floor casually can introduce opcodes (`mcopy` in 0.8.24, etc.) not supported on all target chains — the script's output is the safe answer.\n- **Imports**: 100% named, curly-brace, relative paths within `contracts/`. No wildcards.\n- **Inheritance order is globally consistent**: if you change an `is A, B, C` list, run `npm run compile && npm run test:inheritance` before pushing.\n- **Changesets**: every PR that changes contract behavior needs one. Skip for NatSpec-only, internal refactors with no user-visible effect, or pure repo plumbing.\n- **Procedurally generated files**: never hand-edit a file whose header says \"procedurally generated from `<template>`\". Edit the template under `scripts/generate/templates/` and run `npm run generate`. CI runs `test:generation` to enforce.\n- **Backward compatibility**: released contracts are inherited downstream and transpiled into the `-upgradeable` package. Don't change existing `public`/`external` signatures, event or error shapes, or the storage layout of existing contracts. Changes are additive; deprecate rather than delete. Breaking changes are a maintainer decision tied to a major release.\n- **Dependencies**: this library has no third-party dependencies that translate to the user, and nothing that could change behavior out from under it. Don't add an npm or Solidity dependency or a new external import without maintainer sign-off; reuse existing `contracts/utils/` helpers first. Code that needs third-party dependencies belongs in `openzeppelin-community-contracts`, not here.\n\n## When to load which skill\n\nClaude Code surfaces these from `.claude/skills/` based on the task. You can also reference them directly:\n\n- **`library-api-design`** — when adding or modifying any contract in `contracts/` (visibility, `virtual`, `memory` vs `calldata`, `_update` pattern, internal/external split, no-ops over reverts, hooks).\n- **`solidity-style`** — when writing or editing Solidity (errors, events, NatSpec, ERC-7201, assembly, `unchecked`, casting, immutables).\n- **`testing`** — when writing tests or mocks (`hardhat-exposed` `$` wrappers, when manual mocks are warranted, `.behavior.js` reuse, multi-target test loops, Foundry fuzz, Halmos symbolic, Certora).\n- **`add-changeset`** — at PR close, or when the user asks for one.\n\n## Things to always verify manually\n\nAI suggestions in these areas are often plausible-but-wrong. Read the surrounding code before accepting:\n\n- **Cryptography** — anything in `contracts/utils/cryptography/`, hashing, ECDSA, EIP-712 domains, BLS, signatures.\n- **ERC-7201 slot derivations** — recompute with `SlotDerivation.erc7201Slot()` and compare to the constant. The formula in the inline comment is mandatory.\n- **Storage layout for transpiler-processed contracts** — adding/removing/reordering fields breaks proxied deployments. CI runs storage-layout diff but read it.\n- **`delegatecall`, inline assembly, memory safety** — verify the `memory-safe` annotation is honest and stack/scratch usage is bounded.\n- **`unchecked` invariants** — re-derive the overflow-impossible claim for every code path. The inline comment is the contract.\n- **`_authorizeUpgrade` access control** — the base is empty by design; every override must restrict.\n- **`_disableInitializers()`** in implementation constructors for UUPS/Transparent proxy targets.\n- **Pragma bumps** — check that any new opcode the compiler will emit is supported on all target chains.\n\n## Out of scope for AI\n\n- Adding/renaming public API or deciding whether a feature belongs in the library — open an issue first.\n- Security assessments or audit opinions.\n- New ERC interfaces or standards without explicit direction.\n- Restructuring tests without a corresponding contract change.\n- Fabricated bug bounties and security rewards. Never claim or imply a reward, amount, or eligibility. The only existing program is [Immunefi](https://immunefi.com/bounty/openzeppelin) (See [`SECURITY.md`](./SECURITY.md)).\n</yakaai_skills>"}