Core Contracts

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

Incorrect Dust Calculation in RToken Leads to Inaccurate Asset Recovery

Relevant Context

The RToken contract implements an interest-bearing token where balances increase over time due to interest accrual. The contract maintains two types of balances:

  1. Scaled balances (shares) - stored internally

  2. Normalized balances (assets) - exposed to users, calculated by multiplying shares by the liquidity index

The calculateDustAmount() function is designed to determine the amount of underlying assets in the contract that are not accounted for by user balances, which can then be recovered through transferAccruedDust().

Finding Description

The calculateDustAmount() function contains a critical error in its calculation logic that results in incorrect dust amount determination. The current implementation:

  1. Scales down the contract's actual asset balance by the liquidity index:

uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this)).rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
  1. Gets the total supply in normalized terms (already in assets):

uint256 currentTotalSupply = totalSupply();
  1. Incorrectly scales up the already-normalized total supply:

uint256 totalRealBalance = currentTotalSupply.rayMul(ILendingPool(_reservePool).getNormalizedIncome());

The fundamental issue is that the function mixes scaled and normalized values incorrectly, leading to a comparison between incompatible units and double-scaling of the total supply.

Impact Explanation

High. The incorrect calculation leads to:

  1. False positives - reporting dust when there isn't any

  2. False negatives - failing to detect actual dust

  3. Incorrect dust amounts when dust exists

This directly affects the contract's ability to recover excess assets through transferAccruedDust(), potentially leading to trapped assets or excessive withdrawals.

Likelihood Explanation

High. The function will consistently produce incorrect results whenever it is called, affecting all dust recovery operations.

Proof of Concept

Consider a scenario with:

  • Liquidity index = 2 RAY

  • Actual asset balance = 1000 tokens

  • Total user balances (normalized) = 900 tokens

Expected dust = 1000 - 900 = 100 tokens

Current implementation:

  1. contractBalance = 1000 / 2 = 500 (incorrectly scaled down)

  2. currentTotalSupply = 900 (correct)

  3. totalRealBalance = 900 * 2 = 1800 (incorrectly scaled up)

  4. Result = 0 (since 500 < 1800)

The function reports no dust when there should be 100 tokens available for recovery.

Recommendation

The function should be rewritten to compare values in the same unit (assets):

function calculateDustAmount() public view returns (uint256) {
uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this));
uint256 totalRealBalance = totalSupply();
return contractBalance <= totalRealBalance ? 0 : contractBalance - totalRealBalance;
}

This implementation:

  1. Uses the raw asset balance from the contract

  2. Uses the normalized total supply (already in asset terms)

  3. Compares compatible units to determine the true dust amount

Updates

Lead Judging Commences

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

RToken::calculateDustAmount incorrectly applies liquidity index, severely under-reporting dust amounts and permanently trapping crvUSD in contract

Support

FAQs

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

Give us feedback!