Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

`LendingPool::_withdrawFromVault` token receiver should be set to RToken address so the `currentBuffer` value would be accurate

Summary

LendingPool::_withdrawFromVaultis used when the function _rebalanceLiquidity is called. If the currentBufferis lower than the desiredBufferthen the contract would withdraw some asset token to satisfy the buffer.

But the receiver of the withdraw function from curve vault is LendingPool token and not the RToken address, this would lead the currentBuffer < desiredBufferto always resulting in true statement. making the subsequent _rebalanceLiquidity always calling _withdrawFromVaultto satisfy the missing buffer.

Vulnerability Details

if we check how currentBuffer and desiredBuffer are calculated inside _rebalanceLiquidity, then this issue would be easy to understand:

LendingPool.sol#L778-L790

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);
}

desiredBuffer is the total liquidty in the system multiplied with the desired buffer ratio, where the currentBufferis the asset token balance of reserve.reserveRTokenAddressand this value is the RTokenaddress.

if the desired buffer is not satisfied, then the shortage would be withdrawn from curve vault:

LendingPool.sol#L809-L812

function _withdrawFromVault(uint256 amount) internal {
curveVault.withdraw(amount, address(this), msg.sender, 0, new address[](0));
totalVaultDeposits -= amount;
}

but the address used to receive the token is address(this)which is the LendingPool itself.

this is problematic because the currentBuffer is calculated from the asset token balance of RToken address, not lending pool address.

so even after the withdraw, later when the liquidity is rebalanced again, the currentBuffer > desiredBufferwould always return to false.

Impact

with the currentBuffer > desiredBufferis always false then making the else if block executed, the contract would always try to withdraw from vault until the shortage amount is bigger than what the contract current deposit balance, this would lead to DoS because the maxLoss params is set to 0.

the said DoS function would be LendingPool deposit, withdraw and _repay function.

Tools Used

manual review

Recommendations

the receiver of curve vault withdraw should be RToken address instead address(this) to make the buffer check logic consistent.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::_depositIntoVault and _withdrawFromVault don't transfer tokens between RToken and LendingPool, breaking Curve vault interactions

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool::_depositIntoVault and _withdrawFromVault don't transfer tokens between RToken and LendingPool, breaking Curve vault interactions

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.