Under normal behavior, collectUsdcFromSelling() should only be callable after a listing has been successfully sold. A canceled listing should not unlock sale proceeds, because no buyer payment was ever made for that listing.
In the current implementation, collectUsdcFromSelling() does not verify that a sale occurred. It only verifies that the listing is inactive.
However, both buy() and cancelListing() set isActive to false. This means the function treats two fundamentally different terminal states as equivalent:
sold listing
canceled listing
Because of that, a seller can create a listing, cancel it, and still call collectUsdcFromSelling() as though the NFT had been sold. The function will then compute a payout from the stored listing.price and collateralForMinting[tokenId], even though no sale took place.
Likelihood:
The bug occurs whenever a seller cancels a listing and then calls collectUsdcFromSelling().
The exploit path is simple because cancellation directly creates the same inactive state that the payout function accepts.
Impact:
A seller can receive “sale proceeds” for a listing that was never sold.
The payout amount is derived from the stored listing price and can drain pooled USDC from the contract.
Legitimate users can be harmed because the contract uses a shared USDC balance rather than isolated per-sale accounting.
Paste this inside NFTDealersTest.t.sol:
Track whether a listing was actually sold, and gate collectUsdcFromSelling() on that state instead of using !isActive as a proxy.
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.