Core Contracts

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

Inconsistent and Incorrect Scaling in transfer and transferFrom Functions

Summary

The transfer and transferFrom functions in the RToken contract incorrectly handle the scaling of transfer amounts, leading to discrepancies in the transferred amounts. The issue arises due to double scaling and inconsistent use of the liquidity index.

Vulnerability Details

  1. Double Scaling Issue:

    • Both transfer and transferFrom functions divide the transfer amount by the liquidity index or normalized income. However, the _update function, which is called internally, also scales the amount by the normalized income. This results in the transfer amount being divided by the liquidity index twice, leading to a lower-than-intended transfer amount.

  2. Inconsistent Index Usage:

    • The transfer function uses ILendingPool(_reservePool).getNormalizedIncome() for scaling, while the transferFrom function uses _liquidityIndex. This inconsistency can lead to different transfer amounts being calculated for the same intended transfer, especially since _liquidityIndex is not updated properly due to the lack of implementation in the LendingPool.

function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
return super.totalSupply().rayMul(ILendingPool(_reservePool).getNormalizedIncome());
}
/**
* @dev Overrides the ERC20 transfer function to use scaled amounts
* @param recipient The recipient address
* @param amount The amount to transfer (in underlying asset units)
*/
function transfer(address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
return super.transfer(recipient, scaledAmount);
}
function _update(address from, address to, uint256 amount) internal override {
// Scale amount by normalized income for all operations (mint, burn, transfer)
uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
super._update(from, to, scaledAmount);
}

Impact

  • Users transferring less than the intended amount.

  • Discrepancies in the transfer amounts between transfer and transferFrom.

  • Potential integration issues with external platforms due to unreliable transfer amounts.

Tools Used

Manual

Recommendations

Remove the scaling operation from the transfer and transferFrom functions, as it is already handled in the _update function. This will prevent double scaling.

Implement the updateLiquidityIndex function in the LendingPool to ensure _liquidityIndex is always up-to-date, aligning it with the normalized income used in transfer.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month 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 and transferFrom double-scale amounts by dividing in both external functions and _update, causing users to transfer significantly less than intended

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

inallhonesty Lead Judge about 1 month 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 and transferFrom double-scale amounts by dividing in both external functions and _update, causing users to transfer significantly less than intended

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.