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

`MarketConfiguration::priceFeedHeartbeatSeconds` is not set, causing Chainlink oracle to revert when requesting the index price

Description

MarketConfiguration::priceFeedHeartbeatSeconds is a variable used in ChainlinkUtil::getPrice to ensure the price is sufficiently recent for the protocol to use the returned price. If this variable is too low or the Oracle has an outdated price, the function reverts:

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) {
@> revert Errors.OraclePriceFeedHeartbeat(address(priceFeed));
}
...
} catch {
revert Errors.InvalidOracleReturn();
}
}

The issue is that this variable is not set due to an omission in MarketConfiguration::update and will therefore always be 0, causing ChainlinkUtil::getPrice to always revert:

struct Data {
string name;
string symbol;
address priceAdapter;
uint128 initialMarginRateX18;
uint128 maintenanceMarginRateX18;
uint128 maxOpenInterest;
uint128 maxSkew;
uint128 maxFundingVelocity;
uint128 minTradeSizeX18;
uint256 skewScale;
OrderFees.Data orderFees;
@> uint32 priceFeedHeartbeatSeconds;
}
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;
@>
}

No other place in the codebase allows setting it and even if the update sets the params like in GlobalConfigurationBranch::updatePerpMarketConfiguration, it won't work:

function updatePerpMarketConfiguration(
uint128 marketId,
UpdatePerpMarketConfigurationParams calldata params
)
external
onlyOwner
onlyWhenPerpMarketIsInitialized(marketId)
{
...
perpMarketConfiguration.update(
MarketConfiguration.Data({
...
@> priceFeedHeartbeatSeconds: params.priceFeedHeartbeatSeconds
})
);
emit LogUpdatePerpMarketConfiguration(msg.sender, marketId);
}

This variable and ChainlinkUtil::getPrice is used in PerpMarket::getIndexPrice, which is used in many other functions in the protocol.

Apart from a lot of getters, the most important/impacted ones will be:

  • OrderBranch::simulateTrade called when a market order is created, causing a DoS of OrderBranch::createMarketOrder.

  • LiquidationBranch::liquidateAccounts preventing liquidation.

  • TradingAccount::getAccountMarginRequirementUsdAndUnrealizedPnlUsd and TradingAccount::getAccountUnrealizedPnlUsd which cause a DoS of SettlementBranch::_fillOrder and TradingAccountBranch::withdrawMargin, leading to funds being stuck in the contract.

Risk

Likelyhood: High

  • priceFeedHeartbeatSeconds is never set in MarketConfiguration::update.

Impact: High

  • DoS of a large number of functionalities.

  • Withdrawal of margin is impossible.

  • Creation of market orders and their liquidation is impossible.

Recommended Mitigation

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 over 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.