
Web3 Testing
Stand up Hardhat and Foundry test suites with coverage, gas reports, fuzzing, and mainnet fork scenarios for Solidity contracts.
Overview
Web3 Testing is an agent skill for the Ship phase that guides comprehensive smart contract testing with Hardhat and Foundry including unit, integration, fork, fuzz, and coverage workflows.
Install
npx skills add https://github.com/wshobson/agents --skill web3-testingWhat is this skill?
- Hardhat toolbox config with optimizer (200 runs), forking, goerli network, gas reporter, and Etherscan API hooks
- Covers unit tests, integration suites, gas optimization checks, fuzzing, and mainnet forking patterns
- Includes solidity-coverage and hardhat-gas-reporter setup for measurable regressions
- Supports contract verification on Etherscan as part of the testing workflow
- Foundry-oriented guidance alongside Hardhat for comprehensive Solidity test strategy
- Example Solidity optimizer configuration uses 200 runs in Hardhat settings
- Hardhat config example pins mainnet fork blockNumber 15000000
- Gas reporter and solidity-coverage plugins integrated in the sample toolchain
Adoption & trust: 7k installs on skills.sh; 36.5k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You have Solidity contracts headed to mainnet but lack a disciplined Hardhat or Foundry suite with forks, gas metrics, and coverage gates.
Who is it for?
Indie protocol authors validating DeFi or NFT contracts on Ethereum testnets with Hardhat toolbox and optional Foundry workflows.
Skip if: Builders who only need frontend wallet connect testing with no on-chain contract logic, or teams already standardized on a different chain stack with no Solidity.
When should I use this skill?
Testing Solidity contracts, setting up blockchain test suites, validating DeFi protocols, or using Hardhat/Foundry with forks, fuzz, gas, or coverage.
What do I get? / Deliverables
You leave with runnable test configuration, patterns for forked and fuzz tests, and reporting hooks so contracts are validated before deploy and verification.
- Configured `hardhat.config.js` (or Foundry profile) with networks and reporters
- Unit and integration test files with optional fork and fuzz coverage
Recommended Skills
Journey fit
How it compares
On-chain contract test harnesses—not generic Jest UI testing or manual testnet clicking alone.
Common Questions / FAQ
Who is web3-testing for?
Solo developers and small teams writing Solidity who need structured Hardhat/Foundry suites before mainnet or audit handoff.
When should I use web3-testing?
In the ship testing phase when writing contract unit tests, enabling mainnet forks, running gas or fuzz campaigns, or preparing Etherscan verification from CI.
Is web3-testing safe to install?
Check the Security Audits panel on this page; the skill references private keys and RPC URLs—never commit real secrets to repos the agent can read.
SKILL.md
READMESKILL.md - Web3 Testing
# Web3 Smart Contract Testing Master comprehensive testing strategies for smart contracts using Hardhat, Foundry, and advanced testing patterns. ## When to Use This Skill - Writing unit tests for smart contracts - Setting up integration test suites - Performing gas optimization testing - Fuzzing for edge cases - Forking mainnet for realistic testing - Automating test coverage reporting - Verifying contracts on Etherscan ## Hardhat Testing Setup ```javascript // hardhat.config.js require("@nomicfoundation/hardhat-toolbox"); require("@nomiclabs/hardhat-etherscan"); require("hardhat-gas-reporter"); require("solidity-coverage"); module.exports = { solidity: { version: "0.8.19", settings: { optimizer: { enabled: true, runs: 200, }, }, }, networks: { hardhat: { forking: { url: process.env.MAINNET_RPC_URL, blockNumber: 15000000, }, }, goerli: { url: process.env.GOERLI_RPC_URL, accounts: [process.env.PRIVATE_KEY], }, }, gasReporter: { enabled: true, currency: "USD", coinmarketcap: process.env.COINMARKETCAP_API_KEY, }, etherscan: { apiKey: process.env.ETHERSCAN_API_KEY, }, }; ``` ## Unit Testing Patterns ```javascript const { expect } = require("chai"); const { ethers } = require("hardhat"); const { loadFixture, time, } = require("@nomicfoundation/hardhat-network-helpers"); describe("Token Contract", function () { // Fixture for test setup async function deployTokenFixture() { const [owner, addr1, addr2] = await ethers.getSigners(); const Token = await ethers.getContractFactory("Token"); const token = await Token.deploy(); return { token, owner, addr1, addr2 }; } describe("Deployment", function () { it("Should set the right owner", async function () { const { token, owner } = await loadFixture(deployTokenFixture); expect(await token.owner()).to.equal(owner.address); }); it("Should assign total supply to owner", async function () { const { token, owner } = await loadFixture(deployTokenFixture); const ownerBalance = await token.balanceOf(owner.address); expect(await token.totalSupply()).to.equal(ownerBalance); }); }); describe("Transactions", function () { it("Should transfer tokens between accounts", async function () { const { token, owner, addr1 } = await loadFixture(deployTokenFixture); await expect(token.transfer(addr1.address, 50)).to.changeTokenBalances( token, [owner, addr1], [-50, 50], ); }); it("Should fail if sender doesn't have enough tokens", async function () { const { token, addr1 } = await loadFixture(deployTokenFixture); const initialBalance = await token.balanceOf(addr1.address); await expect( token.connect(addr1).transfer(owner.address, 1), ).to.be.revertedWith("Insufficient balance"); }); it("Should emit Transfer event", async function () { const { token, owner, addr1 } = await loadFixture(deployTokenFixture); await expect(token.transfer(addr1.address, 50)) .to.emit(token, "Transfer") .withArgs(owner.address, addr1.address, 50); }); }); describe("Time-based tests", function () { it("Should handle time-locked operations", async function () { const { token } = await loadFixture(deployTokenFixture); // Increase time by 1 day await time.increase(86400); // Test time-dependent functionality }); }); describe("Gas optimization", function () { it("Should use gas efficiently", async function () { const { token } = await loadFixture(deployTokenFixture); const tx = a