Incorrect Auction Timer: 15-Min Extension based on Docs/Readme vs. 3-Day Initial Duration Implemented in contract
Description
Documentation specifies 3-day auctions starting on first bid. Code initializes auctionEnd to 15 minutes on first bid, causing early endings instead of full exposure.
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);
}
Risk
Likelihood:
Impact:
-
Premature closures limit bids, undervaluing NFTs.
-
Misaligns with docs, confusing users and reducing adoption.
Proof of Concept
Lists NFT, places first bid to start 15-min timer, warps time past 15 min, checks auction ended and settle works to show early close vs. 3 days.
function testPrematureAuctionEnd() public {
_mintNFT();
_listNFT();
vm.deal(BIDDER_1, MIN_PRICE + 0.01 ether);
vm.prank(BIDDER_1);
market.placeBid{value: MIN_PRICE + 0.01 ether}(TOKEN_ID);
vm.warp(block.timestamp + 16 minutes);
BidBeastsNFTMarket.Listing memory listing = market.getListing(TOKEN_ID);
assertTrue(block.timestamp >= listing.auctionEnd);
market.settleAuction(TOKEN_ID);
}
Recommended Mitigation
Adds 3-day constant for first bid timer; sets initial end to 3 days; resets extensions to full 15 min from now, not add to remaining.
+uint256 public constant S_AUCTION_DURATION = 3 days;
+uint256 public constant S_AUCTION_EXTENSION_DURATION = 15 minutes;
if (previousBidAmount == 0) {
requiredAmount = listing.minPrice;
require(msg.value >= requiredAmount, "First bid must meet min price");
- listing.auctionEnd = block.timestamp + S_AUCTION_EXTENSION_DURATION;
+ listing.auctionEnd = block.timestamp + S_AUCTION_DURATION;
emit AuctionExtended(tokenId, listing.auctionEnd);
} else {
requiredAmount = (previousBidAmount / 100) * (100 + S_MIN_BID_INCREMENT_PERCENTAGE);
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;
+ if (timeLeft < S_AUCTION_EXTENSION_DURATION) {
+ listing.auctionEnd = block.timestamp + S_AUCTION_EXTENSION_DURATION;
emit AuctionExtended(tokenId, listing.auctionEnd);
}
}