code-423n4 / 2024-02-wise-lending-findings

11 stars 8 forks source link

It is impossible to repay all debt. #120

Closed c4-bot-5 closed 6 months ago

c4-bot-5 commented 6 months ago

Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/MainHelper.sol#L127 https://github.com/code-423n4/2024-02-wise-lending/blob/main/contracts/MainHelper.sol#L44

Vulnerability details

Impact

User cannot repay all debt because at least 1 wei of borrowShare remains. When repaying all debt with AaveHub.sol#paybackExactAmountETH, 1 wei of borrowShare remains.

Proof of Concept

AaveHub.sol#paybackExactAmountETH function which repays debt is as follows.

    function paybackExactAmountETH(
        uint256 _nftId
    )
        external
        payable
        nonReentrant
        returns (uint256)
    {
        _checkPositionLocked(
            _nftId
        );

        address aaveWrappedETH = aaveTokenAddress[
            WETH_ADDRESS
        ];

498     uint256 userBorrowShares = WISE_LENDING.getPositionBorrowShares(
            _nftId,
            aaveWrappedETH
        );

        WISE_LENDING.syncManually(
            aaveWrappedETH
        );

507     uint256 maxPaybackAmount = WISE_LENDING.paybackAmount(
            aaveWrappedETH,
            userBorrowShares
        );

        (
            uint256 paybackAmount,
            uint256 ethRefundAmount

        ) = _getInfoPayback(
            msg.value,
            maxPaybackAmount
        );

        _wrapETH(
            paybackAmount
        );

        uint256 actualAmountDeposit = _wrapAaveReturnValueDeposit(
            WETH_ADDRESS,
            paybackAmount,
            address(this)
        );

531     uint256 borrowSharesReduction = WISE_LENDING.paybackExactAmount(
            _nftId,
            aaveWrappedETH,
            actualAmountDeposit
        );

        if (ethRefundAmount > 0) {
            _sendValue(
                msg.sender,
                ethRefundAmount
            );
        }

        emit IsPaybackAave(
            _nftId,
            block.timestamp
        );

        return borrowSharesReduction;
    }

On L498, we say that userBorrowShare is 10. And on 507 the equation which calculates paybackAmount from borrowShare is userBorrowShare * pseudoTotalBorrowAmount / totalBorrowShares + 1. Here if we say pseudoTotalBorrowAmount = 100, totalBorrowShares = 50, maxPaybackAmount becomes 20 + 1. And we say actualAmountDeposit = paybackAmount = maxPaybackAmount. And the equation which calculates borrowShares from paybackAmount is maxPaybackAmount * totalBorrowShares / pseudoTotalBorrowAmount - 1. So borrowSharesReduction becomes (20 + 1) * 50 / 100 - 1 = 10 - 1.

Therefore, userBorrowShares cannot be removed and 1 wei of borrowShare remains.

Tools Used

Manual Review

Recommended Mitigation Steps

We have to modify all processes of rounding up or down correctly. For example, mitigation steps for this problem are as follows.

  1. We have to modify MainHelper.sol#paybackAmount function as follows.

    
    function paybackAmount(
        address _poolToken,
        uint256 _shares
    )
        public
        view
        returns (uint256)
    {
        uint256 product = _shares
            * borrowPoolData[_poolToken].pseudoTotalBorrowAmount;
    
        uint256 totalBorrowShares = borrowPoolData[_poolToken].totalBorrowShares;

Assessed type

Math

c4-pre-sort commented 6 months ago

GalloDaSballo marked the issue as duplicate of #27

c4-pre-sort commented 6 months ago

GalloDaSballo marked the issue as sufficient quality report

trust1995 commented 5 months ago

A much weaker version of the selected report.

c4-judge commented 5 months ago

trust1995 marked the issue as partial-50