# RACE #4

{% embed url="<https://ventral.digital/posts/2022/2/9/secureum-bootcamp-february-race-4>" %}
RACE #4
{% endembed %}

<figure><img src="https://3988450783-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MWVjG_njKgBtvmnKaJh%2Fuploads%2FXKuNRmYeILBLfUrQcJGU%2Fimage.png?alt=media&#x26;token=b435f23c-7ac4-4b30-92c0-2e60377c9560" alt=""><figcaption><p>RACE #4 result</p></figcaption></figure>

{% hint style="info" %}
**Note:** All 8 questions in this quiz are based on the InSecureum contract. This is the same contract you will see for all the 8 questions in this quiz. *InSecureum* is adapted from a widely used ERC20 contract.
{% endhint %}

```solidity
pragma solidity 0.8.10;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Context.sol";

contract InSecureum is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    uint256 private _totalSupply;
    string private _name;
    string private _symbol;

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    function name() public view virtual override returns (string memory) {
        return _name;
    }

    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual override returns (uint8) {
        return 8;
    }

    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][sender];
        if (currentAllowance != type(uint256).max) {
            unchecked {
                _approve(sender, _msgSender(), currentAllowance - amount);
            }
        }
        _transfer(sender, recipient, amount);
        return false;
    }

    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance > subtractedValue, "ERCH20: decreased allowance below zero");
        _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        return true;
    }

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

    function _mint(address account, uint256 amount) external virtual {
        _totalSupply += amount;
        _balances[account] = amount;
        emit Transfer(address(0), account, amount);
    }

    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from zero address");
        require(_balances[account] >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = _balances[account] - amount;
        }
        _totalSupply -= amount;
        emit Transfer(address(0), account, amount);
    }

    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(spender != address(0), "ERC20: approve from the zero address");
        require(owner != address(0), "ERC20: approve to the zero address");
        _allowances[owner][spender] += amount;
        emit Approval(owner, spender, amount);
    }
}
```

## Question 1 :white\_check\_mark:

> “\`InSecureum\` implements”

* [x] &#x20;A. Atypical `decimals` value -> 8, standard is 18
* [x] &#x20;B. Non-standard `decreaseAllowance` and `increaseAllowance` -> These two functions are in OpenZeppelin's implementation for mitigating `approve()` frontrunning but not in ERC20 standard
* [ ] &#x20;C. Non-standard `transfer`
* [ ] &#x20;D. None of the above

## Question 2 :white\_check\_mark:

> “In \`InSecureum\`”

* [x] &#x20;A. `decimals()` can have `pure` state mutability instead of `view`
* [ ] &#x20;B. `_burn()` can have `external` visibility instead of `internal`
* [x] &#x20;C. `_mint()` should have `internal` visibility instead of `external`
* [ ] &#x20;D. None of the above

## Question 3 :white\_check\_mark:

> “\`InSecureum transferFrom()\`”

* [x] &#x20;A. Is susceptible to an integer underflow -> `currentAllowance - amount` when `currentAllowance == 0` triggers integer underflow
* [x] &#x20;B. Has an incorrect allowance check -> function won't revert on integer underflow because of `unchecked{}`
* [x] &#x20;C. Has an optimization indicative of unlimited approvals -> `if (currentAllowance != type(uint256).max) {...}`
* [ ] &#x20;D. None of the above

**My comment:**

Here is OpenZeppelin's ERC20 `transferFrom()` implementation:

```solidity
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
    address spender = _msgSender();
    _spendAllowance(from, spender, amount);
    _transfer(from, to, amount);
    return true;
}
```

```solidity
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
    uint256 currentAllowance = allowance(owner, spender);
    if (currentAllowance != type(uint256).max) {
        require(currentAllowance >= amount, "ERC20: insufficient allowance");
        unchecked {
            _approve(owner, spender, currentAllowance - amount);
        }
    }
}
```

The `require(currentAllowance >= amount, "ERC20: insufficient allowance");` check prevents integer underflow.

## Question 4 :white\_check\_mark:

> “In \`InSecureum\`”

* [ ] &#x20;A. `increaseAllowance` is susceptible to an integer overflow -> No `unchecked{}` block, Solidity version is ^0.8, all good
* [ ] &#x20;B. `decreaseAllowance` is susceptible to an integer overflow -> Same reason
* [x] &#x20;C. `decreaseAllowance` does not allow reducing allowance to zero -> Because it is `require(currentAllowance > subtractedValue)`, not `currentAllowance >= subtractedValue`
* [x] &#x20;D. `decreaseAllowance` can be optimized with `unchecked{}` -> This is fine because of the `require` statement before `_approve()`

**My comment:**

Here is OpenZeppelin's ERC20 `increaseAllowance()` and `decreaseAllowance()` implementation:

```solidity
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
    address owner = _msgSender();
    _approve(owner, spender, allowance(owner, spender) + addedValue);
    return true;
}
```

```solidity
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
    address owner = _msgSender();
    uint256 currentAllowance = allowance(owner, spender);
    require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
    unchecked {
        _approve(owner, spender, currentAllowance - subtractedValue);
    }

    return true;
}
```

## Question 5 :white\_check\_mark:

> “\`InSecureum \_transfer()\`”

* [ ] &#x20;A. Is missing a zero-address validation
* [ ] &#x20;B. Is susceptible to an integer overflow
* [ ] &#x20;C. Is susceptible to an integer underflow
* [x] &#x20;D. None of the above

**My comment:**

Here is OpenZeppelin's ERC20 `_transfer()` implementation:

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

    _beforeTokenTransfer(from, to, amount);

    uint256 fromBalance = _balances[from];
    require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
    unchecked {
        _balances[from] = fromBalance - amount;
        // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
        // decrementing then incrementing.
        _balances[to] += amount;
    }

    emit Transfer(from, to, amount);

    _afterTokenTransfer(from, to, amount);
}
```

