L2 Rollup Risks Remediation
Overview
Related Detector: L2 Rollup Risks
Contracts ported to Optimism, Arbitrum, Base, zkSync, and similar rollups inherit assumptions that do not hold on L2: block time and block.number behave differently, a price feed can go stale if the sequencer is down, and cross-domain messages must be authenticated against the bridge. The remediation is to make these differences explicit rather than assuming L1 semantics.
Recommended Fix
// 1. Check the sequencer uptime feed before trusting an L2 oracle price.
(, int256 up, uint256 startedAt, , ) = sequencerUptimeFeed.latestRoundData();
require(up == 0, "Sequencer down");
require(block.timestamp - startedAt > GRACE_PERIOD, "Grace period");
// 2. Authenticate cross-domain messages against the messenger and remote sender.
require(msg.sender == address(l2Messenger), "Not messenger");
require(l2Messenger.xDomainMessageSender() == trustedL1Sender, "Bad L1 sender");
// 3. Do not use block.number for wall-clock time on L2; use block.timestamp,
// and understand each chain's timing semantics before relying on either.
Read the specific rollup’s documentation for its block-property and messaging semantics; they differ between stacks.
Common Mistakes
- Reading an L2 price feed without checking the sequencer uptime feed, accepting a stale price during downtime.
- Trusting
xDomainMessageSender()without also confirmingmsg.senderis the canonical messenger. - Using
block.numberas a timer on a chain where its cadence differs from L1.