Bid Beasts

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

[High] Missing 3-Day Auction Duration Requirement

[High] Missing 3-Day Auction Duration Requirement

Description

The contract documentation explicitly states that auctions must last exactly 3 days before settlement. However, the placeBid function sets the auction duration to only 15 minutes (S_AUCTION_EXTENSION_DURATION) when the first bid is placed, and extends by 15 minutes for subsequent bids. This creates a critical deviation from the specified business logic.


// BidBeastsNFTMarketPlace.sol:151-157
if (previousBidAmount == 0) {
requiredAmount = listing.minPrice;
require(msg.value > requiredAmount, "First bid must be > min price");
@> listing.auctionEnd = block.timestamp + S_AUCTION_EXTENSION_DURATION; // BUG: Should be 3 days
emit AuctionExtended(tokenId, listing.auctionEnd);
} else {
// ... subsequent bid logic
}
// BidBeastsNFTMarketPlace.sol:36
@> uint256 constant public S_AUCTION_EXTENSION_DURATION = 15 minutes; // BUG: Should be 3 days for initial auction

Risk

Likelihood:

  • Affects every single auction in the marketplace

  • Deviation is guaranteed for all listings

  • The issue is baked into the core constant definition

Impact:

  • Business Logic Violation: Directly contradicts documented auction mechanics

  • Unfair Price Discovery: 15-minute auctions prevent proper market evaluation

  • Reduced Competition: Insufficient time for bidders to discover and participate in auctions

  • Seller Disadvantage: Premature settlement may result in significantly lower sale prices

  • Trust Erosion: Users expecting 3-day auctions will lose confidence in the platform

  • Manipulation Risk: Malicious actors can exploit short windows to win auctions before legitimate bidders participate

Documentation Reference

The contract documentation explicitly requires:

Auction Completion:

  • After 3 days, anyone can call endAuction(tokenId) to finalize the auction.

The contract also supports:

  • Auction deadline of exactly 3 days.

Proof of Concept

Current behaviour demonstrates the vulnerability:

function test_AuctionDurationTooShort() public {
_mintNFT();
_listNFT();
// First bid is placed
vm.prank(BIDDER_1);
market.placeBid{value: 1.1 ether}(TOKEN_ID);
uint256 auctionEnd = market.getListing(TOKEN_ID).auctionEnd;
uint256 actualDuration = auctionEnd - block.timestamp;
// Expected: 3 days (259200 seconds)
// Actual: 15 minutes (900 seconds)
assertEq(actualDuration, 900, "Auction duration is 15 minutes");
assertNotEq(
actualDuration,
3 days,
"Auction should last 3 days per docs"
);
// Auction can settle after just 15 minutes
vm.warp(block.timestamp + 901 seconds);
market.settleAuction(TOKEN_ID);
// Auction settled in < 16 minutes instead of required 3 days
}

Attack Scenario:

  1. Seller lists valuable NFT at 0.01 ETH minimum

  2. Malicious bidder monitors mempool for new listings

  3. Immediately bids 0.011 ETH to start 15-minute timer

  4. After 15 minutes, attacker settles and wins NFT far below the market value

  5. Seller receives drastically undervalued payment

Recommended Mitigation

Update the initial auction duration to match Business logic:

  1. If the auction is set to end after 3 Days with no extension whatsoever:

- event AuctionExtended(uint256 tokenId, uint256 newDeadline);
- uint256 constant public S_AUCTION_EXTENSION_DURATION = 15 minutes;
+ uint256 constant public S_INITIAL_AUCTION_DURATION = 3 days;
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
// ... existing checks ...
if (previousBidAmount == 0) {
requiredAmount = listing.minPrice;
require(msg.value > requiredAmount, "First bid must be > min price");
- listing.auctionEnd = block.timestamp + S_AUCTION_EXTENSION_DURATION;
+ listing.auctionEnd = block.timestamp + S_INITIAL_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;
- emit AuctionExtended(tokenId, listing.auctionEnd);
}
}
// ... rest of function ...
}
  1. If the Initial Auction is set to end after 3 Days, but will be extended by 15 minutes by up to 3 Days upon bids:

- uint256 constant public S_AUCTION_EXTENSION_DURATION = 15 minutes;
+ uint256 constant public S_INITIAL_AUCTION_DURATION = 3 days;
+ uint256 constant public S_BID_EXTENSION_DURATION = 15 minutes;
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
// ... existing checks ...
if (previousBidAmount == 0) {
requiredAmount = listing.minPrice;
require(msg.value > requiredAmount, "First bid must be > min price");
- listing.auctionEnd = block.timestamp + S_AUCTION_EXTENSION_DURATION;
+ listing.auctionEnd = block.timestamp + S_INITIAL_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_INITIAL_AUCTION_DURATION) {
+ listing.auctionEnd = listing.auctionEnd + S_BID_EXTENSION_DURATION;
emit AuctionExtended(tokenId, listing.auctionEnd);
}
}
// ... rest of function ...
}
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.