NFT Dealers

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

`updatePrice` skips the `MIN_PRICE` check — sellers can drop prices to near-zero after listing

Author Revealed upon completion

Root + Impact

Description

list() enforces require(_price >= MIN_PRICE) to ensure no NFT sells below 1 USDC. But updatePrice() only checks _newPrice > 0. A seller can list at exactly MIN_PRICE to pass the initial check, then immediately call updatePrice to set the price to 1 wei (0.000001 USDC).

This completely defeats the minimum price floor.

function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
require(_price >= MIN_PRICE, "Price must be at least 1 USDC"); // @> enforced
...
}
function updatePrice(uint256 _listingId, uint32 _newPrice) external onlySeller(_listingId) {
...
require(_newPrice > 0, "Price must be greater than 0"); // @> MIN_PRICE not checked
s_listings[_listingId].price = _newPrice;
...
}

Risk

Likelihood: Two transactions is all it takes: list at MIN_PRICE, then update to 1. No special setup needed.

Impact: The price floor becomes meaningless. Sellers can do wash trades at negligible cost (fees on 0.000001 USDC are effectively zero), undermining the fee mechanism and potentially manipulating trading volume metrics.


Proof of Concept

  1. Alice lists NFT #1 at MIN_PRICE (1 USDC) — passes the check.

  2. Alice calls updatePrice(1, 1) — sets price to 1 wei (0.000001 USDC). updatePrice only checks > 0, so it passes.

  3. NFT is now listed at 0.000001 USDC, 1,000,000x below the intended minimum.

function testM02_UpdatePriceBelowMinimum() public {
usdc.mint(alice, 20e6);
vm.startPrank(alice);
usdc.approve(address(nftDealers), 20e6);
nftDealers.mintNft();
nftDealers.list(1, uint32(nftDealers.MIN_PRICE()));
vm.stopPrank();
// Update to 1 wei — bypasses MIN_PRICE
vm.prank(alice);
nftDealers.updatePrice(1, 1);
(, uint32 price,,,) = nftDealers.s_listings(1);
assertEq(price, 1);
assertTrue(price < nftDealers.MIN_PRICE());
}

Recommended Mitigation

Apply the same MIN_PRICE check 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);
}

Support

FAQs

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

Give us feedback!