Core Contracts

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

Users can farm RAAC tokens by repeatedly calling deposit and withdraw in Stability pool

Summary

Users can deposit rTokens in the stability pool to get deTokens, and subsequently burn the deTokens to get back their rTokens. In the process, they are eligible for some RAAC tokens rewards as well.

A user can repeat this process infinitely to farm RAAC token rewards. In deposit(), users deposit rTokens and get back deTokens. userDeposits[msg.sender] is increased by the amount of rTokens transferred in.

Vulnerability Details

In StabilityPool.sol,

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

When the user withdraws, his deTokens is burned and rToken is transferred back. calculateRaacRewards() is also called and if returns >0, transfer the RAAC rewards to the user.

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() takes the userDeposit, deToken.totalSupply, and totalRewards, and gets the percentage of the rewards based on the userDeposit.

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

Users can repeatedly call deposit and withdraw simultaneously to farm RAACtoken rewards from the contract.

Impact

RAAC rewards is drained from the contracts.

Tools Used

Manual Review

Recommendations

Recommend having a timelock between the deposit and withdraw process

Updates

Lead Judging Commences

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

Give us feedback!