Description:
The getTotalMeowllateralInAave
will always return values in 8 decimals, which is wrong.
As getTotalMeowllateral()
is defined as:
function getTotalMeowllateral() public view returns (uint256) {
return totalMeowllateralInVault + getTotalMeowllateralInAave();
}
As totalMeowllateralInVault
has token decimals where as getTotalMeowllateralInAave
has 8 decimals, this will lead to incorrect values in case of tokens which do not have 8 decimals.
totalCollateralBase
returned by i_aavePool.getUserAccountData()
will always be in BASE_CURRENCY (i.e USD) with 8 decimals.
collateralToUsdPrice
returned via i_priceFeed.latestRoundData()
also has 8 decimals.
PRECISION
has 18 decimals, ref
EXTRA_DECIMALS
has 10 decimal, ref
Thus the returned value via totalCollateralBase.mulDiv(PRECISION, uint256(collateralToUsdPrice) * EXTRA_DECIMALS)
will be:
(totalCollateralBase
* PRECISION
)/(collateralToUsdPrice
* EXTRA_DECIMALS
)
i.e in terms of decimals:
(8 decimals * 18 decimals)/(8 decimals * 10 decimals)
~> 8 decimals
Impact:
Completely wrong value of getTotalMeowllateral()
thus getUserMeowllateral()
will also return wrong values as:
function getUserMeowllateral(address _user) public view returns (uint256) {
uint256 totalMeowllateralOfVault = getTotalMeowllateral();
return userToCattyNip[_user].mulDiv(totalMeowllateralOfVault, totalCattyNip);
}
The user shares are always wrong and the amount of KittyCoins that can be minted or burnt will be wrong!
Proof of Concept:
function getTotalMeowllateralInAave() public view returns (uint256) {
(uint256 totalCollateralBase, , , , , ) = i_aavePool.getUserAccountData(address(this));
(, int256 collateralToUsdPrice, , , ) = i_priceFeed.latestRoundData();
* will fail for any token which does not have 8 decimals.
*
* totalCollateralBase -> value of collateral in USD with 8 Decimals (amt * Xe8)
* PRECISION -> 1e18
* collateralToUsdPrice -> value of collateral in USD with 8 Decimals (Xe8)
* EXTRA_DECIMALS -> 1e10
* Hence,
* returned value -> (amt * Xe8 * 1e18)/(Xe8 * 1e10) -> (amt * 1e18)/(amt * 1e10)
* Returned value will only have 8 decimal base
*/
return totalCollateralBase.mulDiv(PRECISION, uint256(collateralToUsdPrice) * EXTRA_DECIMALS);
}
TEST
Add the following test to KittyFiTest.t.sol
function test_IncorrectUserMeowllateral() public userDepositsCollateral{
uint256 totalDepositedInVault = 5 ether;
uint256 expectedTotalMeowllateral = totalDepositedInVault;
uint256 toSupply = 1 ether;
vm.prank(meowntainer);
wethVault.purrrCollateralToAave(toSupply);
assertEq(wethVault.totalMeowllateralInVault(), totalDepositedInVault - toSupply);
uint256 totalMeowlateralInVault = wethVault.totalMeowllateralInVault();
uint256 totalMeowlateralInAave = wethVault.getTotalMeowllateralInAave();
uint256 actualTotalMeawllateral = wethVault.getTotalMeowllateral();
console.log("totalMeowlateralInVault", totalMeowlateralInVault);
console.log("totalMeowlateralInAave", totalMeowlateralInAave);
console.log("actualTotalMeawllateral", actualTotalMeawllateral);
assertEq(actualTotalMeawllateral, (totalMeowlateralInVault + totalMeowlateralInAave));
assertNotEq(actualTotalMeawllateral, expectedTotalMeowllateral);
}
run the test via command:
forge test --via-ir --fork-url https:
Console Log
totalMeowlateralInVault 4000000000000000000
totalMeowlateralInAave 100000000
actualTotalMeawllateral 4000000000100000000
Recommended Mitigation:
Change getTotalMeowllateral
to use token decimals instead
function getTotalMeowllateralInAave() public view returns (uint256) {
(uint256 totalCollateralBase, , , , , ) = i_aavePool.getUserAccountData(address(this));
(, int256 collateralToUsdPrice, , , ) = i_priceFeed.latestRoundData();
- return totalCollateralBase.mulDiv(PRECISION, uint256(collateralToUsdPrice) * EXTRA_DECIMALS);
+ uint256 token_decimals = ERC20(i_token).decimals()
+ return totalCollateralBase.mulDiv(10 ** token_decimals, uint256(collateralToUsdPrice));
}