Bid Beasts

First Flight #49
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Incorrect Bid Validation Preventing Bid Increases

Root + Impact

Description

The placeBid function incorrectly prevents the current highest bidder from increasing their bid by implementing require(msg.sender != previousBidder, "Already highest bidder"). This violates standard auction mechanics where bidders should be able to increase their bids to maintain their position.

function placeBid(uint256 tokenId) external payable isListed(tokenId) {
// ... previous logic ...
address previousBidder = bids[tokenId].bidder;
@> require(msg.sender != previousBidder, "Already highest bidder");
// ... rest of bidding logic ...
}

Risk

Likelihood: Medium

  • occurs in normal auction participation

  • None - affects all bidders naturally

  • Every time a bidder wants to increase their bid

Impact:

  • sellers may get lower final prices

  • frustrates legitimate bidders

Proof of Concept

function test_BidIncreasePrevention() public {
// Mint NFT for seller
vm.prank(OWNER);
nft.mint(SELLER);
// List NFT
vm.startPrank(SELLER);
nft.approve(address(market), TOKEN_ID);
market.listNFT(TOKEN_ID, 1 ether, 0);
vm.stopPrank();
// User A places initial bid
vm.prank(BIDDER_1);
market.placeBid{value: 1.1 ether}(TOKEN_ID);
// Verify User A is highest bidder
BidBeastsNFTMarket.Bid memory highest = market.getHighestBid(TOKEN_ID);
assert(highest.bidder == BIDDER_1);
assert(highest.amount == 1.1 ether);
// User A attempts to increase bid but should be prevented
vm.prank(BIDDER_1);
vm.expectRevert("Already highest bidder");
market.placeBid{value: 0.5 ether}(TOKEN_ID);
// User B outbids with minimal increment
vm.prank(BIDDER_2);
market.placeBid{value: 1.155 ether}(TOKEN_ID); // 1.1 * 1.05
// Verify User B is now highest bidder
highest = market.getHighestBid(TOKEN_ID);
assert(highest.bidder == BIDDER_2);
assert(highest.amount == 1.155 ether);
}

Recommended Mitigation

function placeBid(uint256 tokenId) external payable isListed(tokenId) {
Listing storage listing = listings[tokenId];
address previousBidder = bids[tokenId].bidder;
uint256 previousBidAmount = bids[tokenId].amount;
require(listing.seller != msg.sender, "Seller cannot bid");
require(listing.auctionEnd == 0 || block.timestamp < listing.auctionEnd, "Auction ended");
// --- ALLOW BID INCREASES ---
- require(msg.sender != previousBidder, "Already highest bidder");
+ if (msg.sender == previousBidder) {
+ require(msg.value > 0, "Must send ETH to increase bid");
// Calculate new total bid
+ uint256 newTotalBid = previousBidAmount + msg.value;
// Update the bid
+ bids[tokenId].amount = newTotalBid;
// Emit special event for bid increases
+ emit BidIncreased(tokenId, msg.sender, newTotalBid);
+ return; // Exit function after handling bid increase
}
// --- Continue with existing logic for new bidders ---
// ... rest of function ...
}
Updates

Lead Judging Commences

cryptoghost Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.