Bid Beasts

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

Incorrect AuctionSettled Emission

Root + Impact

Description

  • The BidBeastsNFTMarket contract emits the AuctionSettled event in the wrong location inside the placeBid function.

_executeSale(tokenId);
// Refund overpay (if any) to buyer
if (overpay > 0) {
_payout(msg.sender, overpay);
}
return;
}
require(msg.sender != previousBidder, "Already highest bidder");
@ ==> emit AuctionSettled(tokenId, msg.sender, listing.seller, msg.value);

Risk

Likelihood:

Low

Impact:

  • Wrong data emission.

Proof of Concept

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import {Test, console} from "forge-std/Test.sol";
import {BidBeastsNFTMarket} from "../src/BidBeastsNFTMarketPlace.sol";
import {BidBeasts} from "../src/BidBeasts_NFT_ERC721.sol";
// A mock contract that cannot receive Ether, to test the payout failure logic.
contract RejectEther {
// Intentionally has no payable receive or fallback
}
contract BidBeastsNFTMarketTest is Test {
// --- State Variables ---
BidBeastsNFTMarket market;
BidBeasts nft;
RejectEther rejector;
// --- Users ---
address public constant OWNER = address(0x1); // Contract deployer/owner
address public constant SELLER = address(0x2);
address public constant BIDDER_1 = address(0x3);
address public constant BIDDER_2 = address(0x4);
// --- Constants ---
uint256 public constant STARTING_BALANCE = 100 ether;
uint256 public constant TOKEN_ID = 0;
uint256 public constant MIN_PRICE = 1 ether;
uint256 public constant BUY_NOW_PRICE = 5 ether;
function setUp() public {
// Deploy contracts
vm.prank(OWNER);
nft = new BidBeasts();
market = new BidBeastsNFTMarket(address(nft));
rejector = new RejectEther();
vm.stopPrank();
// Fund users
vm.deal(SELLER, STARTING_BALANCE);
vm.deal(BIDDER_1, STARTING_BALANCE);
vm.deal(BIDDER_2, STARTING_BALANCE);
}
// --- Helper function to list an NFT ---
function _listNFT() internal {
vm.startPrank(SELLER);
nft.approve(address(market), TOKEN_ID);
market.listNFT(TOKEN_ID, MIN_PRICE, BUY_NOW_PRICE);
vm.stopPrank();
}
// -- Helper function to mint an NFT ---
function _mintNFT() internal {
vm.startPrank(OWNER);
nft.mint(SELLER);
vm.stopPrank();
}
/*//////////////////////////////////////////////////////////////
LISTING TESTS
//////////////////////////////////////////////////////////////*/
function testAuctionSettledAfter3Days() public {
_mintNFT();
_listNFT();
// First bid (3 ether, no buyNow triggered)
vm.prank(BIDDER_1);
market.placeBid{value: 2 ether}(TOKEN_ID);
vm.prank(BIDDER_2);
market.placeBid{value: 5 ether}(TOKEN_ID);
// Advance time beyond auction end
//vm.warp(block.timestamp + 4 days);
// Seller accepts highest bid
// vm.prank(SELLER);
// market.takeHighestBid(TOKEN_ID);
assertEq(nft.ownerOf(TOKEN_ID), BIDDER_2);
}
}

In the output u will notice that event is emitted despite the auction is not settled

