Core Contracts

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

Improper Pre-start Revocation Handling Leads to Inflated Category Allocations

Summary

The RAACReleaseOrchestrator contract fails to properly handle vesting schedule revocations that occur before the start time, leading to inflated category allocations and unnecessary token transfers.

Vulnerability Details

In the emergencyRevoke function:

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); // Unnecessary self-transfer
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
}

The Issue:

  • When a vesting schedule is created, it increases categoryUsed[category]

  • When emergency revoking before vesting starts, the categoryUsed amount is not decreased

  • This means the category allocation remains inflated

  • Future legitimate vestings might be rejected due to CategoryAllocationExceeded()
    Example Attack Scenario:

// Initial state
categoryAllocations[TEAM_CATEGORY] = 18_000_000 ether;
categoryUsed[TEAM_CATEGORY] = 0;
// 1. Create vesting schedule
createVestingSchedule(alice, TEAM_CATEGORY, 10_000_000 ether, futureTime);
// categoryUsed[TEAM_CATEGORY] = 10_000_000 ether
// 2. Emergency revoke before start
emergencyRevoke(alice);
// categoryUsed[TEAM_CATEGORY] still = 10_000_000 ether (should be 0)
// 3. Try to create new legitimate vesting
createVestingSchedule(bob, TEAM_CATEGORY, 10_000_000 ether, futureTime);
// Will revert with CategoryAllocationExceeded if total > 18_000_000

Impact

  • Category allocations remain inflated after pre-start revocations

  • Future legitimate vestings might be rejected due to incorrect allocation tracking

  • Could prevent legitimate users from receiving their vesting allocations

  • Affects core token distribution mechanism

Tools Used

  • Manual code review

Recommendations

Implement proper pre-start revocation handling:

address public recoveryAddress;
function emergencyRevoke(address beneficiary) external onlyRole(EMERGENCY_ROLE) {
VestingSchedule storage schedule = vestingSchedules[beneficiary];
if (!schedule.initialized) revert NoVestingSchedule();
uint256 unreleasedAmount;
bytes32 category = /* get from events or parameter */;
if (block.timestamp < schedule.startTime) {
unreleasedAmount = schedule.totalAmount;
categoryUsed[category] -= schedule.totalAmount;
} else {
unreleasedAmount = schedule.totalAmount - schedule.releasedAmount;
}
delete vestingSchedules[beneficiary];
if (unreleasedAmount > 0) {
raacToken.safeTransfer(recoveryAddress, unreleasedAmount);
emit EmergencyWithdraw(beneficiary, unreleasedAmount, recoveryAddress);
}
emit VestingScheduleRevoked(beneficiary, block.timestamp < schedule.startTime, category);
}
Updates

Lead Judging Commences

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

Give us feedback!