Ancient Tangelo Canary
Medium
Vulnerable Functions
reserveToYTokenExchangeRate yTokenToReserveExchangeRate
Current Implementation (Vulnerable)
function reserveToYTokenExchangeRate(DataTypes.ReserveData storage reserve) internal view returns (uint256) { (uint256 totalLiquidity,) = totalLiquidityAndBorrows(reserve); uint256 totalYTokens = IERC20(reserve.yTokenAddress).totalSupply();
if (totalYTokens == 0 || totalLiquidity == 0) {
return PRECISION;
}
return totalYTokens * PRECISION / totalLiquidity;
}
function yTokenToReserveExchangeRate(DataTypes.ReserveData storage reserve) external view returns (uint256) { (uint256 totalLiquidity,) = totalLiquidityAndBorrows(reserve); uint256 totalYTokens = IERC20(reserve.yTokenAddress).totalSupply();
if (totalYTokens == 0 || totalLiquidity == 0) {
return PRECISION;
}
return totalLiquidity * PRECISION / totalYTokens;
}
Problem: yToken Supply Can Be Manipulated
The exchange rate is derived using totalSupply() of yToken, which is not controlled inside this contract.
An attacker can manipulate yToken supply by:
Depositing liquidity when totalYTokens is low. Withdrawing liquidity at a different exchange rate, causing loss to the protocol. Attack Scenario (Minting More Than Deposited) Assume totalLiquidity = 1,000 DAI, and totalYTokens = 1,000.
Exchange Rate: 1 yToken = 1 DAI Attacker deposits 1 DAI. Exchange Rate stays 1 yToken = 1 DAI. Attacker gets 1 yToken.
Attacker manipulates liquidity (e.g., flash loan reduces totalLiquidity).
If totalLiquidity drops to 500 DAI, but totalYTokens = 1,001, the exchange rate changes.
When burning 1 yToken, attacker might receive more than 1 DAI. Repeating this across multiple transactions drains protocol reserves.
none
none
none
Manipulable yToken Exchange Rate
No response
Instead of relying on IERC20(yTokenAddress).totalSupply(), store totalYTokens in ReserveData and update it internally.
Improved reserveToYTokenExchangeRate
function reserveToYTokenExchangeRate(DataTypes.ReserveData storage reserve) internal view returns (uint256) { (uint256 totalLiquidity,) = totalLiquidityAndBorrows(reserve); uint256 totalYTokens = reserve.totalYTokens; // Use internal tracking
if (totalYTokens == 0 || totalLiquidity == 0) {
return PRECISION;
}
return totalYTokens * PRECISION / totalLiquidity;
}
Improved yTokenToReserveExchangeRate
function yTokenToReserveExchangeRate(DataTypes.ReserveData storage reserve) external view returns (uint256) { (uint256 totalLiquidity,) = totalLiquidityAndBorrows(reserve); uint256 totalYTokens = reserve.totalYTokens; // Use internal tracking
if (totalYTokens == 0 || totalLiquidity == 0) {
return PRECISION;
}
return totalLiquidity * PRECISION / totalYTokens;
}