tsvetanovv
high
It is possible for a user to completely redeem an amount of asset from Iron Bank, but not leave the market
It is possible for a user to completely Redeem an amount of asset from Iron Bank, but not leave the market. This situation is happening in redeem() function:
function redeem(address from, address to, address market, uint256 amount)
external
nonReentrant
isAuthorized(from)
{
DataTypes.Market storage m = markets[market];
require(m.config.isListed, "not listed");
_accrueInterest(market, m);
uint256 userSupply = m.userSupplies[from];
uint256 totalCash = m.totalCash;
uint256 ibTokenAmount;
bool isRedeemFull;
if (amount == type(uint256).max) {
ibTokenAmount = userSupply;
amount = (ibTokenAmount * _getExchangeRate(m)) / 1e18;
isRedeemFull = true;
} else {
ibTokenAmount = (amount * 1e18) / _getExchangeRate(m);
}
require(userSupply >= ibTokenAmount, "insufficient balance");
require(totalCash >= amount, "insufficient cash");
// Update storage.
unchecked {
m.userSupplies[from] = userSupply - ibTokenAmount;
m.totalCash = totalCash - amount;
// Underflow not possible: ibTokenAmount <= userSupply <= totalSupply.
m.totalSupply -= ibTokenAmount;
}
// Check if need to exit the market.
if (isRedeemFull && _getBorrowBalance(m, from) == 0) {
_exitMarket(market, from);
}
IBTokenInterface(m.config.ibTokenAddress).burn(from, ibTokenAmount); // Only emits Transfer event.
IERC20(market).safeTransfer(to, amount);
_checkAccountLiquidity(from);
emit Redeem(market, from, to, amount, ibTokenAmount);
}
Consider the following situation:
- Alice wants to redeem a full amount of an asset from the Iron Bank and exit the market.
- To redeem the full amount she needs to put 100 amount of tokens.
- After Alice does this we go to the following code:
420: bool isRedeemFull;
421: if (amount == type(uint256).max) {
422: ibTokenAmount = userSupply;
423: amount = (ibTokenAmount * _getExchangeRate(m)) / 1e18;
424: isRedeemFull = true;
425: } else {
426: ibTokenAmount = (amount * 1e18) / _getExchangeRate(m);
427: }
- Alice actually buys all her tokens (100) and wants to get out of the market, but doesn't actually enter the
if
statement. - The condition
amount == type(uint256).max
checks whether theamount
parameter is equal totype(uint256).max
, which is the maximum value representable by auint256
variable, and Alice enters theif
statement. - Because she does not enter into the
if
statement,isRedeemFull
is never set totrue
and staysfalse
. - Then we go to the following code in which Alice should exit the market but actually does not pass the check because isRedeemFull remains
false
440: // Check if need to exit the market.
441: if (isRedeemFull && _getBorrowBalance(m, from) == 0) {
442: _exitMarket(market, from);
443: }
If the user not entering the _exitMarket()
function means that the user's account will still be considered as having an open position in the market. The outstanding borrow balance will remain, and the user will continue to accrue interest on their borrowings.
Manual Review
In the if
statement, check how many tokens a user can have, not the maximum number. like this if
statement is executed and when a user wants to redeem everything isRedeemFull
will be set to true.