pkqs90 - `LenderCommitmentGroup_Smart.sol#_getPriceFromSqrtX96` may overflow
Closed this issue · 0 comments
pkqs90
medium
LenderCommitmentGroup_Smart.sol#_getPriceFromSqrtX96
may overflow
Summary
In LenderCommitmentGroup_Smart.sol
, when calculating the price of tokens, there may be an overflow in _getPriceFromSqrtX96
function.
Vulnerability Detail
The line with overflow issue is uint256 priceX96 = (uint256(_sqrtPriceX96) * uint256(_sqrtPriceX96)) / (2**96);
. Note that _sqrtPriceX96
is equal to the concept in uniswap, which is sqrt(t0/t1) * 2**96
, and it has a uint160
type. The MAX_TICK in uniswap is 887272, which is able to make _sqrtPriceX96
close to uint160.max
. Then, if we perform uint256(_sqrtPriceX96) * uint256(_sqrtPriceX96)
, it would overflow uint256.
UniswapV3#TickMath.sol:
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
int24 internal constant MAX_TICK = -MIN_TICK;
function _getUniswapV3TokenPairPrice(uint32 _twapInterval)
internal
view
returns (uint256)
{
// represents the square root of the price of token1 in terms of token0
uint160 sqrtPriceX96 = getSqrtTwapX96(_twapInterval);
//this output is the price ratio expanded by 1e18
return _getPriceFromSqrtX96(sqrtPriceX96);
}
//this result is expanded by UNISWAP_EXPANSION_FACTOR
function _getPriceFromSqrtX96(uint160 _sqrtPriceX96)
internal
pure
returns (uint256 price_)
{
> uint256 priceX96 = (uint256(_sqrtPriceX96) * uint256(_sqrtPriceX96)) /
> (2**96);
// sqrtPrice is in X96 format so we scale it down to get the price
// Also note that this price is a relative price between the two tokens in the pool
// It's not a USD price
price_ = priceX96;
}
// ---- TWAP
function getSqrtTwapX96(uint32 twapInterval)
public
view
returns (uint160 sqrtPriceX96)
{
if (twapInterval == 0) {
// return the current price if twapInterval == 0
(sqrtPriceX96, , , , , , ) = IUniswapV3Pool(UNISWAP_V3_POOL)
.slot0();
} else {
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = twapInterval; // from (before)
secondsAgos[1] = 0; // to (now)
(int56[] memory tickCumulatives, ) = IUniswapV3Pool(UNISWAP_V3_POOL)
.observe(secondsAgos);
// tick(imprecise as it's an integer) to price
sqrtPriceX96 = TickMath.getSqrtRatioAtTick(
int24(
(tickCumulatives[1] - tickCumulatives[0]) /
int32(twapInterval)
)
);
}
}
Impact
No new loans can be created in LenderCommitmentGroup_Smart.sol
if price is too high.
Code Snippet
Tool used
Manual review.
Recommendation
Handle cases where _sqrtPriceX96
is larger than uint128.max, just like OracleLibrary.sol
did in UniswapV3.
function getQuoteAtTick(
int24 tick,
uint128 baseAmount,
address baseToken,
address quoteToken
) internal pure returns (uint256 quoteAmount) {
uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick);
// Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself
if (sqrtRatioX96 <= type(uint128).max) {
uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96;
quoteAmount = baseToken < quoteToken
? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192)
: FullMath.mulDiv(1 << 192, baseAmount, ratioX192);
} else {
uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64);
quoteAmount = baseToken < quoteToken
? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128)
: FullMath.mulDiv(1 << 128, baseAmount, ratioX128);
}
}
Duplicate of #243