Summary
The LendingPool::_rebalanceLiquidity()
function emits a LiquidityRebalanced
event that shows thebufferAmount
and the vaultAmount
. As described in the event natspec the bufferAmount
is “The amount of liquidity in the buffer“. But in the function, the emitted event captures the buffer state before the rebalancing operations, rather than after. This leads to emitting incorrect state data in the event.
Vulnerability Details
* @notice Emitted when liquidity is rebalanced
@> * @param bufferAmount The amount of liquidity in the buffer
* @param vaultAmount The amount of liquidity in the Curve vault
*/
event LiquidityRebalanced(uint256 bufferAmount, uint256 vaultAmount);
function _rebalanceLiquidity() internal {
if (address(curveVault) == address(0)) {
return;
}
uint256 totalDeposits = reserve.totalLiquidity;
uint256 desiredBuffer = totalDeposits.percentMul(liquidityBufferRatio);
uint256 currentBuffer = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
if (currentBuffer > desiredBuffer) {
uint256 excess = currentBuffer - desiredBuffer;
_depositIntoVault(excess);
} else if (currentBuffer < desiredBuffer) {
uint256 shortage = desiredBuffer - currentBuffer;
_withdrawFromVault(shortage);
}
@> emit LiquidityRebalanced(currentBuffer, totalVaultDeposits);
}
Impact
Monitoring systems and front-end process incorrect information, leading to inaccurate tracking and auditing of the protocol's liquidity movements.
Moreover, Governance decisions relying on historical event data could be compromised, affecting the protocol's integrity and strategic direction.
Tools Used
Manual review
Recommendations
Recalculate the buffer value after performing rebalancing operations and before emitting the event.
function _rebalanceLiquidity() internal {
// if curve vault is not set, do nothing
if (address(curveVault) == address(0)) {
return;
}
uint256 totalDeposits = reserve.totalLiquidity; // Total liquidity in the system
uint256 desiredBuffer = totalDeposits.percentMul(liquidityBufferRatio);
uint256 currentBuffer = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
if (currentBuffer > desiredBuffer) {
uint256 excess = currentBuffer - desiredBuffer;
// Deposit excess into the Curve vault
_depositIntoVault(excess);
} else if (currentBuffer < desiredBuffer) {
uint256 shortage = desiredBuffer - currentBuffer;
// Withdraw shortage from the Curve vault
_withdrawFromVault(shortage);
}
- emit LiquidityRebalanced(currentBuffer, totalVaultDeposits);
+ uint256 finalBuffer = IERC20(reserve.reserveAssetAddress).balanceOf(reserve.reserveRTokenAddress);
+ emit LiquidityRebalanced(finalBuffer, totalVaultDeposits);
}