sherlock-audit / 2023-06-tokensoft-judging

4 stars 4 forks source link

lemonmon - Missing price checks for Chainlink oracle #198

Closed sherlock-admin2 closed 1 year ago

sherlock-admin2 commented 1 year ago

lemonmon

medium

Missing price checks for Chainlink oracle

Summary

When fetching the oracle price there are no checks for the built in circuit breakers from the Chainlink aggregator for the minimum price in PriceTierVesting._getOraclePrice() which is used from PriceTierVestingSale_2_0.getClaimableAmount(). The circuit breaker should be used to check if the price of an asset goes outside of a predetermined price band.

If an asset suffers a huge drop in value (i.e. LUNA crash), the chainlink oracle will still return the minPrice instead of the actual price of the asset, and the protocol can go into debt or suffer losses, see Venus on BSC when LUNA imploded.

Vulnerability Detail

PriceTierVestingSale_2_0.claim() calls PriceTierVestingSale_2_0.getClaimableAmount() which uses PriceTierVesting.getVestedFraction() which calls PriceTierVesting._getOraclePrice() to determine the price which is used to determine the vested fraction.

PriceTierVesting._getOraclePrice() is missing necessary checks via the built in circuit breaks to determine whether the price of an asset went outside of the bounds of min/max prices.

Impact

The protocol can potentially suffer from a wrong price fetched from the Chainlink oracle, where the fetched price potentially is higher than the actual price of the asset, resulting in calculating claimable amounts higher than they should be, resulting in a loss.

Code Snippet

https://github.com/sherlock-audit/2023-06-tokensoft/blob/main/contracts/contracts/claim/abstract/PriceTierVesting.sol#L30-L45

https://github.com/sherlock-audit/2023-06-tokensoft/blob/main/contracts/contracts/claim/abstract/PriceTierVesting.sol#L84-L91

https://github.com/sherlock-audit/2023-06-tokensoft/blob/main/contracts/contracts/claim/PriceTierVestingSale_2_0.sol#L128

https://github.com/sherlock-audit/2023-06-tokensoft/blob/main/contracts/contracts/claim/PriceTierVestingSale_2_0.sol#L100

Tool used

Manual Review

Recommendation

Consider checking the returned answer from the Chainlink oracle against the min/max price of the built in circuit breakers and revert if the answer is outside of the bounds:

// PriceTierVesting.sol
30  function _getOraclePrice() private view returns (uint256) {
31      (
32          uint80 roundID,
33          int256 _price,
34          uint256 startedAt,
35          uint256 timeStamp,
36          uint80 answeredInRound
37      ) = oracle.latestRoundData();
38
39 +    if (_price >= maxPrice || _price <= minPrice) { revert(); }

See Chainlink API docs on minAnswer, maxAnswer that provide the prices for minPrice and maxPrice referenced above.