L2 Sequencer Uptime
On Layer 2 networks, price feeds have a dependency that does not exist on Ethereum mainnet: the sequencer. If the sequencer goes offline, transactions cannot be included in L2 blocks, but the oracle values already posted onchain remain visible and readable. A contract that does not account for this will continue consuming prices as if the network is healthy, even when the market has moved significantly during the outage.
The problem
Consider what happens during a sequencer outage on Arbitrum:
- The sequencer goes down. No new L2 blocks are produced.
- Chainlink oracle updates stop because node operators cannot get their transactions included.
- The last posted price is now frozen onchain.
- Markets continue to move on L1 and other venues.
- The sequencer comes back up. A backlog of pending transactions floods in.
In the window between step 3 and step 5, the onchain price can diverge significantly from the real market price. Any contract that reads the feed during this window is acting on stale data. Worse, the sequencer restart creates a front-running opportunity: sophisticated actors know the backlog will push prices in a particular direction, and they can structure their L2 transactions accordingly.
The Chainlink Sequencer Uptime Feed
Chainlink publishes a Sequencer Uptime Feed for each supported L2 network.
This feed reports whether the sequencer is currently up (answer == 0) or
down (answer == 1), and when it last changed state via startedAt.
Implementation
uint256 public constant GRACE_PERIOD_TIME = 3600;
function _checkSequencer() internal view { (, int256 answer, uint256 startedAt, , ) = AggregatorV3Interface(sequencerUptimeFeed).latestRoundData();
if (answer == 1) revert SequencerDown(); if (block.timestamp - startedAt < GRACE_PERIOD_TIME) revert GracePeriodNotOver();}answer == 1 check: if the sequencer is currently down, reject
immediately. No price should be consumed while the sequencer is offline.
Grace period check: if the sequencer has recently restarted (startedAt
is recent), reject for GRACE_PERIOD_TIME (one hour). This gives the
backlog time to clear and prices time to stabilize before consumption resumes.
The check runs at the top of getPrice(), before any feed reads:
function getPrice() external view returns (int256) { if (sequencerUptimeFeed != address(0)) _checkSequencer(); // ...}The address(0) guard makes the check opt-in. On L1 deployments,
sequencerUptimeFeed is never set and the check is skipped with no gas
overhead.
Configuring for L2
Set the uptime feed address after deployment:
consumer.setSequencerUptimeFeed(UPTIME_FEED_ADDRESS);Or via the deploy script using the UPTIME_FEED environment variable. See
the Deployment Guide for the full workflow.
Uptime feed addresses
| Network | Address |
|---|---|
| Arbitrum One | 0xFdB631F5EE196F0ed6FAa767959853A9F217697D |
| Arbitrum Sepolia | 0x2E96F5E37B01F46f06C2D9c5D09E0003Fa1EC65F |
| Optimism | 0x371EAD81c9102C9BF4874A9075FFFf170F2A5aF7 |
| Optimism Sepolia | 0x4C4DA2e013bFf2A26F1f90cA3c5b0D63e2A0DD1F |
| Base | 0xBCF85224fc0756B9Fa45aA7892530B47e10b6b6B |
| Base Sepolia | 0xD08F45f40Ed02a04AA04f7c56C77b9c9A0e6dBb5 |