广告

Hardhat 开发中的 ethers.parseUnits 的正确用法与版本迁移全攻略

ethers.parseUnits 的正确用法基础

什么是 parseUnits

在以太坊开发循环中,常需要将带小数的以太单位转换为最小单位 wei,以便与合约参数和交易金额对齐。ethers.parseUnits 是 ethers.js 提供的核心工具,用来完成这种单位转换,返回一个,表示对应的 wei 数量。该函数接收两个参数:一个是数额的字符串表示,另一个是单位的名称或小数位数。核心作用是消除浮点计算带来的误差,确保链上交互的一致性。

通过使用字符串输入而非 JavaScript 的 Number 类型,可以避免二进制浮点数运算导致的舍入问题,尤其在涉及小数点后多位时尤为重要。字符串输入让数值的精度保持可控。

const amount = ethers.utils.parseUnits("1.234", 18);
console.log(amount.toString()); // 1234000000000000000 wei

常用单位与参数

parseUnits 的第二个参数既可以是数字表示的小数位,也可以是单位名称的字符串。常见单位包括 'wei'、'kwei'、'mwei'、'gwei'、'ether' 等;使用字符串如 'ether' 可以让表达更直观,等效于 18 位小数。单位灵活性提升了脚本的可读性与维护性。

Hardhat 开发中的 ethers.parseUnits 的正确用法与版本迁移全攻略

例如,等价写法有两种:数值位数单位名称

const oneEthWei = ethers.utils.parseUnits("1.0", 18);      // 1.0 ETH,等同于 1e18 wei
const oneEthWeiAlt = ethers.utils.parseUnits("1.0", "ether");    // 同样是 1.0 ETH,单位名称写法

在 Hardhat 项目中的实际应用

部署脚本中的用法

在 Hardhat 的部署脚本中,往往需要将人类可读的金额转换为 wei,作为交易的 value 参数或合约方法的输入。确保输入始终是字符串形式,且单位写明,以避免隐式类型推导带来的误差。

下面的示例展示了在部署阶段将指定金额以 wei 发送给合约的 deposit 方法。

// 通过 Hardhat 的 ethers 插件获取签名与合约实例
const amountWei = ethers.utils.parseUnits("2.5", "ether");
await contract.deposit({ value: amountWei });

测试用例中的用法

测试场景中,parseUnits 能提供稳定的单位转换,便于断言结果是否如预期。通过将输入统一转换为 wei,可以在不同环境(如本地测试网络、测试网、主网)保持一致性。断言前统一单位,避免因为单位不一致导致的测试失败。

示例测试用例演示如何对余额进行断言。

test('deposit increases balance', async () => {const amount = ethers.utils.parseUnits("0.1", "ether");await contract.connect(user).deposit({ value: amount });const balance = await contract.balanceOf(user.address);expect(balance).to.equal(amount);
});

版本迁移全攻略

评估与准备

在升级 Hardhat、ethers.js 或相关插件之前,先评估对 parseUnits 的影响,阅读官方变更日志与迁移指南,确保新版本对 API 行为的理解一致。在专用分支上逐步迭代,避免直接影响生产分支。

重点检查的依赖包括 Hardhat、@nomiclabs/hardhat-ethers、ethers,以及可能使用的 TypeChain、TypeScript 配置等。对比各版本的变更项,确认 parseUnits 的实现路径、单位识别和 BigNumber 行为是否有改动。先做本地回归,再推向持续集成环境。

{"dependencies": {"ethers": "^5.7.0","hardhat": "^2.20.0","@nomiclabs/hardhat-ethers": "^2.1.6"}
}

代码层面的改动要点

通常 parseUnits 的接口在 v5 与 v6 之间保持兼容性,但若你将 ethers.js 升级到新的主版本,务必关注导入路径、BigNumber 的行为以及 toString 的输出格式等潜在差异。统一使用 ethers.utils.parseUnits 的调用入口,避免在不同文件中混用不同的导入方式。

以下是一个兼容性良好的写法示例,适用于大多数 v5/v6 场景,确保数额与单位的明确性。

// 兼容写法(v5/v6 均可使用)
const weiAmount = ethers.utils.parseUnits("3.1415", 18); // 3.1415 ETH

如果迁移到新版本,注意查看 BigNumber.toString() 的输出是否有差异,必要时统一格式后再进行断言。

expect(balance.toString()).to.equal(weiAmount.toString());

测试与回归

完成版本升级后,执行完整的测试套件,新增或覆盖 parseUnits 的边界用例,例如:超过小数位、非数字字符、空字符串、极大数值等情况,确保新版本下行为稳定。

test('parseUnits boundary cases', () => {const maxDecimals = "1.0000";const wei = ethers.utils.parseUnits(maxDecimals, 4);expect(wei).to.be.an('object'); // BigNumber
});

最佳实践与常见坑点

避免浮点误差与输入格式

核心实践是始终使用字符串形式的金额,并显式指定单位,避免使用 JavaScript 的 Number 类型进行直接运算。字符串输入 + 明确单位是保证精度的关键组合。

此外,优先使用单位名称(如 'ether'、'gwei')来提升代码可读性,减少单位混淆带来的错配风险。

const amount = ethers.utils.parseUnits("0.1", "ether"); // 0.1 ETH

单位兼容性与链上对齐

虽然 parseUnits 支持多种单位,但在跨链或跨网络的场景中,仍应保持单位写法的一致性,以防止链上参数解释错误。对于测试环境和主网部署,尽量采用相同的单位命名和数额格式。统一标准有助于回溯与审计

在跨链脚本中,建议建立一个常量表来维护单位与小数位的映射,减少手动修改的风险。

const ETH = "ether";
const priceWei = ethers.utils.parseUnits("1000", "gwei"); // 1000 Gwei

广告