## Question 6 :white\_check\_mark:

> “\`InSecureum \_mint()\`”

* [x] &#x20;A. Is missing a zero-address validation -> Need `require(account != address(0))`
* [ ] &#x20;B. Has an incorrect event emission
* [x] &#x20;C. Has an incorrect update of account balance -> Should be `_balances[account] += amount;`
* [ ] &#x20;D. None of the above

**My comment:**

Here is OpenZeppelin's ERC20 `_mint()` implementation:

```solidity
function _mint(address account, uint256 amount) internal virtual {
    require(account != address(0), "ERC20: mint to the zero address");

    _beforeTokenTransfer(address(0), account, amount);

    _totalSupply += amount;
    unchecked {
        // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
        _balances[account] += amount;
    }
    emit Transfer(address(0), account, amount);

    _afterTokenTransfer(address(0), account, amount);
}
```

## Question 7 :white\_check\_mark:

> “\`InSecureum \_burn()\`”

* [ ] &#x20;A. Is missing a zero-address validation
* [x] &#x20;B. Has an incorrect event emission -> `event Transfer(address indexed from, address indexed to, uint256 value);`
* [ ] &#x20;C. Has an incorrect update of account balance
* [ ] &#x20;D. None of the above

**My comment:**

Here is OpenZeppelin's ERC20 `_burn()` implementation:

```solidity
function _burn(address account, uint256 amount) internal virtual {
    require(account != address(0), "ERC20: burn from the zero address");

    _beforeTokenTransfer(account, address(0), amount);

    uint256 accountBalance = _balances[account];
    require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
    unchecked {
        _balances[account] = accountBalance - amount;
        // Overflow not possible: amount <= accountBalance <= totalSupply.
        _totalSupply -= amount;
    }

    emit Transfer(account, address(0), amount);

    _afterTokenTransfer(account, address(0), amount);
}
```

## Question 8 :white\_check\_mark:

> “\`InSecureum \_approve()\`”

* [ ] &#x20;A. Is missing a zero-address validation
* [x] &#x20;B. Has incorrect error messages -> Flipped
* [x] &#x20;C. Has an incorrect update of allowance -> Should be `_allowances[owner][spender] = amount;`
* [ ] &#x20;D. None of the above

**My comment:**

Here is OpenZeppelin's ERC20 `_approve()` implementation:

```solidity
function _approve(address owner, address spender, uint256 amount) internal virtual {
    require(owner != address(0), "ERC20: approve from the zero address");
    require(spender != address(0), "ERC20: approve to the zero address");

    _allowances[owner][spender] = amount;
    emit Approval(owner, spender, amount);
}
```
