Platform: Code4rena
Start Date: 19/04/2022
Pot Size: $30,000 USDC
Total HM: 10
Participants: 43
Period: 3 days
Judges: moose-code, JasoonS
Total Solo HM: 7
Id: 90
League: ETH
Rank: 1/43
Findings: 2
Award: $6,765.07
🌟 Selected for report: 1
🚀 Solo Findings: 1
🌟 Selected for report: WatchPug
6743.0159 USDC - $6,743.02
(uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) = address(pair).currentCumulativePrices();
Because the Solidity version used by the current implementation of UniswapV2OracleLibrary.sol
is >=0.8.7
, and there are some breaking changes in Solidity v0.8.0:
Arithmetic operations revert on underflow and overflow.
Ref: https://docs.soliditylang.org/en/v0.8.13/080-breaking-changes.html#silent-changes-of-the-semantics
While in UniswapV2OracleLibrary.sol
, subtraction overflow is desired at blockTimestamp - blockTimestampLast
in currentCumulativePrices()
:
if (blockTimestampLast != blockTimestamp) { // subtraction overflow is desired uint32 timeElapsed = blockTimestamp - blockTimestampLast; // addition overflow is desired // counterfactual price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; // counterfactual price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; }
In another word, Uniswap/v2-periphery/contracts/libraries/UniswapV2OracleLibrary
only works at solidity < 0.8.0
.
As a result, when price0Cumulative
or price1Cumulative
is big enough, currentCumulativePrices
will revert due to overflow.
Since the overflow is desired in the original version, and it's broken because of using Solidity version >0.8. The UniswapV2PriceOracle
contract will break when the desired overflow happens, and further breaks other parts of the system that relies on UniswapV2PriceOracle
.
Note: this recommended fix requires a fork of the library contract provided by Uniswap.
Change to:
if (blockTimestampLast != blockTimestamp) { unchecked { // subtraction overflow is desired uint32 timeElapsed = blockTimestamp - blockTimestampLast; // addition overflow is desired // counterfactual price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed; // counterfactual price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed; } }
22.0499 USDC - $22.05
(, int basePrice, , , ) = baseAggregator.latestRoundData();
On ChainlinkPriceOracle.sol
, we are using latestRoundData
, but there is no check if the return value indicates stale data. This could lead to stale prices according to the Chainlink documentation:
Consider adding missing checks for stale data.
For example:
(uint80 roundID, int256 basePrice, , uint256 timestamp, uint80 answeredInRound) = baseAggregator.latestRoundData(); require(basePrice > 0, "Chainlink price <= 0"); require(answeredInRound >= roundID, "Stale price"); require(timestamp != 0, "Round not complete");
#0 - olivermehr
2022-05-02T20:26:00Z
duplicate of issue #1