Core Contracts

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

Decimal precision loss in RToken minting due to incorrect scaling with RAY math

Summary

In the LendingPool's deposit function, the amount provided in crvUSD (18 decimals) is passed directly to RToken's mint function where it's used in RAY math operations (27 decimals) without proper scaling, leading to significant precision loss or failed mints.

Vulnerability Details

When a user deposits crvUSD through the LendingPool:

// In LendingPool.sol
function deposit(uint256 amount) external { // amount = 100 * 1e18 (crvUSD decimals)
ReserveLibrary.deposit(reserve, rateData, amount, msg.sender);
}

This amount is then passed to RToken's mint function:

// In RToken.sol
function mint(..., uint256 amountToMint, uint256 index) {
// amountToMint = 100 * 1e18 (crvUSD decimals)
// index is in RAY (1e27)
uint256 amountScaled = amountToMint.rayDiv(index);
// 1e18 / 1e27 = 1e-9 (significant precision loss)
if (amountScaled == 0) revert InvalidAmount();
// ... minting logic
}

The issue arises because:

  1. amountToMint is in crvUSD decimals (1e18)

  2. index is in RAY precision (1e27)

  3. The rayDiv operation expects both operands to be in RAY precision

  4. The division results in a very small number (1e-9) due to decimal mismatch

Impact

  1. Significant loss of precision in minted RToken amounts

  2. Potential revert of mint operations if amountScaled rounds to 0

  3. Inconsistent minting behavior for different deposit sizes

This could lead to:

  • Users receiving far fewer RTokens than they should

  • Failed deposits for smaller amounts

  • Accounting errors in the protocol

Tools Used

Manual Review

Recommendations

Scale the deposit amount to RAY precision before performing RAY math operations:

// In RToken.sol
function mint(..., uint256 amountToMint, uint256 index) {
// Scale amountToMint from 18 decimals to 27 decimals (RAY)
uint256 amountInRay = amountToMint * 1e9; // Convert from 1e18 to 1e27
uint256 amountScaled = amountInRay.rayDiv(index);
if (amountScaled == 0) revert InvalidAmount();
// ... rest of minting logic
}

Alternative approach:

// Or handle the scaling in LendingPool before calling mint
function deposit(uint256 amount) external {
uint256 scaledAmount = amount * 1e9; // Scale to RAY
ReserveLibrary.deposit(reserve, rateData, scaledAmount, msg.sender);
}

This ensures all RAY math operations are performed with correctly scaled values, preventing precision loss and maintaining consistent behavior across different deposit sizes.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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