0

I am new to Solidity, and I am trying to create my first smart contract following the ERC-20 token standard. As the standard requires, I have a transfer function which looks like following:

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;
}

As can be seen, I am first doing a sanity check that the sender has sufficient amount to perform a transaction with require(balanceOf[msg.sender] >= _value). I now want to test that specific part of the code to see if it will actually throw an expected error if the amount is too large. I am doing it using truffle with a helpful 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
    )
  );
});

This seems to be working as expected. However, if I were to remove the require part from my transfer function, I would expect the test to fail, but the test somehow passes. Coming from a Java background, this leaves me doubtful of whether I am actually testing the code properly or not. What is the correct and robust way of testing such scenarios in Solidity?

Hidayat Rzayev
  • 339
  • 3
  • 14

1 Answers1

1

Solidity introduced automatic exception on integer overflow/underflow in version 0.8.0.

Arithmetic operations revert on underflow and overflow. You can use unchecked { ... } to use the previous wrapping behaviour.

Source: docs

If you're using Solidity ^0.8, it's still going to throw on balanceOf[msg.sender] -= _value; (assuming value is larger than balanceOf[msg.sender]), because otherwise the balanceOf[msg.sender] value would underflow.


You'll see this exact check using the require statement in many sources because they were either created before the version 0.8, or their authors might have not been aware of this new feature. Or they maybe wanted to pass a custom error message using the second argument of require(false, 'error message') (the auto exception doesn't give any message). But since 0.8, it's not needed anymore.

Petr Hejda
  • 40,554
  • 8
  • 72
  • 100
  • So, basically the `require` portion can be omitted as such? And this is why my test does not fail, since the error will anyways be thrown because of underflow? – Hidayat Rzayev May 24 '21 at 14:03
  • 1
    It can be omitted only if you're using Solidity 0.8+. If you omit the statement in older versions, you'd be opening your contract to the [underflow vulnerability](https://swcregistry.io/docs/SWC-101)... Ad "this is why" - exactly. – Petr Hejda May 24 '21 at 14:06
  • Yes, I am using Solidity 0.8+. Thanks a lot, that's very helpful! – Hidayat Rzayev May 24 '21 at 14:09