The buy function assumes that the NFTDealers contract still has the authority to transfer the NFT when a buyer calls the function. However, the contract does not escrow the NFT (it doesn't take custody) when a user calls list().
Normal Behavior: A user lists an NFT, and when a buyer pays the specified price, the NFT is transferred from the seller (or the contract) to the buyer.
Specific Issue: Because the NFT remains in the seller's wallet after listing, the seller can transfer the NFT to another wallet or sell it on another marketplace (like OpenSea) while the listing on NFTDealers remains "active." When a buyer attempts to purchase the listing, the _safeTransfer call will fail because the contract no longer has the rights to move an NFT that the "seller" no longer owns.
Likelihood: High
A seller can intentionally or accidentally move the NFT after listing it.
There is no synchronization between the blockchain's ownerOf state and the contract's s_listings state until the buy function is called.
Impact: Medium
Denial of Service: Buyers will experience reverted transactions and wasted gas when trying to buy "ghost" listings.
Protocol Integrity: The activeListingsCounter will become permanently inflated as these "dead" listings can never be successfully bought or cleared by anyone other than the seller (via cancellation).
Paste this test function in NFTDealersTest.t.sol
The standard practice for marketplaces is to escrow the NFT. When list() is called, the contract should take custody of the NFT. This ensures the NFT is guaranteed to be available when a buyer appears.
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.