DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Missing "invalid round" check in sequencer status validation could lead to operating during network issues

Description:

The protocol's Sequencer status validation in KeeperProxy contract lacks a check for "invalid rounds".

"Invalid round" is described to mean there was a problem updating the sequencer's status, possibly due to network issues or problems with data from oracles.

According to Chainlink documentation , when startedAt returns 0, it indicates an invalid round where the Sequencer's status wasn't properly updated:

// KeeperProxy.sol
function _validatePrice(address perpVault, MarketPrices memory prices) internal view {
(, int256 answer, uint256 startedAt, , ) = sequencerUptimeFeed.latestRoundData();
// Only checks if sequencer is up
bool isSequencerUp = answer == 0;
require(isSequencerUp, "sequencer is down");
// Doesn't validate if startedAt == 0 (invalid round)
uint256 timeSinceUp = block.timestamp - startedAt;
require(timeSinceUp > GRACE_PERIOD_TIME, "Grace period not over");
}

The current implementation only checks if answer == 0 but misses the validation of startedAt, which could allow operations during network issues or oracle data problems.

As startedAt will be 0, the arithmetic operation block.timestamp - startedAt will result in a value greater than GRACE_PERIOD_TIME (which is hardcoded to be 3600). I.e., block.timestamp = 1739442308, so 1739442308 - 0 = 1739442308 which is bigger than 3600. The code won't revert.

Imagine a scenario where a new round begins: initially, startedAt is set to 0, and answer is also 0. According to the documentation, if answer = 0, it indicates that the sequencer is up, whereas if answer = 1, it means the sequencer is down. However, in this case, both answer and startedAt can initially be 0 until all data is retrieved from the oracles and the update is confirmed, at which point these values are set correctly to reflect the actual status of the sequencer.

From this, it follows that startedAt is a crucial second parameter that should be considered when checking whether the sequencer is up, down, or correctly updated. The checks in KeeperProxy::_validatePrice allow for successful calls even in an invalid round, as reverts do not occur if answer == 0 and startedAt == 0. This effectively defeats the purpose of having a sequencerFeed check, which is meant to determine whether the sequencer on L2 is up, down, active, or if its status has actually been confirmed.

Impact:

Inadequate checks to confirm the correct status of the Sequencer in KeeperProxy contract will cause _validatePrice() to not revert even when the sequencer uptime feed is not updated or is called in an invalid round.

Recommended Mitigation:

Add invalid round check:

require(startedAt != 0, "Invalid sequencer round");
Updates

Lead Judging Commences

n0kto Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

invalid_sequencerUptimeFeed_startedAt_0_no_roundId

startedAt is only 0 when contract is not initialized on Arbitrum, but it is already initialized on Arbitrum. startedAt is sufficient for the protocol, it does not need roundID. Current documentation of Chainlink does not have this sentence: “This timestamp returns `0` if a round is invalid.“

Support

FAQs

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