Integer Division Precision Loss in Bid Increment Calculation
Description
- 
The normal behavior should be that new bids must be at least 5% higher than the previous bid to ensure fair auction progression and prevent spam bidding.
 
- 
The current implementation uses integer division before multiplication, causing precision loss that allows bidders to place bids lower than the intended 5% increment, giving them an unfair advantage.
 
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
    
    
    
    uint256 requiredAmount;
    
    if (previousBidAmount == 0) {
            requiredAmount = listing.minPrice;
            require(msg.value > requiredAmount, "First bid must be > min price");
            listing.auctionEnd = block.timestamp + S_AUCTION_EXTENSION_DURATION;
            emit AuctionExtended(tokenId, listing.auctionEnd);
        } else {
            
@>          requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
            require(msg.value >= requiredAmount, "Bid not high enough");
            uint256 timeLeft = 0;
            if (listing.auctionEnd > block.timestamp) {
                timeLeft = listing.auctionEnd - block.timestamp;
            }
            if (timeLeft < S_AUCTION_EXTENSION_DURATION) {
                
                
                
                listing.auctionEnd = listing.auctionEnd + S_AUCTION_EXTENSION_DURATION;
                emit AuctionExtended(tokenId, listing.auctionEnd);
            }
        }
  
    
    
  }
    
Risk
Likelihood: High
The vulnerability occurs on every bid after the first one
 
Affects all auctions with bid amounts that aren't perfect multiples of 100
 
Precision loss becomes more significant with smaller bid amounts
 
Impact: Medium
Proof of Concept
The PoC demonstrates how the integer division precision loss allows bids that are significantly lower than the required 5% increment.
Add the test below to the BidBeastsMarketPlaceTest.t.sol:
function testBidIncrementMathError() public {
        _mintNFT();
        _listNFT();
        
        uint256 FIRST_BID = MIN_PRICE + 199; 
        vm.prank(BIDDER_1);
        market.placeBid{value: FIRST_BID}(TOKEN_ID);
        
        uint256 S_MIN_BID_INCREMENT_PERCENTAGE = 5;
        
        uint256 calculateWrong = (FIRST_BID / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
        
        uint256 calculateCorrect = (FIRST_BID * (100 + S_MIN_BID_INCREMENT_PERCENTAGE)) / 100;
        console.log("Previous bid:", FIRST_BID); 
        console.log("Wrong calculation result:", calculateWrong); 
        console.log("Correct calculation result:", calculateCorrect); 
        console.log("Precision loss:", calculateCorrect - calculateWrong); 
        
        assertTrue(calculateWrong < calculateCorrect, "Contract calculation should be less than correct calculation");
        
        vm.prank(BIDDER_2);
        market.placeBid{value: calculateWrong}(TOKEN_ID);
        
        assertEq(market.getHighestBid(TOKEN_ID).amount, calculateWrong, "Low bid should be accepted due to math error");
        assertEq(market.getHighestBid(TOKEN_ID).bidder, BIDDER_2, "BIDDER_2 should be highest bidder");
    }
Run the test with:
forge test --match-path test/BidBeastsMarketPlaceTest.t.sol --match-test testBidIncrementMathError
Recommended Mitigation
Fix the order of operations to perform multiplication before division, preventing precision loss:
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
    // ... existing code ...
    
    } else {
-       requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
+       requiredAmount = (previousBidAmount * (100 + S_MIN_BID_INCREMENT_PERCENTAGE)) / 100;
        require(msg.value >= requiredAmount, "Bid not high enough");
        
    // ... existing code ...
}