NFT Dealers

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

### [M-3] `list()` and `updatePrice()` use `uint32` for price, capping it at ~4,294 USDC and making the high fee tier unreachable

Author Revealed upon completion

Description: The _price parameter in list() and _newPrice in updatePrice() are typed as uint32, with a maximum value of 4,294,967,295 — equivalent to approximately 4,294 USDC (in 6-decimal USDC). However, the protocol defines fee thresholds of 10,000e6 and beyond, implying prices above 10,000 USDC are expected. The HIGH_FEE_BPS (5%) tier is therefore unreachable.

@> function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
@> function updatePrice(uint256 _listingId, uint32 _newPrice) external onlySeller(_listingId) {
uint256 private constant MID_FEE_THRESHOLD = 10_000e6; // 10,000 USDC — unreachable
uint256 private constant HIGH_FEE_BPS = 500; // 5% — never applied

Impact: High-value NFTs cannot be listed or have their price updated above ~4,294 USDC. The progressive fee mechanism is partially broken.

Proof of Concept:

When a seller passes a price above uint32 max, it silently truncates to a wrong value with no revert or warning — the NFT ends up listed at a fraction of the intended price.

Run forge test --match-test test_poc_M3 -vvv to see the following output:

Logs:
Desired price (5000 USDC): 5000000000
Truncated uint32 price: 705032704
Actual listed price: 705032704
PoC Test Code
function test_poc_M3_uint32PriceCap() public {
vm.startPrank(owner);
nftDealers.revealCollection();
nftDealers.whitelistWallet(userWithCash);
vm.stopPrank();
vm.startPrank(userWithCash);
usdc.approve(address(nftDealers), 20e6);
nftDealers.mintNft();
vm.stopPrank();
// uint32 max = 4_294_967_295 (~4294 USDC)
// Attempting to list at 5000 USDC silently truncates due to overflow
uint256 desiredPrice = 5000e6;
uint32 truncatedPrice = uint32(desiredPrice);
console.log("Desired price (5000 USDC):", desiredPrice);
console.log("Truncated uint32 price: ", truncatedPrice);
vm.prank(userWithCash);
nftDealers.list(1, truncatedPrice);
(, uint32 listedPrice,,,) = nftDealers.s_listings(1);
console.log("Actual listed price: ", listedPrice);
// Price is silently truncated, seller listed at wrong price without any error
assert(listedPrice != desiredPrice);
}

Recommended Mitigation: Change _price and _newPrice to uint256, and update the Listing.price field type accordingly.

struct Listing {
address seller;
- uint32 price;
+ uint256 price;
address nft;
uint256 tokenId;
bool isActive;
}
-function list(uint256 _tokenId, uint32 _price) external onlyWhitelisted {
+function list(uint256 _tokenId, uint256 _price) external onlyWhenRevealed {
-function updatePrice(uint256 _listingId, uint32 _newPrice) external onlySeller(_listingId) {
+function updatePrice(uint256 _listingId, uint256 _newPrice) external onlySeller(_listingId) {

Support

FAQs

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

Give us feedback!