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.
It is trivial to end an auction, the only precondition is that the 15 minutes cool down period has passed.
The issue creates unfair advantage to early bidders.
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);
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);
}
Keep in state the timestamp of the first bid or the listing time and check against it.