Dria

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

A change in marketParam would cause a new cycle/round to start in the buyer agent before the last cycle has been completed

Summary

When a new marketParam is added via Swan::setMarketParameters, all buyer agents will automatically start a new round and as such phase

for example if the current phase is buy, it will automatically go back to sell without completing its buy period or even entering withdraw at all in that round, also the round itself increases by one

(uint256 lastRound, Phase phase, uint256 timeRemaining) = _computePhase(
marketParams[idx],
block.timestamp - marketParams[idx].timestamp
);
// accumulate the last round as well, along with a single offset round
round += lastRound + 1;

here even if a round has not passed since the additon of the new marketParam, the +1 ensures that the round increases by one

Vulnerability Details

assuming the owners of the buyer agent want to buy an asset and as such call BuyerAgent::oraclePurchaseRequest,

function oraclePurchaseRequest(bytes calldata _input, bytes calldata _models) external onlyAuthorized {
// check that we are in the Buy phase, and return round
(uint256 round, ) = _checkRoundPhase(Phase.Buy);
oraclePurchaseRequests[round] = swan.coordinator().request(
SwanBuyerPurchaseOracleProtocol,
_input,
_models,
swan.getOracleParameters()
);
}

seeing as they have the entirety of the buy period to purchase, they might not place the purchase immediately after making the request , but then in the wait a newMarketParam is added

function purchase() external onlyAuthorized {
// check that we are in the Buy phase, and return round
(uint256 round, ) = _checkRoundPhase(Phase.Buy);
// check if the task is already processed
uint256 taskId = oraclePurchaseRequests[round];
if (isOracleRequestProcessed[taskId]) {
revert TaskAlreadyProcessed();
}
// read oracle result using the latest task id for this round
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);
}
// update taskId as completed
isOracleRequestProcessed[taskId] = true;
}

as can be seen above the function checks the request at the current round, but since the round has changed then there is no request there, also a new cycle would begin with this new round meaning that the authorized would have to wait for the sell period to elapse before making another buy request

Impact

The buyer agent might not be able to complete some functionalities

Tools Used

manual analysis

Recommendations

There shoukd be a check to ensure the previous round has finished and then start counting the new round from that point

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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