Core Contracts

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

Atomic Deposit and Withdrawal Allows Reward Extraction Without Staking Commitment in `StabilityPool` Contract

Summary

In StabilityPool, An attacker can exploit the reward distribution mechanism by depositing and withdrawing in a single transaction immediately after a reward is deposited into the contract via depositRAACFromPool. This allows the attacker to unfairly claim a portion of the rewards without maintaining a long-term stake.

Vulnerability Details

The contract allows users to deposit funds and immediately become eligible for rewards.

If rewards are distributed via an external deposit like depositRAACFromPool,

function depositRAACFromPool(uint256 amount) external onlyLiquidityPool validAmount(amount) {
uint256 preBalance = raacToken.balanceOf(address(this));
raacToken.safeTransferFrom(msg.sender, address(this), amount);
uint256 postBalance = raacToken.balanceOf(address(this));
if (postBalance != preBalance + amount) revert InvalidTransfer();
// TODO: Logic for distributing to managers based on allocation
emit RAACDepositedFromPool(msg.sender, amount);
}

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/pools/StabilityPool/StabilityPool.sol#L326C3-L337C6

an attacker can execute the following sequence in a single transaction:

  1. Wait for a reward deposit event.

  2. Deposit a large amount right after the reward deposit.

  3. Trigger reward calculation, receiving an unfair share of the rewards.

  4. Immediately withdraw the deposit, keeping the earned rewards.

Please see the calculateRaacRewardswhich is called in withdraw()

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

The reaward is called by multiplying raacTokenwith userDeposit.

An attacker perform this attack multiple times to drain the raacTokenbalance from the contract.

Impact

  • Immediate reward extraction: The attacker can gain rewards unfairly in the same transaction.

  • Drains the reward pool: The rewards meant for long-term stakers are stolen by an attacker who does not maintain a legitimate stake.

Tools Used

Manual Review

Recommendations

To mitigate this attack, consider implementing the following solutions:

  1. Impose a minimum staking duration before rewards are claimable.

  2. Use a snapshot mechanism: Calculate rewards based on a user’s balance before their deposit.

  3. Prevent same-block deposit & withdrawal: Introduce a lastActionBlock variable to enforce a time delay between deposit and withdrawal.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 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.