Dria

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

Missing round validation in swan contract allows fee evasion & asset purchase from previous rounds

Github

https://github.com/Cyfrin/2024-10-swan-dria/blob/c8686b199daadcef3161980022e12b66a5304f8e/contracts/swan/Swan.sol#L276-L302

Summary

Assets from previous rounds can be purchased without requiring relists due to missing round validation in Swan's purchase function. As a result, the seller can avoid paying the royalty fee and the Dria protocol fee.

Vulnerability Details

The purchase function in Swan contract lacks round validation:

function purchase(address _asset) external {
AssetListing storage listing = listings[_asset];
// Only checks status and buyer
if (listing.status != AssetStatus.Listed) {
revert InvalidStatus(listing.status, AssetStatus.Listed);
}
if (listing.buyer != msg.sender) {
revert Unauthorized(msg.sender);
}
// Missing: Check if listing.round matches current round @audit

When BuyerAgent receives asset addresses from oracle:

function purchase() external onlyAuthorized {
// Oracle could return old listings
address[] memory assets = abi.decode(output, (address[]));
for (uint256 i = 0; i < assets.length; i++) {
swan.purchase(assets[i]); // Can purchase from any round @audit
}
}

Impact

HIGH

  • Sellers can bypass paying new listing fees for new rounds

  • Protocol loses fee revenue

  • Breaks round-based market mechanism

  • Disrupts protocol's economic model where relisting fees incentivize quality listings

Likelihood

HIGH

  • No round validation in place

  • Oracle can return any Listed asset address

  • Happens any time oracle returns old listing addresses

  • No mitigating factors present

Proof of Concept

  1. Round 1: Seller lists Asset X and pays the listing fee.

  2. Round 2 starts: Asset X should require relisting and a new fee.

  3. The oracle returns Asset X's address.

  4. BuyerAgent can purchase Asset X in Round 2.

  5. The seller bypasses the listing fee because the BuyerAgent can still buy the asset in Round 2 due to the missing check.

  6. The missing listing.round check allows the purchase of a listing from a previous round without relisting if the oracle response includes its address.

Recommendation

Add round validation in Swan's purchase function:

function purchase(address _asset) external {
AssetListing storage listing = listings[_asset];
// Get current round from buyer
(uint256 currentRound,,) = BuyerAgent(msg.sender).getRoundPhase();
// Add round check
if (listing.round != currentRound) {
revert InvalidRound(listing.round, currentRound);
}
// Rest of function...
}

This ensures listings can only be purchased in their created round, maintaining round isolation and economic model.

Updates

Lead Judging Commences

inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

0xtheblackpanther Submitter
8 months ago
inallhonesty Lead Judge
8 months ago
inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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