When a seller calls list(), the NFT should be transferred to the contract so the protocol controls the asset during the sale. This guarantees that buy() can always complete and that the seller cannot sell the NFT through another channel, bypassing protocol fees.
list() never transfers the NFT — it only records the listing in storage. The seller retains full custody and can transfer the NFT away via ERC721 transferFrom at any time. By first canceling and re-listing (to recover collateral at zero cost), the seller can sell the NFT externally, bypass all protocol fees, and leave behind a permanent ghost listing that blocks buyers and the new owner.
Likelihood:
Every time a seller lists an NFT, they retain full ERC721 custody and can transfer it away at any time. The protocol has no mechanism to prevent external sales.
The collateral recovery path is trivial: list → cancel (get collateral back) → re-list. The second listing costs nothing, so creating ghost listings is free.
Impact:
Sellers bypass protocol fees entirely by selling through external channels. The protocol collects zero revenue on these sales.
Ghost listings permanently block the tokenId on NFTDealers — buyers who call buy() get reverted, and the new NFT owner cannot list the same token because isActive is still true. The activeListingsCounter is permanently inflated.
A seller mints an NFT (20 USDC collateral), lists it at 100 USDC, then immediately cancels — recovering the 20 USDC collateral. The seller re-lists the same token for free (no collateral at risk) and transfers the NFT externally via transferFrom. The seller gets paid on another channel with zero fees to the protocol. The ghost listing stays active permanently: a buyer who tries to buy() gets reverted because the seller no longer owns the NFT, and the new owner is blocked from listing on NFTDealers because isActive is still true.
Escrow the NFT when listing. Transferring the NFT to the contract on list() ensures the seller cannot sell it externally. The protocol controls the asset during the sale, guaranteeing that buy() always succeeds and fees cannot be bypassed.
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.