sherlock-audit / 2024-06-makerdao-endgame-judging

1 stars 1 forks source link

pwning_dev - unchecked transfers in the `_mint` and `_burn` functions #97

Closed sherlock-admin2 closed 1 month ago

sherlock-admin2 commented 1 month ago

pwning_dev

Medium

unchecked transfers in the _mint and _burn functions

pwning_dev

Summary

Vulnerability Detail

In the _mint function, the transferFrom method of the nst token is called without checking its return value:

function _mint(uint256 assets, uint256 shares, address receiver) internal {
    require(receiver != address(0) && receiver != address(this), "SNst/invalid-address");

    nst.transferFrom(msg.sender, address(this), assets); // Unchecked transfer

    unchecked {
        balanceOf[receiver] = balanceOf[receiver] + shares;
        totalSupply = totalSupply + shares;
    }

    emit Deposit(msg.sender, receiver, assets, shares);
    emit Transfer(address(0), receiver, shares);
}

In the _burn function, the transfer method of the nst token is called without checking its return value:

function _burn(uint256 assets, uint256 shares, address receiver, address owner) internal {
    uint256 balance = balanceOf[owner];
    require(balance >= shares, "SNst/insufficient-balance");

    if (owner != msg.sender) {
        uint256 allowed = allowance[owner][msg.sender];
        if (allowed != type(uint256).max) {
            require(allowed >= shares, "SNst/insufficient-allowance");

            unchecked {
                allowance[owner][msg.sender] = allowed - shares;
            }
        }
    }

    unchecked {
        balanceOf[owner] = balance - shares;
        totalSupply      = totalSupply - shares;
    }

    nst.transfer(receiver, assets); // Unchecked transfer

    emit Transfer(owner, address(0), shares);
    emit Withdraw(msg.sender, receiver, owner, assets, shares);
}

Impact

Scenario 1: Transfer Fails During Minting

If the transferFrom call fails during the _mint operation:

Scenario 2: Transfer Fails During Burning

If the transfer call fails during the _burn operation:

Code Snippet

https://github.com/sherlock-audit/2024-06-makerdao-endgame/blob/main/sdai/src/SNst.sol#L284C5-L324C1

Tool used

Manual Review

Recommendation

_mint Function with Checked Transfer

function _mint(uint256 assets, uint256 shares, address receiver) internal {
    require(receiver != address(0) && receiver != address(this), "SNst/invalid-address");

    bool success = nst.transferFrom(msg.sender, address(this), assets);
    require(success, "SNst/transferFrom-failed"); // Check the transfer

    unchecked {
        balanceOf[receiver] = balanceOf[receiver] + shares;
        totalSupply = totalSupply + shares;
    }

    emit Deposit(msg.sender, receiver, assets, shares);
    emit Transfer(address(0), receiver, shares);
}

_burn Function with Checked Transfer

function _burn(uint256 assets, uint256 shares, address receiver, address owner) internal {
    uint256 balance = balanceOf[owner];
    require(balance >= shares, "SNst/insufficient-balance");

    if (owner != msg.sender) {
        uint256 allowed = allowance[owner][msg.sender];
        if (allowed != type(uint256).max) {
            require(allowed >= shares, "SNst/insufficient-allowance");

            unchecked {
                allowance[owner][msg.sender] = allowed - shares;
            }
        }
    }

    unchecked {
        balanceOf[owner] = balance - shares;
        totalSupply      = totalSupply - shares;
    }

    bool success = nst.transfer(receiver, assets);
    require(success, "SNst/transfer-failed"); // Check the transfer

    emit Transfer(owner, address(0), shares);
    emit Withdraw(msg.sender, receiver, owner, assets, shares);
}
telome commented 1 month ago

Checking the return value of the transfer is unnecessary as nst reverts if the transfer fails.