sl1
medium
_getExchangeRate
uses wrong formula for calculating the exchangeRate.
Each market has a totalReserves
variable that hold the amount of excess ibTokens which will be used as an additional liquidity for the protocol.
absorbToReserves
function is used to increase reserves by absorbing the slurpus cash.
uint256 ibTokenAmount = (amount * 1e18) / _getExchangeRate(m);
// Update internal cash, and total reserves.
m.totalCash += amount;
m.totalReserves += ibTokenAmount;
It updates internal cash and m.totalReserves
but does not increment m.totalSupply
as excess ibTokens should not be added to totalSupply.
The problem arises when using redeem
function. This function calculates the amount to redeem in exchange for ibTokens. It calculates the exchangeRate using _getExchangeRate
function.
return ((m.totalCash + m.totalBorrow) * 1e18) / totalSupplyPlusReserves;
The exchange rate here is based on totalCash + totalBorrow / totalSupplyPlusReserves, meaning that this excess of ibTokens is used in calculations while it should not be because it is held separately to provide additional liquidity
The proper way would be to store not only the excess of ibTokens but also the excess of underlying asset, and calculate the exchangeRate usign formula: (totalUnderlying - excessUnderlying) / totalSupply
. This approach accurately reflects the real liquidity available in the lending protocol, and does not allow users to access protocol's reserves
Assume 2 cases:
- Protocol reserves are used in calculations Market state: totalCash = 1000 totalBorrow = 500 totalSupply = 1000 totalReserves = 200
ExchangeRate is calculated as follows = (totalCash + totalBorrow) / totalSupplyPlusReserves
;
This leads to exchangeRate being = (1000 + 500) / (1000 + 200) = 1500 / 1200 = 1.25
Now user wants to redeem 100 of underlying asset, he calls redeem.
Redeem function calcualtes the amount of ibToken that will be redeem as: amountUnderlying / exchangeRate
, this leaves us with 100 / 1.25 = 80 ibTokens that will be redeemd.
- Second scenario where additional liquidity is not being used in calculations. Market state: totalCash = 1000 totalBorrow = 500 totalSupply = 1000 totalReserves = 200 excessUnderlying = 50
ExchangeRate here is = (totalCash + totalBorrow - excessUnderying) / totalSupply
(1000 + 500 - 50) / 1000 = 1450 / 1000 = 1.45
;
User wants to redeem 100 of underlying tokens, redeem function calcualtes the amount of ibTokens that will be redeemed as follows:
amount / exchangeRate = 100 / 1.45 = 69
ibToken that will be redeemed.
This shows how wrong calculations affect the amount to be redeemed. When additional liquidity is used in calculations every user will redeem more than he should be able to essentially draining protocol liquidity.
Because of the wrong calculations when users redeem they can actually redeem a part of the totalReserves because they are used in calculation while they shouldn't be, which also means that protocol is not functioning as it supposed to.
https://github.com/sherlock-audit/2023-05-ironbank/blob/main/ib-v2/src/protocol/pool/IronBank.sol#L662C12-L666 https://github.com/sherlock-audit/2023-05-ironbank/blob/main/ib-v2/src/protocol/pool/IronBank.sol#L406-L451 https://github.com/sherlock-audit/2023-05-ironbank/blob/main/ib-v2/src/protocol/pool/IronBank.sol#L805-L811
Manual Review
Change calculations so they don't include the excess of underlying token and excess of ibTokens (which should be used as additional liquidity)