31383 - [SC - Low] price feeds sanity checks isnt correct in funct...

Submitted on May 17th 2024 at 20:40:50 UTC by @jasonxiale for Boost | Alchemix

Report ID: #31383

Report type: Smart Contract

Report severity: Low

Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/RewardsDistributor.sol

Impacts:

  • Protocol insolvency

Description

Brief/Intro

The timestamp of the price update (priceTimestamp) is checked to be less than 60 days in the past, however the oracle's heartbeat is 24 hours. Hence, any price older than the heartbeat might actually be stale

Vulnerability Details

In RewardsDistributor.amountToCompound, the function use require(block.timestamp - priceTimestamp < staleThreshold, "Price is stale"); to check for stale price, which is not correct.

116     function amountToCompound(uint256 _alcxAmount) public view returns (uint256, uint256[] memory) {
117         // Increased for testing since tests go into future
118         uint256 staleThreshold = 60 days;
119 
120         (uint80 roundId, int256 alcxEthPrice, , uint256 priceTimestamp, uint80 answeredInRound) = priceFeed
121             .latestRoundData();
122 
123         require(answeredInRound >= roundId, "Stale price");
124         require(block.timestamp - priceTimestamp < staleThreshold, "Price is stale"); <<<--- Here the function checks stale price using 60 days
125         require(alcxEthPrice > 0, "Chainlink answer reporting 0");
126 
127         uint256[] memory normalizedWeights = IManagedPool(address(balancerPool)).getNormalizedWeights();
128 
129         uint256 amount = (((_alcxAmount * uint256(alcxEthPrice)) / 1 ether) * normalizedWeights[0]) /
130             normalizedWeights[1];
131 
132         return (amount, normalizedWeights);
133     }

Impact Details

The timestamp of the price update (priceTimestamp) is checked to be less than 60 days in the past, however the oracle's heartbeat is 24 hours. Hence, any price older than the heartbeat might actually be stale.

References

Add any relevant links to documentation or code

Proof of Concept

In the follow code, I will demo that RewardsDistributor.amountToCompound can use stale price. And because RewardsDistributor.amountToCompound is used by RewardsDistributor.claim in RewardsDistributor.sol#L175, so the stale price might impact the transaction.

Add the following code in src/test/Minter.t.sol and run

For the output above, we can see that even after 50 days, function RewardsDistributor.amountToCompound doesn't revert, which isn't right.

Last updated

Was this helpful?