sherlock-audit / 2024-08-flayer-judging

2 stars 0 forks source link

Minato7namikazi - Premature Claim in Collection Shutdown Due to NFT Re-Listing #786

Closed sherlock-admin4 closed 1 month ago

sherlock-admin4 commented 1 month ago

Minato7namikazi

Invalid

Premature Claim in Collection Shutdown Due to NFT Re-Listing

Summary

Bug Report: Premature Claim in Collection Shutdown Due to NFT Re-Listing

1. Bug Title:

Premature Claim Enabled by Relisted NFTs in SudoSwap Liquidation Pool

2. Why this could be triggered?

The current collectionLiquidationComplete() function solely verifies if the SudoSwap pool directly owns the NFTs. However, a user can buy an NFT from the SudoSwap liquidation pool and subsequently re-list it. This results in the pool no longer being the owner, misleading the function into wrongly concluding that all NFTs have been sold. Consequently, it prematurely enables claims.

3. PoC flow to trigger:

  1. Initiate Collection Shutdown: Trigger the collection shutdown process, send NFTs to a SudoSwap pool for liquidation.
  2. Partial Purchase: A user (User A) buys a subset of the NFTs in the SudoSwap pool.
  3. Re-Listing: User A lists their recently purchased NFTs, on a platform outside the flayer ecosystem, at a potentially higher price.
  4. False Completion Signal: The collectionLiquidationComplete() function incorrectly returns true because the SudoSwap pool no longer owns the relisted NFTs.
  5. Premature Claims: Token holders, assuming liquidation is complete, invoke the claim() function and withdraw their proportional ETH prematurely, despite remaining unsold NFTs.

4. Impact:

5. Code Snippet:

 function collectionLiquidationComplete(address _collection) public view returns (bool) {
     CollectionShutdownParams memory params = _collectionParams[_collection];
     uint sweeperPoolTokenIdsLength = params.sweeperPoolTokenIds.length;

     // If we have no registered tokens, then there is nothing to check
     if (sweeperPoolTokenIdsLength == 0) {
         return true;
     }

     // Store our loop iteration variables
     address sweeperPool = params.sweeperPool;
     IERC721 collection = IERC721(_collection);

     // Check that all token IDs have been bought from the pool
     for (uint i; i < sweeperPoolTokenIdsLength; ++i) {
         // BUG: Only checks for direct SudoSwap pool ownership, 
         //ignores the possibility of NFT re-listing
         if (collection.ownerOf(params.sweeperPoolTokenIds[i]) == sweeperPool) {
             return false;
         }
     }
     return true;
 }

Root Cause

No response

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

No response

PoC

No response

Mitigation

No response