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 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

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

Support

FAQs

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