Core Contracts

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

Immediate vesting exploit in RAACReleaseOrchestrator#createVestingSchedule()

Summary

The RAACReleaseOrchestrator#createVestingSchedule() allows a beneficiary to set startTime in the past, bypassing the VESTING_CLIFF check. This enables the immediate release of all vested tokens instead of waiting for the cliff and linear vesting schedule.

Vulnerability Details

There's no validation for startTime. If the function does not enforce startTime >= block.timestamp, allowing an attacker to set startTime far in the past.
This skips the vesting cliff and allows instant release of tokens.

function createVestingSchedule(
address beneficiary,
bytes32 category,
uint256 amount,
uint256 startTime
) external onlyRole(ORCHESTRATOR_ROLE) whenNotPaused {
if (beneficiary == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
if (vestingSchedules[beneficiary].initialized) revert VestingAlreadyInitialized();
if (categoryAllocations[category] == 0) revert InvalidCategory();
// Check category allocation limits
uint256 newCategoryTotal = categoryUsed[category] + amount;
if (newCategoryTotal > categoryAllocations[category]) revert CategoryAllocationExceeded();
categoryUsed[category] = newCategoryTotal;
VestingSchedule storage schedule = vestingSchedules[beneficiary];
schedule.totalAmount = amount;
@> schedule.startTime = startTime;
schedule.duration = VESTING_DURATION;
schedule.initialized = true;
emit VestingScheduleCreated(beneficiary, category, amount, startTime);
}

Impact

No waiting period; funds are released instantly by beneficiary.

If exploited, an attacker can withdraw all of tokens immediately without duration periods.

Tools Used

manual

Recommendations

Modify createVestingSchedule() to reject past timestamps:

function createVestingSchedule(
address beneficiary,
bytes32 category,
uint256 amount,
uint256 startTime
) external onlyRole(ORCHESTRATOR_ROLE) whenNotPaused {
...
if (categoryAllocations[category] == 0) revert InvalidCategory();
+ if (startTime < block.timestamp) revert InvalidStartTime();
...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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