Summary
The _createPodListing function is responsible for creating new pod listings in the Listing contract. It performs several checks to ensure the validity of the listing parameters, such as verifying that the pod amount is positive, the plot size is adequate, the price per pod is greater than zero, and the listing has not expired. However, it lacks a crucial validation to ensure that the minFillAmount is less than or equal to the podAmount. This omission allows users to create listings that are impossible to fill, as the minimum fill amount can exceed the total pod amount available in the listing. This issue can clutter the marketplace with invalid listings, waste resources, and potentially be exploited by malicious actors to disrupt the marketplace.
Proof of concept
-
User attempts to create a listing with a podAmount of 50 and a minFillAmount of 60.
-
The system accepts the listing without validation.
-
Other users are unable to fill the listing as the minimum fill amount exceeds the total pods available.
-
The listing remains unfilled, cluttering the marketplace.
-
Legitimate users have a harder time finding and interacting with valid listings.
Test
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import {Listing} from "../src/Listing.sol";
contract ListingTest is Test {
Listing listing;
function setUp() public {
listing = new Listing();
}
function testCreatePodListing_InvalidMinFillAmount() public {
Listing.PodListing memory podListing = Listing.PodListing({
lister: address(this),
fieldId: 1,
index: 0,
start: 0,
podAmount: 50,
pricePerPod: 100,
maxHarvestableIndex: 100,
minFillAmount: 60,
mode: LibTransfer.To(address(this))
});
vm.expectRevert("Marketplace: minFillAmount must be <= podAmount.");
listing._createPodListing(podListing);
}
function testCreatePodListing_ValidMinFillAmount() public {
Listing.PodListing memory podListing = Listing.PodListing({
lister: address(this),
fieldId: 1,
index: 0,
start: 0,
podAmount: 50,
pricePerPod: 100,
maxHarvestableIndex: 100,
minFillAmount: 30,
mode: LibTransfer.To(address(this))
});
listing._createPodListing(podListing);
}
}
Impact
-
Listings with minFillAmount greater than podAmount are impossible to fulfill, rendering them useless.
-
The presence of unfillable listings clutters the marketplace, making it harder for users to find valid listings.
-
Users waste time and gas fees interacting with invalid listings.
-
Malicious actors could create unfillable listings to disrupt the marketplace.
Tools Used
Manual Review
Recommendations
1: Include the additional validation
function _createPodListing(PodListing calldata podListing) internal {
uint256 plotSize = s.accts[podListing.lister].fields[podListing.fieldId].plots[
podListing.index
];
require(podListing.podAmount > 0, "Marketplace: Invalid Amount.");
require(
plotSize >= (podListing.start + podListing.podAmount),
"Marketplace: Invalid Plot."
);
require(podListing.pricePerPod > 0, "Marketplace: Pod price must be greater than 0.");
require(
s.sys.fields[podListing.fieldId].harvestable <= podListing.maxHarvestableIndex,
"Marketplace: Expired."
);
require(podListing.minFillAmount <= podListing.podAmount, "Marketplace: minFillAmount must be <= podAmount.");
if (s.sys.podListings[podListing.fieldId][podListing.index] != bytes32(0))
LibMarket._cancelPodListing(podListing.lister, podListing.fieldId, podListing.index);
s.sys.podListings[podListing.fieldId][podListing.index] = _hashListing(podListing);
emit PodListingCreated(
podListing.lister,
podListing.fieldId,
podListing.index,
podListing.start,
podListing.podAmount,
podListing.pricePerPod,
podListing.maxHarvestableIndex,
podListing.minFillAmount,
podListing.mode
);
}
Alternatively the appropriate validation can be implemented in the createPodListing of the MarketplaceFacert
function createPodListing(
PodListing calldata podListing
) external payable fundsSafu noNetFlow noSupplyChange {
require(podListing.lister == LibTractor._user(), "Marketplace: Non-user create listing.");
require(podListing.minFillAmount <= podListing.podAmount, "Marketplace: minFillAmount must be <= podAmount.");
_createPodListing(podListing);
}