Search code examples
ethereumsolidityopenzeppelin

What is the benefit of using uncheked?


In OpenZeppelin ERC20 implementation, there is a _transfer method:

function _transfer(
    address sender,
    address recipient,
    uint256 amount
) internal virtual {
    require(sender != address(0), "ERC20: transfer from the zero address");
    require(recipient != address(0), "ERC20: transfer to the zero address");

    _beforeTokenTransfer(sender, recipient, amount);

    uint256 senderBalance = _balances[sender];
    require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
    unchecked {
        _balances[sender] = senderBalance - amount;
    }
    _balances[recipient] += amount;

    emit Transfer(sender, recipient, amount);

    _afterTokenTransfer(sender, recipient, amount);
}

Why do they use uncheked arithmetic for decreasing the balance? I know that in case of unchecked, 2-3 will return 2**256-1 and not case an exception. But why do we need this?


Solution

  • unchecked produces smaller bytecode compared to regular arthimetic operations, as it doesn't contain the underflow/overflow validation.

    So if you want to have a custom error message in case overflow would occur, this code costs less gas to run

    uint256 senderBalance = _balances[sender];
    require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
    unchecked {
    
        // no validation here as it's already validated in the `require()` condition
        _balances[sender] = senderBalance - amount;
    }
    

    compared to this one

    uint256 senderBalance = _balances[sender];
    require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
    
    // redundant validation here
    _balances[sender] = senderBalance - amount;
    

    Without the custom message, this would be the cheapest, but still safe, option:

    // contains the check and fails without custom message in case of underflow
    _balances[sender] -= amount;
    

    And this would be even cheaper compared to the previous one. But it's unsafe as it doesn't check for undeflow:

    unchecked {
        // UNSAFE, DO NOT USE
        _balances[sender] -= amount;
    }