Core Contracts

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

StabilityPool: Front-Running in RAAC Reward Calculation

Summary

In the contract StabilityPool the vulnerability allows malicious actors to manipulate the RAAC token balance in the contract immediately before users withdraw funds, leading to unfair reward distribution. The reward calculation mechanism relies on real-time contract state, making it susceptible to front-running attacks.


Vulnerability Details

Code Location:

  • Function calculateRaacRewards(), which uses raacToken.balanceOf(address(this)).

  • Function withdraw(), which calls calculateRaacRewards() before sending tokens.

Exploitation Conditions:

  1. User Alice checks her expected rewards via getPendingRewards().

  2. Before Alice’s withdraw() transaction is confirmed, attacker Bob:

    • Adds RAAC to the contract (via depositRAACFromPool), increasing Alice’s rewards.

    • Reduces the RAAC balance (via withdrawing their own tokens), decreasing Alice’s rewards.

  3. Alice’s actual rewards will differ from her expectations.

Example Scenario:

  • RAAC balance = 1000. Alice’s deposit = 50% of the total pool.

  • Alice expects 500 RAAC.

  • Bob calls depositRAACFromPool(1000 RAAC) before her transaction.

  • New RAAC balance = 2000, so Alice receives 1000 RAAC (50% of 2000).


Impact

  • High risk of unfair distribution: Users may receive more/fewer rewards due to others’ actions.

  • Loss of protocol trust: Unpredictable rewards reduce system attractiveness.

  • Potential market manipulation: Attackers could influence RAAC prices via mass withdrawals/deposits.


Tools Used

  • Manual code analysis.

  • Theoretical scenario modeling (e.g., altering RAAC balance pre/post-withdrawal).


Recommendations

Option 1: Reward Debt Accumulation

Mechanism:

  • Track a "reward debt" for each user, updated when RAAC balances change.

    Example:

    uint256 public raacPerShare; // RAAC per 1 deToken
    mapping(address => uint256) public userRewardDebt;
    // On deposit/withdrawal:
    userRewardDebt[user] = deToken.balanceOf(user) * raacPerShare;
    // When distributing RAAC:
    raacPerShare += (newRAAC * 1e18) / deToken.totalSupply();

Pros:

  • Full protection against front-running.

  • Low gas costs (no historical data storage).

Cons:

  • Requires precise mathematical implementation (risk of calculation errors).

  • Complexity for auditors due to global variables.


Option 2: Snapshots

Mechanism:

  • Record RAAC balances and user shares at each interaction (deposit/withdrawal).

    Example:

    struct Snapshot {
    uint256 timestamp;
    uint256 raacBalance;
    uint256 userShare; // User’s share of deToken.totalSupply()
    }
    mapping(address => Snapshot[]) public userSnapshots;

Pros:

  • Transparency: users see which snapshot is used for calculations.

  • Protection against all manipulation forms.

Cons:

  • High gas costs (storing transaction history).

  • Increased code complexity (handling snapshot arrays).


Option 3: minReward Parameter (Temporary Fix)

Mechanism:

  • Add a minReward parameter to withdraw() to enforce a minimum reward threshold.

    Example:

    function withdraw(uint256 deCRVUSDAmount, uint256 minReward) external {
    ...
    if (raacRewards < minReward) revert InsufficientRewards();
    }

Pros:

  • Simple implementation.

  • Protects users from sudden reward drops.

Cons:

  • Does not address the root cause.

  • Degrades UX (users must guess minReward).


Final Recommendation

The best solution is Option 1 (Reward Debt Accumulation). It provides:

  • Effective front-running protection.

  • Optimal gas efficiency.

  • Proven reliability in DeFi (e.g., MasterChef).

Next Steps:

  1. Implement reward accumulation via raacPerShare and userRewardDebt.

  2. Rigorously test mathematical logic (edge cases: zero balance, full withdrawal).

  3. Update user documentation to explain the new mechanism.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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