Core Contracts

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

Lack of Transparency in Emergency Revocation in emergencyRevoke() in RAACReleaseOrchestrator.sol

Summary

The emergencyRevoke function has two key issues:

  1. Tokens are transferred to the contract (address(this)) instead of a designated treasury wallet (TREASURY_WALLET). This could lead to inefficient fund management.

  2. The function does not include a reason parameter, making it harder to track why an emergency revocation was triggered.

Vulnerability Details

function emergencyRevoke(address beneficiary) external onlyRole(EMERGENCY_ROLE) { //@audit-issue : add string memory reason, whenPaused to prevent frontrunning
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); //@audit-issue : transfer to address treasury = TREASURY_WALLET; instead of address(this)
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
emit VestingScheduleRevoked(beneficiary); // add reason to event
}

The function currently transfers unreleased vested tokens to address(this).

If the contract is compromised because some issues, these tokens may become inaccessible.

Instead, the funds should be transferred to a designated treasury wallet (TREASURY_WALLET)

Emergency revocations often happen due to fraud, compliance issues, or admin decisions.

Without a reason parameter, there is no way to track why a revocation occurred, leading to poor transparency and accountability.

Adding a reason (string) to the function and event ensures better documentation and future audits.

Impact

Locked or Mismanaged Funds: If tokens are sent to address(this), they may be permanently locked unless another function allows retrieval.

Transparency & Accountability Issues: Without a reason for revocation, it is difficult to justify emergency actions to stakeholders, auditors, or governance bodies.

Tools Used

Manual Review

Recommendations

Transfer Unreleased Tokens to Treasury (TREASURY_WALLET)

Modify the function signature to include a string memory reason:

Modify the event to contain "reason".

-- function emergencyRevoke(address beneficiary) external onlyRole(EMERGENCY_ROLE) {
++ function emergencyRevoke(address beneficiary, string memory reason) external onlyRole(EMERGENCY_ROLE) whenPaused {
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(TREASURY_WALLET, unreleasedAmount);
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
-- emit VestingScheduleRevoked(beneficiary);
++ emit VestingScheduleRevoked(beneficiary, reason);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RAACReleaseOrchestrator::emergencyRevoke sends revoked tokens to contract address with no withdrawal mechanism, permanently locking funds

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

RAACReleaseOrchestrator::emergencyRevoke sends revoked tokens to contract address with no withdrawal mechanism, permanently locking funds

Support

FAQs

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

Give us feedback!