[M-1] Placing a regular bid with the BidBeastsNFTMarketPlace::placeBid function misleadingly emits an AuctionSettled event, causing auction state mismatch
Description
-
Normal behaviour: When a bid is placed on an NFT with a bid price lower than the Buy Now price (and higher than the min price), the BidPlaced event should be emitted. The AuctionSettled event should only be emitted when the auction is settled via a) the Buy Now logic, b) the seller taking the highest bid and ending the auction early, or c) The auction expiring and a user calling the settleAuction method.
-
Problematic behaviour: When a bid is placed on an NFT with a bid price lower than the Buy Now price (and higher than the min price), the BidPlaced event is emitted as well as the AuctionSettled event. The latter event is misleading and falsely emitted as the auction has not been settled under the conditions a), b), and c) listed above.
Root cause:
```solidity
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
...
require(msg.sender != previousBidder, "Already highest bidder");
@> emit AuctionSettled(tokenId, msg.sender, listing.seller, msg.value);
// --- Regular Bidding Logic ---
uint256 requiredAmount
...
emit BidPlaced(tokenId, msg.sender, msg.value);
}
}
## Risk
**Likelihood**: High
* The misleading `AuctionSettled` event is emitted whenever a user places a bid on an NFT, without the auction having ended.
**Impact**: Medium
* The `AuctionSettled` event is used off-chain to track and display the auction status. A misplaced / false emission of this event will cause **critical inconsistencies** between the on-chain stored data and the displayed auction data on the frontend and on analytics dashboards.
## Proof of Concept
As a PoC, running the following test demonstrates that the `BidBeastsNFTMarketPlace::AuctionSettled` event is emitted when a regular bid is placed, when the only emitted event should be `BidPlaced` as the auction is still ongoing.
First, add the following event definitions in the test contract:
```solidity
event AuctionSettled(uint256 tokenId, address winner, address seller, uint256 price);
event BidPlaced(uint256 tokenId, address bidder, uint256 amount);
Then add the following test function:
function test_placeBid_PlacingBidEmitsTwoEvents() public {
_mintNFT();
_listNFT();
vm.startPrank(BIDDER_1);
uint256 bidPrice = MIN_PRICE * 2;
vm.expectEmit(true, true, true, true);
emit AuctionSettled(TOKEN_ID, BIDDER_1, SELLER, bidPrice);
vm.expectEmit(true, true, true, true);
emit BidPlaced(TOKEN_ID, BIDDER_1, bidPrice);
market.placeBid{ value: bidPrice }(TOKEN_ID);
vm.stopPrank();
}
Run the test with forge test --mt test_placeBid_PlacingBidEmitsTwoEvents.
The test will PASS, indicating that the AuctionSettled event is indeed emitted when a bid is placed on an NFT.
Recommended Mitigation
To mitigate this issue, the event emission should be removed entirely:
function placeBid(uint256 tokenId) external payable isListed(tokenId) {
...
require(msg.sender != previousBidder, "Already highest bidder");
- emit AuctionSettled(tokenId, msg.sender, listing.seller, msg.value);
// --- Regular Bidding Logic ---
uint256 requiredAmount;
...