Core Contracts

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

Incorrect dust amount calculation prevents dust recovery

Summary

The calculateDustAmount() function fails to account for assets held in the Curve Vault, preventing dust recovery when the vault is enabled.

Vulnerability Details

The RToken::calculateDustAmount() function calculates dust as the difference between the contract's balance and total obligations to token holders. However, it only checks the balance in the RToken contract itself:

function calculateDustAmount() public view returns (uint256) {
// Only checks this contract's balance
uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this))
.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
uint256 currentTotalSupply = totalSupply();
uint256 totalRealBalance = currentTotalSupply
.rayMul(ILendingPool(_reservePool).getNormalizedIncome());
return contractBalance <= totalRealBalance ? 0 : contractBalance - totalRealBalance;
}

The issue is that when the Curve Vault is enabled, 80% of assets are held in the vault while only 20% remain in the RToken contract. Since calculateDustAmount only checks the RToken contract's balance, it will almost always return 0 as the contract balance will be less than total obligations.

Proof of Concept

  1. Users deposit 100 tokens into the protocol

  2. 80 tokens are moved to Curve Vault, 20 remain in RToken contract

  3. Some dust accumulates in the system

  4. calculateDustAmount() is called:

    • Contract balance = 20 tokens

    • Total obligations = 100 tokens

    • Returns 0 since 20 < 100

  5. transferAccruedDust() reverts with NoDust() error

  6. Dust remains trapped in the protocol

Impact

  • Dust cannot be recovered from the protocol when the Curve Vault is enabled

  • Dust accumulates without a way to be withdrawn

  • The transferAccruedDust function becomes effectively unusable

Recommendations

Include Vault Balance

function calculateDustAmount() public view returns (uint256) {
uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this));
+ uint256 vaulBalance = _reservePool.totalVaultDeposits();
+ uint256 totalBalance = contractBalance + vaultBalance;
uint256 currentTotalSupply = totalSupply();
- return contractBalance <= currentTotalSupply ? 0 : contractBalance - currentTotalSupply;
+ return totalBalance <= currentTotalSupply ? 0 : totalBalance - currentTotalSupply;
}
Updates

Lead Judging Commences

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

RToken dust calculation structurally impossible with outstanding loans or funds deposited in the vault

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

RToken dust calculation structurally impossible with outstanding loans or funds deposited in the vault

Support

FAQs

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