Dria

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

Inconsistent setup of market parameters timestamp in initialize function can lead to time calculations underflow

Summary

The Swan contract's initialize function does not set the timestamp field of the SwanMarketParameters struct to the current block.timestamp. This omission can lead to underflow errors in the BuyerAgent contract's getRoundPhase function when calculating elapsed time, causing the function to revert. This disrupts the normal operation of the protocol, since that function is used project-wide.

Vulnerability Details

In the SwanManager contract, the SwanMarketParameters struct is designed to have its timestamp field set to the current block.timestamp internally, regardless of the user-provided value. This is correctly implemented in the setMarketParameters function:

function setMarketParameters(SwanMarketParameters memory _marketParameters) external onlyOwner {
require(_marketParameters.platformFee <= 100, "Platform fee cannot exceed 100%");
_marketParameters.timestamp = block.timestamp;
marketParameters.push(_marketParameters);
}

However, in the Swan contract—which inherits from SwanManager—the initialize function does not overwrite the timestamp field when pushing the initial market parameters or perform checks that the timestamp field falls within acceptable ranges and that all timestamps are in ascending order:

function initialize(
SwanMarketParameters calldata _marketParameters,
LLMOracleTaskParameters calldata _oracleParameters,
// contracts
address _coordinator,
address _token,
address _buyerAgentFactory,
address _swanAssetFactory
) public initializer {
__Ownable_init(msg.sender);
require(_marketParameters.platformFee <= 100, "Platform fee cannot exceed 100%");
// Market & oracle parameters
marketParameters.push(_marketParameters); // Timestamp is not overwritten here
oracleParameters = _oracleParameters;
// Contracts
coordinator = LLMOracleCoordinator(_coordinator);
token = ERC20(_token);
buyerAgentFactory = BuyerAgentFactory(_buyerAgentFactory);
swanAssetFactory = SwanAssetFactory(_swanAssetFactory);
// Swan is an operator
isOperator[address(this)] = true;
// Owner is an operator
isOperator[msg.sender] = true;
}

As a result, if the provided _marketParameters.timestamp is earlier than the createdAt timestamp of the BuyerAgent contract (which is set to block.timestamp upon deployment), it can cause underflow in time calculations within the BuyerAgent's getRoundPhase function.

In the BuyerAgent contract, the getRoundPhase function calculates the current round and phase based on the elapsed time since the market parameters were set:

function getRoundPhase() public view returns (uint256, Phase, uint256) {
SwanMarketParameters[] memory marketParams = swan.getMarketParameters();
if (marketParams.length == marketParameterIdx + 1) {
// Simplified calculation if no new market parameters have been added
return _computePhase(marketParams[marketParameterIdx], block.timestamp - createdAt);
} else {
// Accumulate rounds and compute phases when multiple market parameters exist
uint256 idx = marketParameterIdx;
uint256 round;
(uint256 initialRound,,) = _computePhase(marketParams[idx], marketParams[idx + 1].timestamp - createdAt);
round += initialRound;
idx++;
// Iterate through market parameters to accumulate rounds
while (idx < marketParams.length - 1) {
(uint256 innerRound,,) = _computePhase(
marketParams[idx],
marketParams[idx + 1].timestamp - marketParams[idx].timestamp
);
round += innerRound + 1;
idx++;
}
// Compute phase and time remaining for the last market parameter
(uint256 lastRound, Phase phase, uint256 timeRemaining) = _computePhase(
marketParams[idx],
block.timestamp - marketParams[idx].timestamp
);
round += lastRound + 1;
return (round, phase, timeRemaining);
}
}

If marketParams[idx + 1].timestamp is less than createdAt, the subtraction marketParams[idx + 1].timestamp - createdAt results in a negative value, causing the function to revert.

Impact

This vulnerability can prevent the BuyerAgent contract from correctly determining the current round and phase, leading to a failure in executing critical functions like purchasing assets, updating the buyer's state, or withdrawing funds. Users may experience denial of service, inability to participate in asset rounds, or financial losses due to the contract's inability to process transactions as expected.

Tools Used

Manual Review

Recommended Mitigation

Modify the Swan contract's initialize function to set the timestamp field to the current block timestamp. This ensures that the timestamps used in time calculations are always accurate and prevents underflow errors in the BuyerAgent contract.

Updates

Lead Judging Commences

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

Support

FAQs

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