Skip to content

Latest commit

 

History

History
91 lines (71 loc) · 3.39 KB

File metadata and controls

91 lines (71 loc) · 3.39 KB

Blurry Crepe Rabbit

Medium

Potential Token Transfer Failure in _removeBid Function

Summary

The _removeBid function in the Auction contract uses safeTransfer for token transfers without proper failure handling. If a user is blacklisted (e.g., in USDC on Base chain) after placing a bid, any attempt to remove their bid will fail due to the transfer reverting. This causes the entire _removeBid function to revert, effectively creating a Denial of Service (DOS) condition where the bid cannot be removed and the auction's functionality may be blocked.

Vulnerability Details

https://github.com/sherlock-audit/2024-12-plaza-finance/blob/main/plaza-evm/src/Auction.sol#L298-L331 In the _removeBid function:

function _removeBid(uint256 bidIndex) internal {
    // ... bid removal logic ...
    
    address bidder = bidToRemove.bidder;
    uint256 buyReserveAmount = bidToRemove.buyReserveAmount;
    uint256 sellCouponAmount = bidToRemove.sellCouponAmount;
    currentCouponAmount -= sellCouponAmount;
    totalSellReserveAmount -= buyReserveAmount;

    // This transfer will revert if bidder is blacklisted
    IERC20(buyCouponToken).safeTransfer(bidder, sellCouponAmount);

    emit BidRemoved(bidIndex, bidder, buyReserveAmount, sellCouponAmount);

    delete bids[bidIndex];
    bidCount--;
}

The vulnerability exists because:

  1. The function relies on a direct safeTransfer call that will revert if the transfer fails
  2. If a user is blacklisted after placing their bid, any attempt to remove their bid will fail
  3. This creates a permanent DOS condition as the bid cannot be removed
  4. The issue is particularly concerning for tokens like USDC on Base chain that maintain blacklists

Impact

  • Denial of Service: If a bidder gets blacklisted, their bid becomes "stuck" in the system and cannot be removed
  • Auction Disruption: Could block critical auction operations that depend on bid removal

Tools Used

  • Manual code review

Recommendations

Implement a try-catch mechanism with pending refunds:

function _removeBid(uint256 bidIndex) internal {
    Bid storage bidToRemove = bids[bidIndex];
    uint256 nextIndex = bidToRemove.nextBidIndex;
    uint256 prevIndex = bidToRemove.prevBidIndex;

    // Update linked list pointers
    if (prevIndex == 0) {
        highestBidIndex = nextIndex;
    } else {
        bids[prevIndex].nextBidIndex = nextIndex;
    }

    if (nextIndex == 0) {
        lowestBidIndex = prevIndex;
    } else {
        bids[nextIndex].prevBidIndex = prevIndex;
    }

    address bidder = bidToRemove.bidder;
    uint256 buyReserveAmount = bidToRemove.buyReserveAmount;
    uint256 sellCouponAmount = bidToRemove.sellCouponAmount;
    currentCouponAmount -= sellCouponAmount;
    totalSellReserveAmount -= buyReserveAmount;

    try IERC20(buyCouponToken).safeTransfer(bidder, sellCouponAmount) {
        emit BidRemoved(bidIndex, bidder, buyReserveAmount, sellCouponAmount);
    } catch {
        pendingRefunds[bidder] += sellCouponAmount;
        emit BidRemovedPendingRefund(bidIndex, bidder, buyReserveAmount, sellCouponAmount);
    }

    delete bids[bidIndex];
    bidCount--;
}

Additional recommendations:

  1. Add a claimPendingRefund function for users to claim their refunds later
  2. Consider implementing a maximum cap on pending refunds per address
  3. Add events to track pending refund status
  4. Add documentation about the refund mechanism in the contract