First Flight #21: KittyFi

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

[H-3] `meowintKittyCoin` or `purrgeBadPawsition` will always cause issue/fail for tokens without 18 decimals

Description:

In case of tokens with different number of decimals than 18 (the decimals for KittyCoin), both meowintKittyCoin and purrgeBadPawsition will fail as they are dependent of _hasEnoughMeowllateral.

The issue is htat _hasEnoughMeowllateral checks fetches getUserMeowllateralInEuros(user) which will return the value in terms of the number of decimals of the token.

Based on this value, the contract checks the collateralRequiredInEuros by upscaliing it with required collateral percent.

Lastly it compares totalCollateralInEuros >= collateralRequiredInEuros; which will fail in case of tokens with different number of decimals.

Impact:

In case of tokens like USDC, WETH which have 6 and 8 decimals respectively, this will always fail thus causing users fund to be stuck in the contract.

Since users deposit collateral first using depawsitMeowllateral and then only they are allowed to mint tokens using meowintKittyCoin() which is defined as:

function meowintKittyCoin(uint256 _ameownt) external {
kittyCoinMeownted[msg.sender] += _ameownt;
i_kittyCoin.mint(msg.sender, _ameownt);
require(_hasEnoughMeowllateral(msg.sender), KittyPool__NotEnoughMeowllateralPurrrr());
}

This will always revert and the users deposited funds will be stuck!

Morever, this can lead to weird scenarios where despite having enough collateral, users can end up in having their positions purged.

function purrgeBadPawsition(address _user) external returns (uint256 _totalAmountReceived) {
// this condition might also turn to be true in case of enough collaterals in case
// of usdc or weth
require(!(_hasEnoughMeowllateral(_user)), KittyPool__UserIsPurrfect());

Proof of Concept:

Add the following code in KittyFiTest.t.sol

modifier userDepositsBTCCollateral(){
deal(config.wbtc, user, 10e8); // wbtc has 8 decimals
uint256 wbtcToDeposit = 5e8;
// create btc vault:
vm.prank(meowntainer);
kittyPool.meownufactureKittyVault(config.wbtc, config.btcUsdPriceFeed);
wbtcVault = KittyVault(kittyPool.getTokenToVault(config.wbtc));
// user deposits 5 btc to vault as collateral
vm.startPrank(user);
IERC20(config.wbtc).approve(address(wbtcVault), wbtcToDeposit);
kittyPool.depawsitMeowllateral(config.wbtc, wbtcToDeposit);
vm.stopPrank();
_;
}
function test_failureInMintingWithBTC() public userDepositsBTCCollateral{
uint256 btcDeposited = 5e8;
// check for BTC price in USD
AggregatorV3Interface btcUsdPriceFeed = AggregatorV3Interface(config.btcUsdPriceFeed);
(, int256 btcUSDPrice, , , ) = btcUsdPriceFeed.latestRoundData();
console.log("btc price in oracle", uint256(btcUSDPrice));
// fetch EURO in terms of USD
AggregatorV3Interface euroPriceFeed = AggregatorV3Interface(config.euroPriceFeed);
(, int256 euroPrice, , , ) = euroPriceFeed.latestRoundData();
console.log("euroPrice", uint256(euroPrice));
// expected value of KittyCoin to be minted
uint256 expectedKittyCoinToBeMinted = btcDeposited.mulDiv(uint256(btcUSDPrice), uint256(euroPrice));
console.log("expectedKittyCoinToBeMinted in EUR with 8 decimals", expectedKittyCoinToBeMinted);
uint256 mintingArgument = expectedKittyCoinToBeMinted * 1e10; // as kittycoin has 18 decimals
vm.startPrank(user);
kittyPool.meowintKittyCoin(mintingArgument);
}
function test_failureInMintingWithBTC() public userDepositsBTCCollateral{
uint256 btcDeposited = 5e8;
// check for BTC price in USD
AggregatorV3Interface btcUsdPriceFeed = AggregatorV3Interface(config.btcUsdPriceFeed);
(, int256 btcUSDPrice, , , ) = btcUsdPriceFeed.latestRoundData();
console.log("btc price in oracle", uint256(btcUSDPrice));
// fetch EURO in terms of USD
AggregatorV3Interface euroPriceFeed = AggregatorV3Interface(config.euroPriceFeed);
(, int256 euroPrice, , , ) = euroPriceFeed.latestRoundData();
console.log("euroPrice", uint256(euroPrice));
// expected value of KittyCoin to be minted
uint256 expectedKittyCoinToBeMinted = btcDeposited.mulDiv(uint256(btcUSDPrice), uint256(euroPrice));
console.log("expectedKittyCoinToBeMinted in EUR with 8 decimals", expectedKittyCoinToBeMinted);
uint256 mintingArgument = expectedKittyCoinToBeMinted * 1e10; // as kittycoin has 18 decimals
vm.startPrank(user);
vm.expectRevert();
kittyPool.meowintKittyCoin(mintingArgument);
}

Console Log:

btc price in oracle 5739293200000
euroPrice 109264000
expectedKittyCoinToBeMinted in EUR with 8 decimals 26263422536242

Recommended Mitigation:

  1. Ensure that inside _hasEnoughMeowllateral the call to getUserMeowllateralInEuros(user) always returns fixed decimals and then handle the number accordingly

Updates

Lead Judging Commences

shikhar229169 Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`getUserVaultMeowllateralInEuros` doesn't considers the collateral decimals, instead uses constant precision which works only for 18 decimals

Support

FAQs

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