Dria

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

Spamming zero price list to prevent user to use Agent.

Summary

The Swan protocol allows listing assets with zero price, enabling malicious users to spam BuyerAgent with worthless listings without paying fees. This can effectively block legitimate users from listing assets due to the maxAssetCount limitation per round.

Vulnerability Details

function list(string calldata _name, string calldata _symbol, bytes calldata _desc, uint256 _price, address _buyer)
external
{
BuyerAgent buyer = BuyerAgent(_buyer);
(uint256 round, BuyerAgent.Phase phase,) = buyer.getRoundPhase();
// buyer must be in the sell phase
if (phase != BuyerAgent.Phase.Sell) {
revert BuyerAgent.InvalidPhase(phase, BuyerAgent.Phase.Sell);
}
// asset count must not exceed `maxAssetCount`
>> if (getCurrentMarketParameters().maxAssetCount == assetsPerBuyerRound[_buyer][round].length) { // @audit only accept first "maxAssetCount" list.
revert AssetLimitExceeded(getCurrentMarketParameters().maxAssetCount);
}
// all is well, create the asset & its listing
address asset = address(swanAssetFactory.deploy(_name, _symbol, _desc, msg.sender));
listings[asset] = AssetListing({
createdAt: block.timestamp,
royaltyFee: buyer.royaltyFee(),
price: _price,
seller: msg.sender,
status: AssetStatus.Listed,
buyer: _buyer,
round: round
});
// add this to list of listings for the buyer for this round
assetsPerBuyerRound[_buyer][round].push(asset);
// transfer royalties
transferRoyalties(listings[asset]);
emit AssetListed(msg.sender, asset, _price);
}
function transferRoyalties(AssetListing storage asset) internal {
// calculate fees
>> uint256 buyerFee = (asset.price * asset.royaltyFee) / 100; // @audit if price is zero, fee is zero.
uint256 driaFee = (buyerFee * getCurrentMarketParameters().platformFee) / 100;
// first, Swan receives the entire fee from seller
// this allows only one approval from the seller's side
token.transferFrom(asset.seller, address(this), buyerFee);
// send the buyer's portion to them
token.transfer(asset.buyer, buyerFee - driaFee);
// then it sends the remaining to Swan owner
token.transfer(owner(), driaFee);
}

To prevent spamming the list, a fee is paid via transferRoyalties at the time of listing. However, if user set the asset's Price to 0, user will not pay the fee.

Since each Agent can only receive maxAssetCount in each round, Attacker can fill it with meaningless lists, which can prevent users from using the Agent.

Impact

Spamming with a Price of 0 can be used to fill the assetsPerBuyerRound for that round, which not only blocks users who want to use the Agent, but also prevents the Agent from working properly.

Tools Used

None

Recommendations

It is recommended to set a minimum price or modify it to take a default fee in addition to the price-dependent fee.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

DOS the buyer / Lack of minimal amount of listing price

Support

FAQs

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