Core Contracts

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

Users Unable to Withdraw Total Deposited Amount of rToken in Stability Pool

Summary

The current implementation of the StabilityPool allows users to deposit and withdraw rToken, but due to the use of scaled amounts in both the deposit() and withdraw() functions, users cannot withdraw the total amount of rToken they originally deposited. This discrepancy arises because the RToken.transferFrom() function uses normalized amounts, leading to inconsistencies in the amounts users can withdraw compared to what they deposited.

Vulnerability Details

In the StabilityPool, the deposit() and withdraw() functions handle rToken deposits and withdrawals using scaled amounts, which are adjusted based on the liquidity index. The relevant code snippets are as follows:

function deposit(uint256 amount) external nonReentrant whenNotPaused validAmount(amount) {
_update();
rToken.safeTransferFrom(msg.sender, address(this), amount); // Scaled amount
uint256 deCRVUSDAmount = calculateDeCRVUSDAmount(amount);
deToken.mint(msg.sender, deCRVUSDAmount);
userDeposits[msg.sender] += amount; // Tracks the deposited 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); // Scaled amount
if (raacRewards > 0) {
raacToken.safeTransfer(msg.sender, raacRewards);
}
emit Withdraw(msg.sender, rcrvUSDAmount, deCRVUSDAmount, raacRewards);
}

The RToken.transferFrom() function normalizes the amount transferred using the liquidity index:

function transferFrom(address sender, address recipient, uint256 amount) public override(ERC20, IERC20) returns (bool) {
uint256 scaledAmount = amount.rayDiv(_liquidityIndex);
return super.transferFrom(sender, recipient, scaledAmount);
}

Since the deposit() function uses the raw amount for the transfer, and the withdraw() function uses the rcrvUSDAmount (which is also a scaled amount), users find that the amount they can withdraw is less than what they initially deposited.

Impact

Due to this vulnerability, users are unable to withdraw the total amount of rToken they deposited in the stability pool. This can lead to frustration and loss of trust in the stability pool's functionality, potentially deterring users from engaging with the protocol.

Tools Used

Manual Review

Recommendations

It is recommended to ensure that both the deposit and withdrawal functions use normalized amounts for rToken. This will allow users to withdraw the total amount they deposited without discrepancies.

Updates

Lead Judging Commences

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

StabilityPool's userDeposits mapping doesn't update with DEToken transfers or interest accrual, and this combined with RToken transfers causes fund loss and permanent lockup

Support

FAQs

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