First Flight #21: KittyFi

First Flight #21
Beginner FriendlyDeFiFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

[H-1] Wrong calculation in `getTotalMeowllateralInAave()` leads to wrong user shares estimation.

Description:

The getTotalMeowllateralInAave will always return values in 8 decimals, which is wrong.

As getTotalMeowllateral() is defined as:

function getTotalMeowllateral() public view returns (uint256) {
// totalMeowllateralInVault -> token decimals, getTotalMeowllateralInAave - > 8 decimals
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();
//@audit High -> WRONG CALCULATION
/**
* 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; // the amount deposited into vault by user
// move some value of users meowllateral to Aave for getTotalMeowllateralInAave
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://mainnet.infura.io/v3/<your_token> -vvv --mt test_IncorrectUserMeowllateral

Console Log

totalMeowlateralInVault 4000000000000000000
totalMeowlateralInAave 100000000
actualTotalMeawllateral 4000000000100000000 // this is 4e18 + 1e8 rather than 5e18

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));
}
Updates

Lead Judging Commences

shikhar229169 Lead Judge
about 1 year ago
shikhar229169 Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`getTotalMeowllateralInAave` always returns price in 8 decimals for every collateral token

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.