Summary
The StabilityPool's reward calculation mechanism allows users to farm RAAC rewards by repeatedly depositing and withdrawing funds, as there's no minimum deposit duration or reward checkpointing system.
Vulnerability Details
function calculateRaacRewards(address user) public view returns (uint256) {
uint256 userDeposit = userDeposits[user];
uint256 totalDeposits = deToken.totalSupply();
uint256 totalRewards = raacToken.balanceOf(address(this));
return (totalRewards * userDeposit) / totalDeposits;
}
function withdraw(uint256 deCRVUSDAmount) external nonReentrant whenNotPaused {
uint256 raacRewards = calculateRaacRewards(msg.sender);
if (raacRewards > 0) {
raacToken.safeTransfer(msg.sender, raacRewards);
}
}
The issue is that:
No minimum deposit duration is enforced
Rewards are calculated based on current deposit amount only
No historical tracking of deposits/rewards
User can deposit right before rewards are distributed and withdraw immediately after
Impact
High. This vulnerability:
Allows unfair distribution of RAAC rewards
Enables reward farming through spamming deposit&withdraw to drain all the raac rewards
Undermines the incentive mechanism for long-term liquidity providers
Recommended Mitigation
Implement a reward checkpointing system:
UserRewardInfo {
uint256 lastRewardTimestamp;
uint256 rewardPerSharePaid;
uint256 rewards;
}
mapping(address => UserRewardInfo) public userRewardInfo;
Add minimum deposit duration:
mapping(address => uint256) public depositTimestamp;
function deposit(uint256 amount) external {
depositTimestamp[msg.sender] = block.timestamp;
}
function withdraw(uint256 amount) external {
require(block.timestamp >= depositTimestamp[msg.sender] + MIN_DEPOSIT_DURATION, "Lock period not elapsed");
}
Calculate rewards based on time-weighted deposits:
function updateReward(address account) internal {
uint256 duration = block.timestamp - userRewardInfo[account].lastRewardTimestamp;
userRewardInfo[account].rewards += userDeposits[account] * duration;
userRewardInfo[account].lastRewardTimestamp = block.timestamp;
}