Summary
In the flow LendingPool::withdraw the function calls RToken::burn. This function contains two issues in handling withdraw of the reserve assets:
The users will withdraw the wrong reserve asset amount without the correct accrued interest.
Vulnerability Details
function burn(
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256) {
if (amount == 0) {
return (0, totalSupply(), 0);
}
uint256 userBalance = balanceOf(from);
@> @audit: missing interest accouting
_userState[from].index = index.toUint128();
if(amount > userBalance){
amount = userBalance;
}
uint256 amountScaled = amount.rayMul(index);
_userState[from].index = index.toUint128();
@> _burn(from, amount.toUint128());
if (receiverOfUnderlying != address(this)) {
IERC20(_assetAddress).safeTransfer(receiverOfUnderlying, amount);
}
emit Burn(from, receiverOfUnderlying, amount, index);
return (amount, totalSupply(), amount);
}
Impact
The burn function omits to calculate the accrued interest and to add them toamount, causing accrued interest to be ignored. Additionally, it uses amount instead ofamountScaled causing incorrect token burning.
Users will withdraw the wrong reserve assets amount without the accrued interest.
Tools Used
Manual review.
Recommendations
Add interest accrual calculation and useScaled amount for burning.
function burn(
address from,
address receiverOfUnderlying,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256) {
if (amount == 0) {
return (0, totalSupply(), 0);
}
uint256 userBalance = balanceOf(from);
+ uint256 balanceIncrease = 0;
+ if (_userState[from].index != 0 && _userState[from].index < index) {
+ uint256 depositIndex = ILendingPool(_reservePool).getNormalizedIncome();
+ balanceIncrease = userBalance.rayMul(depositIndex) - userBalance.rayMul(_userState[from].index);
+ amount = amount + balanceIncrease;
+ }
_userState[from].index = index.toUint128();
if(amount > userBalance){
amount = userBalance;
}
uint256 amountScaled = amount.rayMul(index);
- _userState[from].index = index.toUint128();
- _burn(from, amount.toUint128());
+ _burn(from, amountScaled.toUint128());
if (receiverOfUnderlying != address(this)) {
IERC20(_assetAddress).safeTransfer(receiverOfUnderlying, amount);
}
emit Burn(from, receiverOfUnderlying, amount, index);
- return (amount, totalSupply(), amount);
+ return (amountScaled, totalSupply(), amount);
}