Core Contracts

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

Orphaned Allocation Tracking

Overview

The RAACReleaseOrchestrator contract is responsible for managing the vesting and release of RAAC tokens. A critical issue has been identified regarding the handling of category usage metrics during the emergency revocation of vesting schedules. This report outlines the implications of this issue, the technical impact, and recommendations for remediation.

Vulnerability Details

1. Orphaned Allocation Tracking

Description

The emergencyRevoke function does not update the categoryUsed mapping when a vesting schedule is revoked. This oversight leads to orphaned allocations, which can create significant issues in the contract's financial management and reporting.

Technical Impact

  • Zombie Allocations: When a vesting schedule is revoked, the tokens are returned to the contract, but the corresponding category usage metrics are not adjusted. This results in "zombie" allocations that cannot be reused for new vesting schedules, effectively blocking legitimate usage of the allocated tokens.

  • Distorted Financial Reporting: The failure to update the categoryUsed mapping can lead to inaccurate financial reporting metrics. Stakeholders may be misled about the actual availability of tokens for allocation, impacting decision-making and trust in the contract.

  • Hidden Insolvency: The inability to accurately track category usage can enable hidden insolvency scenarios. If the contract appears to have more tokens allocated than it actually does (due to double-counting), it may lead to situations where the contract cannot fulfill its obligations, undermining its financial integrity.

Problem Code

The following code snippet from the emergencyRevoke function illustrates the issue:

function emergencyRevoke(...) external {
// No categoryUsed reduction
token.safeTransferFrom(beneficiary, address(this), releasable);
}

2. Test Case Highlighting the Issue

The test case test_EmergencyRevokeDoesntUpdateCategory confirms that the categoryUsed mapping is not updated upon revocation, leading to the orphaned allocation issue.

3. Recommendation

To address this issue, it is essential to update the categoryUsed mapping during the revocation process. The following code snippet demonstrates how to implement this change:

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];
// Update category tracking on revocation
categoryUsed[category] -= unreleasedAmount; // Adjust category usage
if (unreleasedAmount > 0) {
raacToken.transfer(address(this), unreleasedAmount);
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
emit VestingScheduleRevoked(beneficiary);
}

4. PoC

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import "../contracts/core/minters/RAACReleaseOrchestrator/RAACReleaseOrchestrator.sol";
import "../contracts/core/tokens/RAACToken.sol";
/**
* @title RAACReleaseOrchestratorVulnerabilitiesTest
* @notice Test suite for identifying and verifying vulnerabilities in the RAACReleaseOrchestrator contract
* @dev These tests focus on edge cases and potential security issues in the vesting mechanism
* Each test simulates real-world scenarios and potential attack vectors
*/
contract RAACReleaseOrchestratorVulnerabilitiesTest is Test {
RAACReleaseOrchestrator public orchestrator;
RAACToken public raacToken;
address public owner;
address public beneficiary;
// Constants from RAACReleaseOrchestrator
uint256 public constant VESTING_CLIFF = 90 days;
uint256 public constant VESTING_DURATION = 700 days;
uint256 public constant MIN_RELEASE_INTERVAL = 1 days;
uint256 public constant GRACE_PERIOD = 7 days;
// Category constants
bytes32 public constant TEAM_CATEGORY = keccak256("TEAM");
bytes32 public constant ADVISOR_CATEGORY = keccak256("ADVISOR");
bytes32 public constant TREASURY_CATEGORY = keccak256("TREASURY");
bytes32 public constant PRIVATE_SALE_CATEGORY = keccak256("PRIVATE_SALE");
bytes32 public constant PUBLIC_SALE_CATEGORY = keccak256("PUBLIC_SALE");
bytes32 public constant LIQUIDITY_CATEGORY = keccak256("LIQUIDITY");
// Role constants
bytes32 public constant ORCHESTRATOR_ROLE = keccak256("ORCHESTRATOR_ROLE");
bytes32 public constant EMERGENCY_ROLE = keccak256("EMERGENCY_ROLE");
// Additional test addresses
address public treasury;
address public advisor;
address public teamMember;
event VestingScheduleCreated(
address indexed beneficiary,
bytes32 indexed category,
uint256 amount,
uint256 startTime
);
event EmergencyWithdraw(address indexed beneficiary, uint256 amount);
event VestingScheduleRevoked(address indexed beneficiary);
/**
* @notice Set up the test environment
* @dev Deploys fresh instances of RAAC token and ReleaseOrchestrator
* Configures initial roles and token distribution
* Sets up test addresses for different stakeholders
*/
function setUp() public {
owner = address(this);
beneficiary = address(0x1);
treasury = address(0x2);
advisor = address(0x3);
teamMember = address(0x4);
// Deploy RAAC token with initial parameters
// owner, initialSwapTaxRate (1%), initialBurnTaxRate (0.5%)
raacToken = new RAACToken(owner, 100, 50);
// Set minter role to owner
raacToken.setMinter(owner);
// Deploy RAACReleaseOrchestrator
orchestrator = new RAACReleaseOrchestrator(address(raacToken));
// Transfer tokens to orchestrator for vesting
raacToken.mint(address(this), 100_000_000 ether);
raacToken.transfer(address(orchestrator), 100_000_000 ether);
// Log initial setup and configuration
emit log_string("=== Test Environment Setup ===");
emit log_named_address("Owner", owner);
emit log_named_address("Orchestrator", address(orchestrator));
emit log_named_uint("Initial Supply", 100_000_000 ether);
emit log_named_uint("Swap Tax Rate", 100); // 1%
emit log_named_uint("Burn Tax Rate", 50); // 0.5%
emit log_string("===============================");
}
/**
* @notice Test emergency revoke's effect on category allocation
* @dev Verifies that:
* 1. Category allocation tracking is not updated after revoke
* 2. This represents a vulnerability in category management
* 3. Funds can be double-allocated after revoke
*/
function test_EmergencyRevokeDoesntUpdateCategory() public {
uint256 amount = 1000 ether;
emit log_string("=== Testing Emergency Revoke Category Tracking ===");
emit log_named_uint("Initial Amount", amount);
// Create vesting schedule
vm.startPrank(owner);
orchestrator.createVestingSchedule(
beneficiary,
TEAM_CATEGORY,
amount,
block.timestamp
);
emit log_string("Vesting schedule created");
// Get initial category used
uint256 initialCategoryUsed = orchestrator.categoryUsed(TEAM_CATEGORY);
emit log_named_uint("Initial Category Used", initialCategoryUsed);
// Emergency revoke
orchestrator.emergencyRevoke(beneficiary);
emit log_string("Emergency revoke executed");
vm.stopPrank();
// Verify category tracking
uint256 finalCategoryUsed = orchestrator.categoryUsed(TEAM_CATEGORY);
emit log_named_uint("Final Category Used", finalCategoryUsed);
// Category used should still be the same
assertEq(
finalCategoryUsed,
initialCategoryUsed,
"Category used should be updated after revoke"
);
emit log_string("Vulnerability confirmed: Category tracking not updated");
emit log_string("===============================");
}
}

Conclusion

The lack of updates to the categoryUsed mapping during the emergency revocation of vesting schedules is a significant issue that can lead to orphaned allocations, distorted financial reporting, and potential hidden insolvency. Implementing the recommended changes will ensure accurate tracking of category usage and maintain the integrity of the contract's financial management. Addressing this issue is crucial for maintaining user trust and ensuring the contract operates as intended.

Updates

Lead Judging Commences

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