Dria

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

H-01 Potential Denial of Service in Swan Purchase Function

Title

Potential Denial of Service in Swan Purchase Function

Summary

A vulnerability has been identified in the Swan smart contract that allows sellers to prevent buyers from completing purchases during an entire round by manipulating asset permissions or ownership. This can result in a denial of service that prevents buyers from completing any asset purchases.

Vulnerability Details

In Swan.sol:294, the purchase function is vulnerable to manipulation by sellers. The relevant code section handles the asset transfer process:

SwanAsset(_asset).transferFrom(listing.seller, address(this), 1);
SwanAsset(_asset).transferFrom(address(this), listing.buyer, 1);

The vulnerability exists because the seller can:

  1. Revoke the Swan contract's permission to transfer the asset

  2. Transfer the asset to a different address before the purchase is completed

Either action will cause the transferFrom operation to fail, resulting in a transaction revert. The purchases of assets are performed on a per-round basis, buying all assets for that round in a single function call to BuyerAgent.purchase(). Therefore, a revert in a single asset will cause the whole transaction to fail.

Impact

The impact of this vulnerability is severe:

  • Buyers are unable to complete legitimate purchases

  • The marketplace functionality is disrupted for entire rounds

  • Sellers can manipulate the system to prevent sales while maintaining their listings

Tools Used

Manual Review

Recommendations

To address this vulnerability, consider implementing the following measures:

  1. Asset Escrow System

    • Require sellers to transfer assets to the contract when creating listings

    • Hold assets in escrow until the listing is either completed or canceled

    • This prevents sellers from manipulating asset availability during active listings

function createListing(address asset, uint256 price) external {
// Transfer asset to contract first
SwanAsset(asset).transferFrom(msg.sender, address(this), 1);
// Create listing with asset already in escrow
listings[asset] = Listing({
seller: msg.sender,
price: price,
status: AssetStatus.Active
});
}
function purchase(address asset) external {
// Asset is already in contract, eliminating seller manipulation
SwanAsset(asset).transferFrom(address(this), msg.sender, 1);
// Complete remaining purchase logic
}
  1. Permission Enforcement

    • Since assets listed on Swan are expected to be instances of SwanAsset, a special case when the Swan contract is the spender of the asset could be specified:

function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual {
if (!_isAuthorized(owner, spender, tokenId) && spender != _swanAddress) {
if (owner == address(0)) {
revert ERC721NonexistentToken(tokenId);
} else {
revert ERC721InsufficientApproval(spender, tokenId);
}
}
}
Updates

Lead Judging Commences

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

DoS in BuyerAgent::purchase

Support

FAQs

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