NFT Dealers

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

`list()` restricts non-whitelisted users from listing, violating the protocol specification

Author Revealed upon completion

Description

The README explicitly defines non-whitelisted users as permitted to: "buy, update price, cancel listing, list NFT, collect USDC after selling". Only minting is reserved for whitelisted wallets.

The list() function applies the onlyWhitelisted modifier, which directly contradicts the specification. Any wallet that holds an NFT but is not whitelisted — whether through receiving a transfer, being removed from the whitelist post-mint, or purchasing via an external marketplace — is permanently blocked from listing their NFT.

// README: "Non whitelisted user/wallet: buy, update price, cancel listing, list NFT, collect USDC"
// @> onlyWhitelisted modifier directly contradicts the documented specification
function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
require(_price >= MIN_PRICE, "Price must be at least 1 USDC");
require(ownerOf(_tokenId) == msg.sender, "Not owner of NFT");
require(s_listings[_tokenId].isActive == false, "NFT is already listed");
require(_price > 0, "Price must be greater than 0");
...
}

Root Cause

File: src/NFTDealers.sol

The root cause is the onlyWhitelisted modifier being applied to list() when the protocol specification explicitly reserves that restriction for mintNft() only. The modifier was either copy-pasted from mintNft() without revision or the intended access control was never updated to match the documented actor permissions.

Risk

Likelihood:

  • Any non-whitelisted wallet that receives an NFT via safeTransferFrom or any external transfer immediately encounters this restriction upon attempting to list.

  • The owner removes a wallet from the whitelist via removeWhitelistedWallet() after the holder has minted — the holder loses the ability to list while retaining their NFT.

Impact:

  • Non-whitelisted NFT holders are permanently unable to list their NFTs on the protocol, trapping their assets with no recourse through the intended marketplace flow.

  • The owner gains unilateral and undisclosed power to freeze any holder's ability to sell by removing them from the whitelist, contradicting the documented actor permissions.

Proof of Concept

function test_nonWhitelistedCannotList() public revealed {
vm.prank(owner);
nftDealers.whitelistWallet(alice);
// Alice mints and transfers NFT to Bob who is not whitelisted
usdc.mint(alice, 20e6);
vm.startPrank(alice);
usdc.approve(address(nftDealers), 20e6);
nftDealers.mintNft();
nftDealers.safeTransferFrom(alice, bob, 1);
vm.stopPrank();
assertEq(nftDealers.ownerOf(1), bob);
assertFalse(nftDealers.isWhitelisted(bob));
// Bob attempts to list — reverts despite README stating non-whitelisted CAN list
vm.prank(bob);
vm.expectRevert("Only whitelisted users can call this function");
nftDealers.list(1, 500e6);
}

Recommended Mitigation

- function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
+ function list(uint256 _tokenId, uint32 _price) external {
require(_price >= MIN_PRICE, "Price must be at least 1 USDC");
require(ownerOf(_tokenId) == msg.sender, "Not owner of NFT");
require(s_listings[_tokenId].isActive == false, "NFT is already listed");
require(_price > 0, "Price must be greater than 0");
...
}

Support

FAQs

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

Give us feedback!