Bid Beasts

First Flight #49
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

Division Before Multiplication Causes Precision Loss in Bid Increment Calculation

Division Before Multiplication Causes Precision Loss in Bid Increment Calculation

Description

  • The auction system should calculate the minimum required bid increment by adding 5% to the previous bid amount with proper precision.

  • The bid increment calculation performs division before multiplication, causing precision loss due to Solidity's integer arithmetic truncation, which results in lower minimum bid requirements than intended.

function placeBid(uint256 tokenId) external payable isListed(tokenId) {
// ... existing code ...
if (previousBidAmount == 0) {
// First bid logic
} else {
@> requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE); //Division before multiplication loses precision
require(msg.value >= requiredAmount, "Bid not high enough");
// ... existing code ...
}
// ... existing code ...
}

Risk

Likelihood:

  • This precision loss occurs on every subsequent bid after the first bid in any auction.

  • The issue is more pronounced with smaller bid amounts where the truncation has a larger relative impact.

  • Happens automatically as part of the core bidding mechanism without any special conditions required.

Impact:

  • Protocol loses potential fee revenue from higher final sale amounts.

  • Sellers receive lower final sale prices due to insufficient bid increments driving up the price.

Proof of Concept

// Example with previousBidAmount = 0.099 ether (99000000000000000 wei)
uint256 previousBidAmount = 0.099 ether; // 99000000000000000 wei
uint256 S_MIN_BID_INCREMENT_PERCENTAGE = 5;
// Current implementation (division first):
uint256 wrongCalculation = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
// = (99000000000000000 / 100) * 105
// = 990000000000000 * 105
// = 103950000000000000 wei (0.10395 ether)
// Correct implementation (multiplication first):
uint256 correctCalculation = (previousBidAmount * (100 + S_MIN_BID_INCREMENT_PERCENTAGE)) / 100;
// = (99000000000000000 * 105) / 100
// = 10395000000000000000 / 100
// = 103950000000000000 wei (0.10395 ether)
// In this case both are equal, but with amounts like 0.0999 ether:
previousBidAmount = 99900000000000000; // 0.0999 ether
// Wrong: (99900000000000000 / 100) * 105 = 999000000000000 * 105 = 104895000000000000
// Correct: (99900000000000000 * 105) / 100 = 10489500000000000000 / 100 = 104895000000000000
// The difference becomes significant with smaller decimals:
previousBidAmount = 99; // Very small amount for demonstration
// Wrong: (99 / 100) * 105 = 0 * 105 = 0 (complete loss!)
// Correct: (99 * 105) / 100 = 10395 / 100 = 103

Recommended Mitigation

Perform multiplication before division to preserve precision:

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 ...
}
// ... rest of function ...
}
Updates

Lead Judging Commences

cryptoghost Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

BidBeasts Marketplace: Integer Division Precision Loss

Integer division in requiredAmount truncates fractions, allowing bids slightly lower than intended.

Support

FAQs

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