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.
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;
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;
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");
bytes32 public constant ORCHESTRATOR_ROLE = keccak256("ORCHESTRATOR_ROLE");
bytes32 public constant EMERGENCY_ROLE = keccak256("EMERGENCY_ROLE");
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);
raacToken = new RAACToken(owner, 100, 50);
raacToken.setMinter(owner);
orchestrator = new RAACReleaseOrchestrator(address(raacToken));
raacToken.mint(address(this), 100_000_000 ether);
raacToken.transfer(address(orchestrator), 100_000_000 ether);
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);
emit log_named_uint("Burn Tax Rate", 50);
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);
vm.startPrank(owner);
orchestrator.createVestingSchedule(
beneficiary,
TEAM_CATEGORY,
amount,
block.timestamp
);
emit log_string("Vesting schedule created");
uint256 initialBalance = raacToken.balanceOf(address(orchestrator));
emit log_named_uint("Initial Contract Balance", initialBalance);
orchestrator.emergencyRevoke(beneficiary);
emit log_string("Emergency revoke executed");
vm.stopPrank();
uint256 finalBalance = raacToken.balanceOf(address(orchestrator));
emit log_named_uint("Final Contract Balance", finalBalance);
uint256 totalTax = (amount * 150) / 10000;
uint256 expectedBalance = initialBalance - totalTax;
emit log_named_uint("Expected Tax Amount", totalTax);
emit log_named_uint("Expected Final Balance", expectedBalance);
assertLt(
finalBalance,
initialBalance,
"Contract balance should decrease due to tax"
);
assertEq(
finalBalance,
expectedBalance,
"Contract balance should decrease by tax amount"
);
emit log_string("===============================");
}
}
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:
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.