sherlock-audit / 2024-04-teller-finance-judging

10 stars 9 forks source link

KupiaSec - The collateral tokens withdrawn `by liquidateDefaultedLoanWithIncentive()` will be frozen in the `LenderCommitmentGroup_Smart` contract. #229

Closed sherlock-admin4 closed 4 months ago

sherlock-admin4 commented 4 months ago

KupiaSec

high

The collateral tokens withdrawn by liquidateDefaultedLoanWithIncentive() will be frozen in the LenderCommitmentGroup_Smart contract.

Summary

TellerV2.lenderCloseLoanWithRecipient() always transfers the collateral to the lender, and the LenderCommitmentGroup_Smart contract is not designed to handle or manage the collateral tokens directly. So, the collateral tokens withdrawn by liquidateDefaultedLoanWithIncentive() will be frozen in the LenderCommitmentGroup_Smart contract.

Vulnerability Detail

The collateral tokens withdrawn by liquidateDefaultedLoanWithIncentive() is sent to the LenderCommitmentGroup_Smart contract.

https://github.com/sherlock-audit/2024-04-teller-finance/blob/main/teller-protocol-v2-audit-2024/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol#L422-L472

    function liquidateDefaultedLoanWithIncentive(
        uint256 _bidId,
        int256 _tokenAmountDifference
    ) public bidIsActiveForGroup(_bidId) {
        [...]

@>      ITellerV2(TELLER_V2).lenderCloseLoanWithRecipient(_bidId, msg.sender);
    }

Because TellerV2.lenderCloseLoanWithRecipient() always transfers the collateral to the lender. Actually, the _lenderCloseLoanWithRecipient() function don't use the _collateralRecipient at all. So all collaterals are sent to the lender, which is the LenderCommitmentGroup_Smart contract.

https://github.com/sherlock-audit/2024-04-teller-finance/blob/main/teller-protocol-v2-audit-2024/packages/contracts/contracts/TellerV2.sol#L738-L774


    function lenderCloseLoanWithRecipient(
        uint256 _bidId,
        address _collateralRecipient
    ) external {
@>      _lenderCloseLoanWithRecipient(_bidId, _collateralRecipient);
    }

    function _lenderCloseLoanWithRecipient(
        uint256 _bidId,
        address _collateralRecipient
    ) internal acceptedLoan(_bidId, "lenderClaimCollateral") {
        [...]
        collateralManager.lenderClaimCollateral(_bidId);
        [...]
    }

However, the LenderCommitmentGroup_Smart contract is not designed to handle or manage the collateral tokens. As a result, all collateral tokens will be frozen in it.

Impact

The collateral tokens withdrawn by liquidateDefaultedLoanWithIncentive() will be frozen in the LenderCommitmentGroup_Smart contract.

Code Snippet

https://github.com/sherlock-audit/2024-04-teller-finance/blob/main/teller-protocol-v2-audit-2024/packages/contracts/contracts/TellerV2.sol#L745-L774

https://github.com/sherlock-audit/2024-04-teller-finance/blob/main/teller-protocol-v2-audit-2024/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol#L422-L472

Tool used

Manual Review

Recommendation

Collateral should be sent to the _collateralRecipient in TellerV2.lenderCloseLoanWithRecipient().


    function _lenderCloseLoanWithRecipient(
        uint256 _bidId,
        address _collateralRecipient
    ) internal acceptedLoan(_bidId, "lenderClaimCollateral") {
        require(isLoanDefaulted(_bidId), "Loan must be defaulted.");

        Bid storage bid = bids[_bidId];
        bid.state = BidState.CLOSED;

        address sender = _msgSenderForMarket(bid.marketplaceId);
        require(sender == bid.lender, "only lender can close loan");

-       collateralManager.lenderClaimCollateral(_bidId);
+       bid.state = BidState.LIQUIDATED;
+       collateralManager.liquidateCollateral(_bidId, _collateralRecipient);
+       bid.state = BidState.CLOSED;

        emit LoanClosed(_bidId);
    }

Duplicate of #46