The s_listings mapping uses tokenId as its key, and all on-chain functions — buy(), cancelListing(), updatePrice(), and collectUsdcFromSelling() — accept a _listingId parameter that resolves as a tokenId against this mapping.
The NFT_Dealers_Listed event emits listingsCounter as the listingId. After the first resale and relisting of any token, listingsCounter increments while s_listings[tokenId] is simply overwritten in place. Any off-chain consumer — front-end, indexer, or bot — that stores the emitted listingId and later calls buy(_listingId) will pass the wrong key, receiving a ListingNotActive revert or interacting with an entirely different listing.
File: src/NFTDealers.sol
The root cause is that s_listings was designed with two conflicting identifiers: the mapping key is tokenId (an NFT property), while the emitted listingId in NFT_Dealers_Listed is listingsCounter (a monotone counter). These two values only coincide by accident when tokenId == listingsCounter, which breaks permanently after any token is relisted. All read functions (buy, cancelListing, updatePrice, collectUsdcFromSelling) were built against the tokenId key but the public-facing event advertises the counter, creating an irreconcilable mismatch for any off-chain consumer.
Likelihood:
Any front-end or indexer consuming the NFT_Dealers_Listed event and using the emitted listingId to call buy() or cancelListing() passes a wrong key from the second listing of any token onward.
listingsCounter and tokenId diverge permanently after the first resale and relisting cycle — there is no on-chain mechanism to reconcile them.
Impact:
Off-chain consumers calling buy() or cancelListing() with the event-emitted listingId receive ListingNotActive errors or silently interact with a different listing than intended.
listingsCounter (exposed via totalListings()) is a misleading metric that does not correspond to any retrievable on-chain state, corrupting protocol analytics and front-end displays.
Alternatively, refactor
s_listingsto uselistingsCounteras the mapping key and storetokenIdinside the struct, ensuring the emitted ID and the on-chain lookup key are always identical. All functions accepting_listingIdwould then resolve correctly against the counter-keyed mapping.
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.