sherlock-audit / 2023-05-perennial-judging

12 stars 9 forks source link

Jaraxxus - Return values from Chainlink's latestRoundData is not checked properly #201

Closed sherlock-admin closed 1 year ago

sherlock-admin commented 1 year ago

Jaraxxus

medium

Return values from Chainlink's latestRoundData is not checked properly

Summary

Chainlink's latestrounddata() is used but there is no check if the return value indicates stale data. This could lead to stale prices according to the Chainlink documentation:

https://docs.chain.link/docs/historical-price-data/#historical-rounds

Vulnerability Detail

The protocol uses chainlink to query the result to get the latest price. However, there is no check if the return value indicates stale data.

    function getLatestRound(ChainlinkAggregator self) internal view returns (ChainlinkRound memory) {
        (uint80 roundId, int256 answer, , uint256 updatedAt, ) =
            AggregatorProxyInterface(ChainlinkAggregator.unwrap(self)).latestRoundData();
        return ChainlinkRound({roundId: roundId, timestamp: updatedAt, answer: answer});
    }

    /**
     * @notice Returns a specific round's data for a specific feed
     * @param self Chainlink Feed Aggregator to operate on
     * @param roundId The specific round to fetch data for
     * @return Specific round's data
     */
    function getRound(ChainlinkAggregator self, uint256 roundId) internal view returns (ChainlinkRound memory) {
        (, int256 answer, , uint256 updatedAt, ) =
            AggregatorProxyInterface(ChainlinkAggregator.unwrap(self)).getRoundData(uint80(roundId));
        return ChainlinkRound({roundId: roundId, timestamp: updatedAt, answer: answer});
    }

Impact

Stale price may be queried.

Code Snippet

https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial-oracle/contracts/types/ChainlinkAggregator.sol#L32-L48

Tool used

Manual Review

Recommendation

Check roundId and roundCompleteness to see whether the data is stale. Also, check whether updatedAt is checked accordingly.

require(answeredInRound >= roundId, "Price stale");
require(block.timestamp - updatedAt < PRICE_ORACLE_STALE_THRESHOLD, "Price round incomplete");