Core Contracts

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

Stability Pool Incorrect Withdrawal Ratio

Summary

The Stability Pool processes deposits and withdrawals at a fixed 1:1 ratio. However, deposited funds are used for liquidation in the Lending Pool, reducing the actual contract balance. This creates a scenario where withdrawals continue at a 1:1 ratio even when the Stability Pool does not have sufficient funds to cover all withdrawals, potentially leading to insolvency and user fund loss.


Vulnerability Details

The Stability Pool allows users to deposit funds and withdraw at a fixed 1:1 ratio with deToken . However, deposited funds are utilized for liquidations, which means that the actual available balance decreases over time. Despite this, the pool still processes withdrawals at the original 1:1 ratio, which can result in users withdrawing more than what is available.

Code Reference:

// @audit deposit and withdrawals are in 1:1 ratio, breaking protocol
function deposit(uint256 amount) external nonReentrant whenNotPaused validAmount(amount) {
_update();
rToken.safeTransferFrom(msg.sender, address(this), amount);
uint256 deCRVUSDAmount = calculateDeCRVUSDAmount(amount);
deToken.mint(msg.sender, deCRVUSDAmount);
userDeposits[msg.sender] += amount;
_mintRAACRewards();
emit Deposit(msg.sender, amount, deCRVUSDAmount);
}
function withdraw(uint256 deCRVUSDAmount) external nonReentrant whenNotPaused validAmount(deCRVUSDAmount) {
_update();
if (deToken.balanceOf(msg.sender) < deCRVUSDAmount) revert InsufficientBalance();
uint256 rcrvUSDAmount = calculateRcrvUSDAmount(deCRVUSDAmount);
uint256 raacRewards = calculateRaacRewards(msg.sender);
if (userDeposits[msg.sender] < rcrvUSDAmount) revert InsufficientBalance();
userDeposits[msg.sender] -= rcrvUSDAmount;
if (userDeposits[msg.sender] == 0) {
delete userDeposits[msg.sender];
}
deToken.burn(msg.sender, deCRVUSDAmount);
rToken.safeTransfer(msg.sender, rcrvUSDAmount);
if (raacRewards > 0) {
raacToken.safeTransfer(msg.sender, raacRewards);
}
emit Withdraw(msg.sender, rcrvUSDAmount, deCRVUSDAmount, raacRewards);
}

Exploitation Scenario:

  1. Users deposit funds and receive deToken at a 1:1 ratio.

  2. The Stability Pool utilizes some deposited funds for liquidations in the Lending Pool, decreasing the contract’s actual balance.

  3. Users request withdrawals and are still processed at a 1:1 ratio, even though there are insufficient funds to cover all withdrawals.

  4. Some users may be able to withdraw more than their fair share, leading to a depletion of available funds, leaving later users unable to withdraw anything.

  5. The Stability Pool effectively becomes insolvent as withdrawals exceed available liquidity.


Impact

  • Protocol Insolvency: The Stability Pool may run out of funds, preventing full withdrawals for later users.


Suggested Fixes

Implement Dynamic Exchange Rate for Deposits and Withdrawals

  • Instead of a fixed 1:1 ratio, introduce an exchange rate that adjusts based on available liquidity.

  • Example Fix:

function getExchangeRate() public view returns (uint256) {
uint256 totalDeposits = rToken.balanceOf(address(this));
uint256 totalSupply = deToken.totalSupply();
if (totalSupply == 0 || totalDeposits == 0) return 1e18;
return (totalDeposits * 1e18) / totalSupply;
}
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.