Crypto Training
L2 Oracle Failure Modes: Sequencer Risk, Stale Feeds, and Liquidation Drift
On L2s, oracle correctness is not only about data source quality. Sequencer liveness, update lag, and fallback design can convert good feeds into bad liquidations.
A strong oracle feed can still produce bad protocol outcomes on L2.
Why: execution environment assumptions change.
flowchart LR
A[Price source OK] --> B[Sequencer outage]
B --> C[Stale or delayed updates]
C --> D[Liquidation logic still active]
D --> E[Unfair liquidations / bad debt]
Core failure modes#
| Failure mode | Trigger | Damage channel |
|---|---|---|
| Sequencer downtime | L2 operator outage | stale execution context |
| Heartbeat mismatch | feed updates slower than expected | stale but “valid” prices |
| Fallback asymmetry | one path checks liveness, another does not | bypassed safety control |
| Grace-period omission | protocol resumes immediately | bot-dominant liquidation race |
Defensive architecture#
flowchart TD
R[Read oracle] --> S[Check sequencer uptime]
S -->|down| P[Pause sensitive ops]
S -->|up| G[Enforce grace period]
G --> V[Validate freshness + deviation bounds]
V --> X[Proceed with state transition]
Solidity guard example#
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
interface ISequencerFeed {
function latestRoundData() external view returns (
uint80, int256, uint256, uint256 updatedAt, uint80
);
}
contract OracleGuard {
ISequencerFeed public immutable sequencer;
uint256 public immutable gracePeriod;
error SequencerDown();
error GracePeriodNotOver();
constructor(address _sequencer, uint256 _grace) {
sequencer = ISequencerFeed(_sequencer);
gracePeriod = _grace;
}
function _assertSequencerHealthy() internal view {
(, int256 answer,, uint256 updatedAt,) = sequencer.latestRoundData();
if (answer != 0) revert SequencerDown();
if (block.timestamp - updatedAt <= gracePeriod) revert GracePeriodNotOver();
}
}
Liquidation fairness model#
Protocols often focus on solvency and ignore fairness.
Both matter.
- Solvency: system remains overcollateralized.
- Fairness: users can reasonably repair positions after outages.
Design levers:
- cooldown after sequencer recovery.
- capped liquidation throughput in first recovery window.
- repay-first priority during recovery.
Test matrix#
| Test | Expected behavior |
|---|---|
| sequencer down + liquidation attempt | revert |
| sequencer up + within grace | revert |
| stale primary feed + healthy fallback | reject stale primary |
| mixed-path call graph | all paths enforce same guard |
Audit questions to ask immediately#
- Is sequencer liveness checked everywhere prices are consumed?
- Is recovery grace period present and non-zero?
- Can owner/admin paths bypass liveness checks?
- Are stale-but-bounded prices still dangerous for this product?