Skip to content

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:

  1. The sequencer goes down. No new L2 blocks are produced.
  2. Chainlink oracle updates stop because node operators cannot get their transactions included.
  3. The last posted price is now frozen onchain.
  4. Markets continue to move on L1 and other venues.
  5. 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.

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

NetworkAddress
Arbitrum One0xFdB631F5EE196F0ed6FAa767959853A9F217697D
Arbitrum Sepolia0x2E96F5E37B01F46f06C2D9c5D09E0003Fa1EC65F
Optimism0x371EAD81c9102C9BF4874A9075FFFf170F2A5aF7
Optimism Sepolia0x4C4DA2e013bFf2A26F1f90cA3c5b0D63e2A0DD1F
Base0xBCF85224fc0756B9Fa45aA7892530B47e10b6b6B
Base Sepolia0xD08F45f40Ed02a04AA04f7c56C77b9c9A0e6dBb5

Full list: docs.chain.link/data-feeds/l2-sequencer-feeds