Core Contracts

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

Taxation During Emergency Revokes

Overview

The RAACReleaseOrchestrator contract has been identified to have a significant issue regarding the taxation of token transfers during emergency revocation operations. This report outlines the implications of this issue, the technical impact, and recommendations for remediation.

Vulnerability Details

1. Taxation During Emergency Revokes

Description

When tokens are transferred back to the contract during an emergency revoke, they incur taxes, specifically a total of 1.5% (1% swap tax and 0.5% burn tax). This taxation occurs even in crisis scenarios where the intention is to recover tokens for the beneficiary.

Technical Impact

  • Unexpected Token Loss: The application of taxes during emergency operations can lead to unexpected losses of tokens. Beneficiaries may not receive the full amount of tokens they are entitled to, as a portion is deducted due to taxes.

  • Financial Leakage: In crisis situations, such as emergency revokes, the imposition of taxes creates financial leakage. This can undermine the effectiveness of the emergency revoke mechanism, as the intended recovery of tokens is diminished by the tax burden.

  • Distorted Revocation Accounting: The application of taxes complicates the accounting of revoked tokens. It becomes challenging to track the actual amounts being returned to the contract versus what is lost to taxes, leading to potential discrepancies in financial reporting and user expectations.

Vulnerability Proof

The test case test_EmergencyRevokeSendsTokensToContract demonstrates that a 1.5% tax is applied during the revoke process, confirming the existence of this issue.

// 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 token handling in emergency revoke
* @dev Verifies that:
* 1. Tokens are sent back to contract
* 2. Transfer taxes are applied correctly
* 3. Final balances match expected values
*/
function test_EmergencyRevokeSendsTokensToContract() public {
uint256 amount = 1000 ether;
emit log_string("=== Testing Emergency Revoke Token Transfer ===");
emit log_named_uint("Vesting Amount", amount);
// Create vesting schedule
vm.startPrank(owner);
orchestrator.createVestingSchedule(
beneficiary,
TEAM_CATEGORY,
amount,
block.timestamp
);
emit log_string("Vesting schedule created");
// Get initial contract balance
uint256 initialBalance = raacToken.balanceOf(address(orchestrator));
emit log_named_uint("Initial Contract Balance", initialBalance);
// Emergency revoke
orchestrator.emergencyRevoke(beneficiary);
emit log_string("Emergency revoke executed");
vm.stopPrank();
// Check final balance
uint256 finalBalance = raacToken.balanceOf(address(orchestrator));
emit log_named_uint("Final Contract Balance", finalBalance);
// Calculate expected values
uint256 totalTax = (amount * 150) / 10000; // 1.5%
uint256 expectedBalance = initialBalance - totalTax;
emit log_named_uint("Expected Tax Amount", totalTax);
emit log_named_uint("Expected Final Balance", expectedBalance);
// Verify balances
assertLt(
finalBalance,
initialBalance,
"Contract balance should decrease due to tax"
);
assertEq(
finalBalance,
expectedBalance,
"Contract balance should decrease by tax amount"
);
emit log_string("===============================");
}
}

test result:

[PASS] test_EmergencyRevokeSendsTokensToContract() (gas: 158335)
Logs:
=== Test Environment Setup ===
Owner: 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496
Orchestrator: 0x2e234DAe75C793f67A35089C9d99245E1C58470b
Initial Supply: 100000000000000000000000000
Swap Tax Rate: 100
Burn Tax Rate: 50
===============================
=== Testing Emergency Revoke Token Transfer ===
Vesting Amount: 1000000000000000000000
Vesting schedule created
Initial Contract Balance: 98500000000000000000000000
Emergency revoke executed
Final Contract Balance: 98499985000000000000000000
Expected Tax Amount: 15000000000000000000
Expected Final Balance: 98499985000000000000000000
===============================

2. Recommendation

To mitigate this issue, it is recommended to whitelist the orchestrator from transfer taxes during emergency operations. This can be achieved by implementing tax-free recovery transfers. The following code snippet illustrates 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];
// Set the orchestrator as tax-exempt for recovery transfers
token.setTaxExempt(address(this), true);
if (unreleasedAmount > 0) {
raacToken.transfer(address(this), unreleasedAmount); // No tax applied
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
emit VestingScheduleRevoked(beneficiary);
}

Conclusion

The taxation of token transfers during emergency revokes is a significant issue that can lead to unexpected token loss, financial leakage, and distorted accounting. Implementing the recommended changes to exempt the orchestrator from transfer taxes during emergency operations will help ensure that beneficiaries receive the full amount of tokens they are entitled to, maintaining the integrity of the emergency revoke mechanism and user trust in the contract. Addressing this issue is crucial for the effective management of token recovery in crisis scenarios.

Updates

Lead Judging Commences

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