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

11 stars 8 forks source link

Not enforcing full liquidation accrues bad debt in the system #189

Closed c4-bot-3 closed 5 months ago

c4-bot-3 commented 6 months ago

Lines of code

https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseLending.sol#L1250-L1309 https://github.com/code-423n4/2024-02-wise-lending/blob/79186b243d8553e66358c05497e5ccfd9488b5e2/contracts/WiseSecurity/WiseSecurityHelper.sol#L876-L900

Vulnerability details

Impact

Not enforcing full liquidation leads to bad debt in the system and gives on incentive to liquidate small positions.

Proof of Concept

When liquidations are made, in WiseCore._coreLiquidation(), the percentage of reward for the liquidator is calculated and checked if it doesn't go over 1e18 (100%):

        uint256 collateralPercentage = WISE_SECURITY.calculateWishPercentage(
            _data.nftId,
            _data.tokenToRecieve,
            WISE_ORACLE.getTokensInETH(
                _data.tokenToPayback,
                _data.paybackAmount
            ),
            _data.maxFeeETH,
            _data.baseRewardLiquidation
        );

        _validateParameter(
            collateralPercentage,
            PRECISION_FACTOR_E18
        );

later, the amount to receive is calculated based on this percentage:

        receiveAmount = _calculateReceiveAmount(
            _data.nftId,
            _data.nftIdLiquidator,
            _data.tokenToRecieve,
            collateralPercentage
        );
    function _calculateReceiveAmount(
        uint256 _nftId,
        uint256 _nftIdLiquidator,
        address _receiveTokens,
        uint256 _removePercentage
    )
        private
        returns (uint256 receiveAmount)
    {
        if (pureCollateralAmount[_nftId][_receiveTokens] > 0) {
            receiveAmount = _withdrawPureCollateralLiquidation(
                _nftId,
                _receiveTokens,
                _removePercentage
            );
        }

        uint256 potentialPureExtraCashout;
        uint256 userShares = userLendingData[_nftId][_receiveTokens].shares;
        uint256 pureCollateral = pureCollateralAmount[_nftId][_receiveTokens];

        if (pureCollateral > 0 && userShares > 0) {
            potentialPureExtraCashout = _calculatePotentialPureExtraCashout(
                userShares,
                _receiveTokens,
                _removePercentage
            );
        }
// [...]
            return receiveAmount + potentialPureExtraCashout;
        }

        if (userShares == 0) {
            return receiveAmount;
        }

        if (userLendingData[_nftId][_receiveTokens].unCollateralized == true) {
            return receiveAmount;
        }

        // @audit it should return what is there to be withdrawn. if we add receiveAmount, it may go over what's available, reverting the transaction
        return _withdrawOrAllocateSharesLiquidation(
            _nftId,
            _nftIdLiquidator,
            _receiveTokens,
            _removePercentage
        ) + receiveAmount;

The problem is that there is check if the position is not liquidated over 100%, but there is no check if the position liquidation leaves dust amounts of bad debt. Additionally, inside both _calculatePotentialPureExtraCashout() and _withdrawPureCollateralLiquidation() it can round down to 0, leaving liquidator with no incentive to liquidate such position, increasing bad debt. So, the bad debt will slowly, but constantly increase.

Tools Used

Manual Review

Recommended Mitigation Steps

Consider enforcing full position liquidation when position can be liquidated in 100% due to bad debt.

Assessed type

Invalid Validation

GalloDaSballo commented 5 months ago

Somewhat incomplete, maybe partial dup of small bad debt positions, but maybe scrap due to low quality

c4-pre-sort commented 5 months ago

GalloDaSballo marked the issue as insufficient quality report

c4-judge commented 5 months ago

trust1995 marked the issue as unsatisfactory: Insufficient quality