Core Contracts

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

Incorrect Liquidity Index Synchronization in RToken Contract Leading to transferFrom Function Miscalculations

Summary

The RToken contract contains a critical issue where the updateLiquidityIndex function, which updates the _liquidityIndex value, is not being called by the LendingPool contract. This results in the _liquidityIndex value becoming stale, leading to incorrect calculations in the transferFrom functions. The _liquidityIndex is used to scale token amounts during transfers, and its incorrect value can cause discrepancies transfers.

Vulnerability Details

The RToken contract relies on the _liquidityIndex to scale token amounts during transfers. The updateLiquidityIndex function is designed to update this index, but it is only callable by the ReservePool (i.e., the LendingPool contract) due to the onlyReservePool modifier. However, the LendingPool contract does not call this function, causing the _liquidityIndex to remain static and outdated.

function updateLiquidityIndex(uint256 newLiquidityIndex) external override onlyReservePool {
if (newLiquidityIndex < _liquidityIndex) revert InvalidAmount();
_liquidityIndex = newLiquidityIndex;
emit LiquidityIndexUpdated(newLiquidityIndex);
}
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);
}

The _liquidityIndex is used in the transferFrom functions to scale the token amounts. If this index is not updated, the scaling calculations will be incorrect, leading to improper token transfers and potential loss of funds and the transfer function used correctly up to date liquidtyIndex calling the lendingPool correclty.

function transfer(address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
@>> uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
return super.transfer(recipient, scaledAmount);
}

Example Scenario

  • Initial _liquidityIndex in RToken: 1e27 (RAY)

  • After interest accrual, reserve.liquidityIndex in LendingPool: 1.1e27 (10% increase)

  • User attempts to transfer 100 tokens.

  • Expected scaled amount: 100 / 1.1e27 ≈ 90.90 tokens.

  • Actual scaled amount: 100 / 1e27 = 100 tokens (incorrect due to stale _liquidityIndex).

Impact

Users may transfer more or fewer tokens than intended, leading to financial losses.

Tools Used

Manual Review

Recommendations

The LendingPool contract must call the updateLiquidityIndex function in the RToken contract whenever the reserve.liquidityIndex is updated. This ensures that the _liquidityIndex in the RToken contract remains synchronized with the reserve.liquidityIndex in the LendingPool.

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!