First Flight #21: KittyFi

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

Calls to Oracles don't check for stale prices

Summary

Get the collateralPrice or euroPrice should check the price are not the stale prices, otherwise will destroy the protocols' feature.

Vulnerability Details

Below functions don't check the prices are the latest updated prices.

/**
* @notice Gets the user's collateral for this vault in euros
* @param _user The user for which the collateral is calculated
*/
function getUserVaultMeowllateralInEuros(address _user) external view returns (uint256) {
(, int256 collateralToUsdPrice, , , ) = i_priceFeed.latestRoundData();
(, int256 euroPriceFeedAns, , ,) = i_euroPriceFeed.latestRoundData();
uint256 collateralAns = getUserMeowllateral(_user).mulDiv(uint256(collateralToUsdPrice) * EXTRA_DECIMALS, PRECISION);
return collateralAns.mulDiv(uint256(euroPriceFeedAns) * EXTRA_DECIMALS, PRECISION);
}
/**
* @notice Gets the total sum of collateral deposited in Aave and the collateral earned by interest from Aave
*/
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);
}

Impact

KittyFi guarantee the COLLATERAL_PERCENT by below funciton, which based on the collateral and Euro's price. but if the prices are not the latest prices, That can leads to the user's COLLATERAL_PERCENT based on the wrong price either higer or lower than the required COLLATERAL_PERCENT. The formar situation:user can mint more kittyCoins, the latter situation: user forced to liquidated.

/**
* @notice Checks if the user has enough Meowllateral
* @param _user address of the user
* @return hasEnoughCollateral true if user has enough Meowllateral
*/
function _hasEnoughMeowllateral(address _user) internal view returns (bool hasEnoughCollateral) {
uint256 totalCollateralInEuros = getUserMeowllateralInEuros(_user);
uint256 collateralRequiredInEuros = kittyCoinMeownted[_user].mulDiv(COLLATERAL_PERCENT, COLLATERAL_PRECISION);
return totalCollateralInEuros >= collateralRequiredInEuros;
}

Tools Used

Manual

Recommendations

According to chainlink pricefeed requirement. Set the corrospending's HEARTBEAT, to make sure the prices are the latest prices.

https://docs.chain.link/data-feeds/price-feeds/addresses/?network=ethereum&page=1

For places applying oracle price services, all add the validaitons.

Here, I just take ETH as example, when apply more other different collaterals, can do the related adjustments.

// KittyVault.sol
error KittyVault__ETHStalePriceFeed();
error KittyVault__EURStalePriceFeed();
uint256 private constant EUROPRICEFEED_HEARTBEAT = 86400; // 1 day
uint256 private constant ETHPRICEFEED_HEARTBEAT = 3600; // 1 hour
function getUserVaultMeowllateralInEuros(
address _user
) external view returns (uint256) {
(
,
int256 collateralToUsdPrice,
,
uint256 collateralUpdatedAt,
) = i_priceFeed.latestRoundData();
require(
collateralUpdatedAt >= block.timestamp - ETHPRICEFEED_HEARTBEAT,
KittyVault__ETHStalePriceFeed()
);
(, int256 euroPriceFeedAns, , uint256 euroUpdatedAt, ) = i_euroPriceFeed
.latestRoundData();
require(
euroUpdatedAt >= block.timestamp - EUROPRICEFEED_HEARTBEAT,
KittyVault__EURStalePriceFeed()
);
uint256 collateralAns = getUserMeowllateral(_user).mulDiv(
uint256(collateralToUsdPrice) * EXTRA_DECIMALS,
PRECISION
);
return
collateralAns.mulDiv(
uint256(euroPriceFeedAns) * EXTRA_DECIMALS,
PRECISION
);
}
function getTotalMeowllateralInAave() public view returns (uint256) {
(uint256 totalCollateralBase, , , , , ) = i_aavePool.getUserAccountData(
address(this)
);
(, int256 collateralToUsdPrice, , uint256 updatedAt, ) = i_priceFeed
.latestRoundData();
require(
updatedAt >= block.timestamp - ETHPRICEFEED_HEARTBEAT,
KittyVault__ETHStalePriceFeed()
);
return
totalCollateralBase.mulDiv(
PRECISION,
uint256(collateralToUsdPrice) * EXTRA_DECIMALS
);
}
Updates

Lead Judging Commences

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

Stale Price from Chainlink Datafeed

Support

FAQs

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