Hardhat Setup Skill
This skill provides templates, scripts, and best practices for setting up Hardhat-based Solidity projects.
When to Use
Use this skill when:
- •Initializing a new Hardhat project
- •Adding Hardhat to an existing Solidity codebase
- •Configuring Hardhat settings (networks, plugins, etc.)
- •Setting up Hardhat in a hybrid Foundry/Hardhat project
- •Updating Hardhat configuration
Prerequisites: Node.js and npm/yarn/pnpm must be installed
Integration with Framework Detection
Before using this skill, reference the framework-detection skill to:
- •Check if Hardhat is already configured
- •Determine if this is a hybrid setup
- •Avoid overwriting existing configuration
Quick Setup
Basic Initialization
# Initialize new Hardhat project npm init -y npm install --save-dev hardhat npx hardhat init # Or with yarn yarn init -y yarn add --dev hardhat npx hardhat init
Project Structure
Hardhat creates this structure:
project/ ├── hardhat.config.js # Configuration ├── .env # Environment variables ├── package.json # Dependencies ├── contracts/ # Contract source files │ └── Lock.sol # Example contract ├── test/ # Test files │ └── Lock.js # Example test ├── scripts/ # Deployment scripts │ └── deploy.js # Example script └── ignition/ # Hardhat Ignition (deployment modules)
Configuration Templates
hardhat.config.js
See ./templates/hardhat.config.js for JavaScript configuration template.
Key Configuration Sections:
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
module.exports = {
solidity: {
version: "0.8.30",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
networks: {
hardhat: {},
sepolia: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
}
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY
}
};
hardhat.config.ts
See ./templates/hardhat.config.ts for TypeScript configuration template.
TypeScript Configuration:
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "dotenv/config";
const config: HardhatUserConfig = {
solidity: "0.8.30",
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
}
}
};
export default config;
Environment Variables
See ./templates/.env.example for complete environment variable template.
Essential Variables:
# RPC URLs MAINNET_RPC_URL= SEPOLIA_RPC_URL= # Private Keys (NEVER commit actual keys) PRIVATE_KEY= # Etherscan API Keys ETHERSCAN_API_KEY= # Gas Settings GAS_PRICE=
Essential Plugins
Hardhat Toolbox (Recommended)
Includes all essential plugins:
npm install --save-dev @nomicfoundation/hardhat-toolbox
Includes:
- •
@nomicfoundation/hardhat-ethers- Ethers.js integration - •
@nomicfoundation/hardhat-chai-matchers- Chai matchers for testing - •
@nomicfoundation/hardhat-verify- Contract verification - •
hardhat-gas-reporter- Gas usage reporting - •
solidity-coverage- Code coverage - •
@typechain/hardhat- TypeScript types for contracts
Individual Plugins
# Contract verification npm install --save-dev @nomicfoundation/hardhat-verify # Gas reporting npm install --save-dev hardhat-gas-reporter # Code coverage npm install --save-dev solidity-coverage # OpenZeppelin Upgrades npm install --save-dev @openzeppelin/hardhat-upgrades
Common Configurations
1. Multiple Networks
networks: {
mainnet: {
url: process.env.MAINNET_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
chainId: 1
},
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
chainId: 11155111
},
arbitrum: {
url: process.env.ARBITRUM_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
chainId: 42161
}
}
2. High Optimization for Production
solidity: {
version: "0.8.30",
settings: {
optimizer: {
enabled: true,
runs: 10000
},
viaIR: true
}
}
3. Multiple Solidity Versions
solidity: {
compilers: [
{
version: "0.8.30",
settings: {
optimizer: { enabled: true, runs: 200 }
}
},
{
version: "0.8.20",
settings: {
optimizer: { enabled: true, runs: 200 }
}
}
]
}
4. Gas Reporter Configuration
gasReporter: {
enabled: process.env.REPORT_GAS === "true",
currency: "USD",
coinmarketcap: process.env.COINMARKETCAP_API_KEY,
outputFile: "gas-report.txt",
noColors: true
}
Dependencies Management
Installing OpenZeppelin
# OpenZeppelin Contracts npm install @openzeppelin/contracts # OpenZeppelin Upgrades Plugin npm install --save-dev @openzeppelin/hardhat-upgrades
Installing Common Libraries
# Ethers.js (usually included with hardhat-toolbox) npm install ethers # Chai for testing npm install --save-dev chai # Dotenv for environment variables npm install --save-dev dotenv
Initialization Script
See ./scripts/init-hardhat.sh for automated setup.
Usage:
# Basic initialization ./scripts/init-hardhat.sh # With project name ./scripts/init-hardhat.sh my-project # With TypeScript ./scripts/init-hardhat.sh my-project --typescript
What the script does:
- •Checks if Node.js is installed
- •Initializes npm/yarn project
- •Installs Hardhat and essential plugins
- •Creates configuration files
- •Sets up .gitignore
- •Installs OpenZeppelin contracts
- •Creates example contract and test
Hybrid Setup (Hardhat + Foundry)
When adding Hardhat to an existing Foundry project:
1. Initialize Hardhat Without Overwriting
# Initialize npm project npm init -y # Install Hardhat npm install --save-dev hardhat npx hardhat init # Choose "Create an empty hardhat.config.js"
2. Configure Shared Directories
// hardhat.config.js
module.exports = {
paths: {
sources: "./src", // Use Foundry's src dir
tests: "./test", // Shared test dir
cache: "./cache_hardhat", // Separate cache
artifacts: "./artifacts"
},
solidity: "0.8.30"
};
3. Update .gitignore
# Hardhat artifacts/ cache/ cache_hardhat/ node_modules/ coverage/ coverage.json typechain-types/ # Foundry out/ lib/ # Environment .env .env.local
4. Shared Dependencies
# Install OpenZeppelin via npm (for Hardhat) npm install @openzeppelin/contracts # Foundry can reference node_modules # Add to foundry.toml: # libs = ["node_modules", "lib"]
Testing Setup
Basic Test Structure (JavaScript)
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("MyContract", function () {
let myContract;
let owner;
beforeEach(async function () {
[owner] = await ethers.getSigners();
const MyContract = await ethers.getContractFactory("MyContract");
myContract = await MyContract.deploy();
});
it("Should work correctly", async function () {
// Test implementation
expect(await myContract.someFunction()).to.equal(expectedValue);
});
});
Basic Test Structure (TypeScript)
import { expect } from "chai";
import { ethers } from "hardhat";
import { MyContract } from "../typechain-types";
describe("MyContract", function () {
let myContract: MyContract;
beforeEach(async function () {
const MyContract = await ethers.getContractFactory("MyContract");
myContract = await MyContract.deploy();
});
it("Should work correctly", async function () {
expect(await myContract.someFunction()).to.equal(expectedValue);
});
});
Running Tests
# Run all tests npx hardhat test # Run specific test npx hardhat test test/MyContract.test.js # With gas reporting REPORT_GAS=true npx hardhat test # With coverage npx hardhat coverage
Security Best Practices for Private Keys
⚠️ CRITICAL: Never store production private keys in .env files!
Recommended Approaches (in order of preference)
1. Hardware Wallets (Most Secure - Production)
Install the Ledger plugin:
npm install --save-dev @nomicfoundation/hardhat-ledger
Configure in hardhat.config.js:
require("@nomicfoundation/hardhat-ledger");
module.exports = {
networks: {
mainnet: {
url: process.env.MAINNET_RPC_URL,
ledgerAccounts: [
"0xYourLedgerAddress"
]
}
}
};
Deploy with Ledger:
npx hardhat run scripts/deploy.js --network mainnet
2. Hardhat Configuration Variables (Recommended - Built-in)
Hardhat 2.x+ includes built-in support for secure configuration variables:
# Set a configuration variable (prompts for value, stores encrypted) npx hardhat vars set PRIVATE_KEY # List all configuration variables npx hardhat vars list # Delete a variable npx hardhat vars delete PRIVATE_KEY
Use in hardhat.config.js:
const { vars } = require("hardhat/config");
module.exports = {
networks: {
sepolia: {
url: vars.get("SEPOLIA_RPC_URL"),
accounts: [vars.get("PRIVATE_KEY")]
}
}
};
This stores encrypted values in your home directory and prompts for decryption when needed.
3. hardhat-keystore Plugin (Encrypted Keystore)
Similar to Foundry's cast wallet, provides encrypted keystore management:
npm install --save-dev hardhat-keystore
Configure in hardhat.config.js:
require("hardhat-keystore");
module.exports = {
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: {
mnemonic: keystore.getMnemonic(),
// or
privateKey: keystore.getPrivateKey("deployer")
}
}
}
};
4. hardhat-secure-accounts Plugin (Third-Party Alternative)
npm install --save-dev hardhat-secure-accounts
Creates encrypted keystore files:
# Create new encrypted account npx hardhat accounts:create # Import existing private key npx hardhat accounts:import --name deployer
Use in scripts:
const { getSecureAccount } = require("hardhat-secure-accounts");
async function main() {
const signer = await getSecureAccount("deployer");
// Use signer for deployment
}
5. Interactive Environment Variable (Development Only)
Prompt for private key at runtime (doesn't store on disk):
const readline = require("readline");
async function getPrivateKey() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
return new Promise((resolve) => {
rl.question("Enter private key: ", (answer) => {
rl.close();
resolve(answer);
});
});
}
async function main() {
const privateKey = await getPrivateKey();
const wallet = new ethers.Wallet(privateKey, ethers.provider);
// Use wallet for deployment
}
6. .env Variables (Development/Testing ONLY)
⚠️ Use ONLY for local development or testnet testing with non-production keys!
require("dotenv").config();
module.exports = {
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
}
}
};
If using .env:
- •✅ Only use accounts created specifically for development/testing
- •✅ Never reuse production private keys
- •✅ Keep test funds minimal
- •✅ Add .env to .gitignore
- •❌ Never commit .env to version control
- •❌ Never use for mainnet deployments
Deployment Setup
Basic Deployment Script
// scripts/deploy.js
const hre = require("hardhat");
async function main() {
const MyContract = await hre.ethers.getContractFactory("MyContract");
const myContract = await MyContract.deploy();
await myContract.waitForDeployment();
console.log("MyContract deployed to:", await myContract.getAddress());
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Deploy Commands
# Deploy to local network npx hardhat run scripts/deploy.js # Deploy to Sepolia with hardware wallet (RECOMMENDED for production) npx hardhat run scripts/deploy.js --network sepolia # (Ledger will prompt for approval) # Deploy to Sepolia with Hardhat vars (RECOMMENDED for all deployments) # First time: npx hardhat vars set PRIVATE_KEY npx hardhat run scripts/deploy.js --network sepolia # Development only: with .env private key npx hardhat run scripts/deploy.js --network sepolia # Verify contract npx hardhat verify --network sepolia DEPLOYED_CONTRACT_ADDRESS
Best Practices
- •Secure private key management - Use hardware wallets or Hardhat Configuration Variables for all deployments; never store production keys in .env
- •Use hardhat-toolbox - Includes all essential plugins
- •TypeScript for large projects - Better type safety and IDE support
- •Comprehensive .env.example - Document all required environment variables (but discourage private keys)
- •Separate networks - Configure all networks you'll deploy to
- •Gas reporting - Enable gas reports for optimization
- •Code coverage - Run coverage regularly
- •Contract verification - Always verify deployed contracts
- •Version lock dependencies - Use exact versions in package.json
Troubleshooting
Issue: "hardhat: command not found"
# Use npx npx hardhat compile # Or install globally (not recommended) npm install -g hardhat
Issue: Module not found errors
# Clear cache and reinstall rm -rf node_modules package-lock.json npm install
Issue: Compilation errors with OpenZeppelin
# Ensure correct import paths import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
Issue: Network connection errors
# Check RPC URL in .env
# Test with curl
curl -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
$SEPOLIA_RPC_URL
Quick Reference
| Task | Command | Notes |
|---|---|---|
| Init project | npx hardhat init | Creates new project |
| Add dependency | npm install <package> | Uses npm |
| Compile | npx hardhat compile | Compiles contracts |
| Test | npx hardhat test | Runs tests |
| Coverage | npx hardhat coverage | Test coverage |
| Gas report | REPORT_GAS=true npx hardhat test | Gas usage |
| Deploy | npx hardhat run scripts/deploy.js | Run deployment |
| Verify | npx hardhat verify | Verify on Etherscan |
| Clean | npx hardhat clean | Clean artifacts |
Template Files
This skill provides the following templates:
- •
./templates/hardhat.config.js- JavaScript configuration - •
./templates/hardhat.config.ts- TypeScript configuration - •
./templates/.env.example- Environment variables template - •
./templates/deploy-script.js- Deployment script template
Scripts
This skill provides the following scripts:
- •
./scripts/init-hardhat.sh- Automated project initialization
Next Steps After Setup:
- •Configure
hardhat.config.jsfor your networks - •Copy
.env.exampleto.envand fill in values - •Install required dependencies with
npm install - •Write contracts in
contracts/ - •Write tests in
test/ - •Run
npx hardhat testto verify setup