Dria

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

Assets with price specified as 0 can be used to DoS buyers

Summary

BuyerAgents have a max asset count per round for buying assets, an attacker can fill this limit with unwanted assets. Under normal conditions this would not cause an issue as sellers have to pay a fee to buyers to list assets for them but an attacker can set the price of an asset to zero, this also sets the buyer fee to zero making this attack possible.

Vulnerability Details

In Swan.sol using list function a seller can list an asset for a buyer, you can observe that if asset count hits the maxAssetCount the function reverts:

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) {
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);
}

For transferRoyalties function responsible for sending fees to the buyer, you can observe that if the asset price is specified as zero, fees will come out as zero:

function transferRoyalties(AssetListing storage asset) internal {
// calculate fees
uint256 buyerFee = (asset.price * asset.royaltyFee) / 100;
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);
}

This makes it possible for an attacker to list as many trash assets as needed to block the intended use of the protocol.

Impact

Buyer's ability to buy assets is denied for a round. Next round the attack can be repeated.

Recommendations

A minimum check for asset price should be introduced.

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 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.