Core Contracts

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

[H] Ignored `liquidityIndex` Update in `ReserveLibrary` and `RToken`

Summary

The liquidityIndex in the RToken contract needs to be updated every time the reserve interests are updated in the ReserveLibrary contract. Failure to do so results in incorrect calculations of the scaled amount in the transferFrom function.

Vulnerability Details

The transferFrom function in the RToken contract depends on the liquidityIndex to calculate the scaled amount:

function transferFrom(address sender, address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
uint256 scaledAmount = amount.rayDiv(_liquidityIndex);
return super.transferFrom(sender, recipient, scaledAmount);
}

updateLiquidityIndex function updates the liquidity index in RToken

/**
* @notice Updates the liquidity index
* @param newLiquidityIndex The new liquidity index
*/
function updateLiquidityIndex(uint256 newLiquidityIndex) external override onlyReservePool {
if (newLiquidityIndex < _liquidityIndex) revert InvalidAmount();
_liquidityIndex = newLiquidityIndex;
emit LiquidityIndexUpdated(newLiquidityIndex);
}

However, the liquidityIndex is not updated every time the reserve interests are updated in the ReserveLibrary contract, leading to incorrect calculations.

link to the issues:

  1. https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RToken.sol#L98

  2. https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RToken.sol#L224C7-L224C8

Impact

This issue can lead to incorrect calculations of the scaled amount in the transferFrom function, potentially causing issues with token transfers and liquidity management within the protocol.

Tools Used

Manual code review.

Recommendations

Ensure that the liquidityIndex in the RToken contract is updated every time the reserve interests are updated in the ReserveLibrary contract. This can be achieved by adding a call to update the liquidityIndex in the updateReserveInterests function.

Example update in ReserveLibrary:

function updateReserveInterests(ReserveData storage reserve, ReserveRateData storage rateData) internal {
uint256 timeDelta = block.timestamp - uint256(reserve.lastUpdateTimestamp);
if (timeDelta < 1) {
return;
}
uint256 oldLiquidityIndex = reserve.liquidityIndex;
if (oldLiquidityIndex < 1) revert LiquidityIndexIsZero();
reserve.liquidityIndex = calculateLiquidityIndex(
rateData.currentLiquidityRate,
timeDelta,
reserve.liquidityIndex
);
reserve.usageIndex = calculateUsageIndex(
rateData.currentUsageRate,
timeDelta,
reserve.usageIndex
);
reserve.lastUpdateTimestamp = uint40(block.timestamp);
// Update the liquidity index in the RToken contract
IRToken(reserve.reserveRTokenAddress).updateLiquidityIndex(reserve.liquidityIndex);
emit ReserveInterestsUpdated(reserve.liquidityIndex, reserve.usageIndex);
}

This ensures that the liquidityIndex is always up-to-date, leading to correct calculations in the transferFrom function.


Updates

Lead Judging Commences

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

RToken::updateLiquidityIndex() has onlyReservePool modifier but LendingPool never calls it, causing transferFrom() to use stale liquidity index values

Support

FAQs

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

Give us feedback!