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 10 months 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 10 months 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.

Give us feedback!