Core Contracts

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

Unnecessary tax application causes loss of funds during emergency revoke as the RAACReleaseOrchestrator contract transfers tokens to itself

Summary

The emergencyRevoke function unnecessarily transfers tokens to itself, triggering unnecessary tax fees due to the RAAC token's tax mechanism which reduces the funds available for future distribution.

Vulnerability Details

The RAACReleaseOrchestrator contract has an option to revoke the release of RAAC tokens to a beneficiary but keep in mind that the RAACReleaseOrchestrator contract actually holds RAAC tokens which are being released to the beneficiaries so the issue is that;

When emergency revoke is called, the function transfers unreleased tokens to address(this):

raacToken.transfer(address(this), unreleasedAmount);

Since RAACToken implements tax on transfers (both swap and burn taxes), this self-transfer will unnecessarily incur taxes, reducing the actual amount of tokens retained by the contract.

PoC

  1. Alice has a vesting schedule with 1000 RAAC tokens (unreleased)

  2. Emergency role calls emergencyRevoke on Alice's address

  3. Contract attempts to transfer 1000 tokens to itself

  4. Due to RAAC's tax mechanism (1.5% total tax):

    • 15 tokens are taken as tax

    • Contract ends up with 985 tokens instead of 1000

Impact

Loss of tokens through unnecessary tax application when performing emergency revoke, reducing the total amount available for future distribution.

Tools Used

Manual review

Recommendations

Remove the unnecessary self-transfer since the tokens are already in the contract:

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) {
// no self-transfer needed here, only emit the event.
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
emit VestingScheduleRevoked(beneficiary);
}
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!