Core Contracts

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

Unlimited deTokens minted against finite rToken reserves

Summary

getExchangeRate() function returns a fixed 1e18 instead of calculating the actual rate based on pool balances. This allows users to mint deToken at a 1:1 ratio regardless of reserves, leading to potential insolvency.

function getExchangeRate() public view returns (uint256) {
// uint256 totalDeCRVUSD = deToken.totalSupply();
// uint256 totalRcrvUSD = rToken.balanceOf(address(this));
// if (totalDeCRVUSD == 0 || totalRcrvUSD == 0) return 10**18;
// uint256 scalingFactor = 10**(18 + deTokenDecimals - rTokenDecimals);
// return (totalRcrvUSD * scalingFactor) / totalDeCRVUSD;
return 1e18;

Vulnerability Details

StabilityPool's getExchangeRate() hardcodes a 1:1 exchange rate between rToken and deToken, completely bypassing the critical pool balance dynamics that should determine the true exchange rate.

See this initial State

  • StabilityPool has 1000 rTokens in reserves

  • 2000 deTokens already minted

  • True exchange rate should be 0.5

Exploitation Steps

// Attacker sees hardcoded rate
function getExchangeRate() public view returns (uint256) {
return 1e18; // Fixed 1:1 rate
}
// Deposits 100 rToken
function deposit(uint256 amount) external {
rToken.safeTransferFrom(msg.sender, address(this), 100);
// Gets 100 deToken due to fixed rate
uint256 deCRVUSDAmount = calculateDeCRVUSDAmount(100);
deToken.mint(msg.sender, 100);
}
  • Pool now has 1100 rToken backing 2100 deToken

  • Exchange rate remains artificially fixed at 1:1

  • System becomes increasingly undercollateralized

The code ignores basic economic principles by hardcoding the exchange rate instead of calculating it based on pool reserves.

// Should be:
function getExchangeRate() public view returns (uint256) {
uint256 totalDeCRVUSD = deToken.totalSupply();
uint256 totalRcrvUSD = rToken.balanceOf(address(this));
return (totalRcrvUSD * 1e18) / totalDeCRVUSD;
}

Similar to Iron Finance's TITAN collapse where fixed price assumptions led to a death spiral. Both cases show how hardcoded exchange rates can break protocol economics and lead to systemic failure.

Impact

Unlimited deTokens minted against limited rToken reserves. Maximum loss = Total Value Locked (TVL) in StabilityPool

The Terra/UST collapse demonstrated how fixed price mechanisms can lead to catastrophic failure. When Luna's price dropped, UST's algorithmic 1:1 peg became unsustainable, leading to a $40B loss. This mirrors how StabilityPool's fixed 1:1 rate ignores market reality and creates similar systemic risks.

Tools Used

vs

Recommendations

Implement the commented-out logic to compute the exchange rate dynamically

function getExchangeRate() public view returns (uint256) {
uint256 totalDeCRVUSD = deToken.totalSupply();
uint256 totalRcrvUSD = rToken.balanceOf(address(this));
// Initial rate for empty pool
if (totalDeCRVUSD == 0 || totalRcrvUSD == 0) return 1e18;
// Calculate real exchange rate based on pool balances
uint256 scalingFactor = 10**(18 + deTokenDecimals - rTokenDecimals);
return (totalRcrvUSD * scalingFactor) / totalDeCRVUSD;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 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 3 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.