// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import { TornadoProposalTest } from "./TornadoProposalTest.sol"; import { PenalisationProposal } from "@root/Proposal.sol"; import { IRelayerRegistry } from "@interfaces/IRelayerRegistry.sol"; import { IStakingRewards } from "@interfaces/IStakingRewards.sol"; import { console2 } from "@forge-std/console2.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract TestProposal is TornadoProposalTest { IRelayerRegistry internal relayerRegistry = IRelayerRegistry(getRegistryProxyAddress()); address[7] cheatingRelayers = [ 0x5007565e69E5c23C278c2e976beff38eF4D27B3d, // official-tornado.eth 0x065f2A0eF62878e8951af3c387E4ddC944f1B8F4, // 0xtorn365.eth 0x18F516dD6D5F46b2875Fd822B994081274be2a8b, // torn69.eth 0x30F96AEF199B399B722F8819c9b0723016CEAe6C, // moon-relayer.eth 0xa42303EE9B2eC1DB7E2a86Ed6C24AF7E49E9e8B9, // relayer-tornado.eth 0x2ffAc4D796261ba8964d859867592B952b9FC158, // safe-tornado.eth 0xCEdac436cEA98E93F471331eCC693fF41D730921 // relayer-secure.eth ]; function createAndExecuteProposal() internal { address currentProposalAddress = address(new PenalisationProposal()); uint256 currentProposalId = easyPropose(currentProposalAddress); waitUntilExecutable(currentProposalId); governance.execute(currentProposalId); } function testCheatingRelayersBalanceNullified() public { for (uint8 i = 0; i < cheatingRelayers.length; i++) { console2.log( "Cheating relayer %s balance before penalisation: %s TORN", cheatingRelayers[i], relayerRegistry.getRelayerBalance(cheatingRelayers[i]) / 1e18 ); } console2.log("\n"); createAndExecuteProposal(); for (uint8 i = 0; i < cheatingRelayers.length; i++) { uint256 relayerBalanceAfterPenalisation = relayerRegistry.getRelayerBalance(cheatingRelayers[i]); console2.log( "Cheating relayer %s balance after penalisation: %s TORN", cheatingRelayers[i], relayerBalanceAfterPenalisation / 1e18 ); require(relayerBalanceAfterPenalisation == 0, "Cheating relayers balance didn't nullified"); } } function testHonestRelayersNotAffected() public { address honestRelayerAddress = 0xa0109274F53609f6Be97ec5f3052C659AB80f012; // relayer007.eth uint256 honestRelayerBalanceBeforeExecution = relayerRegistry.getRelayerBalance(honestRelayerAddress); console2.log( "Honest relayer (relayer00.eth) balance before proposal execution: %s TORN", honestRelayerBalanceBeforeExecution / 1e18 ); createAndExecuteProposal(); uint256 honestRelayerBalanceAfterExecution = relayerRegistry.getRelayerBalance(honestRelayerAddress); console2.log("Honest relayer (relayer00.eth) balance after proposal execution: %s TORN", honestRelayerBalanceAfterExecution / 1e18); require(honestRelayerBalanceBeforeExecution == honestRelayerBalanceAfterExecution, "Execution afftected honest relayers"); } function testRelayersBalancesWithdrawedToGovernance() public { uint256 governanceBalanceBeforeExecution = IERC20(getTornTokenAddress()).balanceOf(address(governance)); console2.log("Governance contract balance before proposal execuition: %s TORN", governanceBalanceBeforeExecution / 1e18); uint256 expectedNullifiedAmount = (new PenalisationProposal()).getCheatingRelayersBalanceSum(cheatingRelayers); createAndExecuteProposal(); uint256 governanceBalanceAfterExecution = IERC20(getTornTokenAddress()).balanceOf(address(governance)); console2.log("Governance contract balance after proposal execuition: %s TORN", governanceBalanceAfterExecution / 1e18); require( governanceBalanceAfterExecution == governanceBalanceBeforeExecution + expectedNullifiedAmount - 25_001 ether, "Relayers balances isn't withdrawed" ); } function testBurnedCorrectAmount() public { IStakingRewards staking = IStakingRewards(getStakingProxyAddress()); IERC20 TORN = IERC20(getTornTokenAddress()); uint256 toBurn = 678 * 1e17; // Burn 67.8 TORN retrieveAndLockBalance(TEST_STAKER_PRIVATE_KEY, TEST_STAKER_ADDRESS, 10_000 ether); uint256 stakerLockedBalance = governance.lockedBalance(TEST_STAKER_ADDRESS); require(stakerLockedBalance == 10_000 ether, "Invalid test staker locked balance"); uint256 stakerRewardsBeforeBurning = staking.checkReward(TEST_STAKER_ADDRESS); console2.log("Staking rewards before burning: %s", stakerRewardsBeforeBurning); createAndExecuteProposal(); uint256 stakerRewardsAfterBurning = staking.checkReward(TEST_STAKER_ADDRESS); console2.log("Staking rewards after burning 67.8 TORN: %s\n", stakerRewardsAfterBurning); require(stakerRewardsAfterBurning > stakerRewardsBeforeBurning, "Rewards isn't changed after burning"); // All TORN, locked by users in Governance, is on the userVault contract balance uint256 governanceLockedAmount = TORN.balanceOf(address(governance.userVault())); uint256 receivedReward = stakerRewardsAfterBurning - stakerRewardsBeforeBurning; uint256 expectedRewards = stakerLockedBalance * toBurn / governanceLockedAmount; console2.log("Expected staking rewards: %s", expectedRewards); console2.log("Staker received rewards: %s\n", receivedReward); require(receivedReward == expectedRewards, "Expected and received rewards don't match"); } }