NFT Dealers

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

M-1: `uint32` Price Field Causes Silent Truncation for High-Value NFTs

Author Revealed upon completion

Description:

The Listing struct stores price as uint32, with a maximum value of approximately 4,294 USDC (4,294,967,295 / 1e6). Both list() and updatePrice() accept uint32 _price, and the test suite casts uint256 values directly: nftDealers.list(_tokenId, uint32(_price)). Any price above ~4,294 USDC silently truncates, setting a completely different (lower) price than the seller intended.

struct Listing {
address seller;
uint32 price; // ← max ~4,294 USDC with 6 decimals
...
}

Impact: A seller pricing an NFT at, say, 5,000 USDC would have their price silently set to 5000e6 % 2^32 ≈ 705,032,704, i.e., ~705 USDC — losing ~4,295 USDC of sale proceeds.

Recommended Mitigation: Change the price field to uint256:

struct Listing {
address seller;
uint256 price; // supports any price
address nft;
uint256 tokenId;
bool isActive;
}

Proof of Concept (Forge):

function test_price_truncation_uint32() public {
vm.prank(owner); nftDealers.revealCollection();
vm.prank(owner); nftDealers.whitelistWallet(userWithCash);
usdc.mint(userWithCash, 20e6);
vm.startPrank(userWithCash);
usdc.approve(address(nftDealers), 20e6);
nftDealers.mintNft();
uint256 intendedPrice = 5000e6; // 5,000 USDC — exceeds uint32 max
uint32 truncatedPrice = uint32(intendedPrice);
nftDealers.list(1, truncatedPrice);
vm.stopPrank();
(, uint32 storedPrice,,,) = nftDealers.s_listings(1);
// Prove truncation occurred
assertNotEq(uint256(storedPrice), intendedPrice,
"Price was NOT truncated — fix needed if this passes");
emit log_named_uint("Intended price (USDC units)", intendedPrice);
emit log_named_uint("Stored price after truncation", uint256(storedPrice));
}

Support

FAQs

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

Give us feedback!