testing
Skill1.2k starsChanged 24 days agoMITView on GitHub
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.
194 other projects use a version of this.
Trending
More talk lately.about the whole repository
Most recently taken up by
- ishandutta2007/Awesome-Claude-Skillstheir own version, first seen Nov 8, 2026
- steerlabs/opensteertheir own version, first seen Nov 8, 2026
- timmo001/notestheir own version, first seen Nov 8, 2026
- codewithmukesh/dotnet-claude-kittheir own version, first seen Nov 8, 2026
- kortix-ai/sunatheir own version, first seen Nov 8, 2026
- microsoft/VSSDK-Analyzerstheir own version, first seen Nov 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
Used with
Projects with it also use bundle-dependency-prs, update-library-template and 10 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
MicrosoftSkillbundle-dependency-prs
Fix broken dependency update PRs and aggregate the ones that work into one PR.
Also in 7 projects that use this
MicrosoftSkillupdate-library-template
Merges the latest Library.Template into this repo (at position of HEAD) and resolves conflicts.
Also in 7 projects that use this
zetaloopSkillassign-copilot
Assigns a GitHub issue to the Copilot coding agent, optionally specifying a custom agent. Use this when asked to assign an issue to Copilot or delegate an issue to CCA.
Also in 3 projects that use this
NVIDIA-NeMoSkillbuild-and-dependency
Dev environment setup for Megatron Bridge — container-based development, uv package management, lockfile regeneration, adding dependencies, Slurm container usage, and common build pitfalls.
Also in 3 projects that use this
timmo001Skillchangeset-scope
Keep all scoped code work contained to the user-defined changeset. Use for implementation, fixes, diagnosis, refactoring, cleanup, and review when explicit instructions, named files, diffs, branches, pull requests, or injected work scopes define the boundary.
Also in 3 projects that use this
JetBrainsSkillmodule-dependencies
Add or modify IntelliJ module dependencies in `.iml` files.
Also in 3 projects that use this
Other versions194 projects use a version of this.
Word for word · 1
- OpenZeppelin/openzeppelin-contracts★ 27k100% same
Edited · 0
None found.
Wrote their own version · 193
- modelcontextprotocol/servers★ 92ksame name
- lobehub/lobe-chat★ 84ksame name
- lobehub/lobehub★ 84ksame name
- penpot/penpot★ 62ksame name
- meteor/meteor★ 45ksame name
- iOfficeAI/AionUi★ 34ksame name
- garrytan/gbrain★ 32ksame name
- tursodatabase/turso★ 25ksame name
- redis/go-redis★ 22ksame name
- desktop/desktop★ 22ksame name
183 more not listed
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
- solidity-styleSkill
The file
We split this skill into the rules it gives an agent
6 rules. 0 are common, 3 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 173 edited versions we read keep these.
3
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 17
Default to Hardhat+Chai unit tests, add Halmos for invariants you can express symbolically, escalate to Certora when state-machine coverage …
- Very specific: line 56
Fixtures: use loadFixture from @nomicfoundation/hardhat-network-helpers.
- Very specific: line 81
Multi-target loops: when the same suite should run against several wrappers (e.g.
- Very specific: line 113
Use assertEq, assertGt, etc.
- Distinctive: line 140
Use Certora when a property is too complex for Halmos, or when a state machine needs deeper rule coverage than fuzzing provides.
- Distinctive: line 144
Every PR that changes contract behavior needs a changeset.
“Common” is measured: kept by at least 6 in 10 of the 173 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Testing
When to use each approach
| Approach | Required when |
| Formal verification (Certora) | Contract includes a state machine or access-control rules complex enough that fuzzing won't cover the cases |
| Symbolic execution (Halmos) | A property is expressible as a Foundry test with symbolic inputs — fast, no external credentials |
| Fuzzing (Foundry) | Math-heavy code, data structures, complex invariants not suited for FV |
| Edge unit cases (Hardhat) | All contracts: golden path + pathological/border cases. This is the baseline. |
Default to Hardhat+Chai unit tests, add Halmos for invariants you can express symbolically, escalate to Certora when state-machine coverage requires it.
A bug fix must add a minimal test that reproduces the bug, committed alongside the fix. The objective is 100% branch coverage per PR.
Accessing internal functions: hardhat-exposed
$-prefixed wrapper contracts are auto-generated by 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:
const token = await ethers.deployContract('$ERC20', ['Name', 'SYM']);
await token.$_mint(holder, 1000n);Do not write $-prefixed wrappers by hand.
Manual mocks in contracts/mocks/
Manual 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:
- Constructor-level setup that auto-generation can't provide — e.g.
ERC721ConsecutiveMockcalling_mintConsecutiveduring construction. - Multi-extension composition to test how extensions compose — e.g.
ERC721Consecutive + ERC721Pausable + ERC721Votes. - Injected behavior that overrides a hook to simulate an edge case — e.g.
ERC20Reentrantfiring a reentrant call inside_update. - Adversarial behavior with no analogue in the base — e.g.
ReentrancyAttack,ERC20ReturnFalseMock. - A plain public wrapper for an internal operation where the auto-generated signature is unsuitable — e.g.
ERC20Mock.mint.
Even inside a manual mock, prefer the auto-generated $_ functions over redeclaring the same exposure:
// prefer (reuses auto-generated exposure):
await token.$_mint(holder, 1000n);
// avoid (redundant — hardhat-exposed already provides this):
contract ERC20Mock is ERC20 {
function __mint(address account, uint256 value) external { _mint(account, value); }
}Hardhat + Chai (.test.js)
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.
async function fixture() {
const [holder, recipient] = await ethers.getSigners();
const token = await ethers.deployContract('$ERC20', [name, symbol]);
await token.$_mint(holder, initialSupply);
return { holder, recipient, token };
}
beforeEach(async function () {
Object.assign(this, await loadFixture(fixture));
});Structure: describe('ContractName') > describe('methodName') > it('description').
Shared behavior: reuse via shouldBehaveLike* functions in sibling .behavior.js files. Mount with this-context state set in the fixture:
const { shouldBehaveLikeERC20 } = require('./ERC20.behavior');
// ...
shouldBehaveLikeERC20(initialSupply, { forcedApproval });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:
const TOKENS = [{ Token: '$ERC20' }, { Token: '$ERC20ApprovalMock', forcedApproval: true }];
for (const { Token, forcedApproval } of TOKENS) {
describe(Token, function () {
/* … */
});
}Assertion style:
bigintliterals (100n) for uint values — notethers.BigNumber.- Custom errors:
.revertedWithCustomError(contract, 'ErrorName').withArgs(...). - Panics:
.revertedWithPanic(PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW). - Events:
.emit(contract, 'EventName').withArgs(...). - Balance changes:
.changeTokenBalance(token, account, delta)/.changeTokenBalances(token, [a, b], [d1, d2]).
Foundry (.t.sol)
Foundry 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:
Fuzzing: function name starts with test, parameters are typed. Foundry generates random inputs.
function testFuzzAdd(uint256 a, uint256 b) public pure {
vm.assume(a < type(uint256).max - b); // restrict if needed
assertEq(Math.add(a, b), a + b);
}Use 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.
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.
function testSymbolicTransfer(address to, uint256 amount) public {
// Halmos treats inputs as fully symbolic (all values).
// Use vm.assume(...) for preconditions.
}Halmos runs on every PR, needs no credentials. Reach for it when a property can be expressed as a Foundry test.
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.
Certora (rule-based formal verification)
Certora 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.
Local workflow:
make -C fv apply # apply harness patches first
node fv/run.js AccessControl # runs fv/specs/AccessControl.conf
node fv/run.js --all # runs every specUse Certora when a property is too complex for Halmos, or when a state machine needs deeper rule coverage than fuzzing provides.
Changesets
Every PR that changes contract behavior needs a changeset. Skip for NatSpec-only edits, internal refactors with no user-visible effect, or pure repo plumbing.
npx changeset addThe generated file in .changeset/ looks like:
---
'openzeppelin-solidity': minor
---
`ComponentName`: One-sentence description starting with a backtick-quoted contract or component name.patch— bug fixes.minor— new features.- One sentence. No bullet points, no multi-paragraph prose. The release pipeline composes these into
CHANGELOG.mdautomatically.
CI matrix you should keep in mind
| Job | What it runs | Triggered by |
lint | npm run lint | Every push/PR |
tests | npm test (JS + Foundry via HH3) + inheritance, pragma, generation | Every push/PR |
tests-upgradeable | Transpile then re-run the test suite + storage-layout diff | Every push/PR |
coverage | npm run coverage → codecov | Every push/PR |
slither | Slither static analysis for common vulnerabilities | Every push/PR |
halmos | halmos --match-test '^symbolic|^testSymbolic' | Every push/PR |
formal verification | Certora, one job per affected config (or all, with the force label) | PRs touching a spec, harness, patch or verified contract |
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.

