Bid Beasts

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

Inconsistent auction ending deadline

Inconsistent auction ending deadline

Description

The protocol documentation states that after 3 days anyone can end an auction. However, it is possible that the auction is ended 15 minutes after the first bid is placed.

Risk

Likelihood: High

It is trivial to end an auction, the only precondition is that the 15 minutes cool down period has passed.

Impact: Medium

The issue creates unfair advantage to early bidders.

Proof of concept

The following test case demonstrates an early settlement of the auction, specifically within 1 hour and 16 minutes.

function test_can_settle_auction_for_listed_item_earlier_than_3_days() public {
vm.prank(PROTOCOL_OWNER);
nft.mint(SELLER);
assertEq(nft.ownerOf(0), SELLER);
uint256 askPrice = MIN_PRICE + 1;
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);
vm.warp(1 hours);
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 hours + 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 hours + 15 minutes);
BidBeastsNFTMarket.Bid memory bid = market.getHighestBid(0);
assertEq(bid.bidder, BIDDER_1);
assertEq(bid.amount, bidPrice);
vm.warp(1 hours + 16 minutes);
uint256 totalFeesStartingBalance = market.s_totalFee();
uint256 startingSellerBalance = address(SELLER).balance;
vm.startPrank(BIDDER_2);
vm.expectEmit(true, true, true, true);
emit AuctionSettled(0, BIDDER_1, SELLER, bidPrice);
market.settleAuction(0);
vm.stopPrank();
uint256 totalFeesEndingBalance = market.s_totalFee();
uint256 endingSellerBalance = address(SELLER).balance;
assertEq(nft.ownerOf(0), BIDDER_1);
assertEq(totalFeesEndingBalance - totalFeesStartingBalance, 5e16); // 5% fee
assertEq(endingSellerBalance - startingSellerBalance + 5e16, bidPrice);
listing = market.getListing(0);
assertEq(listing.listed, false);
assertEq(listing.seller, SELLER);
assertEq(listing.minPrice, askPrice);
assertEq(listing.buyNowPrice, 0);
assertEq(listing.auctionEnd, 1 hours + 15 minutes);
}

Recommended Mitigation

Keep in state the timestamp of the first bid or the listing time and check against it.

function settleAuction(uint256 tokenId) external isListed(tokenId) {
Listing storage listing = listings[tokenId];
+ // Check 3 days after creation have passed
require(listing.auctionEnd > 0, "Auction has not started (no bids)");
require(block.timestamp >= listing.auctionEnd, "Auction has not ended");
require(bids[tokenId].amount >= listing.minPrice, "Highest bid did not meet min price");
_executeSale(tokenId);
}
Updates

Lead Judging Commences

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

BidBeasts Marketplace: Improper Documentation

Documentation for BidBeasts Marketplace is incomplete or inaccurate, potentially leading to misconfigurations or security misunderstandings.

Support

FAQs

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