Dria

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

Core asset parameters can be changed after creation

Summary

The Swan protocol's relist function allows complete mutation of critical asset parameters after listing, breaking core protocol invariants and enabling market manipulation.

function relist(address _asset, address _buyer, uint256 _price) external {
// @audit-info Direct storage mutation without parameter validation
AssetListing storage asset = listings[_asset];
// @audit-info Insufficient checks before mutation
if (asset.seller != msg.sender) {
revert Unauthorized(msg.sender);
}
// @audit-info Complete asset parameter overwrite
listings[_asset] = AssetListing({
createdAt: block.timestamp,
royaltyFee: buyer.royaltyFee(),
price: _price,
seller: msg.sender,
status: AssetStatus.Listed,
buyer: _buyer,
round: round
});
}

Vulnerability Details

In the relist() function, an asset's listing details can be modified after creation: https://github.com/Cyfrin/2024-10-swan-dria/blob/c3f6f027ed51dd31f60b224506de2bc847243eb7/contracts/swan/Swan.sol#L197-L255

function relist(address _asset, address _buyer, uint256 _price) external {
AssetListing storage asset = listings[_asset];
// @audit-info No validation of _price parameter allows arbitrary price changes
// @audit-info Direct storage pointer to listings allows full mutation
// only the seller can relist the asset
if (asset.seller != msg.sender) {
revert Unauthorized(msg.sender);
}
// @audit-info Status check insufficient - allows mutation of listed assets
if (asset.status != AssetStatus.Listed) {
revert InvalidStatus(asset.status, AssetStatus.Listed);
}
// @audit-info Round check can be manipulated by buyer agent
(uint256 oldRound,,) = BuyerAgent(asset.buyer).getRoundPhase();
if (oldRound <= asset.round) {
revert RoundNotFinished(_asset, asset.round);
}
// @audit-info Complete mutation of asset listing parameters
listings[_asset] = AssetListing({
createdAt: block.timestamp, // Modified timestamp
royaltyFee: buyer.royaltyFee(), // Modified fees
price: _price, // Modified price
seller: msg.sender,
status: AssetStatus.Listed,
buyer: _buyer, // Modified buyer
round: round // Modified round
});

The issue is that relist() allows complete mutation of an asset's critical parameters:

  • Price can be changed

  • Buyer can be changed

  • Royalty fee can be changed

  • Round number can be changed

Let's assume

// Initial listing
swan.list("Asset", "AST", "desc", 1 ether, buyer1);
// Malicious relisting
swan.relist(assetAddress, buyer2, 0.1 ether); // 90% price reduction
swan.relist(assetAddress, buyer1, 10 ether); // 10x price increase

Impact

  1. Price Manipulation Risk

  • Sellers can arbitrarily change prices after listing

  • Enables market manipulation through rapid price changes

  • No bounds checking on new prices

  1. Asset Parameter Mutation

  • Core asset parameters can be changed after creation

  • Breaks immutability expectations for buyers

  • Could enable bait-and-switch schemes

  1. Round System Exploitation

  • Round numbers can be manipulated

  • Timing attacks possible through round changes

  • Breaks round-based market phase assumptions

  1. Fee Structure Manipulation

  • Royalty fees can be modified after listing

  • Impacts revenue distribution

  • Could be used to bypass fee requirements

Tools Used

Vs

Recommendations

Implement a strict parameter mutation policy where only specific fields can be modified under well-defined conditions. Add price change limits, preserve core parameters, and implement a time-lock for significant changes.

Updates

Lead Judging Commences

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

Support

FAQs

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