从Ethers.js到Viem的平滑迁移:在Hardhat 3中玩转现代以太坊测试
·
从Ethers.js到Viem的平滑迁移:在Hardhat 3中玩转现代以太坊测试
1. 为什么需要迁移到Viem?
如果你最近打开过Hardhat 3的初始化向导,可能会注意到一个明显的变化:官方现在默认推荐使用Viem而非Ethers.js作为以太坊交互库。这不是一个随意的选择,而是反映了以太坊开发工具链的进化方向。
Viem带来了几个关键优势:
- 更轻量级的架构:相比Ethers.js,Viem的包体积减少了约40%
- 更好的TypeScript支持:完整的类型推导和更严格的类型检查
- 更现代的API设计:简化了常见操作,减少了样板代码
- 原生EIP-1193兼容:与钱包连接更加无缝
// Viem的API设计示例
const client = createPublicClient({
chain: mainnet,
transport: http()
})
const blockNumber = await client.getBlockNumber()
2. 迁移前的准备工作
在开始迁移前,建议采取以下步骤确保平稳过渡:
- 备份现有项目:确保所有代码都已提交到版本控制系统
- 创建迁移分支:
git checkout -b migrate-to-viem - 记录现有测试覆盖率:运行
npx hardhat coverage保存基准数据 - 安装必要依赖:
npm install viem @nomicfoundation/hardhat-viem
npm uninstall ethers @nomicfoundation/hardhat-ethers
3. 核心差异对照表
| 功能点 | Ethers.js实现方式 | Viem等效实现 | 注意事项 |
|---|---|---|---|
| 合约实例化 | ContractFactory + deploy | deployContract | Viem简化了部署流程 |
| 交易等待 | waitForDeployment | 自动处理 | 无需显式等待 |
| 事件过滤 | contract.filters.EventName | getContractEvents | 查询语法更直观 |
| 数值转换 | ethers.parseEther | parseEther (直接从viem导入) | 功能相同但来源不同 |
4. 逐步迁移指南
4.1 更新Hardhat配置
首先修改hardhat.config.ts:
import "@nomicfoundation/hardhat-viem";
const config: HardhatUserConfig = {
// 其他配置保持不变...
networks: {
localhost: {
url: "http://127.0.0.1:8545"
}
}
};
4.2 重写部署脚本
对比两种写法的差异:
// Ethers.js版本
import { ethers } from "hardhat";
async function main() {
const contract = await ethers.deployContract("MyContract", [arg1, arg2]);
await contract.waitForDeployment();
console.log(`Deployed to ${await contract.getAddress()}`);
}
// Viem版本
import { viem } from "hardhat";
async function main() {
const contract = await viem.deployContract("MyContract", [arg1, arg2]);
console.log(`Deployed to ${contract.address}`);
}
4.3 重构测试用例
测试文件的结构变化较大:
// 之前(Mocha + Ethers.js)
import { expect } from "chai";
import { ethers } from "hardhat";
describe("MyContract", function() {
it("should work", async function() {
const contract = await ethers.deployContract("MyContract");
await contract.doSomething();
expect(await contract.getValue()).to.equal(42);
});
});
// 现在(Node Test Runner + Viem)
import { describe, it } from "node:test";
import { expect } from "chai";
import { viem } from "hardhat";
describe("MyContract", () => {
it("should work", async () => {
const contract = await viem.deployContract("MyContract");
await contract.write.doSomething();
expect(await contract.read.getValue()).to.equal(42);
});
});
5. 常见问题解决方案
5.1 类型不匹配错误
Viem有更严格的类型检查,可能会暴露之前隐藏的类型问题。解决方案:
// 明确指定参数类型
const result = await contract.read.getValue<[bigint], bigint>([arg]);
5.2 事件处理差异
Viem的事件查询API更强大但语法不同:
const events = await publicClient.getContractEvents({
address: contract.address,
abi: contract.abi,
eventName: "ValueChanged",
fromBlock: 0n
});
5.3 钱包交互模式
连接钱包的代码需要调整:
// 之前
const signer = await ethers.provider.getSigner();
await contract.connect(signer).doSomething();
// 现在
const [client, walletClient] = await Promise.all([
viem.getPublicClient(),
viem.getWalletClient()
]);
await walletClient.writeContract({
address: contract.address,
abi: contract.abi,
functionName: "doSomething"
});
6. 性能优化技巧
迁移完成后,可以进一步优化测试性能:
- 并行测试:Node Test Runner原生支持并行测试
- 智能合约缓存:利用Hardhat的缓存机制加速重复部署
- 类型预生成:使用
hardhat-typechain生成类型定义
# 并行运行测试
npx hardhat test --parallel
7. 迁移后的验证步骤
为确保迁移没有引入回归问题:
- 运行完整的测试套件:
npx hardhat test - 比较测试覆盖率:
npx hardhat coverage - 执行端到端测试:部署到测试网验证实际交互
- 性能基准测试:比较测试执行时间
提示:可以使用
git diff对比迁移前后的测试输出,确保行为一致
8. 高级用法探索
Viem提供了许多Ethers.js没有的高级功能:
多链交互:
const clients = {
mainnet: createPublicClient({ chain: mainnet, transport: http() }),
polygon: createPublicClient({ chain: polygon, transport: http() })
};
批量交易:
const hash = await walletClient.sendTransactions({
requests: [
{ to: "0x...", value: parseEther("1") },
{ to: contract.address, data: encodeFunctionData(...) }
]
});
实时订阅:
const unwatch = publicClient.watchBlockNumber({
onBlockNumber: (blockNumber) => {
console.log(`New block: ${blockNumber}`);
}
});
// 取消订阅
unwatch();
迁移到Viem和Node Test Runner不仅是技术栈的更新,更是开发体验的升级。虽然初期需要适应新的API,但长期来看,这将带来更高效、更类型安全的开发流程。
更多推荐
所有评论(0)