Dria

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

Unauthorized Access to SwanAsset Token Transfers in `Swan.purchase`

Summary

The Swan::purchase function facilitates the transfer of SwanAsset ERC721 tokens from the seller to the buyer assuming approvals are already set up. However, there’s no guarantee that approvals exist if approvals are altered. It performs two transferFrom operations:

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

Vulnerability Details

These calls assume that the necessary approvals are in place, specifically that the Swan contract is approved to transfer the SwanAsset on behalf of the seller. While the SwanAsset constructor sets Swan as an approved operator, there's an inherent risk if approvals are altered or revoked during the execution of the purchase function. If the seller revokes approval before both transfers complete, it could lead to transaction failures or inconsistencies between the asset's ownership and the contract's state.

Here's how a discrepancy will occur.

  • The seller lists their SwanAsset for sale, ensuring that Swan is set as an approved operator.

  • A buyer initiates the purchase function for the listed asset.

  • Before the purchase function completes, the seller revoke or alter the approval of the Swan contract for the SwanAsset.

  • The first transferFrom call (seller to Swan) succeeds, transferring the asset to the Swan contract.

  • The second transferFrom call (Swan to buyer) fails due to revoked or altered approvals.

  • The listing.status is updated to Sold despite the asset not being successfully transferred to the buyer.

Impact

This enables sellers to receive funds without transferring assets, leading to financial losses for buyers. While direct exploitation requires specific actions by the seller, the impact on market integrity and user trust is significant.

Tools Used

Manual Review

Recommendations

Employ OZ's SafeERC20 and SafeERC721 libraries to handle token transfers securely, ensuring that any failures during transfers revert the entire transaction.

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/utils/SafeERC721.sol";
contract Swan is SwanManager, ReentrancyGuard, UUPSUpgradeable {
using SafeERC20 for ERC20;
using SafeERC721 for ERC721;
// ...
function purchase(address _asset) external onlyAuthorized nonReentrant {
AssetListing storage listing = listings[_asset];
// Checks
if (listing.status != AssetStatus.Listed) {
revert InvalidStatus(listing.status, AssetStatus.Listed);
}
if (listing.buyer != msg.sender) {
revert Unauthorized(msg.sender);
}
if (SwanAsset(_asset).ownerOf(1) != listing.seller) {
revert Unauthorized(listing.seller);
}
// Effects
listing.status = AssetStatus.Sold;
// Interactions
SwanAsset(_asset).safeTransferFrom(listing.seller, address(this), 1);
SwanAsset(_asset).safeTransferFrom(address(this), listing.buyer, 1);
// Transfer funds after successful asset transfers
token.safeTransferFrom(listing.buyer, address(this), listing.price);
token.safeTransfer(listing.seller, listing.price);
emit AssetSold(listing.seller, msg.sender, _asset, listing.price);
}
// ...
}
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.