Gravita-Protocol / Gravita-SmartContracts

GNU General Public License v3.0
48 stars 31 forks source link

Inaccurate prices in the event of an L2 sequencer going down #320

Closed saxenism closed 1 year ago

saxenism commented 1 year ago

Severity

High

Reason: Inaccurate prices can lead to loss of funds for the protocol due to excessive $GRAI minting

Affected Smart Contracts

PriceFeed.sol

Description

New attack vectors open up when protocols decide to deploy their contracts on L2. One of them is that the unavailability of sequencers could affect the freshness of the prices obtained from a price feed.

Optimistic rollup protocols transfer all execution from the Layer 1 (L1) Ethereum chain to a Layer 2 (L2) chain, perform the execution on the L2 chain, and then return the results to the L1 chain.

These protocols employ a sequencer to process and aggregate L2 transactions by consolidating multiple transactions into a single one.

If a sequencer becomes unavailable, access to read/write APIs that consumers rely on becomes impossible, causing applications on the L2 network inaccessible for most users, unless they interact directly through the L1 optimistic rollup contracts.

While the L2 chain has not stopped, it would be inequitable to continue offering service on your applications when only a select few can utilize them.

The Gravita price feed contracts do not implement this check even when it is pretty clear that they will be launching on L2s like Arbitrum.

Recommended Mitigation Steps

  1. A straightforward fix exists. Use the sequencer uptime feed to monitor the sequencer's online status and prevent consumption of the price feed when the sequencer is offline.
  2. Here's a sample implementation to give an idea of how to use the sequencer feed:
constructor() {
    sequencerUptimeFeed = AggregatorV2V3Interface(someAddress);
}

function getLatestPrice() public view returns (int) {
    (
        ,
        int256 answer,
        uint256 startedAt,
        ,

    ) = sequencerUptimeFeed.latestRoundData;

    bool isSequencerUp = answer == 0;
    if(!isSequencerUp) {
        revert SequencerDown();
    }

    uint256 timeSinceUp = block.timestamp - startedAt;
    if(timeSinceUp <= GRACE_PERIOD_TIME) {
        revert GracePeriodNotOver();
    }

    (
        ,
        int price,
        ,
        ,

    ) = priceFeed.latestRoundData();

    return price;
}
0xfornax commented 1 year ago

We appreciate the submission but: 1 - the code as is will be deployed on Ethereum mainnet; 2 - the rules are clear that submissions based on incorrect values from oracles are not eligible.