Core Contracts

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

Inaccurate calculateDustAmount Implementation in RToken Contract

01. Relevant GitHub Links

02. Summary

The calculateDustAmount function in the RToken contract is supposed to identify extra tokens (dust) in the contract that are not accounted for by the total RToken supply. This dust calculation is used by the protocol owner to withdraw fees. However, the function incorrectly depends on the contract’s own ERC20 balance, ignoring assets that could be held in a Curve vault or removed due to borrow actions. As a result, the calculated dust amount may stay at zero, preventing the owner from withdrawing fees.

03. Vulnerability Details

/**
* @notice Calculate the dust amount in the contract
* @return The amount of dust in the contract
*/
function calculateDustAmount() public view returns (uint256) {
// Calculate the actual balance of the underlying asset held by this contract
uint256 contractBalance = IERC20(_assetAddress).balanceOf(address(this)).rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
// Calculate the total real obligations to the token holders
uint256 currentTotalSupply = totalSupply();
// Calculate the total real balance equivalent to the total supply
uint256 totalRealBalance = currentTotalSupply.rayMul(ILendingPool(_reservePool).getNormalizedIncome());
// All balance, that is not tied to rToken are dust (can be donated or is the rest of exponential vs linear)
return contractBalance <= totalRealBalance ? 0 : contractBalance - totalRealBalance;
}
  • Vault Deposits Ignored: If some assets are deposited in a Curve vault, the RToken contract no longer holds them directly. Thus, using IERC20(_assetAddress).balanceOf(address(this)) underestimates the protocol’s total assets.

function _rebalanceLiquidity() internal {
// if curve vault is not set, do nothing
if (address(curveVault) == address(0)) {
return;
}
uint256 totalDeposits = reserve.totalLiquidity; // Total liquidity in the system // @note : 총 deposit한 값
uint256 desiredBuffer = totalDeposits.percentMul(liquidityBufferRatio); // 기본 값이 20%
uint256 currentBuffer = IERC20(reserve.reserveAssetAddress).balanceOf( // @note : 현시점 가지고 있는 token 계산
reserve.reserveRTokenAddress
);
if (currentBuffer > desiredBuffer) { // @note : 만약 가지고 있는 돈이 더 많으면
uint256 excess = currentBuffer - desiredBuffer;
// Deposit excess into the Curve vault
_depositIntoVault(excess);
} else if (currentBuffer < desiredBuffer) { // @note : 만약 가지고 있는 돈이 더 적으면
uint256 shortage = desiredBuffer - currentBuffer;
// Withdraw shortage from the Curve vault
_withdrawFromVault(shortage);
}
emit LiquidityRebalanced(currentBuffer, totalVaultDeposits);
}
  • Borrowed Assets Not Reflected: When a user borrows tokens, the protocol’s total supply might not decrease accordingly, and this mismatch skews the dust calculation.

function borrow(
uint256 amount
) external nonReentrant whenNotPaused onlyValidAmount(amount) {
if (isUnderLiquidation[msg.sender])
revert CannotBorrowUnderLiquidation();
UserData storage user = userData[msg.sender];
uint256 collateralValue = getUserCollateralValue(msg.sender);
if (collateralValue == 0) revert NoCollateral();
// Update reserve state before borrowing
ReserveLibrary.updateReserveState(reserve, rateData);
...
  • Incorrect Dust Logic: The function calculates dust by comparing contractBalance and totalRealBalance. But if contractBalance is less than expected due to external vaults or borrowing, the function returns zero dust, effectively blocking fee withdrawals.

Additionally, the calculateDustAmount function is incorrectly implemented. However, this is not relevant to the vulnerability in the current report, so I'll cover it in another report.

04. Impact

Because the dust always appears to be zero in some scenarios, the owner cannot withdraw protocol fees. This can lead to fee revenue getting stuck in the protocol without any easy mechanism to claim it, reducing the protocol’s ability to properly allocate funds.

05. Tools Used

Manual Code Review and Foundry

06. Recommended Mitigation

Include All Assets in the Calculation

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month 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 about 1 month 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.