15,000 USDC
View results
Submission Details
Severity: medium
Valid

Liquidation should be paused when L2 sequencer is down or restarted in less than grace period.

Bug Description

As it can be deployed to L2 like Optimism, Arbitrum, it should check sequencer uptime feed to check if L2 sequencer is down or restarted in less than grace period.

In that cases, price feed will stop being updated and become stale, thus it might trigger massive liquidation events.

https://docs.chain.link/data-feeds/l2-sequencer-feeds

To prevent such scenario, Aave v3 for example pause liquidation and borrowing when sequencer is down or restarted in less than grace period.

https://docs.aave.com/developers/core-contracts/priceoraclesentinel

https://github.com/aave/aave-v3-core/blob/27a6d5c83560694210849d4abf09a09dec8da388/contracts/protocol/configuration/PriceOracleSentinel.sol#L57C1-L81C1

/// @inheritdoc IPriceOracleSentinel
function isBorrowAllowed() public view override returns (bool) {
return _isUpAndGracePeriodPassed();
}
/// @inheritdoc IPriceOracleSentinel
function isLiquidationAllowed() public view override returns (bool) {
return _isUpAndGracePeriodPassed();
}
/**
* @notice Checks the sequencer oracle is healthy: is up and grace period passed.
* @return True if the SequencerOracle is up and the grace period passed, false otherwise
*/
function _isUpAndGracePeriodPassed() internal view returns (bool) {
(, int256 answer, , uint256 lastUpdateTimestamp, ) = _sequencerOracle.latestRoundData();
return answer == 0 && block.timestamp - lastUpdateTimestamp > _gracePeriod;
}
/// @inheritdoc IPriceOracleSentinel
function setSequencerOracle(address newSequencerOracle) public onlyPoolAdmin {
_sequencerOracle = ISequencerOracle(newSequencerOracle);
emit SequencerOracleUpdated(newSequencerOracle);
}

Impact

An innocent user might be a target of unfair massive liquidation by MEV searchers in case of L2 sequencer down event.

Recommendation

  • Should set sequencer uptime oracle feed if contract is deployed in Arbitrum or Optimism.

  • If should check L2 sequencer uptime status before liquidation.

address private s_sequencerOracle;
+ function _isUpAndGracePeriodPassed() internal view returns (bool) {
+ if(s_sequencerOracle == address(0)) return true;
+ (, int256 answer, , uint256 lastUpdateTimestamp, ) = (AggregatorV3Interface(s_sequencerOracle).latestRoundData();
+ return answer == 0 && block.timestamp - lastUpdateTimestamp > _gracePeriod;
+ }
function liquidate(address collateral, address user, uint256 debtToCover)
external
moreThanZero(debtToCover)
nonReentrant
{
+ require(_isUpAndGracePeriodPassed(), DSCEngine_SequencerDownEvent);
...
}

Support

FAQs

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