Closed c4-bot-7 closed 8 months ago
https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L37-L46
MagicLPAggregator will return wrong price for tokens less than 18 decimals.
MagicLPAggregator
The MagicLPAggregator intends to return price of a LP in terms of USD in 18 decimals.
MagicLpAggregator.sol#L29-L31
function decimals() external pure override returns (uint8) { return 18; }
But let's take a look at the following function. MagicLpAggregator.sol#L37-L46
function latestAnswer() public view override returns (int256) { uint256 baseAnswerNomalized = uint256(baseOracle.latestAnswer()) * (10 ** (WAD - baseOracle.decimals())); uint256 quoteAnswerNormalized = uint256(quoteOracle.latestAnswer()) * (10 ** (WAD - quoteOracle.decimals())); uint256 minAnswer = baseAnswerNomalized < quoteAnswerNormalized ? baseAnswerNomalized : quoteAnswerNormalized; (uint256 baseReserve, uint256 quoteReserve) = _getReserves(); baseReserve = baseReserve * (10 ** (WAD - baseDecimals)); quoteReserve = quoteReserve * (10 ** (WAD - quoteDecimals)); return int256(minAnswer * (baseReserve + quoteReserve) / pair.totalSupply()); }
Let us break down the above function, it first:
baseOracle
quoteOracle
minAnswer
baseReserve + quoteReserve
minAnswer * (baseReserve + quoteReserve) / pair.totalSupply()
But if you look at the above closely, you will notice that although the function seems correct it outputs the price in the wrong decimals (not 18):
We know that minAnswer will always be the normalized oracle price to 18 decimals
(baseReserve + quoteReserve) will always be the normalized token prices to 18 decimals
(baseReserve + quoteReserve)
However, pair.totalSupply() is not normalized, and depends on the decimals of the base token!
pair.totalSupply()
MagicLP.sol#L163-L165
function decimals() public view override returns (uint8) { return IERC20Metadata(_BASE_TOKEN_).decimals(); }
Therefore if IERC20Metadata(_BASE_TOKEN_).decimals() is not 18 decimals, let us say 6 decimals (ie. USDT token)
IERC20Metadata(_BASE_TOKEN_).decimals()
Than the decimals for the final output isn't correct as 18 + 18 - 6 = 30 decimals which is 10^12 times the intended price we wanted to return.
Manual Review
Normalize pair.totalSupply() to 18 decimals so that the final output is 18 + 18 - 18 = 18 decimals.
Oracle
duplicate #191
141345 marked the issue as duplicate of #64
thereksfour marked the issue as duplicate of #90
thereksfour marked the issue as satisfactory
thereksfour changed the severity to 2 (Med Risk)
Lines of code
https://github.com/code-423n4/2024-03-abracadabra-money/blob/main/src/oracles/aggregators/MagicLpAggregator.sol#L37-L46
Vulnerability details
Impact
MagicLPAggregator
will return wrong price for tokens less than 18 decimals.The
MagicLPAggregator
intends to return price of a LP in terms of USD in 18 decimals.MagicLpAggregator.sol#L29-L31
But let's take a look at the following function. MagicLpAggregator.sol#L37-L46
Let us break down the above function, it first:
baseOracle
quoteOracle
minAnswer
baseReserve + quoteReserve
of the MagicLPminAnswer * (baseReserve + quoteReserve) / pair.totalSupply()
But if you look at the above closely, you will notice that although the function seems correct it outputs the price in the wrong decimals (not 18):
We know that
minAnswer
will always be the normalized oracle price to 18 decimals(baseReserve + quoteReserve)
will always be the normalized token prices to 18 decimalsHowever,
pair.totalSupply()
is not normalized, and depends on the decimals of the base token!MagicLP.sol#L163-L165
Therefore if
IERC20Metadata(_BASE_TOKEN_).decimals()
is not 18 decimals, let us say 6 decimals (ie. USDT token)Than the decimals for the final output isn't correct as 18 + 18 - 6 = 30 decimals which is 10^12 times the intended price we wanted to return.
Tools Used
Manual Review
Recommended Mitigation Steps
Normalize
pair.totalSupply()
to 18 decimals so that the final output is 18 + 18 - 18 = 18 decimals.Assessed type
Oracle