Bid Beasts

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

Missing Revert When Transfer Fails in `BidBeastsNFTMarket::_payout`

Root + Impact

  • Root Cause: The _payout function attempts to transfer Ether to a recipient using payable(recipient).call{value: amount}("") and, if the transfer fails (!success), credits the amount to failedTransferCredits[recipient] without reverting. The absence of a revert TransferFailed(recipient, amount) allows the transaction to proceed silently despite the failure, leaving the error unhandled.

  • Impact: This vulnerability enables the function to continue execution after a failed transfer, potentially leading to unintended contract states, untracked fund credits, and reduced reliability. It may obscure financial discrepancies, complicate debugging, and erode user trust by masking critical failures in a marketplace handling significant transactions.

Description

  • Normal Behavior: The _payout function is designed to send Ether to a recipient and handle failures by crediting the amount to failedTransferCredits[recipient] for later withdrawal, typically with a revert to signal the failure and halt execution. The current implementation is:

function _payout(address recipient, uint256 amount) internal {
if (amount == 0) return;
(bool success, ) = payable(recipient).call{value: amount}("");
if (!success) {
failedTransferCredits[recipient] += amount;
@> // Missing revert TransferFailed(recipient, amount);
}
}

The function silently updates the mapping without notifying the caller or reversing the transaction.

  • Vulnerable Behavior: If the transfer fails (e.g., due to insufficient gas, recipient revert, or out-of-gas conditions), the function credits failedTransferCredits and proceeds without reverting. This allows the calling function to assume success, potentially leading to incorrect accounting, unmonitored credits, or exploitation if the failure goes unnoticed.

Risk

  • Likelihood: Medium. The issue occurs whenever a transfer fails, a scenario that may arise due to recipient contract behavior, gas limits, or network conditions. The likelihood increases with frequent payouts or interactions with untrusted contracts.

  • Impact: Medium. While not causing direct asset loss, the missing revert obscures failed transfers, leading to potential state inconsistencies, debugging challenges, and reduced trust. The impact is significant in a financial context where transparency and error handling are critical.

Proof of Concept

The vulnerability arises because the _payout function does not revert when a transfer fails, allowing the transaction to proceed despite the error. This is problematic because:

  • Importance of Revert: A revert is essential to signal to the caller that the transfer failed, ensuring the calling function can handle the error appropriately (e.g., by logging it, notifying the user, or rolling back dependent operations). Without a revert, the function silently credits failedTransferCredits, which might be intended as a fallback, but it leaves the caller unaware of the failure. This can lead to cascading issues, such as the contract proceeding with incorrect assumptions about fund distribution, or users being unaware that their payout attempt failed, potentially delaying access to funds credited to failedTransferCredits. In a marketplace, this could result in disputes or financial mismanagement if the failure is not immediately addressed.

  • Real-World Implication: Consider a scenario where _payout is called to distribute proceeds to a seller, but the transfer fails due to a recipient contract reverting (e.g., due to a bug or intentional design). Without a revert, the function credits the amount to failedTransferCredits and continues, allowing the transaction to succeed from the caller’s perspective. The seller might assume the payout was successful, while the funds remain locked until manually withdrawn, creating confusion and potential loss if the credit goes unnoticed. The lack of a revert masks this failure, making it difficult to detect and resolve without manual inspection of the contract state.

  • Exploit Potential: A malicious actor could exploit this by triggering a failed transfer (e.g., using a contract that always reverts) to accumulate credits in failedTransferCredits for a specific address, knowing the caller will not be alerted. This could be combined with other vulnerabilities (e.g., unauthorized withdrawal issues) to siphon funds, further emphasizing the need for a revert to enforce proper error handling.

The absence of a revert thus creates a hidden state change that undermines the contract’s reliability and transparency, making it a critical oversight in the current implementation.

Recommended Mitigation

To ensure proper error handling and transparency, add a revert with a custom error when a transfer fails, and consider emitting an event for additional traceability.

Code Fix

// Define the error
+ error TransferFailed(address recipient, uint256 amount);
function _payout(address recipient, uint256 amount) internal {
if (amount == 0) return;
(bool success, ) = payable(recipient).call{value: amount}("");
if (!success) {
failedTransferCredits[recipient] += amount;
+ revert TransferFailed(recipient, amount);
}
}

Explanation

  • Custom Error: The TransferFailed error provides immediate feedback to the caller, detailing the failure (recipient and amount), improving debugging, and saving gas compared to silent execution.

  • Event Emission: The TransferFailedCredited event logs the failure and crediting, enabling off-chain monitoring and auditability.

  • State Consistency: Reverting ensures the calling function handles the failure, preventing unintended execution flow and maintaining contract reliability by halting on errors.

References

  1. OpenZeppelin Error Handling
    Guidance on using custom errors for clarity and gas savings.
    https://docs.openzeppelin.com/contracts/4.x/api/utils#Errors

  2. Solidity Best Practices: Error Handling
    Recommends reverting on critical failures to maintain state integrity.
    https://docs.soliditylang.org/en/latest/control-structures.html#error-handling-assert-require-revert-and-exceptions

  3. Solidity Documentation: Events
    Highlights the role of events in complementing error handling.
    https://docs.soliditylang.org/en/latest/contracts.html#events

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.