NFT Dealers

First Flight #58
Beginner FriendlyFoundry
100 EXP
Submission Details
Impact: high
Likelihood: high

Relisting the same token overwrites prior sale record and blocks original seller collection

Author Revealed upon completion

Root + Impact

Description

  • Normal behavior: each completed sale should keep an immutable payout record so the original seller can collect their proceeds.

  • Specific issue: listings are stored by tokenId, so relisting the same token overwrites prior sale data. onlySeller then points to the new seller, and the original seller can no longer call collectUsdcFromSelling for their completed sale.

// Root cause in the codebase with @> marks to highlight the relevant section
modifier onlySeller(uint256 _listingId) {
@> require(s_listings[_listingId].seller == msg.sender, "Only seller can call this function");
_;
}
function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
...
@> s_listings[_tokenId] = Listing({
@> seller: msg.sender,
@> price: _price,
@> nft: address(this),
@> tokenId: _tokenId,
@> isActive: true
@> });
}

Risk

Likelihood:

  • This occurs during normal market behavior because buyers can relist immediately after purchase.

  • The overwritten record is deterministic because s_listings uses tokenId as the storage key.

Impact:

  • Original sellers can lose access to proceeds from already completed sales.

  • Proceeds and collateral flow can be redirected to later listing state, causing payout integrity failures.

Proof of Concept

// Validated by test: testFinding03_relistingOverwritesSellerAndBlocksCollection
_sellerSaleAtMinPrice();
vm.prank(buyer);
nft.list(1, uint32(2e6)); // overwrites prior listing record for tokenId 1
vm.prank(seller);
vm.expectRevert("Only seller can call this function");
nft.collectUsdcFromSelling(1);

Recommended Mitigation

- mapping(uint256 => Listing) public s_listings; // keyed by tokenId
+ mapping(uint256 => Listing) public s_listings; // keyed by listingId
+ mapping(uint256 => uint256) public activeListingByTokenId;
function list(uint256 _tokenId, uint32 _price) external {
...
- s_listings[_tokenId] = Listing(...);
+ listingsCounter++;
+ uint256 listingId = listingsCounter;
+ s_listings[listingId] = Listing(...);
+ activeListingByTokenId[_tokenId] = listingId;
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!