Bid Beasts

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

Missing endAuction function

Description

Leading by the docs we must have endAuction function and 3days lifespan of the Auctions.

Docs: https://github.com/CodeHawks-Contests/2025-09-bid-beasts/blob/main/README.md

Risk

Likelihood:

  • The auction logic is totaly different from documentation

  • Incomplete Implementation / Documentation missmatch - the missing endAuction function

Impact:

  • Unexpected behaviour from described in the docs


Proof of Concept

Missing endAuction function that is described in the docs

Missing/Wrong logic for tracking 3 days required for proper functioning of the contract

Incorrect

The lifecycle of the auction is fundamentally different from what is documented
It starts with 15 minutes window and then refreshes this window if anyone place a bid.

Correct

The 3 days start when the auction is listed
after this 3 days the endAuction function can be called to transfer the NFT
to the seller if no bids were placed or to the highest bidder.

Wrong logic for tracking Auction lifecycle:

function placeBid(uint256 tokenId) external payable isListed(tokenId) {
More code...
// --- Regular Bidding Logic ---
uint256 requiredAmount;
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);
@> }
@> }
More code...
}

There is Incorect Auction lifecycle that leads to ending of listings after 15 minutes of innactivity.

To fix the issues we need to:

  • add the missing function

  • adjust the lifecycle of the Auctions


Recommended Mitigation

To fix all the issues related to the Incomplete/Wrong Implementation
We need to:

Refactor placeBid function

  1. on line 34 we need to change the constant https://github.com/CodeHawks-Contests/2025-09-bid-beasts/blob/449341c55a57d3f078d1250051a7b34625d3aa04/src/BidBeastsNFTMarketPlace.sol#L34


  1. we refactor the placeBid function code to handle correctly the 3 days as expected

- uint256 constant public S_AUCTION_EXTENSION_DURATION = 15 minutes;
+ uint256 public constant S_AUCTION_DEADLINE = 3 days;
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
More code...
// --- Regular Bidding Logic ---
uint256 requiredAmount;
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_AUCTION_DEADLINE;
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);
- }
- }
More code...
}

Implement endAuction

  1. Add the function + Event

// --- Events ---
event NftListed(uint256 tokenId, address seller, uint256 minPrice, uint256 buyNowPrice);
event NftUnlisted(uint256 tokenId);
event BidPlaced(uint256 tokenId, address bidder, uint256 amount);
event AuctionExtended(uint256 tokenId, uint256 newDeadline);
event AuctionSettled(uint256 tokenId, address winner, address seller, uint256 price);
event FeeWithdrawn(uint256 amount);
+ event AuctionEnded(uint256 tokenId);
function endAuction(uint256 tokenId) external isListed(tokenId) {
Listing storage listing = listings[tokenId];
require(block.timestamp >= listing.auctionEnd, "Auction has not ended");
if (listing.auctionEnd != 0) {
_executeSale(tokenId);
} else {
listing.listed = false;
delete bids[tokenId];
BBERC721.transferFrom(address(this), listing.seller, tokenId);
}
emit AuctionEnded(tokenId);
}

Test the endAuction

function test_endAuctionWithBidder() public {
_mintNFT();
_listNFT();
uint256 newBid = MIN_PRICE + 10;
vm.prank(BIDDER_1);
market.placeBid{value: newBid}(TOKEN_ID);
vm.warp(block.timestamp + 3 days);
market.endAuction(TOKEN_ID);
assertEq(nft.ownerOf(TOKEN_ID), BIDDER_1, "NFT should be transferred to highest bidder");
}
function test_endAuctionWithoutBidder() public {
_mintNFT();
_listNFT();
vm.warp(block.timestamp + 3 days);
market.endAuction(TOKEN_ID);
assertEq(nft.ownerOf(TOKEN_ID), SELLER, "NFT should be transferred back to SELLER");
}
Updates

Lead Judging Commences

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!