Dria

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

Asset State Inconsistency in Round-Based Market System

Summary

The Swan protocol's asset tracking system fails to maintain consistent state across rounds and buyers during asset relisting operations, potentially compromising market integrity and bypassing protocol constraints.

Vulnerability Details

The issue is in the asset tracking logic, specifically in the list() and relist() functions where assets are tracked per buyer and round in the assetsPerBuyerRound mapping:

Swan.sol#L93 https://github.com/Cyfrin/2024-10-swan-dria/blob/c3f6f027ed51dd31f60b224506de2bc847243eb7/contracts/swan/Swan.sol#L92-L93

Swan.sol#L93 https://github.com/Cyfrin/2024-10-swan-dria/blob/c3f6f027ed51dd31f60b224506de2bc847243eb7/contracts/swan/Swan.sol#L248-L249

// @audit-info Missing asset count validation and state cleanup
mapping(address buyer => mapping(uint256 round => address[])) public assetsPerBuyerRound;
// @audit-info No validation of asset existence or state before relisting
// Could allow listing with invalid prices including 0
function relist(address _asset, address _buyer, uint256 _price) external {
AssetListing storage asset = listings[_asset];
// ... existing checks ...
// @audit-info Old buyer's round assets aren't cleaned up
// Could lead to duplicate entries
assetsPerBuyerRound[_buyer][round].push(_asset);
}

The contract fails to maintain proper asset count invariants across rounds and buyers. When assets are relisted, the old state isn't cleaned up, leading to potential asset tracking inconsistencies.

The bug manifests in the following scenario.

  1. An asset is listed for a buyer in round N

  2. The asset is relisted for a different buyer in round N+1

  3. The original buyer's asset count isn't properly decremented

Consider this

1. Buyer A lists Asset X in Round 1
2. Asset X appears in assetsPerBuyerRound[BuyerA][1]
3. Seller relists Asset X to Buyer B in Round 2
4. Asset X now appears in both:
- assetsPerBuyerRound[BuyerA][1]
- assetsPerBuyerRound[BuyerB][2]
5. Asset count invariant is violated

The issue connects directly to the round-based market system and buyer agent functionality, potentially disrupting the entire trading mechanism.

Impact

  1. Asset counts can become invalid

  2. Assets can appear in multiple rounds simultaneously

  3. Market constraints (maxAssetCount) can be bypassed

  4. Buyer agent operations could be disrupted

Tools Used

Vs

Recommendations

function relist(address _asset, address _buyer, uint256 _price) external {
AssetListing storage asset = listings[_asset];
+ // Clean up old round state
+ removeAssetFromRound(asset.buyer, asset.round, _asset);
// Continue with relisting logic
assetsPerBuyerRound[_buyer][round].push(_asset);
}
+ function removeAssetFromRound(address buyer, uint256 round, address asset) internal {
+ uint256 len = assetsPerBuyerRound[buyer][round].length;
+ for(uint256 i = 0; i < len; i++) {
+ if(assetsPerBuyerRound[buyer][round][i] == asset) {
+ assetsPerBuyerRound[buyer][round][i] =
+ assetsPerBuyerRound[buyer][round][len - 1];
+ assetsPerBuyerRound[buyer][round].pop();
+ break;
+ }
+ }
+ }
Updates

Lead Judging Commences

inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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