<!-- canonical: https://0xsimao.com/findings/flayer-listing-interest-supposed-burned -->

# In the unlockProtectedListing() function, the interest that was supposed to be distributed to LP holders was instead burned.

Medium · Sherlock · NFT liquidity · 2nd September 2024

Finding M-14 of the Flayer competition.

- Protocol: https://audits.sherlock.xyz/contests/468
- Codebase: https://github.com/0xsimao/2024-08-flayer/tree/0ec252cf9ef0f3470191dcf8318f6835f5ef688c
- Source: https://github.com/sherlock-audit/2024-08-flayer-judging/issues/431

---

## Summary
In the unlockProtectedListing() function, the interest that was supposed to be distributed to LP holders was instead burned.
## Vulnerability Detail
```javascript
function unlockProtectedListing(address _collection, uint _tokenId, bool _withdraw) public lockerNotPaused {
        // Ensure this is a protected listing
        ProtectedListing memory listing = _protectedListings[_collection][_tokenId];

        // Ensure the caller owns the listing
        if (listing.owner != msg.sender) revert CallerIsNotOwner(listing.owner);

        // Ensure that the protected listing has run out of collateral
        int collateral = getProtectedListingHealth(_collection, _tokenId);
        if (collateral < 0) revert InsufficientCollateral();

        // cache
        ICollectionToken collectionToken = locker.collectionToken(_collection);
        uint denomination = collectionToken.denomination();
        uint96 tokenTaken = _protectedListings[_collection][_tokenId].tokenTaken;

        // Repay the loaned amount, plus a fee from lock duration
@>>        uint fee = unlockPrice(_collection, _tokenId) * 10 ** denomination;
@>>        collectionToken.burnFrom(msg.sender, fee);

        // We need to burn the amount that was paid into the Listings contract
@>>        collectionToken.burn((1 ether - tokenTaken) * 10 ** denomination);

        // Remove our listing type
        unchecked { --listingCount[_collection]; }

        // Delete the listing objects
        delete _protectedListings[_collection][_tokenId];

        // Transfer the listing ERC721 back to the user
        if (_withdraw) {
            locker.withdrawToken(_collection, _tokenId, msg.sender);
            emit ListingAssetWithdraw(_collection, _tokenId);
        } else {
            canWithdrawAsset[_collection][_tokenId] = msg.sender;
        }

        // Update our checkpoint to reflect that listings have been removed
        _createCheckpoint(_collection);

        // Emit an event
        emit ListingUnlocked(_collection, _tokenId, fee);
    }
```
从unlockProtectedListing()调用的unlockPrice()可以知道，feeb包含了本金和利息。
```javascript
 function unlockPrice(address _collection, uint _tokenId) public view returns (uint unlockPrice_) {
        // Get the information relating to the protected listing
        ProtectedListing memory listing = _protectedListings[_collection][_tokenId];

        // Calculate the final amount using the compounded factors and principle amount
@>>        unlockPrice_ = locker.taxCalculator().compound({
            _principle: listing.tokenTaken,
            _initialCheckpoint: collectionCheckpoints[_collection][listing.checkpoint],
            _currentCheckpoint: _currentCheckpoint(_collection)
        });
    }
```
Therefore, after burning the fee, the interest paid by the user was also burned. This portion of the interest should have been distributed to the LP holders. Evidence for this can be found in the liquidateProtectedListing() function, where the interest generated by the ProtectedListing NFT is distributed to the LP holders.
```javascript
  function liquidateProtectedListing(address _collection, uint _tokenId) public lockerNotPaused listingExists(_collection, _tokenId) {
        //-------skip-----------

        // Send the remaining tokens to {Locker} implementation as fees
        uint remainingCollateral = (1 ether - listing.tokenTaken - KEEPER_REWARD) * 10 ** denomination;
        if (remainingCollateral > 0) {
            IBaseImplementation implementation = locker.implementation();
            collectionToken.approve(address(implementation), remainingCollateral);
@>>            implementation.depositFees(_collection, 0, remainingCollateral);
        }
 //-------skip-----------
    }
```
After the interest is burned, it causes deflation in the total amount of collectionToken, which leads to serious problems:

	1.	The total number of collectionTokens no longer matches the number of NFTs (it becomes less than the number of NFTs in the Locker contract), making it impossible to redeem some NFTs.
	2.	The utilizationRate() calculation results in a utilization rate greater than 100%, leading to an excessively high interestRate_, which in turn makes it impossible for users to create listings on ProtectedListings.

```javascript
    /**
     * Determines the usage rate of a listing type.
     *
     * @param _collection The collection to calculate the utilization rate of
     *
     * @return listingsOfType_ The number of listings that match the type passed
     * @return utilizationRate_ The utilization rate percentage of the listing type (80% = 0.8 ether)
     */
    function utilizationRate(address _collection) public view virtual returns (uint listingsOfType_, uint utilizationRate_) {
        // Get the count of active listings of the specified listing type
        listingsOfType_ = listingCount[_collection];

        // If we have listings of this type then we need to calculate the percentage, otherwise
        // we will just return a zero percent value.
        if (listingsOfType_ != 0) {
            ICollectionToken collectionToken = locker.collectionToken(_collection);

            // If we have no totalSupply, then we have a zero percent utilization
            uint totalSupply = collectionToken.totalSupply();
            if (totalSupply != 0) {
                utilizationRate_ = (listingsOfType_ * 1e36 * 10 ** collectionToken.denomination()) / totalSupply;
            }
        }
    }
```
## Impact
LP holders suffer losses, and users may be unable to use ProtectedListings normally.
## Code Snippet
https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/ProtectedListings.sol#L287

https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/ProtectedListings.sol#L607

https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/ProtectedListings.sol#L429

https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/ProtectedListings.sol#L261
## Tool used

Manual Review

## Recommendation
Distribute the interest to the LP holders.

---

Related findings:

- [Lack of deadline in PowerToken.buy can lead to user's cashToken being distributed through ZeroToken holders](https://0xsimao.com/findings/m-0-deadline-cash-through-zero): M^0 Minter Gateway
