The FeeCollector::emergencyWithdraw function allows emergency withdrawal of tokens, but fails to reset critical contract state variables like collectedFees and userRewards. This leaves the contract in an inconsistent state where accounting variables indicate funds that are no longer present in the contract.
Whenever the FeeCollector contract receives funds through the collectFee function, the collectedFees struct is updated. These fees are distributed among different receivers using the distributeCollectedFees function. However, after an emergency withdrawal, while the balance of the RAAC token is set to zero, the collectedFees struct still reflects the pre-withdrawal balance. This mismatch causes the distribution process to revert due to the following condition: if (contractBalance < totalFees) revert InsufficientBalance();. Link to relevant code: https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/FeeCollector.sol#L403
After an emergency withdrawal:
distributeCollectedFees() will revert due to insufficient balance
claimRewards() will revert when users try to claim rewards
Contract accounting becomes permanently broken
This creates a situation where the contract becomes effectively deadlocked after emergency withdrawal, requiring manual intervention or contract redeployment to restore functionality.
Add state cleanup to the emergency withdrawal function.
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.