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.
Code Location:
Function calculateRaacRewards()
, which uses raacToken.balanceOf(address(this))
.
Function withdraw()
, which calls calculateRaacRewards()
before sending tokens.
Exploitation Conditions:
User Alice checks her expected rewards via getPendingRewards()
.
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.
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).
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.
Manual code analysis.
Theoretical scenario modeling (e.g., altering RAAC balance pre/post-withdrawal).
Mechanism:
Track a "reward debt" for each user, updated when RAAC balances change.
Example:
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.
Mechanism:
Record RAAC balances and user shares at each interaction (deposit/withdrawal).
Example:
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).
Mechanism:
Add a minReward
parameter to withdraw()
to enforce a minimum reward threshold.
Example:
Pros:
Simple implementation.
Protects users from sudden reward drops.
Cons:
Does not address the root cause.
Degrades UX (users must guess minReward
).
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:
Implement reward accumulation via raacPerShare
and userRewardDebt
.
Rigorously test mathematical logic (edge cases: zero balance, full withdrawal).
Update user documentation to explain the new mechanism.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.