When a seller lists an NFT, the marketplace is expected to hold the NFT (or at minimum ensure the seller cannot transfer it) until the listing is resolved. This prevents the seller from listing at an attractive price and then removing the NFT.
list() does not transfer the NFT to the contract or revoke the seller's transfer ability. The seller retains full ownership and can transfer the NFT to another address at any time while the listing stays active. A buyer calling buy() will have their USDC transferred successfully, after which _safeTransfer reverts because the seller no longer holds the token, causing the full transaction to revert. However, the listing remains permanently active, and the seller can repeat this griefing cycle indefinitely.
Likelihood:
A seller transferring their NFT after listing is a single additional transaction with no cost beyond gas.
The listing remains permanently active with no automatic deactivation mechanism.
Impact:
Buyers waste gas on repeated failed purchase attempts against griefed listings.
Sellers can use a single active listing as a denial-of-service tool against a specific token.
A seller lists an NFT, then immediately transfers it to a dead address. The listing remains active. Every buy attempt by a legitimate buyer reverts, but the listing cannot be cleared.
Transfer the NFT into the contract at listing time and return it to the seller on cancellation. This guarantees atomicity between listing and sale.
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.