Core Contracts

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

Incorrect token routing when depositing excess collateral tokens into the Curve Vault

Summary

When rebalancing the liquidity of the Lending Pool, excess collateral is sent to the Curve vault. However, collateral is taken directly from the Lending Pool contract instead of the Rtoken one, breaking the protocol logic as it is not consistent with the overall system's collateral routing.

Vulnerability Details

LendingPool::rebalanceLiquidity function is called after a collateral deposit or withdrawal, in order to rebalance the system liquidity. When this function determines that there is excess collateral, _depositIntoVault function is called. As a general rule, collateral token is held in the RToken contract, nevertheless, excess amount is taken from the Lending Pool contract instead. This discrepancy compromises the system consistency and even causes the process to revert as the Lending Pool collateral balance is unknown or nonexistent.

> LendingPool.sol
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);
// @audit - collateral balance of RToken contact is the one checked to determine if liquidity rebalancing is needed
@> 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);
}
> LendingPool.sol
function _depositIntoVault(uint256 amount) internal {
// @audit - Approve the curve vault to spend collateral from THIS contract, instead the RToken one
@> IERC20(reserve.reserveAssetAddress).approve(address(curveVault), amount);
// @audit - curveVault is taking the collateral amount from this contract
@> curveVault.deposit(amount, address(this));
totalVaultDeposits += amount;
}

Impact

Impact: High

Likelihood: High

Tools Used

Manual Review

Recommendations

Add the corresponding logic to take the collateral excess from the RToken contract.

> LendingPool.sol
function _depositIntoVault(uint256 amount) internal {
+ // Must first approve this contract to spend funds from the RToken contract
+ IERC20(reserve.reserveAssetAddress).safeTransferFrom(
+ reserve.reserveRTokenAddress, // from
+ address(this), // to
+ amount // amount
+ );
IERC20(reserve.reserveAssetAddress).approve(address(curveVault), amount);
curveVault.deposit(amount, address(this));
totalVaultDeposits += amount;
}
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.