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

DoS of `_validatePrice` function on Avalanche due to mandatory sequencer checks

Summary

The KeeperProxy contract hardcodes the Arbitrum sequencer uptime feed address and enforces mandatory sequencer checks in its price validation logic. This design makes the contract completely unusable on Avalanche, despite the protocol's intended multi-chain deployment strategy, as Avalanche does not utilize a sequencer architecture.

Vulnerability Details

The vulnerability exists in two parts:

  1. Hardcoded Arbitrum-specific address:

sequencerUptimeFeed = AggregatorV2V3Interface(0xFdB631F5EE196F0ed6FAa767959853A9F217697D);

This address is specific to Arbitrum and does not exist on Avalanche. Any attempt to interact with this address on Avalanche will revert.

  1. Mandatory sequencer checks in _validatePrice():

function _validatePrice(address perpVault, MarketPrices memory prices) internal view {
// L2 Sequencer check
(
/*uint80 roundID*/,
int256 answer,
uint256 startedAt,
/*uint256 updatedAt*/,
/*uint80 answeredInRound*/
) = AggregatorV2V3Interface(sequencerUptimeFeed).latestRoundData();
bool isSequencerUp = answer == 0;
require(isSequencerUp, "sequencer is down");
// Make sure the grace period has passed after the sequencer is back up.
uint256 timeSinceUp = block.timestamp - startedAt;
require(timeSinceUp > GRACE_PERIOD_TIME, "Grace period is not over");
...
}

The _validatePrice() function is a critical component that validates prices before executing keeper operations.

On Avalanche, the sequencer check will fail because:

  • The sequencerUptimeFeed address is invalid.

  • The call to latestRoundData() will revert, causing the entire function to fail.

Impact

All calls to _validatePrice will revert on Avalanche because the sequencer check fails. This prevents the execution of critical functions like run, runNextAction, and cancelOrder.

Tools Used

Manual Review

Recommendations

Implement chain-specific deployment logic:

contract KeeperProxy is Initializable, Ownable2StepUpgradeable, ReentrancyGuardUpgradeable {
bool public hasSequencer;
function initialize(address _sequencerFeed, bool _hasSequencer) public initializer {
hasSequencer = _hasSequencer;
if (_hasSequencer) {
require(_sequencerFeed != address(0), "Invalid sequencer feed");
sequencerUptimeFeed = AggregatorV2V3Interface(_sequencerFeed);
}
}
function _validatePrice(address perpVault, MarketPrices memory prices) internal view {
if (hasSequencer) {
// Perform sequencer checks only on L2s with sequencers
(
/*uint80 roundID*/,
int256 answer,
uint256 startedAt,
/*uint256 updatedAt*/,
/*uint80 answeredInRound*/
) = AggregatorV2V3Interface(sequencerUptimeFeed).latestRoundData();
bool isSequencerUp = answer == 0;
require(isSequencerUp, "sequencer is down");
uint256 timeSinceUp = block.timestamp - startedAt;
require(timeSinceUp > GRACE_PERIOD_TIME, "Grace period is not over");
}
// Continue with other price validations...
}
}
Updates

Lead Judging Commences

n0kto Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding_Avalanche_has_no_sequencer

Likelihood: High, run and runNextAction will revert. Impact: Low, any deposit will be retrieve thanks to cancelFlow.

Support

FAQs

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