evm-hardhat
Skill8 starsChanged 2 months agoApache-2.0View on GitHub
Hardhat development for EVM chains including Celo. Use when setting up Hardhat projects, writing Solidity contracts, compiling, testing, deploying, or verifying contracts with Hardhat.
- Reads credentials
- Installs packages
In this repository15 other files it gives its agents.
- 8004Skill
- bridgingSkill
- celo-composerSkill
- celo-defiSkill
- celo-rpcSkill
- celo-stablecoinsSkill
- contract-verificationSkill
- evm-foundrySkill
- evm-wallet-integrationSkill
- fee-abstractionSkill
- minipay-integrationSkill
- thirdwebSkill
- viemSkill
- wagmiSkill
- x402Skill
The file
Files
Hardhat Development for EVM Chains
This skill covers Hardhat setup and development for EVM-compatible chains with emphasis on Celo.
When to Use
- Setting up a new Hardhat project
- Writing and compiling Solidity smart contracts
- Testing contracts with Hardhat
- Deploying contracts to Celo or other EVM chains
- Verifying contracts on block explorers
Quick Start
Initialize Project
mkdir my-project && cd my-project
npm init -y
npm install --save-dev hardhat
npx hardhat initSelect "Create a TypeScript project" when prompted.
Install Additional Dependencies
npm install --save-dev @nomicfoundation/hardhat-toolbox dotenvCelo Network Information
| Network | Chain ID | RPC Endpoint |
| Celo Mainnet | 42220 | https://forno.celo.org |
| Celo Sepolia | 11142220 | https://forno.celo-sepolia.celo-testnet.org |
Configuration
hardhat.config.ts for Celo
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import * as dotenv from "dotenv";
dotenv.config();
const PRIVATE_KEY = process.env.PRIVATE_KEY || "";
const CELOSCAN_API_KEY = process.env.CELOSCAN_API_KEY || "";
const config: HardhatUserConfig = {
solidity: {
version: "0.8.28",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
networks: {
// Celo Mainnet
celo: {
url: "https://forno.celo.org",
chainId: 42220,
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
},
// Celo Sepolia Testnet
celoSepolia: {
url: "https://forno.celo-sepolia.celo-testnet.org",
chainId: 11142220,
accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [],
},
// Local development
hardhat: {
chainId: 31337,
},
},
etherscan: {
apiKey: {
celo: CELOSCAN_API_KEY,
celoSepolia: CELOSCAN_API_KEY,
},
customChains: [
{
network: "celo",
chainId: 42220,
urls: {
apiURL: "https://api.celoscan.io/api",
browserURL: "https://celoscan.io",
},
},
{
network: "celoSepolia",
chainId: 11142220,
urls: {
apiURL: "https://api.etherscan.io/v2/api",
browserURL: "https://sepolia.celoscan.io",
},
},
],
},
};
export default config;Environment Variables (.env)
PRIVATE_KEY=your_private_key_here
CELOSCAN_API_KEY=your_celoscan_api_key_hereWriting Contracts
Basic Contract Structure
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract MyContract {
string public name;
address public owner;
event NameChanged(string oldName, string newName);
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
constructor(string memory _name) {
name = _name;
owner = msg.sender;
}
function setName(string memory _newName) external onlyOwner {
string memory oldName = name;
name = _newName;
emit NameChanged(oldName, _newName);
}
}Using OpenZeppelin
npm install @openzeppelin/contracts// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, Ownable {
constructor() ERC20("MyToken", "MTK") Ownable(msg.sender) {
_mint(msg.sender, 1000000 * 10 ** decimals());
}
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
}Compilation
# Compile all contracts
npx hardhat compile
# Clean and recompile
npx hardhat clean && npx hardhat compileTesting
Test File Structure
// test/MyContract.test.ts
import { expect } from "chai";
import { ethers } from "hardhat";
import { MyContract } from "../typechain-types";
describe("MyContract", function () {
let contract: MyContract;
let owner: any;
let addr1: any;
beforeEach(async function () {
[owner, addr1] = await ethers.getSigners();
const MyContract = await ethers.getContractFactory("MyContract");
contract = await MyContract.deploy("Initial Name");
await contract.waitForDeployment();
});
describe("Deployment", function () {
it("Should set the right name", async function () {
expect(await contract.name()).to.equal("Initial Name");
});
it("Should set the right owner", async function () {
expect(await contract.owner()).to.equal(owner.address);
});
});
describe("setName", function () {
it("Should allow owner to change name", async function () {
await contract.setName("New Name");
expect(await contract.name()).to.equal("New Name");
});
it("Should emit NameChanged event", async function () {
await expect(contract.setName("New Name"))
.to.emit(contract, "NameChanged")
.withArgs("Initial Name", "New Name");
});
it("Should revert if non-owner tries to change name", async function () {
await expect(
contract.connect(addr1).setName("Hacked")
).to.be.revertedWith("Not owner");
});
});
});Running Tests
# Run all tests
npx hardhat test
# Run specific test file
npx hardhat test test/MyContract.test.ts
# Run with gas reporting
REPORT_GAS=true npx hardhat test
# Run with coverage
npx hardhat coverageDeployment
Deployment Script
// scripts/deploy.ts
import { ethers } from "hardhat";
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying with account:", deployer.address);
const balance = await ethers.provider.getBalance(deployer.address);
console.log("Account balance:", ethers.formatEther(balance), "CELO");
const MyContract = await ethers.getContractFactory("MyContract");
const contract = await MyContract.deploy("My Contract Name");
await contract.waitForDeployment();
const address = await contract.getAddress();
console.log("Contract deployed to:", address);
// Wait for confirmations before verification
console.log("Waiting for confirmations...");
await contract.deploymentTransaction()?.wait(5);
console.log("Deployment complete!");
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});Deploy Commands
# Deploy to local Hardhat network
npx hardhat run scripts/deploy.ts
# Deploy to Celo Sepolia testnet
npx hardhat run scripts/deploy.ts --network celoSepolia
# Deploy to Celo Mainnet
npx hardhat run scripts/deploy.ts --network celoVerification
Verify After Deployment
# Verify on Celo Sepolia
npx hardhat verify --network celoSepolia <CONTRACT_ADDRESS> "Constructor Arg 1"
# Verify on Celo Mainnet
npx hardhat verify --network celo <CONTRACT_ADDRESS> "Constructor Arg 1"Programmatic Verification
// Add to deployment script after deployment
import { run } from "hardhat";
async function verify(address: string, constructorArguments: any[]) {
console.log("Verifying contract...");
try {
await run("verify:verify", {
address,
constructorArguments,
});
console.log("Verified!");
} catch (e: any) {
if (e.message.includes("Already Verified")) {
console.log("Already verified");
} else {
console.error(e);
}
}
}Common Tasks
Flatten Contract for Manual Verification
npx hardhat flatten contracts/MyContract.sol > Flattened.solCheck Contract Size
npx hardhat compile --force
npx hardhat size-contractsBlock Explorers
- Celo Mainnet: https://celoscan.io
- Celo Sepolia: https://sepolia.celoscan.io
Additional Resources
- hardhat-config.md - Detailed configuration options
- testing-patterns.md - Advanced testing patterns
- security-checklist.md - Security best practices
Source of truth: Celopedia
These skills are focused, task-level references. For anything broader — verified contract addresses, ecosystem and protocol data, listing requirements, grants, governance, migration guides, or cross-cutting Celo questions — Celopedia is the canonical source and is kept current. Where this skill and Celopedia disagree, Celopedia wins.
npx skills add celo-org/celopedia-skillsDoes 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.

