Bid Beasts

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

Precision loss in price inflation formula

Precision loss in price inflation formula

Description

The protocol inflates the bidding price on every bid apart from the first one. However, the formula losses precision in the maths that can be applied in solidity.

Risk

Likelihood: High

The issue occurs from the second bid onwards

Imapct: Medium

Precision loss on the bidding amount, eventually affecting the protocol fees

Proof of Concept

The following test case proves that a lower inflated percentage is acceptable, the price is inflated by 0.49% instead of 0.5%.

function test_precision_loss() public {
vm.prank(PROTOCOL_OWNER);
nft.mint(SELLER);
uint256 askPrice = 0.01 ether;
uint256 bidPrice = askPrice + 1;
vm.startPrank(SELLER);
nft.approve(address(market), 0);
vm.expectEmit(true, true, true, true);
emit NftListed(0, SELLER, askPrice, 0);
market.listNFT(0, askPrice, 0);
vm.stopPrank();
assertEq(nft.ownerOf(0), address(market));
BidBeastsNFTMarket.Listing memory listing = market.getListing(0);
assertEq(listing.listed, true);
assertEq(listing.seller, SELLER);
assertEq(listing.minPrice, askPrice);
assertEq(listing.buyNowPrice, 0);
assertEq(listing.auctionEnd, 0);
BidBeastsNFTMarket.Bid memory bid = market.getHighestBid(0);
assertEq(bid.bidder, address(0));
assertEq(bid.amount, 0);
vm.warp(1 days);
vm.startPrank(BIDDER_1);
vm.expectEmit(true, true, true, true);
emit AuctionSettled(0, BIDDER_1, SELLER, bidPrice);
vm.expectEmit(true, true, false, false);
emit AuctionExtended(0, 1 days + 15 minutes);
vm.expectEmit(true, true, true, false);
emit BidPlaced(0, BIDDER_1, bidPrice);
market.placeBid{value: bidPrice}(0);
vm.stopPrank();
listing = market.getListing(0);
assertEq(listing.listed, true);
assertEq(listing.seller, SELLER);
assertEq(listing.minPrice, askPrice);
assertEq(listing.buyNowPrice, 0);
assertEq(listing.auctionEnd, 1 days + 15 minutes);
bid = market.getHighestBid(0);
assertEq(bid.bidder, BIDDER_1);
assertEq(bid.amount, 0.01 ether + 1);
// 2nd Bid
vm.warp(1 days + 10 minutes);
// 0.49% increment
@> bidPrice = 0.0105 ether;
vm.startPrank(BIDDER_2);
vm.expectEmit(true, true, true, true);
emit AuctionSettled(0, BIDDER_2, SELLER, bidPrice);
vm.expectEmit(true, true, true, false);
emit BidPlaced(0, BIDDER_2, bidPrice);
market.placeBid{value: bidPrice}(0);
vm.stopPrank();
bid = market.getHighestBid(0);
assertEq(bid.bidder, BIDDER_2);
assertEq(bid.amount, 0.0105 ether);
}

Recommended Mitigation

Use multiplication before division

- requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
+ requiredAmount = (previousBidAmount * S_MIN_BID_INCREMENT_PERCENTAGE / 100);
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.