Core Contracts

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

crvUSD depositors not getting their accrued interest after withdrawel from LendingPool.

Summary

Doc statement -

| By doing so, user will receives a RToken that represents such deposit + any accrued interest. Contrary to the DebtToken,
| those RToken are transferable.

The withdraw function in LendingPool.sol is as follow -

function withdraw(uint256 amount) external nonReentrant whenNotPaused onlyValidAmount(amount) {
if (withdrawalsPaused) revert WithdrawalsArePaused();
// Update the reserve state before the withdrawal
ReserveLibrary.updateReserveState(reserve, rateData);
// Ensure sufficient liquidity is available
_ensureLiquidity(amount);
// Perform the withdrawal through ReserveLibrary
(uint256 amountWithdrawn, uint256 amountScaled, uint256 amountUnderlying) = ReserveLibrary.withdraw(
reserve, // ReserveData storage
rateData, // ReserveRateData storage
amount, // Amount to withdraw
msg.sender // Recipient
);
// Rebalance liquidity after withdrawal
_rebalanceLiquidity();
emit Withdraw(msg.sender, amountWithdrawn);
}

ReserveLibrary.withdraw() ->

function withdraw(
ReserveData storage reserve,
ReserveRateData storage rateData,
uint256 amount,
address recipient
) internal returns (uint256 amountWithdrawn, uint256 amountScaled, uint256 amountUnderlying) {
if (amount < 1) revert InvalidAmount();
// Update the reserve interests
updateReserveInterests(reserve, rateData);
// Burn RToken from the recipient - will send underlying asset to the recipient
(uint256 burnedScaledAmount, uint256 newTotalSupply, uint256 amountUnderlying) = IRToken(reserve.reserveRTokenAddress).burn(
recipient, // from
recipient, // receiverOfUnderlying
amount, // amount
reserve.liquidityIndex // index
);
amountWithdrawn = burnedScaledAmount;
// Update the total liquidity and interest rates
updateInterestRatesAndLiquidity(reserve, rateData, 0, amountUnderlying);
emit Withdraw(recipient, amountUnderlying, burnedScaledAmount);
return (amountUnderlying, burnedScaledAmount, amountUnderlying);
}

IRToken.burn() ->

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

Vulnerability Details

As we can see -

  1. when withdrawel of crvUSD happens, the receiver get only the crvUSD amount he deposited = burned RTokens.

  2. user not getting any accured interest.

  3. Apart from that devs may be assuming that the user can withdraw the accured intrest through any other function (via pull mechanism), but there isn't any function like that, neither there is any state varibale that tracks how much interest a perticular user has earned.

Impact

  • User not getting his accured interest, the incentive behind depositing crvUSD in lending pool.

Tools Used

Manual

Recommendations

Implement reserve.liquidityIndex on withdrawel amount, as it deals with interets accured of deposited crvUSD in pool.

Updates

Lead Judging Commences

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

LendingPool earns yield from Curve Vault deposits but lacks systematic distribution mechanism, leading to protocol-owned value with unclear extraction path

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

LendingPool earns yield from Curve Vault deposits but lacks systematic distribution mechanism, leading to protocol-owned value with unclear extraction path

Support

FAQs

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