solidity-style
Skill1.2k starsChanged 24 days agoMITView on GitHub
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.
1 other project uses a version of this.
Trending
More talk lately.about the whole repository
Most recently taken up by
- OpenZeppelin/openzeppelin-contractsword for word, first seen Oct 8, 2026
Who's talking about OpenZeppelin/openzeppelin-contracts-upgradeable
- smartcontractkit/chainlink“[FEAT] Chainlink upgradeable smart contracts”GitHub · Sep 7, 2021 · 40 reactions
- foundry-rs/foundry“Support EIP-7201 in `forge inspect storage`”GitHub · Apr 14, 2024 · 4 reactions
- OpenZeppelin/openzeppelin-contracts“Use diamond storage instead of gaps in upgradeable contracts”GitHub · Nov 12, 2021 · 3 reactions
- code-423n4/2022-06-nibbl-findings“[PNM-001] Function `permit` directly uses `_approve`, suffering from the well-known double attacks”GitHub · Jun 24, 2022 · 2 reactions
- OpenZeppelin/openzeppelin-contracts“Release 4.7”GitHub · May 4, 2022 · 2 reactions
The biggest 5 of 51 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 51
Talked about on GitHub and Stack Overflow (51 times).
On GitHub · 50
- Sep 7, 2021[FEAT] Chainlink upgradeable smart contracts — smartcontractkit/chainlink40 reactions · 15 comments
- Apr 14, 2024Support EIP-7201 in `forge inspect storage` — foundry-rs/foundry4 reactions · 9 comments
- Nov 12, 2021Use diamond storage instead of gaps in upgradeable contracts — OpenZeppelin/openzeppelin-contracts3 reactions · 37 comments
- Jun 24, 2022[PNM-001] Function `permit` directly uses `_approve`, suffering from the well-known double attacks — code-423n4/2022-06-nibbl-findings2 reactions · 3 comments
- May 4, 2022Release 4.7 — OpenZeppelin/openzeppelin-contracts2 reactions · 4 comments
45 more on GitHub
- Jun 2, 2026chore(deps-dev): bump the openzeppelin group across 1 directory with 3 updates — XFuel-Lab/chit4021 reaction · 5 comments
- Apr 8, 2026foundry-rs#13763 (#398) — Dargon789/foundry1 reaction · 12 comments
- Mar 9, 2026`forge install` submodule cleanup discrepancy when using `--no-git` — foundry-rs/foundry1 reaction
- Dec 17, 2023cergyk - FootiumClub::ERC721Upgradeable/Pausable Cannot pause transfers on FootiumClub NFT — sherlock-audit/2023-12-footium-judging1 reaction · 15 comments
- Oct 27, 2023__Ownable_init() function isn't called properly in the initializer — hats-finance/Smooth-0x64bc275b37e62eec81a00ecaecd2b9567058f9901 reaction · 1 comment
- Oct 17, 2023Ownable2StepUpgradeable requires initializing OwnableUpgradeable — OpenZeppelin/openzeppelin-contracts1 reaction · 12 comments
- Nov 14, 2022removes base contract dependency on hardhat / fixes initializer modifiers — Network-Goods/hypercerts-protocol1 reaction · 3 comments
- Oct 20, 2022[R&D review] Unify ERC20 interfaces — morpho-org/morpho-optimizers-vaults1 reaction · 12 comments
- Oct 18, 2022Contracts without GAP can lead a upgrade disaster — code-423n4/2022-10-traderjoe-findings1 reaction · 2 comments
- Aug 6, 2022 Use of not recommended ERC721 _mint function — code-423n4/2022-08-rigor-findings1 reaction · 1 comment
- Jun 25, 2022QA Report — code-423n4/2022-06-yieldy-findings1 reaction
- Jan 10, 2022New keyword `pin` for fixed sparse storage layouts — argotorg/solidity1 reaction · 10 comments
- Nov 4, 2026chore(deps): bump lib/openzeppelin-contracts-upgradeable from `7bd46d8` to `7839763` — YieldShield/smart-contracts0 reactions · 1 comment
- Nov 4, 2026Bump contracts/lib/openzeppelin-contracts-upgradeable from `7bf4727` to `7839763` — Rong0715/assert-dao0 reactions
- Nov 3, 2026chore(deps): bump lib/openzeppelin-contracts-upgradeable from `ed0d253` to `7839763` — the3rdweblabs/payment-kit.evm0 reactions · 2 comments
- Nov 3, 2026Bump lib/openzeppelin-contracts-upgradeable from `3d5fa5c` to `7839763` — lukema95/flip-core0 reactions
- Nov 2, 2026chore(deps): bump lib/openzeppelin-contracts-upgradeable from `fc6d2f3` to `58c8c82` — owieth/foundry-setup0 reactions
- Nov 2, 2026build(deps): bump contracts/lib/openzeppelin-contracts-upgradeable from `1717bdd` to `ce38bf1` — YieldShield/yieldshield-bnb-open-source0 reactions
- Oct 28, 2026chore(deps): bump lib/openzeppelin-contracts-upgradeable from `7bd46d8` to `fc6d2f3` — YieldShield/smart-contracts0 reactions · 2 comments
- Oct 21, 2026chore(deps): bump @openzeppelin/contracts-upgradeable from 5.3.0 to 5.4.0 — Datura-ai/celium-collateral-contracts0 reactions
- Oct 15, 2026chore(deps): bump @openzeppelin/contracts-upgradeable from 5.4.0 to 5.7.0 in /solidity in the openzeppelin-contracts group across 1 directory — oasisprotocol/privana0 reactions · 1 comment
- Oct 1, 2026chore(deps): Bump contracts/lib/openzeppelin-contracts-upgradeable from `527c1bd` to `66f1abd` — ismellike/nft-gacha0 reactions
- Sep 18, 2026Bump @openzeppelin/contracts-upgradeable from 5.0.0 to 5.0.2 in the npm_and_yarn group across 1 directory — Ronnie-Ahmed/Foundry_Tutorial0 reactions
- Sep 1, 2026chore(deps): bump contracts/lib/openzeppelin-contracts-upgradeable from `c1b1bed` to `f8fc282` — NeoWeb3Nova/NeoDeFi0 reactions · 1 comment
- Jul 27, 2026Bump lib/openzeppelin-contracts-upgradeable from `723f8ca` to `7a37a98` — dust/zen-staking-layer0 reactions
- Jun 20, 2026chore(deps): bump contracts/lib/openzeppelin-contracts-upgradeable from `7bf4727` to `c1b1bed` — tresr-community/tresr-game0 reactions
- Jun 4, 2026deps: bump the openzeppelin group across 1 directory with 3 updates — eq-lab/pipeline-contracts0 reactions
- Apr 20, 2026Track upstream OZ PR to expose ERC-7201 root slot as internal — S01-Issuer/st0x.deploy0 reactions · 3 comments
- Mar 9, 2026chore(deps): bump lib/openzeppelin-contracts-upgradeable from `60b305a` to `524c8c9` — k0yote/newlo-point0 reactions
- Nov 25, 2025Bump @openzeppelin/contracts-upgradeable from 5.0.1 to 5.0.2 — vechain/vevote0 reactions · 2 comments
- Feb 19, 2025Bald Crimson Alligator - Launch contract does not use upgradable versions of parent initializer functions — sherlock-audit/2025-02-rova-judging0 reactions
- Feb 5, 2025Bump @openzeppelin/contracts-upgradeable from 4.9.3 to 4.9.6 — lidofinance/linea-contracts0 reactions
- Dec 5, 2024Energetic Honeysuckle Leopard - corrupt upgdreability pattarn — sherlock-audit/2024-11-ethos-network-ii-judging0 reactions
- Dec 2, 2024Bump contracts/lib/openzeppelin-contracts-upgradeable from `723f8ca` to `5fcea08` — ava-labs/avalanche-interchain-token-transfer0 reactions
- Nov 5, 2024USDE : renounceRole is not fully sealed — hats-finance/Euro-Dollar-0xa4ccd3b6daa763f729ad59eae75f9cbff7baf2cd0 reactions · 1 comment
- Aug 6, 2024chore(deps): update dependency @openzeppelin/contracts to v4.9.6 [security] — ashutoshvarma/sway0 reactions · 1 comment
- Jul 8, 2024Bump @openzeppelin/contracts-upgradeable from 5.0.0 to 5.0.2 — Ultra-Tech-code/Dynamic_NFT0 reactions
- Jun 8, 2024Uninitialized Implementation Contracts are not Completely Protected — code-423n4/2024-05-predy-validation0 reactions
- May 1, 2024CvxRewardDistributor misses `_Ownable2Step_init()` in the `initialize()` function — hats-finance/Convergence---Convex-integration-0xb3df23e155b74ad2b93777f58980d6727e8b40bb0 reactions · 13 comments
- Apr 29, 2024chore(deps): bump lib/openzeppelin-contracts-upgradeable from `762e9bc` to `82e861e` — Akagi201/foundry-template0 reactions
- Apr 24, 2024build(deps-dev): bump @openzeppelin/contracts-upgradeable from 4.9.3 to 4.9.6 — henkaku-center/mono-auction-contract0 reactions
- Apr 16, 2024chore(deps-dev): bump @openzeppelin/contracts-upgradeable from 4.9.3 to 4.9.6 — spherity/ethr-revocation-registry0 reactions
- Feb 29, 2024Bump @openzeppelin/contracts-upgradeable from 4.9.3 to 4.9.6 — Fathom-Fi/fathom-solidity-sdk0 reactions
- Feb 29, 2024Bump @openzeppelin/contracts-upgradeable from 4.9.3 to 4.9.6 in /contracts — 1337CyberSecurity/chainlink0 reactions
- Feb 29, 2024Bump @openzeppelin/contracts-upgradeable from 4.9.3 to 4.9.6 — eniblock/website-demo0 reactions
On Stack Overflow · 1
Other versions1 project uses a version of this.
Word for word · 1
- OpenZeppelin/openzeppelin-contracts★ 27k100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository4 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- add-changesetSkill
- library-api-designSkill
- testingSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 3 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 12
Prefer require(condition, CustomError(args)).
- Distinctive: line 43
Document public and internal functions, events, errors, and constructors.
- Very specific: line 71
Leave a blank line between the SPDX/last updated header and pragma.
- Very specific: line 79
Don't name return values unless their meaning isn't obvious or there are multiple.
- Very specific: line 86
Only mandatory ERC elements go in the base interface.
- Distinctive: line 92
Don't pick the pragma floor by hand.
- Very specific: line 106
Always annotate assembly ("memory-safe").
- Very specific: line 117
Only use unchecked with an inline invariant comment that names the bound.
- Distinctive: line 126
When reviewing, re-derive the bound for every code path that can reach the block.
- Very specific: line 142
If the file header says // This file was procedurally generated from …, do not edit it.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Solidity style and safety
The 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.
Errors
Prefer 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:
- The file's pragma is lower. Do not bump pragma just to use
require. - An error argument is anything other than a variable, constant, literal or cast of those.
requireevaluates 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. - The check is part of an
if/else ifchain.
When 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.
Custom errors follow ERC-6093:
- Domain prefix:
ERC<number>if the error violates an ERC spec; otherwise the component name (Governor,ECDSA,Timelock). - Include offending values as arguments so callers can decode the failure context.
- Never declare the same error name twice across the library — duplicates cause identifier conflicts when inheriting from multiple contracts.
Location priority:
- Reuse the error from the underlying ERC if already defined there.
- Otherwise declare in the interface or library that owns the concept.
- Otherwise declare in the implementation (when the interface/library is already pinned by an ERC).
- Otherwise declare in the extension where it actually fires.
Events
Emit 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.
NatSpec
@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.
- Single-line:
/// @dev … - Multi-line:
/** @dev … */ - Document public and internal functions, events, errors, and constructors. Private functions are optional — they don't appear on the docs site.
Overrides:
- Pure relay with nothing to add:
/// @inheritdoc InterfaceName - Override with added requirements or caveats:
/** @dev See {Interface-functionName}. \n\n Requirements: … */
Cross-references: {ContractName-functionName} — e.g. {IERC20-transfer}, {_update}.
Section headers inside @dev blocks: Requirements:, NOTE:, WARNING:, IMPORTANT:. Frame caveats as conserved properties of the contract, not as alarms about safe behaviors.
Imports and file structure
100% named curly-brace imports. Relative paths within contracts/. No wildcards:
import { IERC20, IERC20Metadata } from '../../interfaces/IERC20.sol';File order:
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated vX.Y.Z) (path/to/file.sol)— release-managed; do not add or edit by hand.scripts/release/update-comment.jsrewrites this line at release time based on whichcontracts/**/*.solfiles changed since the previous tag. New files: write only the SPDX line; the release script appends the second line on first inclusion.pragma solidity …- Imports
- Contract-level NatSpec
- Contract declaration
Leave a blank line between the SPDX/last updated header and pragma.
Inside the body: using → structs/enums → constants → state vars → events → errors → modifiers → constructor → external/public functions → internal → private.
Contracts not meant to be deployed on their own (bases, extensions, mixins) are declared abstract so they can only be used by inheriting from them.
Return values
Don'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).
Interfaces
- 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). - 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.
- In interfaces, functions are
externaland non-value-type parameters arecalldata— this is the spec form. Implementations usepublic/memory(see thelibrary-api-designskill); the two intentionally differ. - Only mandatory ERC elements go in the base interface; optional ones go in an extension interface (
name()is inIERC20Metadata, notIERC20). - Placement:
contracts/interfaces/for ERC interfaces (draft-filename prefix if the ERC is not yet final), next to the implementation otherwise. - Interface files SHOULD NOT import anything that is not itself an interface file.
Pragma
Don't pick the pragma floor by hand. Run:
npm run pragmascripts/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).
When 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.
The 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.
Assembly
Always annotate assembly ("memory-safe"). Omitting the annotation disables the memory optimizer and increases gas. Include inline comments for any non-trivial operation.
Numeric literals inside assembly:
- Memory-related (locations, offsets, lengths): hexadecimal.
mload(0x40),mstore(add(ptr, 0x20), value),keccak256(ptr, 0x55). - Bit operations (shift amounts, bit counts): decimal.
shl(128, value),sar(96, x). - Trivially small values like
1or2may use decimal even in memory contexts:ptr := add(ptr, 1). - In
call/staticcall/delegatecall, decimal zero is acceptable when both location and length are zero.
unchecked
Only 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:
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}When reviewing, re-derive the bound for every code path that can reach the block.
Casting
Any 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.
Inheritance ordering
The 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:
npm run compile && npm run test:inheritanceProcedurally generated contracts
If 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.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

