Development Tools
Frameworks, libraries, and utilities for EVM development
Appendix D: Development Tools
This appendix covers the essential tools for EVM smart contract development in 2025.
Frameworks
Frameworks automate tedious tasks and make development easier. In 2025, the two dominant frameworks for EVM development are Hardhat (JavaScript/TypeScript) and Foundry (Solidity-native). Both work identically on Ethereum Classic and Ethereum—they connect to any EVM chain via RPC.
Hardhat
- GitHub: github.com/NomicFoundation/hardhat
- Website: hardhat.org
- Documentation: hardhat.org/docs
Hardhat is the most widely used Ethereum development framework for JavaScript and TypeScript developers. It provides a flexible, extensible environment for compiling, deploying, testing, and debugging smart contracts.
Key features:
- Hardhat Network: Built-in local blockchain with advanced debugging (console.log in Solidity, stack traces, mainnet forking)
- TypeScript support: First-class TypeScript integration with type generation
- Plugin ecosystem: Rich ecosystem of plugins for testing, deployment, verification
- Multi-network: Easy configuration for ETC, ETH, and testnets
Installing Hardhat
Hardhat requires Node.js v18+ and npm. Install using nvm:
nvm install --lts
node -v # v20.11.0
npm -v # 10.2.4Create a project:
mkdir Faucet
cd Faucet
npm init -y
npm install --save-dev hardhat
npx hardhat initSelect "Create a TypeScript project" for the best experience. The project structure:
Faucet
├── contracts/
│ └── Lock.sol
├── ignition/
│ └── modules/
├── test/
│ └── Lock.ts
├── hardhat.config.ts
├── package.json
└── tsconfig.json
Install additional dependencies:
npm install --save-dev @nomicfoundation/hardhat-toolbox
npm install @openzeppelin/contractsConfiguring for ETC and ETH
Edit hardhat.config.ts:
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import * as dotenv from "dotenv";
dotenv.config();
const config: HardhatUserConfig = {
solidity: {
version: "0.8.24",
settings: {
optimizer: { enabled: true, runs: 200 },
evmVersion: "shanghai",
},
},
networks: {
// ETC Networks
mordor: {
url: process.env.MORDOR_RPC || "https://rpc.mordor.etccooperative.org",
chainId: 63,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
etc: {
url: process.env.ETC_RPC || "https://etc.rivet.link",
chainId: 61,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
// ETH Networks
sepolia: {
url: process.env.SEPOLIA_RPC || "https://rpc.sepolia.org",
chainId: 11155111,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
mainnet: {
url: process.env.ETH_RPC || "https://eth.llamarpc.com",
chainId: 1,
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
},
};
export default config;Create a .env file (add to .gitignore!):
PRIVATE_KEY=your_private_key_here
Deploying with Hardhat Ignition
Hardhat uses Ignition for deployments—a declarative system that tracks deployments and handles dependencies automatically.
Create ignition/modules/Faucet.ts:
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
const FaucetModule = buildModule("FaucetModule", (m) => {
const faucet = m.contract("Faucet");
return { faucet };
});
export default FaucetModule;Deploy:
npx hardhat compile
npx hardhat ignition deploy ignition/modules/Faucet.ts --network mordorUsing the Hardhat Console
npx hardhat console --network mordor> const [signer] = await ethers.getSigners()
> const balance = await ethers.provider.getBalance(signer.address)
> ethers.formatEther(balance)
'3.2'
> const Faucet = await ethers.getContractFactory("Faucet")
> const faucet = Faucet.attach("0x1234...5678")
> await faucet.withdraw(ethers.parseEther("0.1"))Foundry
- GitHub: github.com/foundry-rs/foundry
- Documentation: book.getfoundry.sh
Foundry is a blazing-fast toolkit written in Rust, particularly popular for:
- Solidity-native testing: Write tests in Solidity, not JavaScript
- Speed: Extremely fast compilation and test execution
- Fuzzing: Built-in property-based testing
- Mainnet forking: Test against real mainnet state
- Gas reporting: Detailed gas usage analysis
Foundry consists of:
- Forge: Testing framework
- Cast: Command-line tool for interacting with contracts
- Anvil: Local testnet node
- Chisel: Solidity REPL
Installing Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup
forge --versionCreating a Foundry Project
forge init Faucet
cd FaucetStructure:
Faucet
├── src/
│ └── Counter.sol
├── test/
│ └── Counter.t.sol
├── script/
├── foundry.toml
└── lib/
Configuring for ETC
Edit foundry.toml:
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.24"
evm_version = "shanghai"
optimizer = true
optimizer_runs = 200
[rpc_endpoints]
mordor = "https://rpc.mordor.etccooperative.org"
etc = "https://etc.rivet.link"
sepolia = "https://rpc.sepolia.org"
mainnet = "https://eth.llamarpc.com"Writing Solidity Tests
// test/Faucet.t.sol
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
import "../src/Faucet.sol";
contract FaucetTest is Test {
Faucet public faucet;
address public user;
function setUp() public {
faucet = new Faucet();
user = makeAddr("user");
vm.deal(address(faucet), 1 ether);
}
function test_Withdraw() public {
vm.prank(user);
faucet.withdraw(0.1 ether);
assertEq(user.balance, 0.1 ether);
}
function testFuzz_Withdraw(uint256 amount) public {
amount = bound(amount, 1, 0.1 ether);
vm.prank(user);
faucet.withdraw(amount);
assertEq(user.balance, amount);
}
}Run tests:
forge testDeploying with Foundry
Create script/Deploy.s.sol:
pragma solidity ^0.8.24;
import "forge-std/Script.sol";
import "../src/Faucet.sol";
contract DeployScript is Script {
function run() public {
vm.startBroadcast();
new Faucet();
vm.stopBroadcast();
}
}Deploy:
forge script script/Deploy.s.sol --rpc-url mordor --broadcast --private-key $PRIVATE_KEYOpenZeppelin Contracts
- GitHub: github.com/OpenZeppelin/openzeppelin-contracts
- Documentation: docs.openzeppelin.com/contracts
The most widely used library of secure, audited smart contracts. Version 5.x supports Solidity ^0.8.20.
Key contracts:
- ERC20, ERC721, ERC1155: Token standard implementations
- AccessControl, Ownable: Role-based and simple access control
- ReentrancyGuard: Protection against reentrancy attacks
- Pausable: Emergency stop functionality
- SafeERC20: Safe token transfer wrappers
Installing
With Hardhat:
npm install @openzeppelin/contractsWith Foundry:
forge install OpenZeppelin/openzeppelin-contractsAdd to remappings.txt:
@openzeppelin/=lib/openzeppelin-contracts/
Example: ERC20 Token
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
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) public onlyOwner {
_mint(to, amount);
}
}Libraries
ethers.js
The most widely used Ethereum library in 2025, known for its clean API and TypeScript support. Default library for Hardhat.
- GitHub: github.com/ethers-io/ethers.js
- Documentation: docs.ethers.org
import { ethers } from "ethers";
// Connect to Mordor testnet
const provider = new ethers.JsonRpcProvider(
"https://rpc.mordor.etccooperative.org"
);
// Get balance
const balance = await provider.getBalance("0x...");
console.log(ethers.formatEther(balance), "METC");
// Connect wallet
const wallet = new ethers.Wallet(privateKey, provider);
// Interact with contract
const faucet = new ethers.Contract(address, abi, wallet);
await faucet.withdraw(ethers.parseEther("0.1"));viem
A modern TypeScript-first alternative to ethers.js, designed for smaller bundle sizes. Used by wagmi and RainbowKit.
- GitHub: github.com/wevm/viem
- Documentation: viem.sh
import { createPublicClient, http } from "viem";
// Define ETC Mordor chain
const mordor = {
id: 63,
name: "Mordor",
network: "mordor",
nativeCurrency: { name: "Mordor Ether", symbol: "METC", decimals: 18 },
rpcUrls: {
default: { http: ["https://rpc.mordor.etccooperative.org"] },
},
};
const client = createPublicClient({
chain: mordor,
transport: http(),
});
const balance = await client.getBalance({ address: "0x..." });Other Libraries
| Library | Language | Documentation |
|---|---|---|
| web3.js | JavaScript | docs.web3js.org |
| web3.py | Python | web3py.readthedocs.io |
| web3j | Java/Kotlin | docs.web3j.io |
| Nethereum | .NET | docs.nethereum.com |
Testing Smart Contracts
Test Frameworks Summary
| Framework | Test Language | Testing Framework | Local Blockchain |
|---|---|---|---|
| Hardhat | TypeScript/JavaScript | Mocha + Chai | Hardhat Network |
| Foundry | Solidity | forge-std | Anvil |
Hardhat Testing
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Faucet", function () {
it("Should allow withdrawal", async function () {
const Faucet = await ethers.getContractFactory("Faucet");
const faucet = await Faucet.deploy();
const [owner] = await ethers.getSigners();
await owner.sendTransaction({
to: faucet.target,
value: ethers.parseEther("1.0")
});
await expect(
faucet.withdraw(ethers.parseEther("0.1"))
).to.changeEtherBalance(owner, ethers.parseEther("0.1"));
});
});Run: npx hardhat test
Foundry Testing
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
import "../src/Faucet.sol";
contract FaucetTest is Test {
Faucet faucet;
function setUp() public {
faucet = new Faucet();
vm.deal(address(faucet), 1 ether);
}
function test_Withdraw() public {
address user = makeAddr("user");
vm.prank(user);
faucet.withdraw(0.1 ether);
assertEq(user.balance, 0.1 ether);
}
}Run: forge test
Local Test Blockchains
Hardhat Network:
npx hardhat test # Auto-starts for tests
npx hardhat node # Standalone modeFeatures: Solidity stack traces, console.log, mainnet forking, time manipulation.
Anvil:
anvil
anvil --chain-id 63 # Mordor chain IDFeatures: Fast execution, mainnet forking, state snapshots, address impersonation.
When to Use Mordor vs Local
| Scenario | Recommended |
|---|---|
| Unit tests | Hardhat Network or Anvil |
| Integration tests | Local (with forking if needed) |
| Pre-deployment verification | Mordor testnet |
| Testing PoW mechanics | Mordor testnet |
| CI/CD pipelines | Hardhat Network or Anvil |
Command-Line Utilities
Cast (Foundry)
# Get transaction receipt
cast receipt <txhash> --rpc-url mordor
# Get contract code
cast code <address> --rpc-url mordor
# Read storage slot
cast storage <address> <slot> --rpc-url mordor
# Call a view function
cast call <address> "getBalance()" --rpc-url mordor
# Send a transaction
cast send <address> "withdraw(uint256)" 100000000000000000 --rpc-url mordor --private-key $KEYhelpeth (EthereumJS)
helpeth keyGenerate # Generate new key
helpeth keyDetails # Print key details
helpeth parseTx <tx> # Parse raw transaction
helpeth unitConvert 1 ether wei # Convert unitsAgentic Development Tools
AI-assisted development is transforming EVM development. These tools enable small teams to achieve what previously required large engineering organizations.
Claude Code
Claude Code is Anthropic's official CLI for AI-assisted development. It integrates directly with your development workflow.
- Documentation: docs.anthropic.com/claude-code
- Installation:
npm install -g @anthropic-ai/claude-code
Key features for EVM development:
- Direct integration with Foundry and Hardhat
- Contract generation, review, and testing
- On-chain data access via MCP servers
- Project-specific configuration via CLAUDE.md
Setting Up for EVM Projects
Create a CLAUDE.md file in your project root:
# CLAUDE.md - EVM Development
You are developing smart contracts for Ethereum Classic and Ethereum.
## Project Context
- Framework: Foundry (or Hardhat)
- Networks: Mordor (ETC testnet), Sepolia (ETH testnet)
- Solidity version: 0.8.24
## Development Principles
- Prioritize security over convenience
- Test thoroughly before deployment
- Document all external interactions
- Consider gas optimization for production
## Network Details
- ETC Mainnet: chainId 61, https://etc.rivet.link
- Mordor Testnet: chainId 63, https://rpc.mordor.etccooperative.orgMCP Servers for On-Chain Data
The Model Context Protocol (MCP) connects Claude to external data sources. Configure in .claude/settings.json:
{
"mcpServers": {
"ethereum": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-ethereum"],
"env": {
"RPC_URL": "https://rpc.mordor.etccooperative.org"
}
}
}
}With MCP enabled, Claude can:
- Query on-chain state and balances
- Fetch and analyze deployed contract code
- Check transaction receipts and logs
- Verify contract deployments
Agentic Workflows
Effective patterns for AI-assisted EVM development:
Contract Generation:
Generate an ERC-20 token with fixed supply, no admin functions,
and full NatSpec documentation. Include Foundry tests.
Security Review:
Review this contract for reentrancy, access control issues,
and centralization risks. Check against common vulnerability patterns.
Test Generation:
Generate comprehensive Foundry tests including:
- Unit tests for each function
- Fuzz tests for input validation
- Invariant tests for critical properties
Deployment Verification:
Verify the deployed contract at <address> on Mordor matches
the source code. Check constructor arguments and initialization.
Best Practices
- Encode project principles in CLAUDE.md for consistent output
- Review generated code before deployment—agents can hallucinate
- Test on Mordor/Sepolia before mainnet deployment
- Use agents for routine tasks, humans for architecture decisions
- Combine AI review with human review for production code
See Chapter 18: Agentic Development for comprehensive coverage of AI-assisted blockchain development.
Resources
- Hardhat: hardhat.org
- Foundry Book: book.getfoundry.sh
- OpenZeppelin: docs.openzeppelin.com
- ethers.js: docs.ethers.org
- viem: viem.sh
- Claude Code: docs.anthropic.com/claude-code