Core Contracts

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

Interest Accrual Mismatch on Transfers Leads to Over-Accrual for Sender and Under-Accrual for Receiver In the `RToken` contract

Summary

During token transfers, the RToken contract does not mint accrued interest for the sender before updating _userState. This leads to a mismatch where the sender continues to accrue interest on tokens they no longer own, while the receiver does not accrue interest on the received tokens until they perform a mint or burn operation.

Vulnerability Detail

The contract maintains interest accrual using an index stored in _userState[account].index. However, during transfer() and transferFrom(), the sender’s interest is not settled before updating _userState.

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 transferFrom(address sender, address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
uint256 scaledAmount = amount.rayDiv(_liquidityIndex);
return super.transferFrom(sender, recipient, scaledAmount);
}

This results in:

  1. Over-accrual for the sender: The sender’s balance continues to compound interest as if they still own the tokens, even after transferring them.

  2. Under-accrual for the receiver: The receiver does not start earning interest on the received tokens until they explicitly trigger a mint or burn action.

This breaks the expected behavior of an interest-bearing token where all token balances should consistently reflect accrued interest.

The issue originates in the _update() function, which is called during transfers:

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);
}

Here, the accrued interest is not minted before updating _userState, leading to incorrect tracking of interest accumulation.

Impact

  • Users who transfer tokens will continue earning interest on tokens they no longer own.

  • Recipients will not accrue interest on received tokens until they interact with the contract again.

  • This could lead to discrepancies in yield distribution and potential loss of funds for recipients expecting to accrue interest immediately upon receiving tokens.

Tools Used

Manual Review

Recommendation

Before executing the _update() function in transfer() and transferFrom(), the accrued interest for the sender should be minted and accounted for. This ensures:

  1. The sender stops earning interest on transferred tokens.

  2. The receiver starts earning interest immediately upon receiving tokens.

Modify _update() to calculate and mint accrued interest before updating _userState.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::transfer and transferFrom double-scale amounts by dividing in both external functions and _update, causing users to transfer significantly less than intended

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RToken::transfer and transferFrom double-scale amounts by dividing in both external functions and _update, causing users to transfer significantly less than intended

Support

FAQs

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