relist() is used to relist any token. But the problem is, it assumes that only listed token will be relisted. However, floor tokens can also be relisted using relist(), which will not be included in listingCount.
function relist(CreateListing calldata _listing, bool _payTaxWithEscrow) public nonReentrant lockerNotPaused {
// Load our tokenId
address _collection = _listing.collection;
uint _tokenId = _listing.tokenIds[0];
// Read the existing listing in a single read
Listing memory oldListing = _listings[_collection][_tokenId];
// Ensure the caller is not the owner of the listing
if (oldListing.owner == msg.sender) revert CallerIsAlreadyOwner();
// Load our new Listing into memory
Listing memory listing = _listing.listing;
// Ensure that the existing listing is available
(bool isAvailable, uint listingPrice) = getListingPrice(_collection, _tokenId);
if (!isAvailable) revert ListingNotAvailable();
// We can process a tax refund for the existing listing
(uint _fees,) = _resolveListingTax(oldListing, _collection, true);
if (_fees != 0) {
emit ListingFeeCaptured(_collection, _tokenId, _fees);
}
// Find the underlying {CollectionToken} attached to our collection
ICollectionToken collectionToken = locker.collectionToken(_collection);
// If the floor multiple of the original listings is different, then this needs
// to be paid to the original owner of the listing.
uint listingFloorPrice = 1 ether * 10 ** collectionToken.denomination();
if (listingPrice > listingFloorPrice) {
unchecked {
collectionToken.transferFrom(msg.sender, oldListing.owner, listingPrice - listingFloorPrice);
}
}
// Validate our new listing
_validateCreateListing(_listing);
// Store our listing into our Listing mappings
_listings[_collection][_tokenId] = listing;
// Pay our required taxes
payTaxWithEscrow(address(collectionToken), getListingTaxRequired(listing, _collection), _payTaxWithEscrow);
// Emit events
emit ListingRelisted(_collection, _tokenId, listing);
}
Now this will create problem while sunset/ closing of the collection because it checks the listingCount of the collection to ensure there is no listing. But due to above issue there will be listing & collection is sunset.
function _hasListings(address _collection) internal view returns (bool) {
IListings listings = locker.listings();
if (address(listings) != address(0)) {
> if (listings.listingCount(_collection) != 0) {
return true;
}
//
}
Impact
Collection will be sunset even after having a listing
Muscular Pebble Walrus
Medium
listingCount
is not update inrelist()
Summary
listingCount
is not update inrelist()
Vulnerability Detail
relist() is used to relist any token. But the problem is, it assumes that only listed token will be relisted. However, floor tokens can also be relisted using relist(), which will not be included in listingCount.
Now this will create problem while sunset/ closing of the collection because it checks the listingCount of the collection to ensure there is no listing. But due to above issue there will be listing & collection is sunset.
Impact
Collection will be sunset even after having a listing
Code Snippet
https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/utils/CollectionShutdown.sol#L497C4-L514C6 https://github.com/sherlock-audit/2024-08-flayer/blob/main/flayer/src/contracts/Listings.sol#L625
Tool used
Manual Review
Recommendation
Increase the listingCount if its floor token