Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for the Sequencer downtime oracle to correctly detect invalid rounds #195

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/modules/price-router/permutations/SequencerPriceRouter.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.21;

import {PriceRouter, Registry, ERC20, IChainlinkAggregator} from "src/modules/price-router/PriceRouter.sol";
import { PriceRouter, Registry, ERC20, IChainlinkAggregator } from "src/modules/price-router/PriceRouter.sol";

/**
* @title SequencerPriceRouter
Expand All @@ -13,6 +13,7 @@ contract SequencerPriceRouter is PriceRouter {

error SequencerPriceRouter__SequencerDown();
error SequencerPriceRouter__GracePeriodNotOver();
error SequencerPriceRouter__InvalidRound();

//============================== IMMUTABLES ===============================

Expand All @@ -27,9 +28,13 @@ contract SequencerPriceRouter is PriceRouter {
*/
uint256 internal immutable gracePeriod;

constructor(address _sequencerUptimeFeed, uint256 _gracePeriod, address newOwner, Registry _registry, ERC20 _weth)
PriceRouter(newOwner, _registry, _weth)
{
constructor(
address _sequencerUptimeFeed,
uint256 _gracePeriod,
address newOwner,
Registry _registry,
ERC20 _weth
) PriceRouter(newOwner, _registry, _weth) {
sequencerUptimeFeed = IChainlinkAggregator(_sequencerUptimeFeed);
gracePeriod = _gracePeriod;
}
Expand All @@ -41,7 +46,7 @@ contract SequencerPriceRouter is PriceRouter {
* to price assets, as the datafeeds could be stale, and need to be updated.
*/
function _runPreFlightCheck() internal view override {
(, int256 answer, uint256 startedAt,,) = sequencerUptimeFeed.latestRoundData();
(, int256 answer, uint256 startedAt, , ) = sequencerUptimeFeed.latestRoundData();

// This check should make TXs from L1 to L2 revert if someone tried interacting with the cellar while the sequencer is down.
// Answer == 0: Sequencer is up
Expand All @@ -50,6 +55,11 @@ contract SequencerPriceRouter is PriceRouter {
revert SequencerPriceRouter__SequencerDown();
}

// Detect invalid round in the oracle.
if (startedAt == 0) {
revert SequencerPriceRouter__InvalidRound();
}

// Make sure the grace period has passed after the
// sequencer is back up.
uint256 timeSinceUp = block.timestamp - startedAt;
Expand Down
14 changes: 12 additions & 2 deletions test/testPriceRouter/SequencerPriceRouter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract SequencerPriceRouterTest is Test {

// Variables so this contract can act as a mock sequencer uptime fee.
int256 mockAnswer = type(int256).max;
uint256 mockStartedAt = 0;
uint256 mockStartedAt = 1;

function setUp() external {
// Setup forked environment.
Expand All @@ -45,6 +45,16 @@ contract SequencerPriceRouterTest is Test {
// Sequencer is currently up so pricing succeeds.
sequencerPriceRouter.getPriceInUSD(USDC);

//if the sequencer is oracle experiences an invalid round
mockAnswer = 0;
mockStartedAt = 0;

// Pricing calls revert.
vm.expectRevert(
bytes(abi.encodeWithSelector(SequencerPriceRouter.SequencerPriceRouter__InvalidRound.selector))
);
sequencerPriceRouter.getPriceInUSD(USDC);

// But if sequencer goes down.
mockAnswer = 1;
mockStartedAt = block.timestamp - 1;
Expand Down Expand Up @@ -78,6 +88,6 @@ contract SequencerPriceRouterTest is Test {
(roundID, answer, startedAt, updatedAt, answeredInRound) = IChainlinkAggregator(arbitrumSequencerUptimeFeed)
.latestRoundData();
if (mockAnswer != type(int256).max) answer = mockAnswer;
if (mockStartedAt != 0) startedAt = mockStartedAt;
startedAt = mockStartedAt;
}
}
Loading