NFT Dealers

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

[M-1] Any Address Can Buy - Whitelist Not Enforced on buy()

Author Revealed upon completion

Root + Impact

Description

  • The contract uses a whitelist to restrict minting and listing to approved addresses only. This enforces KYC or access control as part of the core protocol design.

  • buy() has no onlyWhitelisted modifier. Any address, including those not approved by the owner, can purchase NFTs from active listings. This bypasses the whitelist entirely for the buyer side and contradicts the protocol's access-control model.

// src/NFTDealers.sol
function mintNft() external payable onlyWhenRevealed onlyWhitelisted { ... } // guarded
function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted { ... } // guarded
@> function buy(uint256 _listingId) external payable { // no whitelist check
...
}

Risk

Likelihood:

  • The function is publicly callable by any EOA or contract with no restriction.

  • No additional action is needed beyond having USDC.


Impact:

  • Non-whitelisted parties circumvent the protocol's access controls and acquire NFTs.

  • Whitelisted sellers may unknowingly transfer NFTs to untrusted parties, violating the collection's intended user base.

Proof of Concept

A stranger address that has never been whitelisted successfully purchases a listed NFT, ending up as the NFT owner despite not being in the whitelist.

function testNonWhitelistedCanBuy() public revealed {
mintAndListNFTForTesting(1, 1000e6);
address stranger = makeAddr("stranger");
usdc.mint(stranger, 1000e6);
// stranger is not whitelisted but can still buy
vm.startPrank(stranger);
usdc.approve(address(nftDealers), 1000e6);
nftDealers.buy(1); // succeeds
vm.stopPrank();
assertEq(nftDealers.ownerOf(1), stranger);
assertFalse(nftDealers.isWhitelisted(stranger));
}

Recommended Mitigation

Add the onlyWhitelisted modifier to buy() to enforce consistent access control across all market-facing functions.

-function buy(uint256 _listingId) external payable {
+function buy(uint256 _listingId) external onlyWhitelisted {

Support

FAQs

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

Give us feedback!