sherlock-audit / 2023-05-perennial-judging

12 stars 9 forks source link

Madalad - Chainlink aggregators return the incorrect price if it drops below `minAnswer` #116

Closed sherlock-admin closed 1 year ago

sherlock-admin commented 1 year ago

Madalad

medium

Chainlink aggregators return the incorrect price if it drops below minAnswer

Summary

Chainlink aggregators return the incorrect price if it drops below minAnswer, which can lead to inaccurate prices being accepted by the protocol.

Vulnerability Detail

Chainlink aggregators have a built in circuit breaker if the price of an asset goes outside of a predetermined price band. The result is that if an asset experiences a huge drop in value (i.e. LUNA crash) the price of the oracle will continue to return the minAnswer instead of the actual price of the asset. See Chainlink's docs for more info.

Chainlink's latestRoundData pulls the associated aggregator and requests round data from it. ChainlinkAggregators have minAnswer and maxAnswer circuit breakers built into them. This means that if the price of the asset drops below the minAnswer, the protocol will continue to value the token at minAnswer instead of it's actual value. This will allow users to exploit certain parts of the protocol.

Impact

A discrepency between an assets actual price and its oracle price could cause major issues within the protocol and potentially lead to loss of funds throughout the protocol. This is exactly what happened to Venus on BSC when LUNA imploded.

Code Snippet

File: ../perennial-mono/packages/perennial-oracle/contracts/types/ChainlinkRegistry.sol

34:    function getLatestRound(ChainlinkRegistry self, address base, address quote) internal view returns (ChainlinkRound memory) {
35:        (uint80 roundId, int256 answer, , uint256 updatedAt, ) =
36:            FeedRegistryInterface(ChainlinkRegistry.unwrap(self)).latestRoundData(base, quote);
37:        return ChainlinkRound({roundId: roundId, timestamp: updatedAt, answer: answer});
38:    }

https://github.com/equilibria-xyz/perennial-mono/blob/b06d5145db62a312dd88dfcafef0f8e2166c5e32/packages/perennial-oracle/contracts/types/ChainlinkRegistry.sol#L34-L38

File: ../perennial-mono/packages/perennial-oracle/contracts/types/ChainlinkAggregator.sol

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

https://github.com/equilibria-xyz/perennial-mono/blob/b06d5145db62a312dd88dfcafef0f8e2166c5e32/packages/perennial-oracle/contracts/types/ChainlinkAggregator.sol#L32-L36

Tool used

Manual Review

Recommendation

Pull minAnswer from the aggregator and revert it price == minAnswer

Duplicate of #180