evm-hardhat
0
总安装量
1
周安装量
安装命令
npx skills add https://github.com/celo-org/agent-skills --skill evm-hardhat
Agent 安装分布
amp
1
cline
1
opencode
1
cursor
1
kimi-cli
1
codex
1
Skill 文档
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 init
Select “Create a TypeScript project” when prompted.
Install Additional Dependencies
npm install --save-dev @nomicfoundation/hardhat-toolbox dotenv
Celo 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_here
Writing 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 compile
Testing
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 coverage
Deployment
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 celo
Verification
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.sol
Check Contract Size
npx hardhat compile --force
npx hardhat size-contracts
Block 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