Core Contracts

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

StabilityPool reward mechanism is broken

Summary

The StabilityPool contract allows users to stake RToken and receive DEToken. Stakers earn RaacToken rewards, which are minted by RaacMinter. However, there are two critical issues in the reward mechanism:

  1. Total Supply Inflation: The reward calculation tracks deToken.totalSupply(), which can be artificially inflated if DEToken had existing tokens before being used in this protocol.

  2. Repeated Withdrawal Exploit: A user can repeatedly withdraw small amounts to claim rewards multiple times, leading to an unfair advantage.

Relevant Code:

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

Vulnerability Details

1. Total Supply Inflation

  • The function calculateRaacRewards uses deToken.totalSupply() to distribute rewards.

  • If DEToken had an initial balance before being used in this contract, it artificially increases totalSupply, leading to incorrect reward calculations.

  • This results in fewer rewards per user, creating an unfair distribution.

2. Repeated Withdrawal Exploit

  • The withdraw function calls calculateRaacRewards.

  • A user can repeatedly withdraw small amounts and continuously receive rewards as if they still had the full deposit.

  • This effectively allows them to drain the reward pool unfairly.

Example Attack Scenario:

  1. A user deposits 100 RToken.

  2. They withdraw 1 RToken and receive rewards based on the full 100 deposit.

  3. They repeat this for every 1 RToken withdrawn, receiving rewards multiple times.

  4. This depletes the reward pool, leaving less rewards for other users.

Impact

  • Unfair Reward Distribution: Some users can claim more rewards than they should.

  • Denial of Service (DoS): If a user drains most of the rewards, others may be unable to withdraw their funds due to lack of incentives.

Tools Used

Manual Review

Recommendations

1. Track User’s Earned Rewards Accurately

Instead of relying on deToken.totalSupply(), track each user’s earned rewards separately to avoid manipulation.

mapping(address => uint256) public userRewardDebt;
function calculateRaacRewards(address user) public view returns (uint256) {
uint256 userDeposit = userDeposits[user];
uint256 totalDeposits = getTotalDeposits(); // Use an internal tracking variable instead of totalSupply
uint256 totalRewards = raacToken.balanceOf(address(this));
if (totalDeposits == 0) return 0;
return (totalRewards * userDeposit) / totalDeposits - userRewardDebt[user];
}

2. Implement Reward Claim Mechanism

Ensure users can only claim rewards once per eligible deposit instead of multiple times.

Updates

Lead Judging Commences

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

StabilityPool::withdraw can be called with partial amounts, but it always send the full rewards

Support

FAQs

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