list() is expected to create a listing that buyers can reference by the ID emitted in the NFT_Dealers_Listed event.
list() stores the listing at s_listings[_tokenId], keyed by the NFT token ID. However, the event emits listingsCounter as the listing identifier. All consumer functions (buy, cancelListing, updatePrice, collectUsdcFromSelling) accept a _listingId parameter and look up s_listings[_listingId]. When a user passes the emitted counter value, they read a different — possibly empty — storage slot, causing the call to revert with ListingNotActive.
Likelihood: High
Triggered by any of these routine actions: a seller cancels and re-lists the same NFT (counter increments past tokenId permanently), or multiple NFTs are listed in any order other than strict sequential minting order. Both are normal user behaviours.
Any off-chain integration or marketplace UI built on the emitted listingId — the standard expectation for event-driven contracts — will produce incorrect call arguments.
Impact: High
Buyers who use the emitted listing ID cannot purchase the NFT; their transaction always reverts.
The listing is effectively invisible to any external marketplace UI or indexer following the contract's own events.
Sellers cannot cancel or update listings via the event-announced ID, creating permanently stuck listings from an off-chain perspective.
Two scenarios are demonstrated. The first shows out-of-order listing. The second shows cancel-and-relist, a routine action that permanently desynchronises the counter from the tokenId for every future listing of that NFT.
Scenario 1 — Out-of-order listing:
Scenario 2 — Cancel and relist (routine user action):
Emit _tokenId instead of listingsCounter in the NFT_Dealers_Listed event so that off-chain callers always receive the correct key for all consumer functions.
Alternatively, redesign the storage key to use listingsCounter and maintain a listingIdToTokenId mapping so that counter-based IDs in events match storage keys.
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.