<!-- canonical: https://0xsimao.com/findings/teller-finance-liquidate-defaulted-incentive-collateral -->

# liquidateDefaultedLoanWithIncentive sends the collateral to the wrong account

Crit/High · Sherlock · Lending · 23rd April 2024

Finding H-4 of the Teller Finance competition.

- Protocol: https://audits.sherlock.xyz/contests/295
- Codebase: https://github.com/0xsimao/2024-04-teller-finance/tree/defe55469a2576735af67483acf31d623e13592d
- Source: https://github.com/sherlock-audit/2024-04-teller-finance-judging/issues/46

---

## Summary
[liquidateDefaultedLoanWithIncentive](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) sends the collateral to the Lender - LenderCommitmentGroup (LCG) instead of the liquidator. Liquidators will not be incentivized to liquidate.

## Vulnerability Detail
[liquidateDefaultedLoanWithIncentive](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) is intended to liquidate bids, where liquidators pay off the debt and receive the collateral. However, currently the collateral is sent to the lender - LCG, because [lenderCloseLoanWithRecipient](https://github.com/sherlock-audit/2024-04-teller-finance/blob/main/teller-protocol-v2-audit-2024/packages/contracts/contracts/TellerV2.sol#L738-L774) includes `msg.sender` as its second parameter but does not utilize it:

```solidity
    function lenderCloseLoanWithRecipient(uint256 _bidId, address _collateralRecipient) external {
        _lenderCloseLoanWithRecipient(_bidId, _collateralRecipient);
    }

    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");

        //@audit we directly call `lenderClaimCollateral`
        collateralManager.lenderClaimCollateral(_bidId);
    }
```
[lenderClaimCollateral](https://github.com/sherlock-audit/2024-04-teller-finance/blob/main/teller-protocol-v2-audit-2024/packages/contracts/contracts/CollateralManager.sol#L271-L283) in its place withdraws the collateral directly to the lender - LCG, without updating `totalPrincipalTokensRepaid`. Some effects:

- Liquidators will gain 0 profits, so they will not liquidate.
- LPs suffer losses as liquidations are not carried out, and returned collateral from liquidations is not accrued as `totalPrincipalTokensRepaid`, increasing utilization, eventually bricking the contract.

## Impact
Liquidators will not be incentivized to liquidate and incorrect accounting occurs inside LCG.

## Code Snippet
```solidity
    function _lenderCloseLoanWithRecipient(
        uint256 _bidId,
        address _collateralRecipient // @audit never used
    ) 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);
    }
```
## Tool used
Manual Review

## Recommendation
Ensure [_lenderCloseLoanWithRecipient](https://github.com/sherlock-audit/2024-04-teller-finance/blob/main/teller-protocol-v2-audit-2024/packages/contracts/contracts/TellerV2.sol#L745-L755) sends the funds to `_collateralRecipient`.

---

Related findings:

- [`borrowing::liquidate()` sends the wrong liquidation index to the destination chain, overwritting liquidation information and getting collateral stuck](https://0xsimao.com/findings/autonomint-liquidate-liquidation-index-stuck): Autonomint
- [`Market::liquidate()` will not work when most of the liquidity is borrowed due to wrong liquidator `transferFrom()` order](https://0xsimao.com/findings/exactly-protocol-update-staking-contract-liquidate-borrowed-liquidator-transfer): Exactly Protocol Update - Staking Contract
- [MidnightVaultExecutor::onLiquidate forces liquidators to provide callback data and never refunds leftover loan tokens](https://0xsimao.com/findings/tenor-markets-liquidate-callback-refunds-leftover): Tenor Morpho Migrations
- [Liquidation can be finished without calling triggerDefault() (and repossessing the loan) if pool delegate or governor call finishCollateralLiquidation(...) after impairment.](https://0xsimao.com/findings/maple-finance-iii-liquidation-repossessing-delegate-collateral): Maple Finance
