Core Contracts

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

stability pool rewards distribution is flawed

Summary

Stability pool rewards are distributed based on how much of the deToken supply a user has and the contract's reward token balance. Because rewards are not time-weighed, an attacker can deposit rTokens to get rewards instantly and get out of stability pool.

Vulnerability Details

Users can deposit rTokens to stability pool and deposits are recorded:

function deposit(uint256 amount) external nonReentrant whenNotPaused validAmount(amount) {
_update(); //@audit-info _mintRAACRewards ==> function tick of RAACMinter
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);
}

This will trigger RAACMinter to mint rewards to stability pool and later users can withdraw their rTokens and rewards:

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);
}

calculateRaacRewards does not take into account how much a user has been in the system and rewards user based on how much of the deposits a user owns and reward balance of stability pool:

function calculateRaacRewards(address user) public view returns (uint256) {
uint256 userDeposit = userDeposits[user];
uint256 totalDeposits = deToken.totalSupply();
uint256 totalRewards = raacToken.balanceOf(address(this));
if (totalDeposits < 1e6) return 0;
return (totalRewards * userDeposit) / totalDeposits;
}

Attackers can use their rTokens to get rewards out of stability pool in one block by depositing and withdrawing, effectively getting rewarded while not participating in the stability pool.

Impact

Loss of rewards.

Recommendations

Introduce time weights so that reward distribution is done fairly.

Updates

Lead Judging Commences

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

StabilityPool::calculateRaacRewards is vulnerable to just in time deposits

Support

FAQs

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