Core Contracts

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

`RToken::transferFrom` Uses Cached Liquidity Index Instead of Fetching Fresh Value

[M-02] RToken::transferFrom Uses Cached Liquidity Index Instead of Fetching Fresh Value

Summary

The RToken::transferFrom function uses the cached _liquidityIndex value instead of fetching the latest normalized income from the lending pool. If _liquidityIndex is outdated, transfers may be processed using an incorrect scale factor, leading to inaccurate token transfers.

Vulnerability Details

In the implementation below, transferFrom uses the cached _liquidityIndex instead of calling ILendingPool(_reservePool).getNormalizedIncome() like transfer does:

function transfer(
address recipient,
uint256 amount
) public override(ERC20, IERC20) returns (bool) {
uint256 scaledAmount = amount.rayDiv(
ILendingPool(_reservePool).getNormalizedIncome() // ✅ Fetches fresh value
);
return super.transfer(recipient, scaledAmount);
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) public override(ERC20, IERC20) returns (bool) {
// ❌ Uses cached liquidity index instead of fetching the latest value
uint256 scaledAmount = amount.rayDiv(_liquidityIndex);
return super.transferFrom(sender, recipient, scaledAmount);
}

Why Is This a Problem?

  • Stale Index Leads to Incorrect Transfers: _liquidityIndex is not updated dynamically, meaning if interest accrues and _liquidityIndex has not been refreshed, the scaled amount may be inaccurate.

  • Transfers May Be Less or More Than Expected: If _liquidityIndex is lower than the actual normalized income, users may transfer more tokens than required. If it's higher, transfers will be too low.

  • Inconsistency Between transfer and transferFrom: transfer fetches the correct value from getNormalizedIncome(), but transferFrom does not, leading to unpredictable behavior.

Impact

  • Users May Lose or Gain Tokens Unexpectedly: If _liquidityIndex is outdated, transfers will be incorrectly scaled, affecting user balances.

  • Potential Exploit Vector: Attackers could front-run transactions when _liquidityIndex is stale to take advantage of incorrect transfer amounts.

  • Accounting Issues: A mismatch between expected and actual balances could cause issues in protocols relying on accurate token tracking.

Proof of Concept

Modify the should allow transfers between users test case to include a case where _liquidityIndex is outdated:

it("should detect outdated liquidity index issue", async function () {
const mintAmount = ethers.parseEther("100");
// Mint with an initial index
await mockLendingPool.mockMint(reservePool.address, user1.address, mintAmount, ethers.parseUnits("1.0", 27));
// Simulate index update but without updating _liquidityIndex
await mockLendingPool.mockGetNormalizedIncome(ethers.parseUnits("1.1", 27));
// transferFrom still uses outdated index
let transferAmount = await rToken.balanceOf(user1);
await expect(rToken.connect(user2).transferFrom(user1.address, user2.address, transferAmount))
.to.emit(rToken, "Transfer")
.withArgs(user1.address, user2.address, 90909090909090909091n); // Incorrect transfer due to outdated index
// Check unexpected residual balance
expect(await rToken.balanceOf(user1)).to.not.equal(0);
});

Recommendation

Modify transferFrom to fetch the latest liquidity index from the lending pool instead of using the cached _liquidityIndex:

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

By ensuring that both transfer and transferFrom use the latest liquidity index, transfers remain accurate, preventing incorrect balance calculations.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

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::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.