Core Contracts

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

Inconsistent scaling logic in transfer/transferFrom in RToken contract

Target

contracts/core/tokens/RToken.sol

Vulnerability Details

In the RToken contract, the transfer and transferFrom functions use different scaling factors when adjusting the amount before executing a transfer.

  • In the transfer function, the amount is scaled using the value retrieved from the lending pool contract’s getNormalizedIncome function.

    function transfer(address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
    uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
    return super.transfer(recipient, scaledAmount);
    }
  • In contrast, the transferFrom function scales the amount using the _liquidityIndex value, which is stored within the RToken contract.

    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 variable in the RToken contract is initialized to 1e27 (WadRayMath.RAY) and can only be updated via the updateLiquidityIndex function. However, this function can only be called by the reservePool contract, and the lendingPool contract does not include any logic to trigger this update. As a result, the _liquidityIndex value remains static.

On the other hand, the liquidity index obtained from the lending pool contract through the getNormalizedIncome function is dynamic, as it updates whenever lending or borrowing occurs in the pool.

Impact

As a result, the actual value transferred differs between the two functions, leading to inconsistent outcomes. This deviation from the ERC-20 standard could introduce bugs in other contracts that interact with the token.

Tools Used

Manual Review

Recommendations

Use the liquidity index value obtained from the lending pool contract when scaling transfer amounts in both the transfer and transferFrom functions.

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 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

RToken::transfer uses getNormalizedIncome() while transferFrom uses _liquidityIndex, creating inconsistent transfer amounts depending on function used

inallhonesty Lead Judge 6 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

RToken::transfer uses getNormalizedIncome() while transferFrom uses _liquidityIndex, creating inconsistent transfer amounts depending on function used

Support

FAQs

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