Bid Beasts

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

Buy-Now flow: ordering of _executeSale vs overpay refund

Root + Impact

Description

The Buy-Now flow allows a bidder to purchase an NFT immediately by sending msg.value >= buyNowPrice.
In the current implementation, the _executeSale function is called before refunding any overpay.

This creates a situation where the sale is finalized (NFT transferred, seller paid, fee collected) before ensuring that the buyer’s overpayment is refunded. If the refund transaction fails (e.g., due to gas issues or malicious fallback), the NFT transfer remains valid, and the buyer may be left with unrecoverable overpayment.

if (listing.buyNowPrice > 0 && msg.value >= listing.buyNowPrice) {
uint256 salePrice = listing.buyNowPrice;
uint256 overpay = msg.value - salePrice;
bids[tokenId] = Bid(msg.sender, salePrice);
listing.listed = false;
if (previousBidder != address(0)) {
_payout(previousBidder, previousBidAmount);
}
// @> NFT is transferred + seller is paid here
_executeSale(tokenId);
// @> Refund of overpay happens *after* finalizing sale
if (overpay > 0) {
_payout(msg.sender, overpay);
}
return;
}

Risk

Likelihood:

  • The issue occurs whenever a user sends msg.value > buyNowPrice.

  • This is a common scenario since bidders may overpay to guarantee Buy-Now execution.

  • Refund failures are plausible in case of malicious smart contract bidders or network quirks.

Impact:

  • The buyer could lose ETH permanently if the refund fails but the sale has already been finalized.

  • Creates inconsistency between buyer’s expected ETH balance and actual balance post-transaction.

  • Possible reputational/UX harm for the marketplace.

Proof of Concept

  1. Buyer calls placeBid with msg.value = buyNowPrice + 1.

  2. _executeSale finalizes the NFT transfer, deducting fee and paying seller.

  3. _payout(buyer, 1) fails (e.g., buyer contract reverts on receiving ETH).

  4. Buyer receives NFT but loses the 1 wei overpay permanently.

Recommended Mitigation

Refund overpayment before executing the sale. This ensures the buyer never risks permanent loss of funds:

- _executeSale(tokenId);
-
- // Refund overpay (if any) to buyer
- if (overpay > 0) {
- _payout(msg.sender, overpay);
- }
+ // Refund overpay (if any) to buyer before finalizing sale
+ if (overpay > 0) {
+ _payout(msg.sender, overpay);
+ }
+
+ _executeSale(tokenId);
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.