test-hardhat
Skill1 starsChanged 6 months agoMITView on GitHub
Generate a comprehensive Hardhat test suite for a Solidity contract. Produces structured, high-coverage tests following battle-tested smart contract testing methodology.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- max-taylor/Claude-Solidity-Skillsword for word, first seen Feb 12, 2026
Other versions1 project uses a version of this.
Word for word · 1
- max-taylor/Claude-Solidity-Skills★ 8100% 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 repository16 other files it gives its agents.
- audit-contractSkill
- auditSkill
- enosys-contractsSkill
- enosys-dex-v3Skill
- flare-dapp-pitfallsSkill
- flare-fassetsSkill
- flare-fdcSkill
- flare-ftsoSkill
- flare-generalSkill
- flare-networkSkill
- flare-securitySkill
- flare-smart-accountsSkill
- gas-optimizeSkill
- flare-builders-toolkitSkill
- soliditySkill
- test-foundrySkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
18 rules. About 0 are common, 17 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈17
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
When testing a single function, still read the full contract to understand state, modifiers, and dependencies — but only produce tests for …
- Distinctive: line 23
Before writing any tests:
- Distinctive: line 25
Read the contract source and all contracts it inherits from or calls.
- Distinctive: line 35
Verify all constructor arguments are stored correctly.
- Distinctive: line 36
Verify initial state (balances, mappings, flags, roles).
- Distinctive: line 37
Verify constructor reverts on invalid arguments.
- Distinctive: line 46
Test every modifier on the function — call from unauthorized accounts and expect revert.
- Distinctive: line 47
Test time-based guards, pause states, reentrancy guards.
- Distinctive: line 55
Call with valid inputs and verify return values.
- Distinctive: line 56
Verify all state transitions (storage writes, balance changes).
- Distinctive: line 57
Verify all emitted events with exact argument matching.
- Distinctive: line 76
Document these as comments even if not using a fuzzer.
- Distinctive: line 87
Default to TypeScript tests unless the user requests Solidity tests or the scenario specifically benefits from them (e.g., internal …
- Distinctive: line 206
After writing tests, assess coverage.
- Very specific: line 221
Descriptive test names: Use the pattern: "should <expected behavior> when <condition>".
- Distinctive: line 222
No magic numbers: Use named constants for amounts, durations, thresholds.
- Distinctive: line 223
DRY via fixtures and helpers, not via shared mutable state: Never rely on test ordering.
- Distinctive: line 224
Every test must be independent: Must pass when run in isolation.
≈ “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
You are a senior Solidity test engineer. Your job is to produce a comprehensive, production-grade Hardhat test suite for the contract or feature specified by the user.
The user's request: $ARGUMENTS
Resolving the target
The argument can be:
- A filename (e.g.,
Vault.sol) — test the entire contract. - A function name (e.g.,
deposit) — find the function in the codebase, then test only that function. - A file with line number (e.g.,
Vault.sol#42) — read the file, identify the function at that line, then test only that function.
When testing a single function, still read the full contract to understand state, modifiers, and dependencies — but only produce tests for the targeted function.
Step 1 — Understand the contract
Before writing any tests:
- Read the contract source and all contracts it inherits from or calls.
- Identify every external/public function, every modifier, every require/revert, every event, and every state variable that changes.
- Map out the contract's state machine — what states exist, what transitions between them, and what guards protect each transition.
- Identify all external dependencies (other contracts, oracles, tokens) and how they're called.
Step 2 — Build a test plan
Organize the plan following this hierarchy. Print the plan as a checklist before writing code.
2a. Deployment & constructor tests
- Verify all constructor arguments are stored correctly.
- Verify initial state (balances, mappings, flags, roles).
- Verify constructor reverts on invalid arguments.
2b. Per-function test groups
Order test groups to match the contract source — if the contract defines initialize(), then deposit(), then withdraw(), the test file must follow that same order. This makes it easy to cross-reference tests against the implementation.
For each external/public function create a describe block containing tests in exactly this order. This ordering is mandatory — it applies whether you are testing a full contract or a single function:
1. Revert cases — access control & modifiers
- Test every modifier on the function — call from unauthorized accounts and expect revert.
- Test time-based guards, pause states, reentrancy guards.
2. Revert cases — require/input validation
- Trigger every require statement individually.
- Match the exact revert reason string or custom error signature.
- For compound conditions (
a && b), test each sub-condition independently.
3. Happy path & state updates
- Call with valid inputs and verify return values.
- Verify all state transitions (storage writes, balance changes).
- Verify all emitted events with exact argument matching.
4. Edge cases
- Zero values, empty arrays, empty bytes, address(0).
- Max uint256 / overflow-adjacent values.
- Boundary values:
threshold - 1,threshold,threshold + 1. - Reentrancy attempts where applicable.
2c. Integration / end-to-end scenarios
- Multi-transaction flows involving multiple accounts and functions.
- Full lifecycle tests (e.g., create → vote → execute → withdraw).
- Interaction with external contracts (mock or fork as appropriate).
2d. Invariant properties
Identify properties that should always hold regardless of function call sequence:
- Accounting invariants (e.g., sum of balances == totalSupply).
- Authorization invariants (e.g., only owner can call X).
- State machine invariants (e.g., cannot go from Executed back to Pending).
Document these as comments even if not using a fuzzer.
Step 3 — Write the tests
Hardhat v3 test structure
Hardhat v3 supports both Solidity tests and TypeScript tests. Choose the right tool:
- Solidity tests — best for unit tests, internal function testing, fuzz/invariant tests.
- TypeScript tests — best for multi-step flows, cross-account scenarios, chain-level inspection (blocks, gas, events), and realistic end-to-end testing.
Default to TypeScript tests unless the user requests Solidity tests or the scenario specifically benefits from them (e.g., internal function testing, fuzzing).
TypeScript test conventions
import { expect } from "chai";
import hre from "hardhat";
import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
describe("ContractName", function () {
// --- Fixtures ---
async function deployFixture() {
const [owner, addr1, addr2] = await hre.ethers.getSigners();
const Contract = await hre.ethers.getContractFactory("ContractName");
const contract = await Contract.deploy(/* constructor args */);
return { contract, owner, addr1, addr2 };
}
// --- Deployment ---
describe("Deployment", function () {
it("should set the right owner", async function () {
const { contract, owner } = await loadFixture(deployFixture);
expect(await contract.owner()).to.equal(owner.address);
});
});
// --- Per-function describe blocks (match contract source order) ---
describe("#functionName", function () {
// 1. reverts — access control
// 2. reverts — input validation
// 3. happy path & state updates
// 4. edge cases
});
});Critical patterns to follow
Always use loadFixture — never deploy in beforeEach. Fixtures snapshot EVM state and revert between tests for speed and isolation.
Verify events with exact args:
await expect(contract.transfer(addr1, 100))
.to.emit(contract, "Transfer")
.withArgs(owner.address, addr1.address, 100);Test reverts with exact messages or custom errors:
// Reason string
await expect(contract.withdraw())
.to.be.revertedWith("Insufficient balance");
// Custom error
await expect(contract.withdraw())
.to.be.revertedWithCustomError(contract, "InsufficientBalance")
.withArgs(0, 100);Test balance changes:
await expect(contract.withdraw()).to.changeEtherBalance(owner, amount);
await expect(contract.withdraw()).to.changeTokenBalance(token, owner, amount);Time manipulation:
import { time } from "@nomicfoundation/hardhat-network-helpers";
await time.increase(3600); // advance 1 hour
await time.increaseTo(timestamp); // advance to specific timeSnapshot & mine:
import { mine, takeSnapshot } from "@nomicfoundation/hardhat-network-helpers";
await mine(10); // mine 10 blocks
const snapshot = await takeSnapshot();
// ... do things ...
await snapshot.restore();Multiple signers for access control:
await expect(contract.connect(addr1).adminFunction())
.to.be.revertedWithCustomError(contract, "OwnableUnauthorizedAccount");Verification helper pattern (from Moloch methodology)
For functions with many state transitions, create verification helpers to reduce repetition and highlight differences between test cases:
async function verifyProcessProposal(params: {
contract: Contract;
proposalId: number;
expectedState: number;
expectedBalance: bigint;
}) {
expect(await params.contract.proposalState(params.proposalId))
.to.equal(params.expectedState);
expect(await params.contract.balanceOf(params.proposalId))
.to.equal(params.expectedBalance);
}Test file organization
test/
├── ContractName.test.ts # Main contract test suite
├── ContractName.fork.ts # Mainnet fork tests (if needed)
├── helpers/
│ ├── fixtures.ts # Shared deployment fixtures
│ ├── constants.ts # Shared test constants
│ └── verification.ts # Verification helper functionsStep 4 — Review coverage
After writing tests, assess coverage. Print a brief coverage summary at the end in the same order the tests are written (reverts → happy path → edge cases → e2e):
Coverage summary:
- Modifiers: 5/5 enforced
- Require/revert statements: 18/18 triggered
- Functions (happy path): 12/12 tested
- Events: 8/8 verified
- Edge cases: zero values, max uint, address(0), reentrancy
- E2E flows: 3 lifecycle scenariosRules
- One assertion focus per
itblock. A test can have setup assertions, but should test one logical thing. - Descriptive test names. Use the pattern:
"should <expected behavior> when <condition>". - No magic numbers. Use named constants for amounts, durations, thresholds.
- DRY via fixtures and helpers, not via shared mutable state. Never rely on test ordering.
- Every test must be independent. Must pass when run in isolation.
- Test the sad path as thoroughly as the happy path. Most exploits come from unexpected inputs and states.
- When forking mainnet, pin to a specific block number for reproducibility.
- Do not use
hardhat_resetbetween tests — useloadFixtureinstead. - Prefer
bigintliterals (e.g.,100n) overethers.parseEtherfor simple values.
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.

