NFT Dealers

First Flight #58
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: low
Valid

`updatePrice` bypasses `MIN_PRICE`

list() enforces MIN_PRICE only at listing creation, but updatePrice() later allows any value greater than zero. As a result, active listings can remain on the marketplace below the configured minimum price.

Description

  • Normal behavior: the configured minimum price should remain enforced throughout the full listing lifecycle, not only when a listing is first created.

function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
@> require(_price >= MIN_PRICE, "Price must be at least 1 USDC");
...
}
  • Issue: updatePrice() does not preserve the same rule. It only checks that the new price is greater than zero:

function updatePrice(uint256 _listingId, uint32 _newPrice) external onlySeller(_listingId) {
Listing memory listing = s_listings[_listingId];
uint256 oldPrice = listing.price;
if (!listing.isActive) revert ListingNotActive(_listingId);
@> require(_newPrice > 0, "Price must be greater than 0");
s_listings[_listingId].price = _newPrice;
emit NFT_Dealers_Price_Updated(_listingId, oldPrice, _newPrice);
}

Because of this inconsistency, a seller can create a valid listing and later reduce the price below MIN_PRICE, breaking the stated minimum-price policy for active listings.

Risk

Likelihood:

  • Every active seller can access updatePrice() after creating a valid listing.

  • The update path explicitly allows sub-floor values as long as the price is non-zero.

Impact:

  • Active listings can exist below the configured MIN_PRICE, violating marketplace pricing rules.

  • Integrators and users may rely on an invariant that the protocol does not actually preserve across listing updates.

Proof of Concept

The PoC demonstrates that a seller can create a listing at a valid price and then update it to a value below the configured minimum.

function test_UpdatePrice_AllowsValueBelowMinPrice() public revealed {
uint256 tokenId = 1;
uint32 initialPrice = 1000e6;
uint32 newPrice = 1e5;
mintAndListNFTForTesting(tokenId, initialPrice);
vm.prank(userWithCash);
nftDealers.updatePrice(tokenId, newPrice);
(, uint32 currentPrice,,,) = nftDealers.s_listings(tokenId);
assertEq(currentPrice, newPrice);
assertLt(currentPrice, nftDealers.MIN_PRICE());
}

PoC rationale:
The test shows that the minimum-price rule is enforced only at listing creation, not across the full active-listing lifecycle.

Recommended Mitigation

The same price floor enforced in list() should also be enforced in updatePrice().

function updatePrice(uint256 _listingId, uint32 _newPrice) external onlySeller(_listingId) {
Listing memory listing = s_listings[_listingId];
uint256 oldPrice = listing.price;
if (!listing.isActive) revert ListingNotActive(_listingId);
- require(_newPrice > 0, "Price must be greater than 0");
+ require(_newPrice >= MIN_PRICE, "Price must be at least 1 USDC");
s_listings[_listingId].price = _newPrice;
emit NFT_Dealers_Price_Updated(_listingId, oldPrice, _newPrice);
}

Mitigation rationale:
Applying the same floor check on both creation and update preserves pricing invariants for all active listings.

Updates

Lead Judging Commences

rubik0n Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

price-can-be-set-bleow-min

Support

FAQs

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

Give us feedback!