English | 简体中文
这是一个用于学习和开发的本地以太坊区块链节点环境,使用 Hardhat + Ganache v7 + ethers.js。
- Hardhat v2.19.5 - 以太坊开发环境
- Ganache v7.9.2 - 本地区块链节点(支持最新 EVM)
- ethers.js v5.7.2 - 以太坊 JavaScript 库
- Solidity 0.8.20 - 智能合约语言
- Mocha + Chai - 测试框架
- Node.js (v20 或更高版本) - 推荐 v20 LTS
- npm (v10+)
本项目需要 Node.js v20+。如果你还没有,使用 nvm:
# 安装 nvm (如果还没有)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
# 安装并使用 Node.js 20
nvm install 20
nvm use 20
nvm alias default 20make install
# 或
npm install在一个终端窗口中运行:
make node这将启动一个本地以太坊节点(Ganache v7):
- RPC URL:
http://127.0.0.1:8545 - Chain ID:
1337 - 自动生成 10 个测试账户,每个账户有 10000 ETH
- 支持自动挖矿
- 使用固定助记词(便于开发)
节点启动后会显示所有可用账户及其私钥。
在另一个终端窗口中运行:
make accountsmake compile编译使用 Hardhat,生成 ABI 和字节码,保存在 artifacts/ 目录。
确保节点正在运行,然后:
make deploy这将:
- 部署
SimpleStorage合约 - 设置初始值为 42
- 保存合约地址到
deployment.json
make interact这将执行以下操作:
- 读取合约当前存储的值
- 设置新值为 100
- 增加 50(最终值 150)
- 查询所有事件日志
eth/
├── contracts/ # 智能合约
│ └── SimpleStorage.sol
├── scripts/ # 脚本
│ ├── deploy-hardhat.js # Hardhat 部署脚本
│ ├── interact-hardhat.js # Hardhat 交互脚本
│ ├── deploy.js # 原生 ethers.js 脚本(备用)
│ ├── interact.js # 原生 ethers.js 脚本(备用)
│ └── accounts.js # 账户查询脚本
├── test/ # 测试文件
│ └── SimpleStorage.test.js
├── examples/ # 学习示例
│ ├── basic-operations.js
│ ├── contract-events.js
│ └── wallet-operations.js
├── artifacts/ # 编译输出(Hardhat生成)
├── cache/ # 编译缓存
├── deployment.json # 部署信息
├── hardhat.config.js # Hardhat 配置
├── package.json # 项目依赖
├── makefile # 快捷命令
├── start-node.sh # 节点启动脚本
└── README.md # 说明文档
| 命令 | 说明 |
|---|---|
make help |
显示所有可用命令 |
make install |
安装依赖 |
make node |
启动 Ganache v7 节点(允许远程访问) |
make node-local |
启动节点(仅本机访问) |
make stop |
停止节点 |
make compile |
编译智能合约(Hardhat) |
make deploy |
部署合约到本地节点 |
make interact |
与合约交互(读写数据、查询事件) |
make accounts |
查看所有账户及余额 |
make test |
运行测试套件 |
make clean |
清理编译文件 |
创建一个新的 JavaScript 文件来与本地节点交互:
const { ethers } = require('ethers');
// 连接到本地节点
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
async function main() {
// 获取区块号
const blockNumber = await provider.getBlockNumber();
console.log('当前区块高度:', blockNumber);
// 获取账户
const accounts = await provider.listAccounts();
console.log('第一个账户:', accounts[0]);
// 获取余额
const balance = await provider.getBalance(accounts[0]);
console.log('余额:', ethers.utils.formatEther(balance), 'ETH');
}
main();const { ethers } = require('ethers');
const fs = require('fs');
async function interactWithContract() {
// 连接到本地节点
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
// 获取签名者
const signer = provider.getSigner();
// 读取合约信息
const deployment = JSON.parse(fs.readFileSync('deployment.json', 'utf8'));
const artifact = JSON.parse(fs.readFileSync('artifacts/SimpleStorage.json', 'utf8'));
// 连接到合约
const contract = new ethers.Contract(
deployment.contractAddress,
artifact.abi,
signer
);
// 读取数据(不消耗 gas)
const value = await contract.get();
console.log('存储的值:', value.toString());
// 写入数据(需要 gas)
const tx = await contract.set(200);
await tx.wait(); // 等待交易确认
console.log('新值已设置');
// 监听事件
contract.on('DataStored', (oldValue, newValue, setter) => {
console.log('值变化:', oldValue.toString(), '->', newValue.toString());
});
}
interactWithContract();const { ethers } = require('ethers');
async function sendETH() {
const provider = new ethers.providers.JsonRpcProvider('http://127.0.0.1:8545');
const signer = provider.getSigner();
// 发送 1 ETH
const tx = await signer.sendTransaction({
to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
value: ethers.utils.parseEther('1.0')
});
console.log('交易哈希:', tx.hash);
await tx.wait();
console.log('交易已确认');
}
sendETH();默认助记词:
test test test test test test test test test test test junk
默认账户(前 3 个):
账户 #0: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
私钥: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
账户 #1: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
私钥: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
账户 #2: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
私钥: 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
这是一个简单的存储合约,包含以下功能:
get()- 获取存储的值(只读)set(uint256 x)- 设置新值increment(uint256 x)- 增加值owner()- 获取合约所有者地址
DataStored(uint256 oldValue, uint256 newValue, address setter)- 当值改变时触发
- 在
contracts/目录创建新的.sol文件 - 运行
make compile(Hardhat 会自动编译所有合约) - 创建对应的部署脚本(参考
scripts/deploy-hardhat.js) - 部署并测试
- Hardhat 文档 - 开发框架
- Ethers.js v5 文档 - JavaScript 库
- Solidity 文档 - 智能合约语言
- 以太坊开发文档 - 官方开发指南
- Ganache 文档 - 本地节点
- OpenZeppelin - 标准合约库
- Node.js 版本: 需要 v20 或更高版本(建议使用 nvm 管理)
- 数据不持久化: 每次重启节点,所有数据都会重置
- 仅用于开发: 这是本地测试网络,不要用真实私钥
- Gas 费用低: 本地节点的 Gas 价格与主网不同
- 自动挖矿: 交易立即被打包确认
- 固定助记词: 便于开发,但不要在生产中使用
- 远程访问:
make node允许局域网访问,make node-local仅本机
make stop或者在运行节点的终端按 Ctrl+C。
确保节点正在运行:
make node验证节点状态:
ps aux | grep ganache检查 Solidity 语法,确保使用 0.8.x 版本。Hardhat 会自动检测错误。
- 检查节点是否运行(
make stop然后make node) - 确保账户有足够的 ETH
- 查看错误信息
如果遇到 Node.js 版本错误:
nvm install 20
nvm use 20现在你已经有了一个完整的本地以太坊开发环境!你可以:
- 修改 SimpleStorage 合约,添加新功能
- 创建自己的智能合约
- 学习 Solidity 语言
- 尝试更复杂的合约交互
- 构建 DApp 前端
Happy Coding! 🎉