diff --git a/test/Proposal.t.sol b/test/Proposal.t.sol new file mode 100644 index 0000000..1be3b0e --- /dev/null +++ b/test/Proposal.t.sol @@ -0,0 +1,116 @@ +// 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"); + } +} diff --git a/test/TornadoProposalTest.sol b/test/TornadoProposalTest.sol index 279e14a..115d7a3 100644 --- a/test/TornadoProposalTest.sol +++ b/test/TornadoProposalTest.sol @@ -34,6 +34,10 @@ contract TornadoProposalTest is Test, TornadoAddresses { address public constant TEST_RELAYER_ADDRESS = 0x30F96AEF199B399B722F8819c9b0723016CEAe6C; + // Address and private key to test staking, Governance lock and rewards accruals + address public constant TEST_STAKER_ADDRESS = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; + uint256 public constant TEST_STAKER_PRIVATE_KEY = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; + uint256 public constant TEST_PRIVATE_KEY_ONE = 0x66ddbd7cbe4a566df405f6ded0b908c669f88cdb1656380c050e3a457bd21df0; uint256 public constant TEST_PRIVATE_KEY_TWO = 0xa4c8c98120e77741a87a116074a2df4ddb20d1149069290fd4a3d7ee65c55064; @@ -62,7 +66,6 @@ contract TornadoProposalTest is Test, TornadoAddresses { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ TEST UTILS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ function setUp() public virtual { - vm.createSelectFork(vm.envString("MAINNET_RPC_URL")); _fetchConfiguration(); }