Summary
Buyers interested in purchasing a Martenitsa call the MartenitsaMarketplace::buyMartenitsa
function to do so, the function checks the value sent in the transaction is greater or equal than the listed price, but in the situation where the buyer send more ETH than the price, the function does not refund the extra amount, considering the marketplace contract does not include a function to sweep ETH these funds will remain stuck in the contract.
function buyMartenitsa(uint256 tokenId) external payable {
Listing memory listing = tokenIdToListing[tokenId];
require(listing.forSale, "Token is not listed for sale");
require(msg.value >= listing.price, "Insufficient funds");
address seller = listing.seller;
address buyer = msg.sender;
uint256 salePrice = listing.price;
martenitsaToken.updateCountMartenitsaTokensOwner(buyer, "add");
martenitsaToken.updateCountMartenitsaTokensOwner(seller, "sub");
delete tokenIdToListing[tokenId];
emit MartenitsaSold(tokenId, buyer, salePrice);
(bool sent,) = seller.call{value: salePrice}("");
require(sent, "Failed to send Ether");
martenitsaToken.safeTransferFrom(seller, buyer, tokenId);
}
Impact
Buyers overpaying for a Martenitsa will lose those funds.
Tools Used
Manual review, Foundry
Recommendations
There are two mitigation that could be implemented.
- require(msg.value >= listing.price, "Insufficient funds");
+ require(msg.value == listing.price, "Offer must be equal to price");
function buyMartenitsa(uint256 tokenId) external payable {
// rest of the function...
+ uint256 offer = msg.value;
(bool sent,) = seller.call{value: salePrice}("");
require(sent, "Failed to send Ether");
+ if (offer > salePrice) {
+ (bool refundSent,) = buyer.call{value: offer - salePrice}("");
+ require(refundSent, "Failed to refund Ether");
+ }
// rest of the function...
}