Bid Beasts

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

Auction Duration Does Not Comply with "Precisely 3-Day Auction End Time"

Auction Duration Does Not Comply with "Precisely 3-Day Auction End Time"

Description

  • According to the documentation, once a seller lists an NFT, the auction duration should be exactly 3 days.

  • This is also consistent with the platform’s promotional materials when attracting sellers, such as: "Our platform ensures that sellers’ NFTs receive 3 days of continuous market exposure."

  • However, based on the logic in the placeBid function, the auction duration behaves as follows: "When a bid is placed, the duration is extended by 15 minutes. If bids are placed continuously, the auction may never end." This clearly does not align with the "3-day" description.

  • BidBeastsNFTMarketPlace.sol is as follows:

function placeBid(uint256 tokenId) external payable isListed(tokenId) {
// ...original code
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);
} 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);
}
}
// ...original code
}

Risk

Likelihood:

  • Each time a buyer places a bid, the duration is extended. If no further bids are placed, the duration stops extending.
    Impact:

  • Although there is no direct financial loss, this makes it impossible for both buyers and sellers to accurately predict "whether the auction will end abruptly or how long it will last," which severely undermines the protocol’s original intent.

  • In most cases, the auction is more likely to end early. While it is possible for the auction to exceed 3 days, the conditions required are relatively strict.

Proof of Concept

  • It is relatively easy for the auction to end abruptly. Here, I demonstrate how the auction duration can exceed 3 days.

  • Add the following to BidBeastsMarketPlaceTest.t.sol:

function test__auction_extension_duration() public {
// Create seller account
address seller = makeAddr("seller");
vm.deal(seller, 10 ether);
// Mint NFT for seller
vm.prank(OWNER);
uint256 tokenId = nft.mint(seller);
// Seller lists NFT
vm.startPrank(seller);
nft.approve(address(market), tokenId);
market.listNFT(tokenId, 0.01 ether, 0);
vm.stopPrank();
// Create buyer accounts
address buyer_01 = makeAddr("buyer_01");
vm.deal(buyer_01, type(uint256).max);
address buyer_02 = makeAddr("buyer_02");
vm.deal(buyer_02, type(uint256).max);
uint256 timeStart = block.timestamp;
uint256 startPrice = MIN_PRICE + 1;
console.log("startPrice = ", startPrice);
for(uint256 i=0; i<800; i++) {
console.log("309 i = ", i);
// Buyer places bid
vm.prank(buyer_01);
if (i > 0) {
startPrice = startPrice * 106 / 100;
} else {
startPrice = startPrice + 0.01 ether;
}
market.placeBid{value: startPrice}(tokenId);
console.log("319 i = ", i);
vm.warp(block.timestamp + market.S_AUCTION_EXTENSION_DURATION()/2);
// Buyer places bid
vm.prank(buyer_02);
startPrice = startPrice * 106 / 100;
market.placeBid{value: startPrice}(tokenId);
console.log("332 i = ", i);
console.log("startPrice = ", startPrice);
console.log("buyer_01.balance = ", buyer_01.balance);
console.log("buyer_02.balance = ", buyer_02.balance);
}
(,,,uint256 auctionEnd,) = market.listings(tokenId);
uint256 costSeconds = auctionEnd - timeStart;
console.log("costSeconds = ", costSeconds);
vm.assertTrue(costSeconds > 3 days);
}

Recommended Mitigation

  • Only when the first bid is placed, change the auction end timestamp to 3 days later.

function placeBid(uint256 tokenId) external payable isListed(tokenId) {
// ...original code
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 + 3 days;
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);
}
}
// ...original code
}
Updates

Lead Judging Commences

cryptoghost Lead Judge
2 months ago
cryptoghost Lead Judge 2 months 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.

Give us feedback!