sherlock-audit / 2024-08-flayer-judging

2 stars 0 forks source link

utsav - `ProtectedListings:createListings()` doesn't create checkPoints for subsequent listing of a collection #722

Open sherlock-admin3 opened 1 month ago

sherlock-admin3 commented 1 month ago

utsav

Medium

ProtectedListings:createListings() doesn't create checkPoints for subsequent listing of a collection

Summary

ProtectedListings:createListings() doesn't create checkPoints for subsequent listing of a collection

Vulnerability Detail

When a tokenId of a collection is protected listed for the fist time then checkPoint is created but not for the subsequent tokenId of that collection because it doesn't call _createCheckpoint() at the end of the function

 function createListings(CreateListing[] calldata _createListings) public nonReentrant lockerNotPaused {
        // Loop variables
        uint checkpointIndex;
        bytes32 checkpointKey;
        uint tokensIdsLength;
        uint tokensReceived;

        // Loop over the unique listing structures
        for (uint i; i < _createListings.length; ++i) {
            // Store our listing for cheaper access
            CreateListing calldata listing = _createListings[i];

            // Ensure our listing will be valid
            _validateCreateListing(listing);

            // Update our checkpoint for the collection if it has not been done yet for
            // the listing collection.
            checkpointKey = keccak256(abi.encodePacked('checkpointIndex', listing.collection));
            assembly { checkpointIndex := tload(checkpointKey) }
            if (checkpointIndex == 0) {
                checkpointIndex = _createCheckpoint(listing.collection);
                assembly { tstore(checkpointKey, checkpointIndex) }
            }

            // Map our listings
            tokensIdsLength = listing.tokenIds.length;
            tokensReceived = _mapListings(listing, tokensIdsLength, checkpointIndex) * 10 ** locker.collectionToken(listing.collection).denomination();

            // Register our listing type
            unchecked {
                listingCount[listing.collection] += tokensIdsLength;
            }

            // Deposit the tokens into the locker and distribute ERC20 to user
            _depositNftsAndReceiveTokens(listing, tokensReceived);

            // Event fire
            emit ListingsCreated(listing.collection, listing.tokenIds, listing.listing, tokensReceived, msg.sender);
        }
    }

This is an issue because _depositNftsAndReceiveTokens() deposits the NFTs into locker.sol which mints collectionToken. And the totalSupply of the collectionToken is used while calculating utilizationRate of the collection.

function utilizationRate(address _collection) public view virtual returns (uint listingsOfType_, uint utilizationRate_) {
//
        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

checkPoint is not updated, as result utilizationRate of that collection will also not be updated correctly

Code Snippet

https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/ProtectedListings.sol#L117C4-L157C1 https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/ProtectedListings.sol#L165C5-L187C1 https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/ProtectedListings.sol#L261C5-L276C6

Tool used

Manual Review

Recommendation

Call _createCheckpoint() at the end of the function call