Search code examples
solidityweb3js

Removing require(_balances[user] >= amount) in solidity smart contract


if i remove the require(this) from a transfer function to save gas and leave it up to the error produced if the user doesn't have enough amount will it affect the function in any bad way ?

    function transfer(address recipient, uint256 amount) external returns (bool) {
        require(_balances[msg.sender] >= amount)
            _balances[msg.sender] -= amount;
            _balances[recipient] += amount;
            
            return true;
    }

    function transfer(address recipient, uint256 amount) external returns (bool) {
            _balances[msg.sender] -= amount;
            _balances[recipient] += amount;
            
            return true;
    }


Solution

  • It depends on your solidity version and the datatype of balances.

    I'm going to assume the balances is mapping (address => uint256), because that's what most tokens use. As for the solidity versions, I'm going to split the answer.

    Solidity <= 0.7.6

    DO NOT remove the check. If you do, you open the function to integer underflow/overflow vulnerability.

    Example:

    • _balances[msg.sender] is 50
    • amount is 100

    So 50-100 usually results in -50. Since there are no negative numbers in uint, it wraps around zero, and continues the substraction from the maximum number available in the datatype. In case of uint256, the max number is 2^256 - 1 (approx. 10^77).

    Without the check, the _balances[msg.sender] -= amount; would underflow, resulting in the sender having not -50 but 2^256 - 51 tokens. Which you don't want.

    Read more about the vulnerability on https://swcregistry.io/docs/SWC-101

    Edit: There's a well-known library called SafeMath (by OpenZeppelin), that also helps you to prevent underflow/overflow. Example use:

    // reverts if underflow would happen
    _balances[msg.sender] = _balances[msg.sender].sub(amount);
    

    Solidity >= 0.8.0

    It's safe to remove the check. Solidity 0.8+ performs the underflow/overflow check by default and the transaction reverts if underflow/overflow would occur.