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?