Precision loss in BidBeastsNFTMarket::placeBid function calculation of requiredAmount for next bid, possibly causing denial of service.
Description
-
The current implementation of the `requiredAmount` formula calculation loses precision for some bid amounts, causing some valid bids to revert.
-
Denial of service for users.
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
...
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 {
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);
}
}
bids[tokenId] = Bid(msg.sender, msg.value);
if (previousBidder != address(0)) {
_payout(previousBidder, previousBidAmount);
}
emit BidPlaced(tokenId, msg.sender, msg.value);
}
Risk
Likelihood: Medium
Impact: Medium
Proof of Concept
Add the following code snippet to the `BidBeastsMarketPlaceTest.t.sol` test file.
function testRequiredAmountCalculation(uint256 bidAmount) public pure {
bidAmount = bound(bidAmount, 0.01 ether, 1 ether);
uint256 S_MIN_BID_INCREMENT_PERCENTAGE = 5;
uint256 previousBidAmount = bidAmount;
uint256 requiredAmountCorrect = previousBidAmount + (previousBidAmount * S_MIN_BID_INCREMENT_PERCENTAGE) / 100;
uint256 requiredAmountInCorrect = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
uint256 precisionDifference = requiredAmountCorrect - requiredAmountInCorrect;
console.log("Precision difference: ", precisionDifference);
assertEq(precisionDifference, 0, "Precision difference is greater than 0");
}
Recommended Mitigation
Modify the `BidBeastsNFTMarket::placeBid` function to calculate the `requiredAmount` correctly.
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
...
- requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
+ requiredAmount = previousBidAmount + (previousBidAmount * S_MIN_BID_INCREMENT_PERCENTAGE) / 100;
}