如何在 Solidity 中正确测试异常?

问题描述

我是 solidity 的新手,我正在尝试按照 ERC-20 token standard 创建我的第一个智能合约。按照标准要求,我有一个 transfer 函数,如下所示:

function transfer(address _to,uint256 _value) public returns (bool success) {
    require(balanceOf[msg.sender] >= _value);

    // transfer the amount
    balanceOf[msg.sender] -= _value;
    balanceOf[_to] += _value;

    // trigger a Transfer event
    emit Transfer(msg.sender,_to,_value);

    return true;
}

正如所见,我首先进行健全性检查,确保发件人有足够的金额与 require(balanceOf[msg.sender] >= _value) 执行交易。我现在想测试代码的特定部分,看看如果数量太大,它是否真的会抛出预期的错误。我正在使用 truffle 和有用的 truffle-assertions package

const truffleAssert = require("truffle-assertions");

it("should revert transfer if the amount is larger than the sender's balance",() => {
  return MyToken.deployed().then((instance) =>
    truffleAssert.fails(
      instance.transfer.call(accounts[1],10000000),truffleAssert.ErrorType.REVERT
    )
  );
});

这似乎按预期工作。但是,如果我从 require 函数删除 transfer 部分,我希望测试失败,但测试以某种方式通过。来自 Java 背景,这让我怀疑我是否真正正确地测试了代码。在 solidity 中测试此类场景的正确且可靠的方法是什么?

解决方法

Solidity 在 0.8.0 版本中引入了整数上溢/下溢的自动异常。

算术运算在下溢和上溢时恢复。您可以使用 unchecked { ... } 来使用之前的包装行为。

来源:docs

如果您使用 Solidity ^0.8,它仍然会抛出 balanceOf[msg.sender] -= _value;(假设 value 大于 balanceOf[msg.sender]),否则 balanceOf[msg.sender] 值会下溢。


您会在许多来源中看到使用 require 语句进行的精确检查,因为它们要么是在 0.8 版之前创建的,要么它们的作者可能没有意识到这一新功能。或者他们可能想使用 require(false,'error message') 的第二个参数传递自定义错误消息(自动异常不提供任何消息)。但从 0.8 开始,不再需要它了。