Core Contracts

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

Exchange Rate Calculation Allows Disproportionate DEToken Minting

Summary

StabilityPool contract's fixed 1:1 exchange rate between DEToken and RToken creates an arbitrage opportunity where users can receive disproportionate amounts of DETokens during deposits. The fixed exchange rate of 1e18 in StabilityPool.sol doesn't account for potential supply/demand imbalances: StabilityPool.sol#getExchangeRate

function getExchangeRate() public view returns (uint256) {
return 1e18; // Fixed rate creates vulnerability
}

This fails to account for the actual ratio between DEToken supply and RToken reserves.

This breaks the deposit/withdrawal parity in the Stability Pool. An attacker could:

  1. Deposit when exchange rate is favorable

  2. Receive more DETokens than intended

  3. Withdraw later for a profit

Vulnerability Details

The StabilityPool implements a simplified exchange rate mechanism that doesn't reflect the true ratio between DEToken supply and RToken reserves. This creates arbitrage opportunities during supply/demand imbalances.

StabilityPool.sol#deposit

// Exchange rate should reflect actual token ratios
function deposit(uint256 amount) external nonReentrant whenNotPaused validAmount(amount) {
_update();
// First transfer of rToken occurs before exchange rate calculation
rToken.safeTransferFrom(msg.sender, address(this), amount);
// Exchange rate calculation uses fixed 1:1 rate
// This allows exploitation when actual reserves don't match DEToken supply
uint256 deCRVUSDAmount = calculateDeCRVUSDAmount(amount);
// DEToken minting occurs at potentially incorrect rate
// Could mint more tokens than economically justified
deToken.mint(msg.sender, deCRVUSDAmount);
// User deposit tracking doesn't account for exchange rate discrepancy
userDeposits[msg.sender] += amount;
_mintRAACRewards();
emit Deposit(msg.sender, amount, deCRVUSDAmount);
}

The vulnerability flows through calculateDeCRVUSDAmount() which uses the fixed exchange rate. This function should be calculating amounts based on current pool reserves and total DEToken supply to maintain economic alignment.

Imagine a bank that always exchanges foreign currency at yesterday's rate, regardless of market changes. That's exactly what's happening in the StabilityPool contract. The fixed exchange rate mechanism creates a predictable arbitrage opportunity that savvy users can exploit.

When users deposit RTokens into the StabilityPool, they receive DETokens based on a static 1:1 exchange rate. This seemingly simple approach ignores the fundamental principle of supply and demand. The real danger emerges during market stress scenarios when the pool's RToken reserves don't match the DEToken supply.

Let's walk through a real exploit scenario: An attacker monitors the StabilityPool's reserves. They notice the RToken balance has dropped to 500,000 tokens while 1,000,000 DETokens are in circulation. Despite this 2:1 imbalance, the contract's getExchangeRate() function blindly returns 1e18, treating every RToken as equal to one DEToken.

The attacker deposits 100,000 RTokens and receives 100,000 DETokens, even though the true economic value should be 50,000 DETokens based on current reserves. When the pool's reserves later increase, they can withdraw more value than initially deposited.

This vulnerability stems from a single line in StabilityPool

function getExchangeRate() public view returns (uint256) {
return 1e18; // The source of our troubles
}

Impact

  • Breaks deposit/withdrawal parity

  • Enables profitable arbitrage through timing deposits

  • Undermines stability mechanism effectiveness

Recommendations

function getExchangeRate() public view returns (uint256) {
// Get total supply of DEToken (Debitum Emptor token)
uint256 totalDeCRVUSD = deToken.totalSupply();
// Get total RToken balance in the Stability Pool
uint256 totalRcrvUSD = rToken.balanceOf(address(this));
// Critical safety check for initialization case
// Handles both empty pool and zero supply scenarios
if (totalDeCRVUSD == 0 || totalRcrvUSD == 0) return 1e18;
// Calculate exchange rate with proper scaling
// Multiply by 1e18 first to maintain precision
return (totalRcrvUSD * 1e18) / totalDeCRVUSD;
}
  1. We handles both empty pool and zero supply edge cases

  2. Maintains precision by scaling with 1e18 before division

  3. Matches the token naming conventions in the StabilityPool contract (DeCRVUSD and RcrvUSD)

  4. Aligns with the existing decimal handling in the protocol

Updates

Lead Judging Commences

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

StabilityPool::getExchangeRate hardcodes 1:1 ratio instead of calculating real rate, enabling unlimited deToken minting against limited reserves

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

StabilityPool::getExchangeRate hardcodes 1:1 ratio instead of calculating real rate, enabling unlimited deToken minting against limited reserves

Support

FAQs

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