DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: medium
Valid

When creating or updating a marketConfig the priceFeedHeartbeatSeconds is not set, leading to a DOS of the protocol

Lines of Code

https://github.com/Cyfrin/2024-07-zaros/blob/d687fe96bb7ace8652778797052a38763fbcbb1b/src/perpetuals/leaves/MarketConfiguration.sol#L37-L49

Impact

Because the priceFeedHeartbeatSeconds is never set, it is always 0. This results in a DOS of the protocol since every call to ChainlinkUtil.getPrice reverts because the price provided by the chainlink oracle is seen as stale.

Proof of Concept

When creating a new market or updating the MarketConfiguration of an existing market, the function MarketConfiguration.update() is called to set the new values of the MarketConfiguration.
The issue arises from the fact that even though a value for priceFeedHeartbeatSeconds is provided, the value is never set in the market’s MarketConfiguration:

function update(Data storage self, Data memory params) internal {
self.name = params.name;
self.symbol = params.symbol;
self.priceAdapter = params.priceAdapter;
self.initialMarginRateX18 = params.initialMarginRateX18;
self.maintenanceMarginRateX18 = params.maintenanceMarginRateX18;
self.maxOpenInterest = params.maxOpenInterest;
self.maxSkew = params.maxSkew;
self.maxFundingVelocity = params.maxFundingVelocity;
self.minTradeSizeX18 = params.minTradeSizeX18;
self.skewScale = params.skewScale;
self.orderFees = params.orderFees;
<==@audit “priceFeedHeartbeatSeconds” is not set
}

The priceFeedHeartbeatSeconds parameter is crucial for determining the validity of the price feed data. If this parameter is not set, it defaults to 0, causing the system to always consider the price feed data as outdated when calling the priceAdapter and will revert.

function getPrice(
IAggregatorV3 priceFeed,
uint32 priceFeedHeartbeatSeconds,
IAggregatorV3 sequencerUptimeFeed
)
internal
view
returns (UD60x18 price)
{
try priceFeed.latestRoundData() returns (uint80, int256 answer, uint256, uint256 updatedAt, uint80) {
if (block.timestamp - updatedAt > priceFeedHeartbeatSeconds) { <== @audit will always revert because the priceFeedHeartbeatSeconds is 0
revert Errors.OraclePriceFeedHeartbeat(address(priceFeed));
}
}

Since a valid price is crucial for creating and filling orders, both functionalities will be DOSed since they call getPrice.

Recommended Mitigation Steps

Ensure that the priceFeedHeartbeatSeconds parameter is properly set during the market creation and updating process.

function update(Data storage self, Data memory params) internal {
self.name = params.name;
self.symbol = params.symbol;
self.priceAdapter = params.priceAdapter;
self.initialMarginRateX18 = params.initialMarginRateX18;
self.maintenanceMarginRateX18 = params.maintenanceMarginRateX18;
self.maxOpenInterest = params.maxOpenInterest;
self.maxSkew = params.maxSkew;
self.maxFundingVelocity = params.maxFundingVelocity;
self.minTradeSizeX18 = params.minTradeSizeX18;
self.skewScale = params.skewScale;
self.orderFees = params.orderFees;
+ self.priceFeedHeartbeatSeconds = params.priceFeedHeartbeatSeconds;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`MarketConfiguration::update` function lacks `priceFeedHeartbeatSeconds` argument

Support

FAQs

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