Beginner FriendlyFoundryGameFi
100 EXP
View results
Submission Details
Severity: high
Valid

[H4] If a user sends more funds than the NFT Token price, the difference will be locked forever in the marketplace.

Summary

A user that sends more funds than the MartenitsaToken NFT price , will lose the surplus sent by mistake because there is no method to withdraw ether from the marketplace.

Vulnerability Details

A user wants to buy a MartenitsaToken NFT priced at 1 wei but sends 1 ether by mistake; the 0.999999999999999999 ether will be locked forever in the marketplace.

POC

Add this function to the contract and run the command

forge test --mt testLostFundBuyMartenitsa
function testLostFundBuyMartenitsa() public listMartenitsa {
vm.prank(chasy);
martenitsaToken.approve(address(marketplace), 0);
// Bob makes a mistake and sends 1 ether instead of 1 wei
vm.prank(bob);
marketplace.buyMartenitsa{value: 1 ether}(0);
assert(martenitsaToken.ownerOf(0) == bob);
assert(martenitsaToken.getCountMartenitsaTokensOwner(bob) == 1);
assert(martenitsaToken.getCountMartenitsaTokensOwner(chasy) == 0);
uint256 bobBalance = address(bob).balance;
console.log("balance",bobBalance);
assert(bobBalance == 4 ether);
// the 0.9999999999999999 ether is locked forever in the smart contract
uint256 marketplaceBalance = 1 ether - 1 ;
assert(address(marketplace).balance == marketplaceBalance );
}

We get the output:

Ran 1 test for test/MartenitsaMarketplace.t.sol:MartenitsaMarketplace
[PASS] testLostFundBuyMartenitsa() (gas: 293463)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.62ms (232.08µs CPU time)
Ran 1 test suite in 147.63ms (1.62ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

Users who mistakenly send more than the MartenitsaToken price to buy the NFT will lose the rest of the funds they sent.

Tools Used

manual Review

Recommendations

Create a function withdraw that the user will use to withdraw the rest of their funds that weren't used to buy the NFT, or change the function buy.

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");
+ 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");
// Clear the listing
delete tokenIdToListing[tokenId];
emit MartenitsaSold(tokenId, buyer, salePrice);
// Transfer funds to seller
(bool sent,) = seller.call{value: salePrice}("");
require(sent, "Failed to send Ether");
// Transfer the token to the buyer
martenitsaToken.safeTransferFrom(seller, buyer, tokenId);
}
Updates

Lead Judging Commences

bube Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Excess ETH not refunded to the user

Support

FAQs

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