Skip to content

fix cleanupGhostListings leaking listedNFTs entry for ghost listing#115

Open
joshuahannan wants to merge 2 commits intojosh/is-ghost-listingfrom
josh/cleanup-ghost-listings-fix
Open

fix cleanupGhostListings leaking listedNFTs entry for ghost listing#115
joshuahannan wants to merge 2 commits intojosh/is-ghost-listingfrom
josh/cleanup-ghost-listings-fix

Conversation

@joshuahannan
Copy link
Member

Summary

Fixes Bug 1 (Critical) from the code review: cleanupGhostListings removes the ghost listing from self.listings and cleans up its duplicates, but never calls removeDuplicateListing for the ghost listing itself. This leaves a dangling entry in listedNFTs — the secondary index that tracks which listing IDs exist per NFT type+ID.

Trace with listings A, B, C all for the same NFT:

  1. self.listings.remove(A) — gone from listings
  2. getDuplicateListingIDs(A) → returns [B, C] (listedNFTs still has A, B, C)
  3. cleanup(B) → calls removeDuplicateListing(B) → listedNFTs = [A, C]
  4. cleanup(C) → calls removeDuplicateListing(C) → listedNFTs = [A]
  5. Burner.burn(A) — burned, but listedNFTs still has [A]dangling entry

Every other removal path (removeListing, cleanupPurchasedListings, cleanup) correctly calls removeDuplicateListing for the primary listing before processing duplicates.

Fix: call removeDuplicateListing for the ghost listing immediately after removing it from self.listings, and before calling getDuplicateListingIDs. This ensures getDuplicateListingIDs sees the correct state and returns only the true duplicates.

Changes

  • contracts/NFTStorefrontV2.cdc — insert removeDuplicateListing call in cleanupGhostListings
  • scripts/get_existing_listing_ids.cdc — new script wrapping getExistingListingIDs for test use
  • tests/NFTStorefrontV2_test.cdctestCleanupGhostListingsRemovesListedNFTsEntry creates two listings for the same NFT, ghosts both, cleans up, and asserts getExistingListingIDs returns empty

Test plan

  • make ci passes
  • testCleanupGhostListingsRemovesListedNFTsEntry fails on the unfixed contract and passes on the fix (regression test for the dangling listedNFTs entry)
  • testCleanupGhostListings still passes

🤖 Generated with Claude Code

joshuahannan and others added 2 commits March 24, 2026 16:21
After removing the ghost listing from self.listings, the function fetched
duplicate listing IDs and cleaned each one up — but never called
removeDuplicateListing for the ghost listing itself. Every other removal
path (removeListing, cleanupPurchasedListings, cleanup) correctly removes
the primary listing from listedNFTs before processing duplicates.

The fix calls removeDuplicateListing for the ghost listing immediately after
removing it from self.listings, and before getDuplicateListingIDs is called.
This ensures getDuplicateListingIDs sees the correct state and the listedNFTs
entry is fully cleared.

Also adds:
- scripts/get_existing_listing_ids.cdc to expose getExistingListingIDs for tests
- testCleanupGhostListingsRemovesListedNFTsEntry to regression-test the fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous commit called removeDuplicateListing on the primary listing
before getDuplicateListingIDs, but getDuplicateListingIDs uses
contains(listingID) as a guard — once the primary is removed from
listedNFTs, it returns [] and duplicates are never cleaned up.

Correct order: fetch duplicates first (while the primary is still in
listedNFTs), then call removeDuplicateListing for the primary, then
clean up each duplicate.

Also bumps testRemoveItem's accumulated ListingCompleted event count
from 6 to 8: testCleanupGhostListingsRemovesListedNFTsEntry emits two
additional events (one for the primary ghost, one for its duplicate).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@joshuahannan joshuahannan requested a review from nvdtf March 24, 2026 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant