NFT Dealers

First Flight #58
Beginner FriendlyFoundry
100 EXP
Submission Details
Impact: medium
Likelihood: high

[M-02] Non-Whitelisted Buyers Cannot Resell NFTs

Author Revealed upon completion

Root Cause:

buy() has no whitelist restriction, allowing anyone to purchase NFTs. However list() requires onlyWhitelisted, permanently trapping non-whitelisted buyers with NFTs they cannot resell.

Impact: Non-whitelisted buyers are stuck with NFTs they cannot sell, and the owner loses fee revenue from secondary sales.


Description:

The protocol allows anyone to buy NFTs via buy() but restricts listing to whitelisted users only via list(). This creates a situation where a non-whitelisted buyer can purchase an NFT but has no way to resell it unless the owner manually whitelists them, breaking the core resale functionality of the marketplace.

// anyone can buy needing no whitelist check
function buy(uint256 _listingId) external payable {
@> // no onlyWhitelisted modifier
Listing memory listing = s_listings[_listingId];
...
}
// but only whitelisted users can list
@> function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
...
}

Risk

Likelihood: High

  • This occurs every time a non-whitelisted user purchases an NFT with no special conditions required

  • The owner must manually whitelist every buyer for resale to work, which is an unreasonable operational burden

Impact: Medium

  • Non-whitelisted buyers are permanently trapped with NFTs they cannot sell

  • The owner loses progressive fee revenue from every secondary sale that cannot happen due to this restriction

Proof of Concept

// PoC: prove that a non-whitelisted buyer can purchase an NFT
// but is permanently blocked from reselling it via list()
// showing their NFT is trapped with no way out
function test_POC_NonWhitelistedBuyerTrapped() public revealed {
// Whitelist only the seller
vm.prank(owner);
nftDealers.whitelistWallet(userWithCash);
// Seller mints and lists NFT
vm.startPrank(userWithCash);
usdc.approve(address(nftDealers), 20e6);
nftDealers.mintNft();
nftDealers.list(1, 1000e6);
vm.stopPrank();
// Non-whitelisted buyer purchases the NFT successfully
vm.startPrank(userWithEvenMoreCash);
usdc.approve(address(nftDealers), 1000e6);
nftDealers.buy(1);
// Confirm buyer now owns the NFT
assertEq(nftDealers.ownerOf(1), userWithEvenMoreCash);
// Buyer tries to relist and reverts because not whitelisted
vm.expectRevert("Only whitelisted users can call this function");
nftDealers.list(1, 2000e6);
vm.stopPrank();
}

Recommended Mitigation

// Option 1 which is to auto-whitelist buyers on purchase
function buy(uint256 _listingId) external payable {
...
_safeTransfer(listing.seller, msg.sender, listing.tokenId, "");
+ // auto-whitelist buyer so they can resell
+ whitelistedUsers[msg.sender] = true;
s_listings[_listingId].isActive = false;
}
// Option 2 which is to allow any NFT owner to list regardless of whitelist
- function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
+ function list(uint256 _tokenId, uint32 _price) external {

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!