pragma solidity ^0.8.19;
import "forge-std/Test.sol";
import {IERC20, ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "../../contracts/libraries/math/PercentageMath.sol";
import "../../contracts/libraries/math/WadRayMath.sol";
import {BaseSetup} from "./BaseSetup.t.sol";
import "../../contracts/interfaces/core/pools/LendingPool/ILendingPool.sol";
import "../../contracts/interfaces/core/governance/proposals/IGovernance.sol";
import "../../contracts/interfaces/core/minters/RAACReleaseOrchestrator/IReleaseOrchestrator.sol";
import "../../contracts/core/collectors/FeeCollector.sol";
import "../../contracts/mocks/core/governance/proposals/TimelockTestTarget.sol";
import "../../contracts/core/governance/proposals/TimelockController.sol";
contract RAACReleaseOrchestratorTest is BaseSetup {
using WadRayMath for uint256;
using PercentageMath for uint256;
using SafeCast for uint256;
using SafeERC20 for IERC20;
IERC20 mainnetUSDC = IERC20(address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48));
address mainnetUsdcCurveUSDVault = address(0x7cA00559B978CFde81297849be6151d3ccB408A9);
address curveUSDWhale = address(0x4e59541306910aD6dC1daC0AC9dFB29bD9F15c67);
address newUser = makeAddr("new user");
address newUser2 = makeAddr("new user2");
address newUser3 = makeAddr("new user3");
address newUser4 = makeAddr("new user4");
function setUp() public override {
string memory MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL");
uint256 mainnetFork = vm.createFork(MAINNET_RPC_URL, 19614507);
vm.selectFork(mainnetFork);
super.setUp();
vm.startPrank(owner);
lendingPool.setParameter(ILendingPool.OwnerParameter.LiquidationThreshold, 8000);
lendingPool.setParameter(ILendingPool.OwnerParameter.HealthFactorLiquidationThreshold, 1e18);
lendingPool.setParameter(ILendingPool.OwnerParameter.LiquidationGracePeriod, 3 days);
lendingPool.setParameter(ILendingPool.OwnerParameter.LiquidityBufferRatio, 2000);
lendingPool.setParameter(ILendingPool.OwnerParameter.WithdrawalStatus, 0);
lendingPool.setParameter(ILendingPool.OwnerParameter.CanPaybackDebt, 1);
uint256 housePrice = 1_000_000e18;
housePrices.setHousePrice(1, housePrice);
housePrices.setHousePrice(2, housePrice);
housePrices.setHousePrice(3, housePrice);
housePrices.setHousePrice(4, housePrice);
housePrices.setHousePrice(5, housePrice);
housePrices.setHousePrice(6, housePrice);
housePrices.setHousePrice(7, housePrice);
housePrices.setHousePrice(8, housePrice);
vm.stopPrank();
vm.startPrank(curveUSDWhale);
crvUSD.transfer(newUser, 15_000_000e18);
crvUSD.transfer(newUser2, 1_000_000e18);
crvUSD.transfer(newUser3, 5_000_000e18);
crvUSD.transfer(newUser4, 5_000_000e18);
crvUSD.transfer(address(stabilityPool), 10_000_000e18);
vm.stopPrank();
}
function test_emergencyRevokeOrchestrator() public {
vm.startPrank(address(minter));
raacToken.mint(address(raacReleaseOrchestrator),65_100_000e18);
uint256 orchestratorOriginalBalance = raacToken.balanceOf(address(raacReleaseOrchestrator));
assertEq(orchestratorOriginalBalance, 65_100_000e18);
assertTrue(raacReleaseOrchestrator.hasRole(raacReleaseOrchestrator.ORCHESTRATOR_ROLE(), owner));
vm.startPrank(owner);
uint256 currentRaacBalOfOwner = raacToken.balanceOf(owner);
address beneficiary1 = makeAddr("beneficiary1");
bytes32 teamCategory = raacReleaseOrchestrator.TEAM_CATEGORY();
uint256 amount = raacReleaseOrchestrator.categoryAllocations(teamCategory);
uint256 startTime = block.timestamp;
raacReleaseOrchestrator.createVestingSchedule(beneficiary1, teamCategory, amount, startTime);
raacReleaseOrchestrator.emergencyRevoke(beneficiary1);
uint256 baseTax = raacToken.swapTaxRate() + raacToken.burnTaxRate();
uint256 totalTax = amount.percentMul(baseTax);
uint256 orchestratorBalanceAfterRevoke = orchestratorOriginalBalance - totalTax;
assertEq(raacToken.balanceOf(address(raacReleaseOrchestrator)),orchestratorBalanceAfterRevoke);
assertEq(raacReleaseOrchestrator.categoryUsed(teamCategory), raacReleaseOrchestrator.categoryAllocations(teamCategory));
vm.expectRevert(IReleaseOrchestrator.CategoryAllocationExceeded.selector);
raacReleaseOrchestrator.createVestingSchedule(beneficiary1, teamCategory, 1, startTime);
}
}
contract RAACReleaseOrchestrator is IReleaseOrchestrator, ReentrancyGuard, AccessControl, Pausable {
+ address public deployer;
// ...omitted for brevity
constructor(address _raacToken) {
+ deployer = msg.sender;
// ...omitted for brevity
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) {
- raacToken.transfer(address(this), unreleasedAmount);
+ raacToken.transfer(deployer, unreleasedAmount); // transfer to any trusted EOA
emit EmergencyWithdraw(beneficiary, unreleasedAmount);
}
emit VestingScheduleRevoked(beneficiary);
}