Core Contracts

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

`RAACReleaseOrchestrator.categoryUsed` Not Decremented on `RAACReleaseOrchestrator.emergencyRevoke`

Summary

The RAACReleaseOrchestrator contract fails to properly manage category allocation accounting when emergency revoking a vesting schedule. When a vesting schedule is revoked, the categoryUsed amount remains unchanged, effectively reducing the available allocation for future vesting schedules in that category.

Vulnerability Details

[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/minters/RAACReleaseOrchestrator/RAACReleaseOrchestrator.sol#L126-L131)

[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/minters/RAACReleaseOrchestrator/RAACReleaseOrchestrator.sol#L28-L29)

In the emergencyRevoke function, the contract deletes the vesting schedule but does not update the categoryUsed mapping to reflect this change. This creates a discrepancy between the actual tokens allocated and the tracked amounts in categoryUsed.

The issue manifests in these steps:

  1. A vesting schedule is created, incrementing categoryUsed

  2. The schedule is emergency revoked via emergencyRevoke

  3. The vestingSchedules mapping entry is deleted

  4. The categoryUsed amount remains unchanged

function emergencyRevoke(address beneficiary) external onlyRole(EMERGENCY_ROLE) {
VestingSchedule storage schedule = vestingSchedules[beneficiary];
if (!schedule.initialized) revert NoVestingSchedule();
uint256 unreleasedAmount = schedule.totalAmount - schedule.releasedAmount;
// MISSING: subtracting unreleasedAmount from categoryUsed
@> delete vestingSchedules[beneficiary];
if (unreleasedAmount > 0) {
raacToken.transfer(address(this), unreleasedAmount);
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
emit VestingScheduleRevoked(beneficiary);
}

Impact

  • Categories permanently lose allocatable capacity after revokes

  • Contract cannot utilize its full intended allocation for each category

  • May prevent new legitimate vesting schedules from being created

Tools Used

Manual code review

Recommendations

  1. Store the category information in the VestingSchedule struct:

struct VestingSchedule {
uint256 totalAmount;
uint256 releasedAmount;
uint256 startTime;
uint256 duration;
uint256 lastClaimTime;
bool initialized;
+ bytes32 category; // Add category field
}
2. Store the category when creating a vesting schedule:
```diff
function createVestingSchedule(
address beneficiary,
bytes32 category,
uint256 amount,
uint256 startTime // @audit - make sure startTime is in future
) external onlyRole(ORCHESTRATOR_ROLE) whenNotPaused {
...
VestingSchedule storage schedule = vestingSchedules[beneficiary];
schedule.totalAmount = amount;
schedule.startTime = startTime;
schedule.duration = VESTING_DURATION;
schedule.initialized = true;
+ schedule.category = category;
...
  1. Update the emergencyRevoke function to decrement categoryUsed:

function emergencyRevoke(address beneficiary) external onlyRole(EMERGENCY_ROLE) {
VestingSchedule storage schedule = vestingSchedules[beneficiary];
if (!schedule.initialized) revert NoVestingSchedule();
uint256 unreleasedAmount = schedule.totalAmount - schedule.releasedAmount;
bytes32 category = schedule.category;
+ // Decrement category used
+ categoryUsed[category] -= schedule.totalAmount;
delete vestingSchedules[beneficiary];
emit VestingScheduleRevoked(beneficiary);
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

RAACReleaseOrchestrator::emergencyRevoke fails to decrement categoryUsed, causing artificial category over-allocation and rejection of valid vesting schedules

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

RAACReleaseOrchestrator::emergencyRevoke fails to decrement categoryUsed, causing artificial category over-allocation and rejection of valid vesting schedules

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.