Core Contracts

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

Vesting schedule bypass by setting startTime to arbitrary past value

Summary

In RAACReleaseOrchestrator users with the role "ORCHESTRATOR_ROLE" can create vesting schedules. Due to a lack of parameter validation, it is possible to start the vesting schedule in the past, resulting in a partial or complete bypass of the scheduled token release.

Vulnerability Details

In RAACReleaseOrchestrator.sol:L77, there's a function that allows users with the role "ORCHESTRATOR_ROLE" to create vesting schedules.

Accidentally or intentionally, users may create a vesting schedule that starts in the past, as there is no validation on the function parameter startTime.

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);
}

The variable schedule.startTime is then used to calculate the releasable amount.

function _calculateReleasableAmount(
VestingSchedule memory schedule
) internal view returns (uint256) {
if (block.timestamp < schedule.startTime + VESTING_CLIFF) return 0;
if (block.timestamp < schedule.lastClaimTime + MIN_RELEASE_INTERVAL) return 0;
uint256 timeFromStart = block.timestamp - schedule.startTime;
if (timeFromStart >= schedule.duration) {
return schedule.totalAmount - schedule.releasedAmount;
}
uint256 vestedAmount = (schedule.totalAmount * timeFromStart) / schedule.duration;
return vestedAmount - schedule.releasedAmount;
}

Impact

Affected users can withdraw their vested tokens partially or completely before the vesting process normally would have finished.

Tools Used

  • Manual review

Recommendations

Consider validating the parameter startTime so that it is greater than or equal to the current timestamp.

if (startTime < block.timestamp) revert StartInPast();
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.