Summary
The emergencyRevoke
function in the RAACReleaseOrchestrator contract contains a critical flaw where it attempts to transfer tokens to itself when the tokens are already held by the contract, effectively creating a redundant transfer that could lead to accounting issues.
Vulnerability Details
The vulnerability lies in the following code snippet:
function emergencyRevoke(address beneficiary) external onlyRole(EMERGENCY_ROLE) {
VestingSchedule storage schedule = vestingSchedules[beneficiary];
if (!schedule.initialized) revert NoVestingSchedule();
uint256 unreleasedAmount = schedule.totalAmount - schedule.releasedAmount;
delete vestingSchedules[beneficiary];
if (unreleasedAmount > 0) {
raacToken.transfer(address(this), unreleasedAmount);
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
emit VestingScheduleRevoked(beneficiary);
}
The issue occurs because:
The contract already holds the vested tokens in its balance
The function attempts to transfer these tokens to itself using raacToken.transfer(address(this),unreleasedAmount)
Impact
DoS. It is not possible to make an emergencyRevoke
as the funds are not sent to the beneficiary.
Tools Used
Manual Review
Recommendations
Send the tokens to the beneficiary.
function emergencyRevoke(address beneficiary) external onlyRole(EMERGENCY_ROLE) {
VestingSchedule storage schedule = vestingSchedules[beneficiary];
if (!schedule.initialized) revert NoVestingSchedule();
uint256 unreleasedAmount = schedule.totalAmount - schedule.releasedAmount;
delete vestingSchedules[beneficiary];
if (unreleasedAmount > 0) {
- raacToken.transfer(address(this), unreleasedAmount);
+ raacToken.transfer(beneficiary, unreleasedAmount);
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
emit VestingScheduleRevoked(beneficiary);
}