Closed sherlock-admin2 closed 1 year ago
Avci
high
the liquidators arent able to call repay function for liquidating the trader
the team said that one of the ways of the liquidation of the borrowers is though the Repay
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
Another user csn take over position, liquidator can close it, the owner of position can close it
also team said Closing a position, emergency closing and liquidation are carried out by calling the 'repay' function.
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
liquidatores cannot liquidated trough the repay function way that the team said its not possible
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
Manual Review
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
The position is closed either by the trader or by the liquidator
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 waysalso 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
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
https://github.com/sherlock-audit/2023-10-real-wagmi/blob/b33752757fd6a9f404b8577c1eae6c5774b3a0db/wagmi-leverage/contracts/LiquidityBorrowingManager.sol#L532
Tool used
Manual Review
Recommendation