From 6a500d930dde299c030d0ed8274309f91fa93263 Mon Sep 17 00:00:00 2001 From: Zaki Manian Date: Fri, 23 Aug 2024 14:15:35 +0100 Subject: [PATCH] Fixes for the Sequencer downtime oracle to correctly detect invalid rounds --- .../permutations/SequencerPriceRouter.sol | 20 ++++++++++++++----- .../SequencerPriceRouter.t.sol | 14 +++++++++++-- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/modules/price-router/permutations/SequencerPriceRouter.sol b/src/modules/price-router/permutations/SequencerPriceRouter.sol index dd7e1b0c1..aa44a2c4d 100644 --- a/src/modules/price-router/permutations/SequencerPriceRouter.sol +++ b/src/modules/price-router/permutations/SequencerPriceRouter.sol @@ -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 @@ -13,6 +13,7 @@ contract SequencerPriceRouter is PriceRouter { error SequencerPriceRouter__SequencerDown(); error SequencerPriceRouter__GracePeriodNotOver(); + error SequencerPriceRouter__InvalidRound(); //============================== IMMUTABLES =============================== @@ -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; } @@ -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 @@ -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; diff --git a/test/testPriceRouter/SequencerPriceRouter.t.sol b/test/testPriceRouter/SequencerPriceRouter.t.sol index 054ff5e5e..581555425 100644 --- a/test/testPriceRouter/SequencerPriceRouter.t.sol +++ b/test/testPriceRouter/SequencerPriceRouter.t.sol @@ -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. @@ -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; @@ -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; } }