pragma solidity ^0.8.19;
import {Test} from "forge-std/Test.sol";
import {RAACToken} from "../../contracts/core/tokens/RAACToken.sol";
import {FeeCollector} from "../../contracts/core/collectors/FeeCollector.sol";
import {veRAACToken} from "../../contracts/core/tokens/veRAACToken.sol";
import {PercentageMath} from "../../contracts/libraries/math/PercentageMath.sol";
import {IFeeCollector} from "../../contracts/interfaces/core/collectors/IFeeCollector.sol";
import {Treasury} from "../../contracts/core/collectors/Treasury.sol";
import {ITreasury} from "../../contracts/interfaces/core/collectors/ITreasury.sol";
import {RAACReleaseOrchestrator} from "../../contracts/core/minters/RAACReleaseOrchestrator/RAACReleaseOrchestrator.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "forge-std/console2.sol";
contract FoundryTest is Test {
using PercentageMath for uint256;
RAACToken public raacToken;
FeeCollector public feeCollector;
veRAACToken public VeRaacToken;
Treasury public treasury;
RAACReleaseOrchestrator public raacReleaseOrchestrator;
address public owner;
address public repairFund;
address public minter;
address public user1;
address public user2;
function setUp() public {
owner = address(this);
minter = makeAddr("minter");
user1 = makeAddr("user1");
user2 = makeAddr("user2");
repairFund = makeAddr("repairFund");
uint256 initialSwapTaxRate = 100;
uint256 initialBurnTaxRate = 50;
treasury = new Treasury(owner);
raacToken = new RAACToken(owner, initialSwapTaxRate, initialBurnTaxRate);
raacReleaseOrchestrator = new RAACReleaseOrchestrator(address(raacToken));
VeRaacToken = new veRAACToken(address(raacToken));
feeCollector = new FeeCollector(
address(raacToken),
address(VeRaacToken),
address(treasury),
address(repairFund),
owner
);
vm.prank(owner);
raacToken.setMinter(minter);
vm.prank(owner);
raacToken.setFeeCollector(address(feeCollector));
}
function test_getTotalAllocation() public view {
uint256 expectedTotalAllocation = 18_000_000 ether +
10_300_000 ether +
5_000_000 ether +
10_000_000 ether +
15_000_000 ether +
6_800_000 ether;
uint256 totalAllocation = raacReleaseOrchestrator.getTotalAllocation();
assertEq(totalAllocation, expectedTotalAllocation);
}
function test_emergencyFlow() public {
address beneficiary = user1;
bytes32 category = raacReleaseOrchestrator.PUBLIC_SALE_CATEGORY();
uint256 amount = 1000 ether;
uint256 startTime = block.timestamp + 30 days;
vm.prank(minter);
raacToken.mint(owner, amount);
vm.startPrank(owner);
raacToken.manageWhitelist(address(raacReleaseOrchestrator), true);
raacToken.approve(address(raacReleaseOrchestrator), amount);
raacToken.transfer(address(raacReleaseOrchestrator), amount);
assertEq(raacToken.balanceOf(address(raacReleaseOrchestrator)), amount);
raacReleaseOrchestrator.createVestingSchedule(beneficiary, category, amount, startTime);
RAACReleaseOrchestrator.VestingSchedule memory schedule = raacReleaseOrchestrator.getVestingSchedule(
beneficiary
);
assertEq(schedule.totalAmount, amount);
assertEq(schedule.releasedAmount, 0);
vm.warp(startTime + 30 days);
raacReleaseOrchestrator.emergencyRevoke(beneficiary);
assertEq(raacToken.balanceOf(address(raacReleaseOrchestrator)), amount);
vm.stopPrank();
vm.prank(beneficiary);
vm.expectRevert();
raacReleaseOrchestrator.release();
}
}
interface IReleaseOrchestrator {
function audit_emergencyRevoke(address beneficiary, address recoveryAddress) external;
}
function audit_emergencyRevoke(address beneficiary, address recoveryAddress) external override onlyRole(EMERGENCY_ROLE) {
require(recoveryAddress != address(0), "Invalid recovery address");
VestingSchedule storage schedule = vestingSchedules[beneficiary];
if (!schedule.initialized) revert NoVestingSchedule();
uint256 vestedAmount = _calculateReleasableAmount(schedule);
if (vestedAmount > 0) {
raacToken.safeTransfer(beneficiary, vestedAmount);
emit TokensReleased(beneficiary, vestedAmount);
}
uint256 unvestedAmount = schedule.totalAmount - schedule.releasedAmount - vestedAmount;
if (unvestedAmount > 0) {
raacToken.safeTransfer(recoveryAddress, unvestedAmount);
}
delete vestingSchedules[beneficiary];
emit VestingScheduleRevoked(beneficiary);
}