Summary
The listMartenitsaForSale
function in MartenitsaMarketplace
allows producers to list the same martenitsa token multiple times, overwriting previous listings.
Vulnerability Details
Currently, the listMartenitsaForSale function does not prevent the re-listing of a martenitsa token already marked for sale, causing an overwrite of the previous listing data without checks.
function listMartenitsaForSale(uint256 tokenId, uint256 price) external {
require(
msg.sender == martenitsaToken.ownerOf(tokenId),
"You do not own this token"
);
require(
martenitsaToken.isProducer(msg.sender),
"You are not a producer!"
);
require(price > 0, "Price must be greater than zero");
Listing memory newListing = Listing({
tokenId: tokenId,
seller: msg.sender,
price: price,
design: martenitsaToken.getDesign(tokenId),
forSale: true
});
tokenIdToListing[tokenId] = newListing;
emit MartenitsaListed(tokenId, msg.sender, price);
}
POC
Add this test to MartenitsaMarketplace.test.sol
:
function testListMartenitsaForSaleMultipleTimes() public createMartenitsa {
marketplace.listMartenitsaForSale(0, 1 wei);
marketplace.listMartenitsaForSale(0, 2 wei);
list = marketplace.getListing(0);
assert(list.price == 2 wei);
}
When the Producer lists the same martenitsa token multiple times for sale, the transaction is successful. This will overwrite the previous listing.
Test output:
run forge test --mt testListMartenitsaForSaleMultipleTimes and the result should PASS:
Ran 1 test for test/MartenitsaMarketplace.t.sol:MartenitsaMarketplace
[PASS] testListMartenitsaForSaleMultipleTimes() (gas: 341750)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 7.62ms (449.13µs CPU time)
Impact
The ability to list the same token multiple times without proper validation can lead to inconsistent listing states and potential misuse in token sales processes.
Tools Used
Foundry and Manual review.
Recommendations
To mitigate this issue, it is advisable to introduce a check that validates whether a token is already listed for sale:
function listMartenitsaForSale(uint256 tokenId, uint256 price) external {
+ require(tokenIdToListing[tokenId].forSale, "Token is already listed");
require(
msg.sender == martenitsaToken.ownerOf(tokenId),
"You do not own this token"
);
require(
martenitsaToken.isProducer(msg.sender),
"You are not a producer!"
);
require(price > 0, "Price must be greater than zero");
Listing memory newListing = Listing({
tokenId: tokenId,
seller: msg.sender,
price: price,
design: martenitsaToken.getDesign(tokenId),
forSale: true
});
tokenIdToListing[tokenId] = newListing;
emit MartenitsaListed(tokenId, msg.sender, price);
}
run forge test --mt testListMartenitsaForSaleMultipleTimes and the result should FAIL:
Failing tests:
Failing tests:
Encountered 1 failing test in test/MartenitsaMarketplace.t.sol:MartenitsaMarketplace
[FAIL. Reason: revert: Token is already listed] testListMartenitsaForSaleMultipleTimes() (gas: 137952)