Pieces Protocol

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

Missing Refund Mechanism in buyOrder Function

Summary

The buyOrder function does not have a refund mechanism in place in case of a failure after receiving Ether but before completing the token transfer.

Vulnerability Details

If the transfer to the seller or the owner fails, the Ether is not refunded to the user.

This results in loss of funds for the buyer, even if the order is not completed successfully.

Impact

This results in loss of funds for the buyer, even if the order is not completed successfully.

Tools Used

Manuel review

Recommendations

Introduce a refund mechanism to ensure that the user gets their Ether back in case of a failure during the transaction process.

function buyOrder(uint256 orderIndex, address seller) external payable {
if(seller == address(0)) {
revert TokenDivider__InvalidSeller();
}
SellOrder memory order = s_userToSellOrders[seller][orderIndex];
if(msg.value < order.price) {
revert TokenDivider__IncorrectEtherAmount();
}
uint256 fee = order.price / 100;
uint256 sellerFee = fee / 2;
if(msg.value < order.price + sellerFee) {
revert TokenDivider__InsuficientEtherForFees();
}
// Refund mechanism in case of failure
uint256 buyerRefundAmount = msg.value;
// Update balances before sending Ether to prevent inconsistent state
balances[msg.sender][order.erc20Address] += order.amount;
// Remove the order
s_userToSellOrders[seller][orderIndex] = s_userToSellOrders[seller][s_userToSellOrders[seller].length - 1];
s_userToSellOrders[seller].pop();
emit OrderSelled(msg.sender, order.price);
// Try to transfer the Ether to the seller
(bool success, ) = payable(order.seller).call{value: (order.price - sellerFee)}("");
if (!success) {
revert TokenDivider__TransferFailed();
}
// Try to transfer the fee to the owner
(bool taxSuccess, ) = payable(owner()).call{value: fee}("");
if (!taxSuccess) {
revert TokenDivider__TransferFailed();
}
// Transfer the tokens to the buyer
bool tokenSuccess = IERC20(order.erc20Address).transfer(msg.sender, order.amount);
if (!tokenSuccess) {
// If token transfer fails, refund the buyer and revert
payable(msg.sender).transfer(buyerRefundAmount);
revert TokenDivider__TokenTransferFailed();
}
}
Updates

Lead Judging Commences

fishy Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Token misshandling

The extra eth sent by the user in the buy order will be locked in the contract forever

Support

FAQs

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