Dria

Swan
NFTHardhat
21,000 USDC
View results
Submission Details
Severity: medium
Invalid

Unfair fee deduction for sellers due to lack of purchase limit enforcement at listing time

Summary

There’s no mechanism to enforce amountPerRound at listing time. Sellers may list assets with a cumulative value that exceeds this limit without knowing their assets won’t be purchased. They end up paying listing fees (in transferRoyalties()) without any guarantee their assets can be bought due to the buyer agent’s settings.

Vulnerability Details

The amountPerRound parameter in BuyerAgent limits the total spending allowed per round when buying assets. However, this limit is only checked in the purchase() function and is not enforced during the listing process.

Sellers pay a non-refundable buyerFee at listing, assuming their assets will be purchased:

token.transferFrom(asset.seller, address(this), buyerFee);

In purchase() within BuyerAgent, assets are iterated, and their cumulative price is tracked against amountPerRound. If this amount is exceeded mid-loop, the function reverts, and none of the assets in that batch get purchased.

for (uint256 i = 0; i < assets.length; i++) {
address asset = assets[i];
// Get the listing price
uint256 price = swan.getListingPrice(asset);
spendings[round] += price;
// Enforce the round buy limit
>> if (spendings[round] > amountPerRound) {
revert BuyLimitExceeded(spendings[round], amountPerRound);
}
// Proceed with asset purchase in Swan
inventory[round].push(asset);
swan.purchase(asset);
}

Example Flow:

  • amountPerRound is set to 1,000 USDC.

  • Sellers list assets totaling 2,000 USDC.

  • In the purchase() function, as the cumulative price surpasses 1,000 USDC, the transaction reverts.

  • All sellers who listed do not receive a refund on their buyerFee, and their assets remain unsold.

Impact

  • Sellers pay listing fees (buyerFee) without a guarantee that their assets will be purchased, even though BuyerAgent has full control over amountPerRound.

  • Malicious or negligent BuyerAgents can set amountPerRound very low, potentially even to 0, to exploit this and continuously collect listing fees without any intention to buy.

Tools Used

Manual Review

Recommendations

Modify the listing functions to calculate the cumulative price of all listed assets for a given buyer. If this cumulative amount exceeds amountPerRound, prevent further listings.

// Add a mapping to track the total price of assets listed per buyer per round
+ mapping(address => mapping(uint256 => uint256)) public totalListedPricePerRound;
// Inside list() & relist(), add the following:
---SNIP---
BuyerAgent buyer = BuyerAgent(_buyer);
+ uint256 currentTotalPrice = totalListedPricePerRound[_buyer][round];
+ if (currentTotalPrice + _price > buyer.amountPerRound()) {
+ revert ListingLimitExceeded(currentTotalPrice + _price, buyer.amountPerRound());
+ }
+ totalListedPricePerRound[_buyer][round] += _price;
---SNIP---
Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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