Dria

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

SwanAssets can be transferred while seller details remain updateable which can lead to external market honeypots

Summary

Sellers can sell/transfer their still active SwanAssets to other users(outside of buyers), including external marketplaces, but the various seller details are not updated when this is done.

Vulnerability Details

Listed SwanAsset is transferable by default. So without the buyer acutally purchasing the SwanAsset, a seller can transfer or mostlikely list it for sale on an external marketplace for unsuspecting users. Upon the sale completion, the SwanAsset is transferred to the new owner (new seller) who might want to relist the sale to another buyer.

The function will revert since the relister has to be the original seller.

function relist(address _asset, address _buyer, uint256 _price) external {
AssetListing storage asset = listings[_asset];
// only the seller can relist the asset
@>> if (asset.seller != msg.sender) {
revert Unauthorized(msg.sender);
}
// asset must be listed
if (asset.status != AssetStatus.Listed) {
revert InvalidStatus(asset.status, AssetStatus.Listed);
}
// relist can only happen after the round of its listing has ended
// we check this via the old buyer, that is the existing asset.buyer
//
// note that asset is unlisted here, but is not bought at all
//
// perhaps it suffices to check `==` here, since buyer round
// is changed incrementially
(uint256 oldRound,,) = BuyerAgent(asset.buyer).getRoundPhase();
if (oldRound <= asset.round) {
revert RoundNotFinished(_asset, asset.round);
}
//...

And the new seller makes no funds off of the SwanAsset upon purchase because the fee token is transferred to the seller listed, who is the old seller.

function purchase(address _asset) external {
AssetListing storage listing = listings[_asset];
// asset must be listed to be purchased
if (listing.status != AssetStatus.Listed) {
revert InvalidStatus(listing.status, AssetStatus.Listed);
}
// can only the buyer can purchase the asset
if (listing.buyer != msg.sender) {
revert Unauthorized(msg.sender);
}
// update asset status to be sold
listing.status = AssetStatus.Sold;
// transfer asset from seller to Swan, and then from Swan to buyer
// this ensure that only approval to Swan is enough for the sellers
SwanAsset(_asset).transferFrom(listing.seller, address(this), 1);
SwanAsset(_asset).transferFrom(address(this), listing.buyer, 1);
// transfer money
token.transferFrom(listing.buyer, address(this), listing.price);
@>> token.transfer(listing.seller, listing.price);
emit AssetSold(listing.seller, msg.sender, _asset, listing.price);
}

Impact

Loss of funds for unsuspecting users.

Tools Used

Manual review.

Recommendations

Prevent the assets from being transferrable before purchase, or update the various listing parameters during transfers.

Updates

Lead Judging Commences

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