Dria

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

DoS in BuyerAgent purchase.

Summary

Listed asset sellers can revoke their approval to the Swan contract, causing BuyerAgent's purchase operations to fail. This not only wastes the Agent's request fees but can effectively prevent normal protocol operation.

Vulnerability Details

function purchase() external onlyAuthorized {
...
bytes memory output = oracleResult(taskId);
address[] memory assets = abi.decode(output, (address[]));
// we purchase each asset returned
for (uint256 i = 0; i < assets.length; i++) {
address asset = assets[i];
// must not exceed the roundly buy-limit
uint256 price = swan.getListingPrice(asset);
spendings[round] += price;
if (spendings[round] > amountPerRound) {
revert BuyLimitExceeded(spendings[round], amountPerRound);
}
// add to inventory
inventory[round].push(asset);
// make the actual purchase
>> swan.purchase(asset); // @audit DoS
}
// update taskId as completed
isOracleRequestProcessed[taskId] = true;
}
// swan.purchase()
function purchase(address _asset) external {
...
// 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); // @audit need approve
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);
}

In purchase, the asset that the agent wants to purchase must be approved by the seller in the swan contract.

Otherwise, revert will occur in the asset transferFrom of swan.purchase.

/// @notice SwanAsset is an ERC721 token with a single token supply.
contract SwanAsset is ERC721, Ownable {
/// @notice Creation time of the token
uint256 public createdAt;
/// @notice Description of the token
bytes public description;
/// @notice Constructor sets properties of the token.
constructor(
string memory _name,
string memory _symbol,
bytes memory _description,
address _owner,
address _operator
) ERC721(_name, _symbol) Ownable(_owner) {
description = _description;
createdAt = block.timestamp;
// owner is minted the token immediately
ERC721._mint(_owner, 1);
// Swan (operator) is approved to by the owner immediately.
>> ERC721._setApprovalForAll(_owner, _operator, true); // @audit _owner(seller) can revoke approve
}
}

When a seller creates a SwanAsset, it approves it to the swan contract through _setApprovalForAll, but it can be revoked by the owner (seller).

Therefore, if you revoke the approve after listing, it will be reverted when Agenr try to purchase the asset.

Impact

A malicious actor can prevent an agent from making an entire purchase, forcing the agent to pay the request fee and ultimately preventing the agent from functioning properly.

Tools Used

None

Recommendations

It is recommended that the SwanAsset is held by the swan contract rather than the Owner, or modify swan.purchase with a try catch statement so that the entire purchase does not fail.

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.