sherlock-audit / 2023-10-real-wagmi-judging

16 stars 14 forks source link

Avci - the liquidators arent able to call repay function for liquidating the trader #196

Closed sherlock-admin2 closed 11 months ago

sherlock-admin2 commented 11 months ago

Avci

high

the liquidators arent able to call repay function for liquidating the trader

Summary

the liquidators arent able to call repay function for liquidating the trader

Vulnerability Detail

the team said that one of the ways of the liquidation of the borrowers is though the Repay

the team said Another user csn take over position, liquidator can close it, the owner of position can close it these are the liq ways

also team said Closing a position, emergency closing and liquidation are carried out by calling the 'repay' function.

but the problem is the repay function doesn't actually allow the liquidators liquidate the borrowers how? lets look at this code

  (msg.sender != borrowing.borrower && collateralBalance >= 0).revertError(
                ErrLib.ErrorCode.INVALID_CALLER
            );

this code in repay function reverts if the caller is other than borrower it self

Impact

liquidatores cannot liquidated trough the repay function way that the team said its not possible

Code Snippet

 function repay(
        RepayParams calldata params,
        uint256 deadline
    ) external nonReentrant checkDeadline(deadline) {
        BorrowingInfo memory borrowing = borrowingsInfo[params.borrowingKey];
        // Check if the borrowing key is valid
        (borrowing.borrowedAmount == 0).revertError(ErrLib.ErrorCode.INVALID_BORROWING_KEY);

        bool zeroForSaleToken = borrowing.saleToken < borrowing.holdToken;
        uint256 liquidationBonus = borrowing.liquidationBonus;
        int256 collateralBalance;
        // Update token rate information and get holdTokenRateInfo storage reference
        (, TokenInfo storage holdTokenRateInfo) = _updateTokenRateInfo(
            borrowing.saleToken,
            borrowing.holdToken
        );
        {
            // Calculate collateral balance and validate caller
            uint256 accLoanRatePerSeconds = holdTokenRateInfo.accLoanRatePerSeconds;
            uint256 currentFees;
            (collateralBalance, currentFees) = _calculateCollateralBalance(
                borrowing.borrowedAmount,
                borrowing.accLoanRatePerSeconds,
                borrowing.dailyRateCollateralBalance,
                accLoanRatePerSeconds
            );

            (msg.sender != borrowing.borrower && collateralBalance >= 0).revertError(
                ErrLib.ErrorCode.INVALID_CALLER
            );

            // Calculate liquidation bonus and adjust fees owed

            if (
                collateralBalance > 0 &&
                (currentFees + borrowing.feesOwed) / Constants.COLLATERAL_BALANCE_PRECISION >
                Constants.MINIMUM_AMOUNT
            ) {
                liquidationBonus +=
                    uint256(collateralBalance) /
                    Constants.COLLATERAL_BALANCE_PRECISION;
            } else {
                currentFees = borrowing.dailyRateCollateralBalance;
            }

            // Calculate platform fees and adjust fees owed
            borrowing.feesOwed += _pickUpPlatformFees(borrowing.holdToken, currentFees);
        }
        // Check if it's an emergency repayment
        if (params.isEmergency) {
            (collateralBalance >= 0).revertError(ErrLib.ErrorCode.FORBIDDEN);
            (
                uint256 removedAmt,
                uint256 feesAmt,
                bool completeRepayment
            ) = _calculateEmergencyLoanClosure(
                    zeroForSaleToken,
                    params.borrowingKey,
                    borrowing.feesOwed,
                    borrowing.borrowedAmount
                );
            (removedAmt == 0).revertError(ErrLib.ErrorCode.LIQUIDITY_IS_ZERO);
            // prevent overspent
            // Subtract the removed amount and fees from borrowedAmount and feesOwed
            borrowing.borrowedAmount -= removedAmt;
            borrowing.feesOwed -= feesAmt;
            feesAmt /= Constants.COLLATERAL_BALANCE_PRECISION;
            // Deduct the removed amount from totalBorrowed
            holdTokenRateInfo.totalBorrowed -= removedAmt;
            // If loansInfoLength is 0, remove the borrowing key from storage and get the liquidation bonus
            if (completeRepayment) {
                LoanInfo[] memory empty;
                _removeKeysAndClearStorage(borrowing.borrower, params.borrowingKey, empty);
                feesAmt += liquidationBonus;
            } else {
                BorrowingInfo storage borrowingStorage = borrowingsInfo[params.borrowingKey];
                borrowingStorage.dailyRateCollateralBalance = 0;
                borrowingStorage.feesOwed = borrowing.feesOwed;
                borrowingStorage.borrowedAmount = borrowing.borrowedAmount;
                // Calculate the updated accLoanRatePerSeconds
                borrowingStorage.accLoanRatePerSeconds =
                    holdTokenRateInfo.accLoanRatePerSeconds -
                    FullMath.mulDiv(
                        uint256(-collateralBalance),
                        Constants.BP,
                        borrowing.borrowedAmount // new amount
                    );
            }
            // Transfer removedAmt + feesAmt to msg.sender and emit EmergencyLoanClosure event
            Vault(VAULT_ADDRESS).transferToken(
                borrowing.holdToken,
                msg.sender,
                removedAmt + feesAmt
            );
            emit EmergencyLoanClosure(borrowing.borrower, msg.sender, params.borrowingKey);
        } else {
            // Deduct borrowedAmount from totalBorrowed
            holdTokenRateInfo.totalBorrowed -= borrowing.borrowedAmount;

            // Transfer the borrowed amount and liquidation bonus from the VAULT to this contract
            Vault(VAULT_ADDRESS).transferToken(
                borrowing.holdToken,
                address(this),
                borrowing.borrowedAmount + liquidationBonus
            );
            // Restore liquidity using the borrowed amount and pay a daily rate fee
            LoanInfo[] memory loans = loansInfo[params.borrowingKey];
            _maxApproveIfNecessary(
                borrowing.holdToken,
                address(underlyingPositionManager),
                type(uint128).max
            );
            _maxApproveIfNecessary(
                borrowing.saleToken,
                address(underlyingPositionManager),
                type(uint128).max
            );

            _restoreLiquidity(
                RestoreLiquidityParams({
                    zeroForSaleToken: zeroForSaleToken,
                    fee: params.internalSwapPoolfee,
                    slippageBP1000: params.swapSlippageBP1000,
                    totalfeesOwed: borrowing.feesOwed,
                    totalBorrowedAmount: borrowing.borrowedAmount
                }),
                params.externalSwap,
                loans
            );
            // Get the remaining balance of saleToken and holdToken
            (uint256 saleTokenBalance, uint256 holdTokenBalance) = _getPairBalance(
                borrowing.saleToken,
                borrowing.holdToken
            );
            // Remove borrowing key from related data structures
            _removeKeysAndClearStorage(borrowing.borrower, params.borrowingKey, loans);
            // Pay a profit to a msg.sender
            _pay(borrowing.holdToken, address(this), msg.sender, holdTokenBalance);
            _pay(borrowing.saleToken, address(this), msg.sender, saleTokenBalance);

            emit Repay(borrowing.borrower, msg.sender, params.borrowingKey);
        }
    }

https://github.com/sherlock-audit/2023-10-real-wagmi/blob/b33752757fd6a9f404b8577c1eae6c5774b3a0db/wagmi-leverage/contracts/LiquidityBorrowingManager.sol#L532

Tool used

Manual Review

Recommendation

sherlock-admin2 commented 11 months ago

1 comment(s) were left on this issue during the judging contest.

tsvetanovv commented:

Invalid. From the NatSpec we see: The position is closed either by the trader or by the liquidator