code-423n4 / 2022-01-yield-findings

1 stars 0 forks source link

Chainlink's `latestRoundData` might return stale results #104

Closed code423n4 closed 2 years ago

code423n4 commented 2 years ago

Handle

WatchPug

Vulnerability details

https://github.com/code-423n4/2022-01-yield/blob/e946f40239b33812e54fafc700eb2298df1a2579/contracts/Cvx3CrvOracle.sol#L110-L127

    function _peek(
        bytes6 base,
        bytes6 quote,
        uint256 baseAmount
    ) private view returns (uint256 quoteAmount, uint256 updateTime) {
        require(
            (base == ethId && quote == cvx3CrvId) ||
                (base == cvx3CrvId && quote == ethId),
            "Invalid quote or base"
        );
        (, int256 daiPrice, , , ) = DAI.latestRoundData();
        (, int256 usdcPrice, , , ) = USDC.latestRoundData();
        (, int256 usdtPrice, , , ) = USDT.latestRoundData();

        require(
            daiPrice > 0 && usdcPrice > 0 && usdtPrice > 0,
            "Chainlink pricefeed reporting 0"
        );

On Cvx3CrvOracle.sol, we are using Chainlink's latestRoundData API, but there is no check if the return value indicates stale data. This could lead to stale prices according to the Chainlink documentation:

See also: https://github.com/yieldprotocol/vault-v2/blob/master/contracts/oracles/chainlink/ChainlinkMultiOracle.sol#L88-L90

Recommendation

Consider adding missing checks for stale data.

For example:

(uint80 daiRoundID, int256 daiPrice, , uint256 daiTimestamp, uint80 daiAnsweredInRound) = DAI.latestRoundData();
require(daiPrice > 0, "DAI: Chainlink price <= 0"); 
require(daiAnsweredInRound >= daiRoundID, "DAI: Stale price");
require(daiTimestamp != 0, "DAI: Round not complete");

(uint80 usdcRoundID, int256 usdcPrice, , uint256 usdcTimestamp, uint80 usdcAnsweredInRound) = USDC.latestRoundData();
require(usdcPrice > 0, "USDC: Chainlink price <= 0"); 
require(usdcAnsweredInRound >= usdcRoundID, "USDC: Stale price");
require(usdcTimestamp != 0, "USDC: Round not complete");

(uint80 usdtRoundID, int256 usdtPrice, , uint256 usdtTimestamp, uint80 usdtAnsweredInRound) = USDT.latestRoundData();
require(usdtPrice > 0, "USDT: Chainlink price <= 0"); 
require(usdtAnsweredInRound >= usdtRoundID, "USDT: Stale price");
require(usdtTimestamp != 0, "USDT:Round not complete");
iamsahu commented 2 years ago

136 And the suggested solution would lead to a stack too deep error.