NFT Dealers

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

[L-02] updatePrice() Does Not Enforce MIN_PRICE

Author Revealed upon completion

Root Cause: list() enforces a minimum price of 1 USDC via MIN_PRICE but updatePrice() only checks that the new price is greater than 0, allowing sellers to bypass MIN_PRICE after listing.

Impact: Sellers can set prices below MIN_PRICE after listing, causing fee calculations to round down to 0 and the owner to collect no fees.


Description:

When a user first lists an NFT, the price is validated against MIN_PRICE. However after listing, the seller can call updatePrice() to set the price to as low as 1 wei. At such low prices, the fee calculation returns 0 due to integer division, meaning the owner collects nothing on the sale.

function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
@> require(_price >= MIN_PRICE, "Price must be at least 1 USDC"); // MIN_PRICE enforced here
...
}
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"); // MIN_PRICE NOT enforced here
...
}

Risk

Likelihood: Low

  • Requires a seller to deliberately update their price below MIN_PRICE

  • Only affects fee collection, not user funds directly

Impact: Low

  • Owner collects zero fees on sales with prices below MIN_PRICE due to integer division rounding down

  • Enables dust listings that pollute the marketplace

Proof of Concept

// PoC: We prove a seller can bypass MIN_PRICE via updatePrice()
// by listing at a valid price then updating to 1 wei
// showing the price falls below MIN_PRICE with no revert
function test_POC_UpdatePriceBelowMinPrice() public revealed {
vm.prank(owner);
nftDealers.whitelistWallet(userWithCash);
// Mint and list at valid MIN_PRICE
vm.startPrank(userWithCash);
usdc.approve(address(nftDealers), 20e6);
nftDealers.mintNft();
nftDealers.list(1, 1000e6);
// Update price to 1 wei and bypasses MIN_PRICE check
nftDealers.updatePrice(1, 1);
vm.stopPrank();
(, uint32 price,,,) = nftDealers.s_listings(1);
// Price is now below MIN_PRICE with no revert
assertLt(price, nftDealers.MIN_PRICE());
// Fee calculation rounds down to 0 and owner collects nothing
assertEq(nftDealers.calculateFees(1), 0);
}

Recommended Mitigation

function updatePrice(uint256 _listingId, uint32 _newPrice) external onlySeller(_listingId) {
- require(_newPrice > 0, "Price must be greater than 0");
+ // enforce same minimum price as list() to prevent fee bypass
+ require(_newPrice >= MIN_PRICE, "Price below minimum");
...
}

Support

FAQs

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

Give us feedback!