Traces:
[413245] BidBeastsNFTMarketTest::testAuctionSettledAfter3Days()
├─ [0] VM::startPrank(ECRecover: [0x0000000000000000000000000000000000000001])
│ └─ ← [Return]
├─ [74067] BidBeasts::mint(SHA-256: [0x0000000000000000000000000000000000000002])
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: SHA-256: [0x0000000000000000000000000000000000000002], tokenId: 0)
│ ├─ emit BidBeastsMinted(to: SHA-256: [0x0000000000000000000000000000000000000002], tokenId: 0)
│ └─ ← [Return] 0
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [0] VM::startPrank(SHA-256: [0x0000000000000000000000000000000000000002])
│ └─ ← [Return]
├─ [25508] BidBeasts::approve(BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 0)
│ ├─ emit Approval(owner: SHA-256: [0x0000000000000000000000000000000000000002], approved: BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], tokenId: 0)
│ └─ ← [Stop]
├─ [128588] BidBeastsNFTMarket::listNFT(0, 1000000000000000000 [1e18], 5000000000000000000 [5e18])
│ ├─ [1094] BidBeasts::ownerOf(0) [staticcall]
│ │ └─ ← [Return] SHA-256: [0x0000000000000000000000000000000000000002]
│ ├─ [29510] BidBeasts::transferFrom(SHA-256: [0x0000000000000000000000000000000000000002], BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 0)
│ │ ├─ emit Transfer(from: SHA-256: [0x0000000000000000000000000000000000000002], to: BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], tokenId: 0)
│ │ └─ ← [Stop]
│ ├─ emit NftListed(tokenId: 0, seller: SHA-256: [0x0000000000000000000000000000000000000002], minPrice: 1000000000000000000 [1e18], buyNowPrice: 5000000000000000000 [5e18])
│ └─ ← [Stop]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [0] VM::prank(RIPEMD-160: [0x0000000000000000000000000000000000000003])
│ └─ ← [Return]
├─ [77244] BidBeastsNFTMarket::placeBid{value: 2000000000000000000}(0)
│ ├─ [0] console::log("I was here", RIPEMD-160: [0x0000000000000000000000000000000000000003]) [staticcall]
│ │ └─ ← [Stop]
@==> ├─ emit AuctionSettled(tokenId: 0, winner: RIPEMD-160: [0x0000000000000000000000000000000000000003], seller: SHA-256: [0x0000000000000000000000000000000000000002], price: 2000000000000000000 [2e18])
├─ emit AuctionExtended(tokenId: 0, newDeadline: 259201 [2.592e5])
│ ├─ [0] console::log("First Bid - Required Amount:", 1000000000000000000 [1e18]) [staticcall]
│ │ └─ ← [Stop]
│ ├─ emit BidPlaced(tokenId: 0, bidder: RIPEMD-160: [0x0000000000000000000000000000000000000003], amount: 2000000000000000000 [2e18])
│ └─ ← [Stop]
├─ [0] VM::prank(Identity: [0x0000000000000000000000000000000000000004])
│ └─ ← [Return]
├─ [73907] BidBeastsNFTMarket::placeBid{value: 5000000000000000000}(0)
│ ├─ [600] PRECOMPILES::ripemd{value: 2000000000000000000}(0x)
│ │ └─ ← [Return] 0x0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31
│ ├─ [26871] BidBeasts::transferFrom(BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], Identity: [0x0000000000000000000000000000000000000004], 0)
│ │ ├─ emit Transfer(from: BidBeastsNFTMarket: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: Identity: [0x0000000000000000000000000000000000000004], tokenId: 0)
│ │ └─ ← [Stop]
│ ├─ [0] console::log("Bid amount ", 5000000000000000000 [5e18]) [staticcall]
│ │ └─ ← [Stop]
│ ├─ [60] PRECOMPILES::sha256{value: 4750000000000000000}(0x)
│ │ └─ ← [Return] 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
│ ├─ emit AuctionSettled(tokenId: 0, winner: Identity: [0x0000000000000000000000000000000000000004], seller: SHA-256: [0x0000000000000000000000000000000000000002], price: 5000000000000000000 [5e18])
│ └─ ← [Stop]
├─ [1094] BidBeasts::ownerOf(0) [staticcall]
│ └─ ← [Return] Identity: [0x0000000000000000000000000000000000000004]
└─ ← [Stop]


Recommended Mitigation

Removing the line of code would eliminate the wrong even emission.

- emit AuctionSettled(tokenId, msg.sender, listing.seller, msg.value);
Updates

Lead Judging Commences

cryptoghost Lead Judge 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BidBeasts Marketplace: Incorrect Event Emission

placeBid emits AuctionSettled even though the auction hasn’t ended, causing misleading event logs.

Support

FAQs

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

Give us feedback!