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) {
address previousBidder = bids[tokenId].bidder;
@> require(msg.sender != previousBidder, "Already highest bidder");
}
Risk
Likelihood: Medium
-
occurs in normal auction participation
-
None - affects all bidders naturally
-
Every time a bidder wants to increase their bid
Impact:
Proof of Concept
function test_BidIncreasePrevention() public {
vm.prank(OWNER);
nft.mint(SELLER);
vm.startPrank(SELLER);
nft.approve(address(market), TOKEN_ID);
market.listNFT(TOKEN_ID, 1 ether, 0);
vm.stopPrank();
vm.prank(BIDDER_1);
market.placeBid{value: 1.1 ether}(TOKEN_ID);
BidBeastsNFTMarket.Bid memory highest = market.getHighestBid(TOKEN_ID);
assert(highest.bidder == BIDDER_1);
assert(highest.amount == 1.1 ether);
vm.prank(BIDDER_1);
vm.expectRevert("Already highest bidder");
market.placeBid{value: 0.5 ether}(TOKEN_ID);
vm.prank(BIDDER_2);
market.placeBid{value: 1.155 ether}(TOKEN_ID);
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 ...
}