The list() function (L127-139) enforces the onlyWhitelisted modifier (L77-80), but the protocol specification explicitly states that non-whitelisted users can "buy, update price, cancel listing, list NFT, collect USDC after selling". This is the only marketplace function that contradicts the specification -- all four other functions that the spec says non-whitelisted users can access are correctly unrestricted:
| Function | Line | Modifier | Whitelist Required? | Spec Says? | Match? |
|---|---|---|---|---|---|
buy() |
L141 | none | No | Can use | ✅ |
updatePrice() |
L185 | onlySeller |
No | Can use | ✅ |
cancelListing() |
L157 | none | No | Can use | ✅ |
list() |
L127 | onlyWhitelisted |
Yes | Can use | ❌ |
collectUsdcFromSelling() |
L171 | onlySeller |
No | Can use | ✅ |
The root cause is the onlyWhitelisted modifier on list() at L127:
This creates an asymmetric marketplace where anyone can buy, but only whitelisted users can sell. Two realistic scenarios produce trapped NFT owners:
Non-whitelisted buyer: buy() (L141) has no whitelist restriction, so any user can purchase an NFT. But the buyer cannot re-list it because list() requires whitelist status. The protocol's own marketplace creates users who can buy but not sell.
De-whitelisted user: The owner can call removeWhitelistedWallet() (L106-108) at any time per the specification. A user who minted NFTs while whitelisted and is later removed from the whitelist permanently loses the ability to list their NFTs on the marketplace.
Likelihood:
Occurs for every non-whitelisted user who acquires an NFT -- whether via buy() (L141, no whitelist restriction), standard ERC721 transferFrom, or after being removed from the whitelist via removeWhitelistedWallet() (L106-108)
The owner can remove wallets from the whitelist at any time per the specification, affecting previously whitelisted users
buy() is completely unrestricted, so non-whitelisted buyers routinely enter the marketplace and acquire NFTs they cannot resell
Impact:
Non-whitelisted NFT owners cannot sell on the protocol's marketplace, contradicting the documented behavior
Creates a buy-only trap: users purchase NFTs via buy() expecting to participate fully in the marketplace, but cannot resell
De-whitelisted users lose marketplace access for NFTs they legitimately minted and own
NFTs are not completely locked (ERC721 transferFrom still works), but the protocol's core selling functionality is inaccessible
Two scenarios demonstrate the issue. Run with forge test --match-contract M02PoCTest -vv:
Scenario 1: Non-whitelisted buyer purchases an NFT via buy() then cannot re-list it.
Scenario 2: User mints while whitelisted, then owner removes them from whitelist -- they can no longer list.
Remove the onlyWhitelisted modifier from list() to align with the specification. The ownerOf(_tokenId) == msg.sender check at L129 already provides sufficient access control -- only the actual NFT owner can list their token.
This fix:
Aligns list() with the specification and with all other marketplace functions (buy, cancelListing, updatePrice, collectUsdcFromSelling) which are already unrestricted
Preserves the ownership check at L129 (ownerOf(_tokenId) == msg.sender) as the access control mechanism
Allows non-whitelisted buyers who acquired NFTs via buy() to participate fully in the marketplace
Allows users removed from the whitelist to still manage and sell their existing NFTs
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.