From 0891ce829b4606a9c7ac8ccec9cb727e02db35aa Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Mon, 22 Apr 2024 20:18:08 +0700 Subject: [PATCH 1/8] feat: setGovernance tested --- test/unit/EmergencyProtectedTimelock.t.sol | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 test/unit/EmergencyProtectedTimelock.t.sol diff --git a/test/unit/EmergencyProtectedTimelock.t.sol b/test/unit/EmergencyProtectedTimelock.t.sol new file mode 100644 index 00000000..11c075d9 --- /dev/null +++ b/test/unit/EmergencyProtectedTimelock.t.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {Test, Vm} from "forge-std/Test.sol"; + +import {EmergencyProtectedTimelock} from "contracts/EmergencyProtectedTimelock.sol"; +import {IConfiguration, Configuration} from "contracts/Configuration.sol"; +import {ConfigurationProvider} from "contracts/ConfigurationProvider.sol"; + +import "forge-std/console.sol"; + +contract EmergencyProtectedTimelockUnitTests is Test { + EmergencyProtectedTimelock private _timelock; + Configuration private _config; + + address private _emergencyGovernance = makeAddr("emergencyGovernance"); + address private _dualGovernance = makeAddr("dualGovernance"); + address private _executor = makeAddr("executor"); + + function setUp() external { + _config = new Configuration(_executor, _emergencyGovernance, new address[](0)); + _timelock = new EmergencyProtectedTimelock(address(_config)); + } + + function test_admin_executor_can_set_governance() external { + vm.recordLogs(); + + assertEq(_timelock.getGovernance(), address(0)); + + vm.prank(_executor); + _timelock.setGovernance(_dualGovernance); + + assertEq(_timelock.getGovernance(), _dualGovernance); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 1); + assertEq(entries[0].topics[0], EmergencyProtectedTimelock.GovernanceSet.selector); + assertEq(abi.decode(entries[0].data, (address)), _dualGovernance); + } + + function test_cannot_set_governance_to_zero() external { + vm.prank(_executor); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.InvalidGovernance.selector, address(0))); + _timelock.setGovernance(address(0)); + } + + function test_cannot_set_governance_to_the_same_address() external { + vm.startPrank(_executor); + + _timelock.setGovernance(_dualGovernance); + assertEq(_timelock.getGovernance(), _dualGovernance); + + vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.InvalidGovernance.selector, _dualGovernance)); + _timelock.setGovernance(_dualGovernance); + assertEq(_timelock.getGovernance(), _dualGovernance); + + vm.stopPrank(); + } + + function testFuzz_stranger_cannot_set_governance(address stranger) external { + vm.assume(stranger != _executor); + + vm.prank(stranger); + vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); + _timelock.setGovernance(makeAddr("newGovernance")); + } +} From 976a1d7c1a3ca4de378f4b830c74cabaeb72445e Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Mon, 22 Apr 2024 21:16:26 +0700 Subject: [PATCH 2/8] feat: transferExecutorOwnership tested --- test/unit/EmergencyProtectedTimelock.t.sol | 60 +++++++++++++++++++--- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/test/unit/EmergencyProtectedTimelock.t.sol b/test/unit/EmergencyProtectedTimelock.t.sol index 11c075d9..da891896 100644 --- a/test/unit/EmergencyProtectedTimelock.t.sol +++ b/test/unit/EmergencyProtectedTimelock.t.sol @@ -2,7 +2,9 @@ pragma solidity 0.8.23; import {Test, Vm} from "forge-std/Test.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Executor} from "contracts/Executor.sol"; import {EmergencyProtectedTimelock} from "contracts/EmergencyProtectedTimelock.sol"; import {IConfiguration, Configuration} from "contracts/Configuration.sol"; import {ConfigurationProvider} from "contracts/ConfigurationProvider.sol"; @@ -15,37 +17,43 @@ contract EmergencyProtectedTimelockUnitTests is Test { address private _emergencyGovernance = makeAddr("emergencyGovernance"); address private _dualGovernance = makeAddr("dualGovernance"); - address private _executor = makeAddr("executor"); + address private _adminExecutor = makeAddr("executor"); function setUp() external { - _config = new Configuration(_executor, _emergencyGovernance, new address[](0)); + _config = new Configuration(_adminExecutor, _emergencyGovernance, new address[](0)); _timelock = new EmergencyProtectedTimelock(address(_config)); } - function test_admin_executor_can_set_governance() external { - vm.recordLogs(); + // EmergencyProtectedTimelock.setGovernance() + function test_admin_executor_can_set_governance() external { assertEq(_timelock.getGovernance(), address(0)); - vm.prank(_executor); + vm.recordLogs(); + + vm.prank(_adminExecutor); _timelock.setGovernance(_dualGovernance); assertEq(_timelock.getGovernance(), _dualGovernance); Vm.Log[] memory entries = vm.getRecordedLogs(); assertEq(entries.length, 1); + assertEq(entries[0].emitter, address(_timelock)); assertEq(entries[0].topics[0], EmergencyProtectedTimelock.GovernanceSet.selector); + // There is no topic with value in the event (foundry bug??) + assertEq(entries[0].topics.length, 1); + assertEq(abi.decode(entries[0].data, (address)), _dualGovernance); } function test_cannot_set_governance_to_zero() external { - vm.prank(_executor); + vm.prank(_adminExecutor); vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.InvalidGovernance.selector, address(0))); _timelock.setGovernance(address(0)); } function test_cannot_set_governance_to_the_same_address() external { - vm.startPrank(_executor); + vm.startPrank(_adminExecutor); _timelock.setGovernance(_dualGovernance); assertEq(_timelock.getGovernance(), _dualGovernance); @@ -58,10 +66,46 @@ contract EmergencyProtectedTimelockUnitTests is Test { } function testFuzz_stranger_cannot_set_governance(address stranger) external { - vm.assume(stranger != _executor); + vm.assume(stranger != _adminExecutor); vm.prank(stranger); vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); _timelock.setGovernance(makeAddr("newGovernance")); } + + // EmergencyProtectedTimelock.transferExecutorOwnership() + + function testFuzz_admin_executor_can_transfer_executor_ownership(address newOwner) external { + vm.assume(newOwner != _adminExecutor); + vm.assume(newOwner != address(0)); + + Executor executor = new Executor(address(_timelock)); + + assertEq(executor.owner(), address(_timelock)); + + vm.recordLogs(); + + vm.prank(_adminExecutor); + _timelock.transferExecutorOwnership(address(executor), newOwner); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 1); + assertEq(entries[0].emitter, address(executor)); + assertEq(entries[0].topics[0], Ownable.OwnershipTransferred.selector); + assertEq(entries[0].topics[1], bytes32(uint256(uint160(address(_timelock))))); + assertEq(entries[0].topics[2], bytes32(uint256(uint160(newOwner)))); + + // There is no data in the event (foundry bug??) + assertEq(bytes32(entries[0].data), bytes32(uint256(0))); + + assertEq(executor.owner(), newOwner); + } + + function test_stranger_cannot_transfer_executor_ownership(address stranger) external { + vm.assume(stranger != _adminExecutor); + + vm.prank(stranger); + vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); + _timelock.transferExecutorOwnership(_adminExecutor, makeAddr("newOwner")); + } } From fd066faf042126601e8d3cd229c7fe966343807c Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Wed, 24 Apr 2024 21:56:36 +0700 Subject: [PATCH 3/8] feat: emergency protected timelock tested --- contracts/EmergencyProtectedTimelock.sol | 4 - contracts/libraries/Proposals.sol | 15 +- test/unit/EmergencyProtectedTimelock.t.sol | 844 +++++++++++++++++++-- test/utils/interfaces.sol | 6 + test/utils/scenario-test-blueprint.sol | 10 +- 5 files changed, 817 insertions(+), 62 deletions(-) diff --git a/contracts/EmergencyProtectedTimelock.sol b/contracts/EmergencyProtectedTimelock.sol index c4f314cc..3686e0ab 100644 --- a/contracts/EmergencyProtectedTimelock.sol +++ b/contracts/EmergencyProtectedTimelock.sol @@ -14,11 +14,8 @@ contract EmergencyProtectedTimelock is ConfigurationProvider { error InvalidGovernance(address governance); error NotGovernance(address account, address governance); - error SchedulingDisabled(); - error UnscheduledExecutionForbidden(); event GovernanceSet(address governance); - event ProposalLaunched(address indexed proposer, address indexed executor, uint256 indexed proposalId); address internal _governance; @@ -30,7 +27,6 @@ contract EmergencyProtectedTimelock is ConfigurationProvider { function submit(address executor, ExecutorCall[] calldata calls) external returns (uint256 newProposalId) { _checkGovernance(msg.sender); newProposalId = _proposals.submit(executor, calls); - emit ProposalLaunched(msg.sender, executor, newProposalId); } function schedule(uint256 proposalId) external { diff --git a/contracts/libraries/Proposals.sol b/contracts/libraries/Proposals.sol index 0cbb4317..7b0e1ee0 100644 --- a/contracts/libraries/Proposals.sol +++ b/contracts/libraries/Proposals.sol @@ -10,7 +10,7 @@ enum Status { Submitted, Scheduled, Executed, - Canceled + Cancelled } struct Proposal { @@ -34,12 +34,12 @@ library Proposals { struct State { // any proposals with ids less or equal to the given one cannot be executed - uint256 lastCanceledProposalId; + uint256 lastCancelledProposalId; ProposalPacked[] proposals; } error EmptyCalls(); - error ProposalCanceled(uint256 proposalId); + error ProposalCancelled(uint256 proposalId); error ProposalNotFound(uint256 proposalId); error ProposalNotScheduled(uint256 proposalId); error ProposalNotSubmitted(uint256 proposalId); @@ -49,7 +49,7 @@ library Proposals { event ProposalScheduled(uint256 indexed id); event ProposalSubmitted(uint256 indexed id, address indexed executor, ExecutorCall[] calls); event ProposalExecuted(uint256 indexed id, bytes[] callResults); - event ProposalsCanceledTill(uint256 proposalId); + event ProposalsCancelledTill(uint256 proposalId); // The id of the first proposal uint256 private constant PROPOSAL_ID_OFFSET = 1; @@ -97,8 +97,8 @@ library Proposals { function cancelAll(State storage self) internal { uint256 lastProposalId = self.proposals.length; - self.lastCanceledProposalId = lastProposalId; - emit ProposalsCanceledTill(lastProposalId); + self.lastCancelledProposalId = lastProposalId; + emit ProposalsCancelledTill(lastProposalId); } function get(State storage self, uint256 proposalId) internal view returns (Proposal memory proposal) { @@ -109,6 +109,7 @@ library Proposals { proposal.status = _getProposalStatus(self, proposalId); proposal.executor = packed.executor; proposal.submittedAt = packed.submittedAt; + proposal.scheduledAt = packed.scheduledAt; proposal.executedAt = packed.executedAt; proposal.calls = packed.calls; } @@ -202,7 +203,7 @@ library Proposals { ProposalPacked storage packed = _packed(self, proposalId); if (packed.executedAt != 0) return Status.Executed; - if (proposalId <= self.lastCanceledProposalId) return Status.Canceled; + if (proposalId <= self.lastCancelledProposalId) return Status.Cancelled; if (packed.scheduledAt != 0) return Status.Scheduled; if (packed.submittedAt != 0) return Status.Submitted; assert(false); diff --git a/test/unit/EmergencyProtectedTimelock.t.sol b/test/unit/EmergencyProtectedTimelock.t.sol index da891896..3179fe0d 100644 --- a/test/unit/EmergencyProtectedTimelock.t.sol +++ b/test/unit/EmergencyProtectedTimelock.t.sol @@ -8,42 +8,231 @@ import {Executor} from "contracts/Executor.sol"; import {EmergencyProtectedTimelock} from "contracts/EmergencyProtectedTimelock.sol"; import {IConfiguration, Configuration} from "contracts/Configuration.sol"; import {ConfigurationProvider} from "contracts/ConfigurationProvider.sol"; +import {Executor} from "contracts/Executor.sol"; +import {Proposal, Proposals, ExecutorCall, Status} from "contracts/libraries/Proposals.sol"; +import {EmergencyProtection, EmergencyState} from "contracts/libraries/EmergencyProtection.sol"; -import "forge-std/console.sol"; +import {TargetMock} from "test/utils/utils.sol"; +import {ExecutorCallHelpers} from "test/utils/executor-calls.sol"; +import {IDangerousContract} from "test/utils/interfaces.sol"; contract EmergencyProtectedTimelockUnitTests is Test { EmergencyProtectedTimelock private _timelock; Configuration private _config; + TargetMock private _targetMock; + Executor private _executor; + + address private _emergencyActivator = makeAddr("EMERGENCY_ACTIVATION_COMMITTEE"); + address private _emergencyEnactor = makeAddr("EMERGENCY_EXECUTION_COMMITTEE"); + uint256 private _emergencyModeDuration = 180 days; + uint256 private _emergencyProtectionDuration = 90 days; - address private _emergencyGovernance = makeAddr("emergencyGovernance"); - address private _dualGovernance = makeAddr("dualGovernance"); - address private _adminExecutor = makeAddr("executor"); + address private _emergencyGovernance = makeAddr("EMERGENCY_GOVERNANCE"); + address private _dualGovernance = makeAddr("DUAL_GOVERNANCE"); + address private _adminExecutor; function setUp() external { - _config = new Configuration(_adminExecutor, _emergencyGovernance, new address[](0)); + _executor = new Executor(address(this)); + _config = new Configuration(address(_executor), _emergencyGovernance, new address[](0)); _timelock = new EmergencyProtectedTimelock(address(_config)); + _targetMock = new TargetMock(); + + _executor.transferOwnership(address(_timelock)); + _adminExecutor = address(_executor); + + vm.startPrank(_adminExecutor); + _timelock.setGovernance(_dualGovernance); + _timelock.setEmergencyProtection( + _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration + ); + vm.stopPrank(); + } + + // EmergencyProtectedTimelock.submit() + + function testFuzz_stranger_cannot_submit_proposal(address stranger) external { + vm.assume(stranger != _dualGovernance); + + vm.prank(stranger); + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtectedTimelock.NotGovernance.selector, [stranger, _dualGovernance]) + ); + _timelock.submit(_adminExecutor, new ExecutorCall[](0)); + assertEq(_timelock.getProposalsCount(), 0); + } + + function test_governance_can_submit_proposal() external { + vm.prank(_dualGovernance); + _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls()); + + assertEq(_timelock.getProposalsCount(), 1); + + Proposal memory proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Submitted); + } + + // EmergencyProtectedTimelock.schedule() + + function test_governance_can_schedule_proposal() external { + _submitProposal(); + + assertEq(_timelock.getProposalsCount(), 1); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + + _scheduleProposal(1); + + Proposal memory proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Scheduled); + } + + function testFuzz_stranger_cannot_schedule_proposal(address stranger) external { + vm.assume(stranger != _dualGovernance); + vm.assume(stranger != address(0)); + + _submitProposal(); + + vm.prank(stranger); + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtectedTimelock.NotGovernance.selector, [stranger, _dualGovernance]) + ); + _timelock.schedule(1); + + Proposal memory proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Submitted); + } + + // EmergencyProtectedTimelock.execute() + + function testFuzz_anyone_can_execute_proposal(address stranger) external { + vm.assume(stranger != _dualGovernance); + vm.assume(stranger != address(0)); + + _submitProposal(); + assertEq(_timelock.getProposalsCount(), 1); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + + _scheduleProposal(1); + + vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + + vm.prank(stranger); + _timelock.execute(1); + + Proposal memory proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Executed); + } + + function test_cannot_execute_proposal_if_emergency_mode_active() external { + _submitProposal(); + + assertEq(_timelock.getProposalsCount(), 1); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _scheduleProposal(1); + + vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + + _activateEmergencyMode(); + + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeActiveValue.selector, [true, false]) + ); + _timelock.execute(1); + + Proposal memory proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Scheduled); + } + + // EmergencyProtectedTimelock.cancelAllNonExecutedProposals() + + function test_governance_can_cancel_all_non_executed_proposals() external { + ExecutorCall[] memory executorCalls = _getTargetRegularStaffCalls(); + + _submitProposal(); + _submitProposal(); + + assertEq(_timelock.getProposalsCount(), 2); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + + _scheduleProposal(1); + + Proposal memory proposal1 = _timelock.getProposal(1); + Proposal memory proposal2 = _timelock.getProposal(2); + + assert(proposal1.status == Status.Scheduled); + assert(proposal2.status == Status.Submitted); + + vm.prank(_dualGovernance); + _timelock.cancelAllNonExecutedProposals(); + + proposal1 = _timelock.getProposal(1); + proposal2 = _timelock.getProposal(2); + + assertEq(_timelock.getProposalsCount(), 2); + assert(proposal1.status == Status.Cancelled); + assert(proposal2.status == Status.Cancelled); + } + + function testFuzz_stranger_cannot_cancel_all_non_executed_proposals(address stranger) external { + vm.assume(stranger != _dualGovernance); + vm.assume(stranger != address(0)); + + vm.prank(stranger); + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtectedTimelock.NotGovernance.selector, [stranger, _dualGovernance]) + ); + _timelock.cancelAllNonExecutedProposals(); + } + + // EmergencyProtectedTimelock.transferExecutorOwnership() + + function testFuzz_admin_executor_can_transfer_executor_ownership(address newOwner) external { + vm.assume(newOwner != _adminExecutor); + vm.assume(newOwner != address(0)); + + Executor executor = new Executor(address(_timelock)); + + assertEq(executor.owner(), address(_timelock)); + + vm.prank(_adminExecutor); + + vm.expectEmit(address(executor)); + emit Ownable.OwnershipTransferred(address(_timelock), newOwner); + + _timelock.transferExecutorOwnership(address(executor), newOwner); + + assertEq(executor.owner(), newOwner); + } + + function test_stranger_cannot_transfer_executor_ownership(address stranger) external { + vm.assume(stranger != _adminExecutor); + + vm.prank(stranger); + vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); + _timelock.transferExecutorOwnership(_adminExecutor, makeAddr("newOwner")); } // EmergencyProtectedTimelock.setGovernance() - function test_admin_executor_can_set_governance() external { - assertEq(_timelock.getGovernance(), address(0)); + function testFuzz_admin_executor_can_set_governance(address newGovernance) external { + vm.assume(newGovernance != _dualGovernance); + vm.assume(newGovernance != address(0)); - vm.recordLogs(); + vm.expectEmit(address(_timelock)); + emit EmergencyProtectedTimelock.GovernanceSet(newGovernance); + vm.recordLogs(); vm.prank(_adminExecutor); - _timelock.setGovernance(_dualGovernance); + _timelock.setGovernance(newGovernance); - assertEq(_timelock.getGovernance(), _dualGovernance); + assertEq(_timelock.getGovernance(), newGovernance); Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 1); - assertEq(entries[0].emitter, address(_timelock)); - assertEq(entries[0].topics[0], EmergencyProtectedTimelock.GovernanceSet.selector); - // There is no topic with value in the event (foundry bug??) - assertEq(entries[0].topics.length, 1); - assertEq(abi.decode(entries[0].data, (address)), _dualGovernance); + assertEq(entries.length, 1); } function test_cannot_set_governance_to_zero() external { @@ -53,16 +242,12 @@ contract EmergencyProtectedTimelockUnitTests is Test { } function test_cannot_set_governance_to_the_same_address() external { - vm.startPrank(_adminExecutor); - - _timelock.setGovernance(_dualGovernance); - assertEq(_timelock.getGovernance(), _dualGovernance); - + address currentGovernance = _timelock.getGovernance(); + vm.prank(_adminExecutor); vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.InvalidGovernance.selector, _dualGovernance)); - _timelock.setGovernance(_dualGovernance); - assertEq(_timelock.getGovernance(), _dualGovernance); + _timelock.setGovernance(currentGovernance); - vm.stopPrank(); + assertEq(_timelock.getGovernance(), currentGovernance); } function testFuzz_stranger_cannot_set_governance(address stranger) external { @@ -73,39 +258,612 @@ contract EmergencyProtectedTimelockUnitTests is Test { _timelock.setGovernance(makeAddr("newGovernance")); } - // EmergencyProtectedTimelock.transferExecutorOwnership() + // EmergencyProtectedTimelock.activateEmergencyMode() - function testFuzz_admin_executor_can_transfer_executor_ownership(address newOwner) external { - vm.assume(newOwner != _adminExecutor); - vm.assume(newOwner != address(0)); + function test_emergency_activator_can_activate_emergency_mode() external { + vm.prank(_emergencyActivator); + _timelock.activateEmergencyMode(); - Executor executor = new Executor(address(_timelock)); + assertEq(_isEmergencyStateActivated(), true); + } - assertEq(executor.owner(), address(_timelock)); + function testFuzz_stranger_cannot_activate_emergency_mode(address stranger) external { + vm.assume(stranger != _emergencyActivator); + vm.assume(stranger != address(0)); - vm.recordLogs(); + vm.prank(stranger); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyActivator.selector, stranger)); + _timelock.activateEmergencyMode(); + + assertEq(_isEmergencyStateActivated(), false); + } + + function test_cannot_activate_emergency_mode_if_already_active() external { + _activateEmergencyMode(); + + assertEq(_isEmergencyStateActivated(), true); + + vm.prank(_emergencyActivator); + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeActiveValue.selector, [true, false]) + ); + _timelock.activateEmergencyMode(); + + assertEq(_isEmergencyStateActivated(), true); + } + + // EmergencyProtectedTimelock.emergencyExecute() + + function test_emergency_executior_can_execute_proposal() external { + _submitProposal(); + + assertEq(_timelock.getProposalsCount(), 1); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + + _scheduleProposal(1); + + vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + + _activateEmergencyMode(); + + assertEq(_isEmergencyStateActivated(), true); + + vm.prank(_emergencyEnactor); + _timelock.emergencyExecute(1); + + Proposal memory proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Executed); + } + + function test_cannot_emergency_execute_proposal_if_mode_not_activated() external { + vm.startPrank(_dualGovernance); + _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls()); + + assertEq(_timelock.getProposalsCount(), 1); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _timelock.schedule(1); + + vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + vm.stopPrank(); + + EmergencyState memory state = _timelock.getEmergencyState(); + assertEq(state.isEmergencyModeActivated, false); + + vm.prank(_emergencyActivator); + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeActiveValue.selector, [false, true]) + ); + _timelock.emergencyExecute(1); + } + + function testFuzz_stranger_cannot_emergency_execute_proposal(address stranger) external { + vm.assume(stranger != _emergencyEnactor); + vm.assume(stranger != address(0)); + + _submitProposal(); + + assertEq(_timelock.getProposalsCount(), 1); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + + _scheduleProposal(1); + + vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + + _activateEmergencyMode(); + + assertEq(_isEmergencyStateActivated(), true); + + vm.prank(stranger); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, stranger)); + _timelock.emergencyExecute(1); + } + + // EmergencyProtectedTimelock.deactivateEmergencyMode() + + function test_admin_executor_can_deactivate_emergency_mode_if_delay_not_passed() external { + _activateEmergencyMode(); vm.prank(_adminExecutor); - _timelock.transferExecutorOwnership(address(executor), newOwner); + _timelock.deactivateEmergencyMode(); - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 1); - assertEq(entries[0].emitter, address(executor)); - assertEq(entries[0].topics[0], Ownable.OwnershipTransferred.selector); - assertEq(entries[0].topics[1], bytes32(uint256(uint160(address(_timelock))))); - assertEq(entries[0].topics[2], bytes32(uint256(uint160(newOwner)))); + assertEq(_isEmergencyStateActivated(), false); + } - // There is no data in the event (foundry bug??) - assertEq(bytes32(entries[0].data), bytes32(uint256(0))); + function test_after_deactivation_all_proposals_are_cancelled() external { + _submitProposal(); - assertEq(executor.owner(), newOwner); + assertEq(_timelock.getProposalsCount(), 1); + + Proposal memory proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Submitted); + + _activateEmergencyMode(); + + _deactivateEmergencyMode(); + + proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Cancelled); } - function test_stranger_cannot_transfer_executor_ownership(address stranger) external { + function testFuzz_stranger_can_deactivate_emergency_mode_if_passed(address stranger) external { vm.assume(stranger != _adminExecutor); + _activateEmergencyMode(); + + EmergencyState memory state = _timelock.getEmergencyState(); + assertEq(_isEmergencyStateActivated(), true); + + vm.warp(state.emergencyModeEndsAfter + 1); + + vm.prank(stranger); + _timelock.deactivateEmergencyMode(); + + state = _timelock.getEmergencyState(); + assertEq(_isEmergencyStateActivated(), false); + } + + function testFuzz_cannot_deactivate_emergency_mode_if_not_activated(address stranger) external { + vm.assume(stranger != _adminExecutor); + + vm.prank(stranger); + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeActiveValue.selector, [false, true]) + ); + _timelock.deactivateEmergencyMode(); + + vm.prank(_adminExecutor); + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeActiveValue.selector, [false, true]) + ); + _timelock.deactivateEmergencyMode(); + } + + function testFuzz_stranger_cannot_deactivate_emergency_mode_if_not_passed(address stranger) external { + vm.assume(stranger != _adminExecutor); + + _activateEmergencyMode(); + assertEq(_isEmergencyStateActivated(), true); + vm.prank(stranger); vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); - _timelock.transferExecutorOwnership(_adminExecutor, makeAddr("newOwner")); + _timelock.deactivateEmergencyMode(); + } + + // EmergencyProtectedTimelock.emergencyReset() + + function test_execution_committee_can_emergency_reset() external { + _activateEmergencyMode(); + assertEq(_isEmergencyStateActivated(), true); + assertEq(_timelock.isEmergencyProtectionEnabled(), true); + + vm.prank(_emergencyEnactor); + _timelock.emergencyReset(); + + EmergencyState memory newState = _timelock.getEmergencyState(); + + assertEq(_isEmergencyStateActivated(), false); + assertEq(_timelock.getGovernance(), _emergencyGovernance); + assertEq(_timelock.isEmergencyProtectionEnabled(), false); + + assertEq(newState.activationCommittee, address(0)); + assertEq(newState.executionCommittee, address(0)); + assertEq(newState.protectedTill, 0); + assertEq(newState.emergencyModeDuration, 0); + assertEq(newState.emergencyModeEndsAfter, 0); + } + + function test_after_emergency_reset_all_proposals_are_cancelled() external { + _submitProposal(); + _activateEmergencyMode(); + + Proposal memory proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Submitted); + + vm.prank(_emergencyEnactor); + _timelock.emergencyReset(); + + proposal = _timelock.getProposal(1); + assert(proposal.status == Status.Cancelled); + } + + function testFuzz_stranger_cannot_emergency_reset_governance(address stranger) external { + vm.assume(stranger != _emergencyEnactor); + vm.assume(stranger != address(0)); + + _activateEmergencyMode(); + + assertEq(_isEmergencyStateActivated(), true); + + vm.prank(stranger); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, stranger)); + _timelock.emergencyReset(); + + assertEq(_isEmergencyStateActivated(), true); + } + + function test_cannot_emergency_reset_if_emergency_mode_not_activated() external { + assertEq(_isEmergencyStateActivated(), false); + + EmergencyState memory state = _timelock.getEmergencyState(); + + vm.expectRevert( + abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeActiveValue.selector, [false, true]) + ); + vm.prank(_emergencyEnactor); + _timelock.emergencyReset(); + + EmergencyState memory newState = _timelock.getEmergencyState(); + + assertEq(newState.executionCommittee, state.executionCommittee); + assertEq(newState.activationCommittee, state.activationCommittee); + assertEq(newState.protectedTill, state.protectedTill); + assertEq(newState.emergencyModeEndsAfter, state.emergencyModeEndsAfter); + assertEq(newState.emergencyModeDuration, state.emergencyModeDuration); + assertEq(newState.isEmergencyModeActivated, state.isEmergencyModeActivated); + } + + // EmergencyProtectedTimelock.setEmergencyProtection() + + function test_admin_executor_can_set_emenrgency_protection() external { + EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); + + vm.prank(_adminExecutor); + _localTimelock.setEmergencyProtection( + _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration + ); + + EmergencyState memory state = _localTimelock.getEmergencyState(); + + assertEq(state.activationCommittee, _emergencyActivator); + assertEq(state.executionCommittee, _emergencyEnactor); + assertEq(state.protectedTill, block.timestamp + _emergencyProtectionDuration); + assertEq(state.emergencyModeDuration, _emergencyModeDuration); + assertEq(state.emergencyModeEndsAfter, 0); + assertEq(state.isEmergencyModeActivated, false); + } + + function testFuzz_stranger_cannot_set_emergency_protection(address stranger) external { + vm.assume(stranger != _adminExecutor); + vm.assume(stranger != address(0)); + + EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); + + vm.prank(stranger); + vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); + _localTimelock.setEmergencyProtection( + _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration + ); + + EmergencyState memory state = _localTimelock.getEmergencyState(); + + assertEq(state.activationCommittee, address(0)); + assertEq(state.executionCommittee, address(0)); + assertEq(state.protectedTill, 0); + assertEq(state.emergencyModeDuration, 0); + assertEq(state.emergencyModeEndsAfter, 0); + assertEq(state.isEmergencyModeActivated, false); + } + + // EmergencyProtectedTimelock.isEmergencyProtectionEnabled() + + function test_is_emergency_protection_enabled_deactivate() external { + EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); + + assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); + + vm.prank(_adminExecutor); + _localTimelock.setEmergencyProtection( + _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration + ); + + assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); + + vm.prank(_emergencyActivator); + _localTimelock.activateEmergencyMode(); + + assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); + + vm.prank(_adminExecutor); + _localTimelock.deactivateEmergencyMode(); + + assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); + } + + function test_is_emergency_protection_enabled_reset() external { + EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); + + assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); + + vm.prank(_adminExecutor); + _localTimelock.setEmergencyProtection( + _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration + ); + + assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); + + vm.prank(_emergencyActivator); + _localTimelock.activateEmergencyMode(); + + assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); + + vm.prank(_emergencyEnactor); + _localTimelock.emergencyReset(); + + assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); + } + + // EmergencyProtectedTimelock.getEmergencyState() + + function test_get_emergency_state_deactivate() external { + EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); + + EmergencyState memory state = _localTimelock.getEmergencyState(); + + assertEq(state.isEmergencyModeActivated, false); + assertEq(state.activationCommittee, address(0)); + assertEq(state.executionCommittee, address(0)); + assertEq(state.protectedTill, 0); + assertEq(state.emergencyModeDuration, 0); + assertEq(state.emergencyModeEndsAfter, 0); + + vm.prank(_adminExecutor); + _localTimelock.setEmergencyProtection( + _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration + ); + + state = _localTimelock.getEmergencyState(); + + assertEq(_localTimelock.getEmergencyState().isEmergencyModeActivated, false); + assertEq(state.activationCommittee, _emergencyActivator); + assertEq(state.executionCommittee, _emergencyEnactor); + assertEq(state.protectedTill, block.timestamp + _emergencyProtectionDuration); + assertEq(state.emergencyModeDuration, _emergencyModeDuration); + assertEq(state.emergencyModeEndsAfter, 0); + + vm.prank(_emergencyActivator); + _localTimelock.activateEmergencyMode(); + + state = _localTimelock.getEmergencyState(); + + assertEq(_localTimelock.getEmergencyState().isEmergencyModeActivated, true); + assertEq(state.activationCommittee, _emergencyActivator); + assertEq(state.executionCommittee, _emergencyEnactor); + assertEq(state.protectedTill, block.timestamp + _emergencyProtectionDuration); + assertEq(state.emergencyModeDuration, _emergencyModeDuration); + assertEq(state.emergencyModeEndsAfter, block.timestamp + _emergencyModeDuration); + + vm.prank(_adminExecutor); + _localTimelock.deactivateEmergencyMode(); + + state = _localTimelock.getEmergencyState(); + + assertEq(state.isEmergencyModeActivated, false); + assertEq(state.activationCommittee, address(0)); + assertEq(state.executionCommittee, address(0)); + assertEq(state.protectedTill, 0); + assertEq(state.emergencyModeDuration, 0); + assertEq(state.emergencyModeEndsAfter, 0); + } + + function test_get_emergency_state_reset() external { + EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); + + vm.prank(_adminExecutor); + _localTimelock.setEmergencyProtection( + _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration + ); + + vm.prank(_emergencyActivator); + _localTimelock.activateEmergencyMode(); + + vm.prank(_emergencyEnactor); + _localTimelock.emergencyReset(); + + EmergencyState memory state = _localTimelock.getEmergencyState(); + + assertEq(state.isEmergencyModeActivated, false); + assertEq(state.activationCommittee, address(0)); + assertEq(state.executionCommittee, address(0)); + assertEq(state.protectedTill, 0); + assertEq(state.emergencyModeDuration, 0); + assertEq(state.emergencyModeEndsAfter, 0); + } + + // EmergencyProtectedTimelock.getGovernance() + + function testFuzz_get_governance(address governance) external { + vm.assume(governance != address(0)); + vm.prank(_adminExecutor); + _timelock.setGovernance(governance); + assertEq(_timelock.getGovernance(), governance); + } + + // EmergencyProtectedTimelock.getProposal() + + function test_get_proposal() external { + assertEq(_timelock.getProposalsCount(), 0); + + vm.startPrank(_dualGovernance); + ExecutorCall[] memory executorCalls = _getTargetRegularStaffCalls(); + _timelock.submit(_adminExecutor, executorCalls); + _timelock.submit(_adminExecutor, executorCalls); + + Proposal memory submittedProposal = _timelock.getProposal(1); + + uint256 submitTimestamp = block.timestamp; + + assertEq(submittedProposal.id, 1); + assertEq(submittedProposal.executor, _adminExecutor); + assertEq(submittedProposal.submittedAt, submitTimestamp); + assertEq(submittedProposal.scheduledAt, 0); + assertEq(submittedProposal.executedAt, 0); + // assertEq doesn't support comparing enumerables so far + assert(submittedProposal.status == Status.Submitted); + assertEq(submittedProposal.calls.length, 1); + assertEq(submittedProposal.calls[0].value, executorCalls[0].value); + assertEq(submittedProposal.calls[0].target, executorCalls[0].target); + assertEq(submittedProposal.calls[0].payload, executorCalls[0].payload); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + + _timelock.schedule(1); + uint256 scheduleTimestamp = block.timestamp; + + Proposal memory scheduledProposal = _timelock.getProposal(1); + + assertEq(scheduledProposal.id, 1); + assertEq(scheduledProposal.executor, _adminExecutor); + assertEq(scheduledProposal.submittedAt, submitTimestamp); + assertEq(scheduledProposal.scheduledAt, scheduleTimestamp); + assertEq(scheduledProposal.executedAt, 0); + // // assertEq doesn't support comparing enumerables so far + assert(scheduledProposal.status == Status.Scheduled); + assertEq(scheduledProposal.calls.length, 1); + assertEq(scheduledProposal.calls[0].value, executorCalls[0].value); + assertEq(scheduledProposal.calls[0].target, executorCalls[0].target); + assertEq(scheduledProposal.calls[0].payload, executorCalls[0].payload); + + vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + + _timelock.execute(1); + + Proposal memory executedProposal = _timelock.getProposal(1); + uint256 executeTimestamp = block.timestamp; + + assertEq(executedProposal.id, 1); + assertEq(executedProposal.executor, _adminExecutor); + assertEq(executedProposal.submittedAt, submitTimestamp); + assertEq(executedProposal.scheduledAt, scheduleTimestamp); + assertEq(executedProposal.executedAt, executeTimestamp); + // assertEq doesn't support comparing enumerables so far + assert(executedProposal.status == Status.Executed); + assertEq(executedProposal.calls.length, 1); + assertEq(executedProposal.calls[0].value, executorCalls[0].value); + assertEq(executedProposal.calls[0].target, executorCalls[0].target); + assertEq(executedProposal.calls[0].payload, executorCalls[0].payload); + + _timelock.cancelAllNonExecutedProposals(); + + Proposal memory cancelledProposal = _timelock.getProposal(2); + + assertEq(cancelledProposal.id, 2); + assertEq(cancelledProposal.executor, _adminExecutor); + assertEq(cancelledProposal.submittedAt, submitTimestamp); + assertEq(cancelledProposal.scheduledAt, 0); + assertEq(cancelledProposal.executedAt, 0); + // assertEq doesn't support comparing enumerables so far + assert(cancelledProposal.status == Status.Cancelled); + assertEq(cancelledProposal.calls.length, 1); + assertEq(cancelledProposal.calls[0].value, executorCalls[0].value); + assertEq(cancelledProposal.calls[0].target, executorCalls[0].target); + assertEq(cancelledProposal.calls[0].payload, executorCalls[0].payload); + } + + function test_get_not_existing_proposal() external { + assertEq(_timelock.getProposalsCount(), 0); + + vm.expectRevert(); + _timelock.getProposal(1); + } + + // EmergencyProtectedTimelock.getProposalsCount() + + function testFuzz_get_proposals_count(uint256 count) external { + vm.assume(count > 0); + vm.assume(count <= type(uint8).max); + assertEq(_timelock.getProposalsCount(), 0); + + for (uint256 i = 1; i <= count; i++) { + _submitProposal(); + assertEq(_timelock.getProposalsCount(), i); + } + } + + // EmergencyProtectedTimelock.canExecute() + + function test_can_execute() external { + assertEq(_timelock.canExecute(1), false); + + _submitProposal(); + + Proposal memory proposal = _timelock.getProposal(1); + + assertEq(_timelock.canExecute(1), false); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + + _scheduleProposal(1); + + assertEq(_timelock.canExecute(1), false); + + vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + + assertEq(_timelock.canExecute(1), true); + + vm.prank(_dualGovernance); + _timelock.cancelAllNonExecutedProposals(); + + assertEq(_timelock.canExecute(1), false); + } + + // EmergencyProtectedTimelock.canSchedule() + + function test_can_schedule() external { + assertEq(_timelock.canExecute(1), false); + + _submitProposal(); + + Proposal memory proposal = _timelock.getProposal(1); + + assertEq(_timelock.canSchedule(1), false); + + vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + + assertEq(_timelock.canSchedule(1), true); + + _scheduleProposal(1); + + assertEq(_timelock.canSchedule(1), false); + + vm.prank(_dualGovernance); + _timelock.cancelAllNonExecutedProposals(); + + assertEq(_timelock.canSchedule(1), false); + } + + // Utils + + function _submitProposal() internal { + vm.prank(_dualGovernance); + _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls()); + } + + function _scheduleProposal(uint256 proposalId) internal { + vm.prank(_dualGovernance); + _timelock.schedule(proposalId); + } + + function _isEmergencyStateActivated() internal view returns (bool) { + EmergencyState memory state = _timelock.getEmergencyState(); + return state.isEmergencyModeActivated; + } + + function _activateEmergencyMode() internal { + vm.prank(_emergencyActivator); + _timelock.activateEmergencyMode(); + assertEq(_isEmergencyStateActivated(), true); + } + + function _deactivateEmergencyMode() internal { + vm.prank(_adminExecutor); + _timelock.deactivateEmergencyMode(); + assertEq(_isEmergencyStateActivated(), false); + } + + function _getTargetRegularStaffCalls() internal view returns (ExecutorCall[] memory) { + return ExecutorCallHelpers.create(address(_targetMock), abi.encodeCall(IDangerousContract.doRegularStaff, (42))); } } diff --git a/test/utils/interfaces.sol b/test/utils/interfaces.sol index 2d3c56f3..40cd495f 100644 --- a/test/utils/interfaces.sol +++ b/test/utils/interfaces.sol @@ -106,3 +106,9 @@ interface IWithdrawalQueue is IERC721 { function hasRole(bytes32 role, address account) external view returns (bool); function isPaused() external view returns (bool); } + +interface IDangerousContract { + function doRegularStaff(uint256 magic) external; + function doRugPool() external; + function doControversialStaff() external; +} \ No newline at end of file diff --git a/test/utils/scenario-test-blueprint.sol b/test/utils/scenario-test-blueprint.sol index c2e711d7..6ccbcde2 100644 --- a/test/utils/scenario-test-blueprint.sol +++ b/test/utils/scenario-test-blueprint.sol @@ -26,7 +26,7 @@ import {DualGovernance, DualGovernanceState, State} from "contracts/DualGovernan import {Proposal, Status as ProposalStatus} from "contracts/libraries/Proposals.sol"; import {Percents, percents} from "../utils/percents.sol"; -import {IERC20, IStEth, IWstETH, IWithdrawalQueue, WithdrawalRequestStatus} from "../utils/interfaces.sol"; +import {IERC20, IStEth, IWstETH, IWithdrawalQueue, WithdrawalRequestStatus, IDangerousContract} from "../utils/interfaces.sol"; import {ExecutorCallHelpers} from "../utils/executor-calls.sol"; import {Utils, TargetMock, console} from "../utils/utils.sol"; @@ -47,12 +47,6 @@ function countDigits(uint256 number) pure returns (uint256 digitsCount) { } while (number / 10 != 0); } -interface IDangerousContract { - function doRegularStaff(uint256 magic) external; - function doRugPool() external; - function doControversialStaff() external; -} - contract ScenarioTestBlueprint is Test { address internal immutable _ADMIN_PROPOSER = DAO_VOTING; uint256 internal immutable _EMERGENCY_MODE_DURATION = 180 days; @@ -374,7 +368,7 @@ contract ScenarioTestBlueprint is Test { } function _assertProposalCanceled(uint256 proposalId) internal { - assertEq(_timelock.getProposal(proposalId).status, ProposalStatus.Canceled, "Proposal not in 'Canceled' state"); + assertEq(_timelock.getProposal(proposalId).status, ProposalStatus.Cancelled, "Proposal not in 'Canceled' state"); } function _assertNormalState() internal { From 6b7bd1185b907707071719280a325cbaa6f03e2c Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 2 May 2024 15:21:42 +0700 Subject: [PATCH 4/8] feat: add unit test util --- test/unit/EmergencyProtectedTimelock.t.sol | 55 +++++++++------------- test/utils/unit-test.sol | 11 +++++ 2 files changed, 34 insertions(+), 32 deletions(-) create mode 100644 test/utils/unit-test.sol diff --git a/test/unit/EmergencyProtectedTimelock.t.sol b/test/unit/EmergencyProtectedTimelock.t.sol index 3179fe0d..9e10ef98 100644 --- a/test/unit/EmergencyProtectedTimelock.t.sol +++ b/test/unit/EmergencyProtectedTimelock.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.23; -import {Test, Vm} from "forge-std/Test.sol"; +import {Vm} from "forge-std/Test.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Executor} from "contracts/Executor.sol"; @@ -12,11 +12,12 @@ import {Executor} from "contracts/Executor.sol"; import {Proposal, Proposals, ExecutorCall, Status} from "contracts/libraries/Proposals.sol"; import {EmergencyProtection, EmergencyState} from "contracts/libraries/EmergencyProtection.sol"; +import {UnitTest} from "test/utils/unit-test.sol"; import {TargetMock} from "test/utils/utils.sol"; import {ExecutorCallHelpers} from "test/utils/executor-calls.sol"; import {IDangerousContract} from "test/utils/interfaces.sol"; -contract EmergencyProtectedTimelockUnitTests is Test { +contract EmergencyProtectedTimelockUnitTests is UnitTest { EmergencyProtectedTimelock private _timelock; Configuration private _config; TargetMock private _targetMock; @@ -78,7 +79,7 @@ contract EmergencyProtectedTimelockUnitTests is Test { assertEq(_timelock.getProposalsCount(), 1); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _scheduleProposal(1); @@ -111,11 +112,11 @@ contract EmergencyProtectedTimelockUnitTests is Test { _submitProposal(); assertEq(_timelock.getProposalsCount(), 1); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _scheduleProposal(1); - vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + _wait(_config.AFTER_SCHEDULE_DELAY()); vm.prank(stranger); _timelock.execute(1); @@ -129,10 +130,10 @@ contract EmergencyProtectedTimelockUnitTests is Test { assertEq(_timelock.getProposalsCount(), 1); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _scheduleProposal(1); - vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + _wait(_config.AFTER_SCHEDULE_DELAY()); _activateEmergencyMode(); @@ -148,14 +149,12 @@ contract EmergencyProtectedTimelockUnitTests is Test { // EmergencyProtectedTimelock.cancelAllNonExecutedProposals() function test_governance_can_cancel_all_non_executed_proposals() external { - ExecutorCall[] memory executorCalls = _getTargetRegularStaffCalls(); - _submitProposal(); _submitProposal(); assertEq(_timelock.getProposalsCount(), 2); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _scheduleProposal(1); @@ -299,11 +298,11 @@ contract EmergencyProtectedTimelockUnitTests is Test { assertEq(_timelock.getProposalsCount(), 1); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _scheduleProposal(1); - vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + _wait(_config.AFTER_SCHEDULE_DELAY()); _activateEmergencyMode(); @@ -322,10 +321,10 @@ contract EmergencyProtectedTimelockUnitTests is Test { assertEq(_timelock.getProposalsCount(), 1); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _timelock.schedule(1); - vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + _wait(_config.AFTER_SCHEDULE_DELAY()); vm.stopPrank(); EmergencyState memory state = _timelock.getEmergencyState(); @@ -346,11 +345,11 @@ contract EmergencyProtectedTimelockUnitTests is Test { assertEq(_timelock.getProposalsCount(), 1); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _scheduleProposal(1); - vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + _wait(_config.AFTER_SCHEDULE_DELAY()); _activateEmergencyMode(); @@ -396,7 +395,7 @@ contract EmergencyProtectedTimelockUnitTests is Test { EmergencyState memory state = _timelock.getEmergencyState(); assertEq(_isEmergencyStateActivated(), true); - vm.warp(state.emergencyModeEndsAfter + 1); + _wait(state.emergencyModeDuration + 1); vm.prank(stranger); _timelock.deactivateEmergencyMode(); @@ -707,7 +706,7 @@ contract EmergencyProtectedTimelockUnitTests is Test { assertEq(submittedProposal.calls[0].target, executorCalls[0].target); assertEq(submittedProposal.calls[0].payload, executorCalls[0].payload); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _timelock.schedule(1); uint256 scheduleTimestamp = block.timestamp; @@ -726,7 +725,7 @@ contract EmergencyProtectedTimelockUnitTests is Test { assertEq(scheduledProposal.calls[0].target, executorCalls[0].target); assertEq(scheduledProposal.calls[0].payload, executorCalls[0].payload); - vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + _wait(_config.AFTER_SCHEDULE_DELAY()); _timelock.execute(1); @@ -786,20 +785,16 @@ contract EmergencyProtectedTimelockUnitTests is Test { function test_can_execute() external { assertEq(_timelock.canExecute(1), false); - - _submitProposal(); - - Proposal memory proposal = _timelock.getProposal(1); - + _submitProposal(); assertEq(_timelock.canExecute(1), false); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); _scheduleProposal(1); assertEq(_timelock.canExecute(1), false); - vm.warp(block.timestamp + _config.AFTER_SCHEDULE_DELAY()); + _wait(_config.AFTER_SCHEDULE_DELAY()); assertEq(_timelock.canExecute(1), true); @@ -813,14 +808,10 @@ contract EmergencyProtectedTimelockUnitTests is Test { function test_can_schedule() external { assertEq(_timelock.canExecute(1), false); - - _submitProposal(); - - Proposal memory proposal = _timelock.getProposal(1); - + _submitProposal(); assertEq(_timelock.canSchedule(1), false); - vm.warp(block.timestamp + _config.AFTER_SUBMIT_DELAY()); + _wait(_config.AFTER_SUBMIT_DELAY()); assertEq(_timelock.canSchedule(1), true); diff --git a/test/utils/unit-test.sol b/test/utils/unit-test.sol new file mode 100644 index 00000000..6fdb5cfd --- /dev/null +++ b/test/utils/unit-test.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +// solhint-disable-next-line +import {Test} from "forge-std/Test.sol"; + +contract UnitTest is Test { + function _wait(uint256 duration) internal { + vm.warp(block.timestamp + duration); + } +} \ No newline at end of file From 51ea69138d5520612228cd05c4d9169bd4e57cfb Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 2 May 2024 17:26:45 +0700 Subject: [PATCH 5/8] feat: add tests for EmergencyProtection library --- contracts/libraries/EmergencyProtection.sol | 14 +- test/unit/EmergencyProtectedTimelock.t.sol | 10 +- test/unit/libraries/EmergencyProtection.t.sol | 391 ++++++++++++++++++ 3 files changed, 402 insertions(+), 13 deletions(-) create mode 100644 test/unit/libraries/EmergencyProtection.t.sol diff --git a/contracts/libraries/EmergencyProtection.sol b/contracts/libraries/EmergencyProtection.sol index 758525e1..bbbee390 100644 --- a/contracts/libraries/EmergencyProtection.sol +++ b/contracts/libraries/EmergencyProtection.sol @@ -15,13 +15,11 @@ struct EmergencyState { library EmergencyProtection { error NotEmergencyActivator(address account); error NotEmergencyEnactor(address account); - error EmergencyPeriodFinished(); - error EmergencyCommitteeExpired(); + error EmergencyCommitteeExpired(uint256 timestamp, uint256 protectedTill); error InvalidEmergencyModeActiveValue(bool actual, bool expected); - event EmergencyModeActivated(); - event EmergencyModeDeactivated(); - event EmergencyGovernanceReset(); + event EmergencyModeActivated(uint256 timestamp); + event EmergencyModeDeactivated(uint256 timestamp); event EmergencyActivationCommitteeSet(address indexed activationCommittee); event EmergencyExecutionCommitteeSet(address indexed executionCommittee); event EmergencyModeDurationSet(uint256 emergencyModeDuration); @@ -74,10 +72,10 @@ library EmergencyProtection { function activate(State storage self) internal { if (block.timestamp > self.protectedTill) { - revert EmergencyCommitteeExpired(); + revert EmergencyCommitteeExpired(block.timestamp, self.protectedTill); } self.emergencyModeEndsAfter = SafeCast.toUint40(block.timestamp + self.emergencyModeDuration); - emit EmergencyModeActivated(); + emit EmergencyModeActivated(block.timestamp); } function deactivate(State storage self) internal { @@ -86,7 +84,7 @@ library EmergencyProtection { self.protectedTill = 0; self.emergencyModeDuration = 0; self.emergencyModeEndsAfter = 0; - emit EmergencyModeDeactivated(); + emit EmergencyModeDeactivated(block.timestamp); } function getEmergencyState(State storage self) internal view returns (EmergencyState memory res) { diff --git a/test/unit/EmergencyProtectedTimelock.t.sol b/test/unit/EmergencyProtectedTimelock.t.sol index 9e10ef98..a3a97f15 100644 --- a/test/unit/EmergencyProtectedTimelock.t.sol +++ b/test/unit/EmergencyProtectedTimelock.t.sol @@ -550,7 +550,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { function test_is_emergency_protection_enabled_deactivate() external { EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); - + assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); vm.prank(_adminExecutor); @@ -573,7 +573,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { function test_is_emergency_protection_enabled_reset() external { EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); - + assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); vm.prank(_adminExecutor); @@ -600,7 +600,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); EmergencyState memory state = _localTimelock.getEmergencyState(); - + assertEq(state.isEmergencyModeActivated, false); assertEq(state.activationCommittee, address(0)); assertEq(state.executionCommittee, address(0)); @@ -785,7 +785,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { function test_can_execute() external { assertEq(_timelock.canExecute(1), false); - _submitProposal(); + _submitProposal(); assertEq(_timelock.canExecute(1), false); _wait(_config.AFTER_SUBMIT_DELAY()); @@ -808,7 +808,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { function test_can_schedule() external { assertEq(_timelock.canExecute(1), false); - _submitProposal(); + _submitProposal(); assertEq(_timelock.canSchedule(1), false); _wait(_config.AFTER_SUBMIT_DELAY()); diff --git a/test/unit/libraries/EmergencyProtection.t.sol b/test/unit/libraries/EmergencyProtection.t.sol new file mode 100644 index 00000000..8f05b257 --- /dev/null +++ b/test/unit/libraries/EmergencyProtection.t.sol @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {Test, Vm} from "forge-std/Test.sol"; + +import {EmergencyProtection, EmergencyState} from "contracts/libraries/EmergencyProtection.sol"; + +import {UnitTest} from "test/utils/unit-test.sol"; + +contract EmergencyProtectionUnitTests is UnitTest { + EmergencyProtection.State internal _emergencyProtection; + + function testFuzz_setup_emergency_protection( + address activationCommittee, + address executionCommittee, + uint256 protectedTill, + uint256 duration + ) external { + vm.assume(protectedTill > 0 && protectedTill < type(uint40).max); + vm.assume(duration > 0 && duration < type(uint32).max); + vm.assume(activationCommittee != address(0)); + vm.assume(executionCommittee != address(0)); + + vm.expectEmit(); + emit EmergencyProtection.EmergencyActivationCommitteeSet(activationCommittee); + vm.expectEmit(); + emit EmergencyProtection.EmergencyExecutionCommitteeSet(executionCommittee); + vm.expectEmit(); + emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(block.timestamp + protectedTill); + vm.expectEmit(); + emit EmergencyProtection.EmergencyModeDurationSet(duration); + + vm.recordLogs(); + + EmergencyProtection.setup( + _emergencyProtection, activationCommittee, executionCommittee, protectedTill, duration + ); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 4); + + assertEq(_emergencyProtection.activationCommittee, activationCommittee); + assertEq(_emergencyProtection.executionCommittee, executionCommittee); + assertEq(_emergencyProtection.protectedTill, block.timestamp + protectedTill); + assertEq(_emergencyProtection.emergencyModeDuration, duration); + assertEq(_emergencyProtection.emergencyModeEndsAfter, 0); + } + + function test_setup_same_activation_committee() external { + address activationCommittee = makeAddr("activationCommittee"); + + EmergencyProtection.setup(_emergencyProtection, activationCommittee, address(0x2), 100, 100); + + vm.expectEmit(); + emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x3)); + vm.expectEmit(); + emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(block.timestamp + 200); + vm.expectEmit(); + emit EmergencyProtection.EmergencyModeDurationSet(200); + + vm.recordLogs(); + EmergencyProtection.setup(_emergencyProtection, activationCommittee, address(0x3), 200, 200); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 3); + + assertEq(_emergencyProtection.activationCommittee, activationCommittee); + assertEq(_emergencyProtection.executionCommittee, address(0x3)); + assertEq(_emergencyProtection.protectedTill, block.timestamp + 200); + assertEq(_emergencyProtection.emergencyModeDuration, 200); + assertEq(_emergencyProtection.emergencyModeEndsAfter, 0); + } + + function test_setup_same_execution_committee() external { + address executionCommittee = makeAddr("executionCommittee"); + + EmergencyProtection.setup(_emergencyProtection, address(0x1), executionCommittee, 100, 100); + + vm.expectEmit(); + emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x2)); + vm.expectEmit(); + emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(block.timestamp + 200); + vm.expectEmit(); + emit EmergencyProtection.EmergencyModeDurationSet(200); + + vm.recordLogs(); + EmergencyProtection.setup(_emergencyProtection, address(0x2), executionCommittee, 200, 200); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 3); + + assertEq(_emergencyProtection.activationCommittee, address(0x2)); + assertEq(_emergencyProtection.executionCommittee, executionCommittee); + assertEq(_emergencyProtection.protectedTill, block.timestamp + 200); + assertEq(_emergencyProtection.emergencyModeDuration, 200); + assertEq(_emergencyProtection.emergencyModeEndsAfter, 0); + } + + function test_setup_same_protected_till() external { + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + + vm.expectEmit(); + emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x3)); + vm.expectEmit(); + emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x4)); + vm.expectEmit(); + emit EmergencyProtection.EmergencyModeDurationSet(200); + + vm.recordLogs(); + EmergencyProtection.setup(_emergencyProtection, address(0x3), address(0x4), 100, 200); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 3); + + assertEq(_emergencyProtection.activationCommittee, address(0x3)); + assertEq(_emergencyProtection.executionCommittee, address(0x4)); + assertEq(_emergencyProtection.protectedTill, block.timestamp + 100); + assertEq(_emergencyProtection.emergencyModeDuration, 200); + assertEq(_emergencyProtection.emergencyModeEndsAfter, 0); + } + + function test_setup_same_emergency_mode_duration() external { + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + + vm.expectEmit(); + emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x3)); + vm.expectEmit(); + emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x4)); + vm.expectEmit(); + emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(block.timestamp + 200); + + vm.recordLogs(); + EmergencyProtection.setup(_emergencyProtection, address(0x3), address(0x4), 200, 100); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 3); + + assertEq(_emergencyProtection.activationCommittee, address(0x3)); + assertEq(_emergencyProtection.executionCommittee, address(0x4)); + assertEq(_emergencyProtection.protectedTill, block.timestamp + 200); + assertEq(_emergencyProtection.emergencyModeDuration, 100); + assertEq(_emergencyProtection.emergencyModeEndsAfter, 0); + } + + function test_activate_emergency_mode() external { + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + + vm.expectEmit(); + emit EmergencyProtection.EmergencyModeActivated(block.timestamp); + + vm.recordLogs(); + + EmergencyProtection.activate(_emergencyProtection); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + + assertEq(entries.length, 1); + assertEq(_emergencyProtection.emergencyModeEndsAfter, block.timestamp + 100); + } + + function test_cannot_activate_emergency_mode_if_protected_till_expired() external { + uint256 protectedTill = 100; + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), protectedTill, 100); + + _wait(protectedTill + 1); + + vm.expectRevert( + abi.encodeWithSelector( + EmergencyProtection.EmergencyCommitteeExpired.selector, + [block.timestamp, _emergencyProtection.protectedTill] + ) + ); + EmergencyProtection.activate(_emergencyProtection); + } + + function testFuzz_deactivate_emergency_mode( + address activationCommittee, + address executionCommittee, + uint256 protectedTill, + uint256 duration + ) external { + vm.assume(protectedTill > 0 && protectedTill < type(uint40).max); + vm.assume(duration > 0 && duration < type(uint32).max); + vm.assume(activationCommittee != address(0)); + vm.assume(executionCommittee != address(0)); + + EmergencyProtection.setup( + _emergencyProtection, activationCommittee, executionCommittee, protectedTill, duration + ); + EmergencyProtection.activate(_emergencyProtection); + + vm.expectEmit(); + emit EmergencyProtection.EmergencyModeDeactivated(block.timestamp); + + vm.recordLogs(); + + EmergencyProtection.deactivate(_emergencyProtection); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 1); + + assertEq(_emergencyProtection.activationCommittee, address(0)); + assertEq(_emergencyProtection.executionCommittee, address(0)); + assertEq(_emergencyProtection.protectedTill, 0); + assertEq(_emergencyProtection.emergencyModeDuration, 0); + assertEq(_emergencyProtection.emergencyModeEndsAfter, 0); + } + + function test_get_emergency_state() external { + EmergencyState memory state = EmergencyProtection.getEmergencyState(_emergencyProtection); + + assertEq(state.activationCommittee, address(0)); + assertEq(state.executionCommittee, address(0)); + assertEq(state.protectedTill, 0); + assertEq(state.emergencyModeDuration, 0); + assertEq(state.emergencyModeEndsAfter, 0); + assertEq(state.isEmergencyModeActivated, false); + + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + + state = EmergencyProtection.getEmergencyState(_emergencyProtection); + + assertEq(state.activationCommittee, address(0x1)); + assertEq(state.executionCommittee, address(0x2)); + assertEq(state.protectedTill, block.timestamp + 100); + assertEq(state.emergencyModeDuration, 100); + assertEq(state.emergencyModeEndsAfter, 0); + assertEq(state.isEmergencyModeActivated, false); + + EmergencyProtection.activate(_emergencyProtection); + + state = EmergencyProtection.getEmergencyState(_emergencyProtection); + + assertEq(state.activationCommittee, address(0x1)); + assertEq(state.executionCommittee, address(0x2)); + assertEq(state.protectedTill, block.timestamp + 100); + assertEq(state.emergencyModeDuration, 100); + assertEq(state.emergencyModeEndsAfter, block.timestamp + 100); + assertEq(state.isEmergencyModeActivated, true); + + EmergencyProtection.deactivate(_emergencyProtection); + + state = EmergencyProtection.getEmergencyState(_emergencyProtection); + + assertEq(state.activationCommittee, address(0)); + assertEq(state.executionCommittee, address(0)); + assertEq(state.protectedTill, 0); + assertEq(state.emergencyModeDuration, 0); + assertEq(state.emergencyModeEndsAfter, 0); + assertEq(state.isEmergencyModeActivated, false); + } + + function test_is_emergency_mode_activated() external { + assertEq(EmergencyProtection.isEmergencyModeActivated(_emergencyProtection), false); + + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + + assertEq(EmergencyProtection.isEmergencyModeActivated(_emergencyProtection), false); + + EmergencyProtection.activate(_emergencyProtection); + + assertEq(EmergencyProtection.isEmergencyModeActivated(_emergencyProtection), true); + + EmergencyProtection.deactivate(_emergencyProtection); + + assertEq(EmergencyProtection.isEmergencyModeActivated(_emergencyProtection), false); + } + + function test_is_emergency_mode_passed() external { + assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), false); + + uint256 duration = 100; + + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, duration); + + assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), false); + + EmergencyProtection.activate(_emergencyProtection); + + assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), false); + + _wait(duration + 1); + + assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), true); + + EmergencyProtection.deactivate(_emergencyProtection); + + assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), false); + } + + function test_is_emergency_protection_enabled() external { + uint256 protectedTill = 100; + uint256 duration = 100; + + assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), false); + + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), protectedTill, duration); + + assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), true); + + _wait(protectedTill - block.timestamp); + + EmergencyProtection.activate(_emergencyProtection); + + _wait(duration); + + assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), true); + + _wait(100); + + assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), true); + + EmergencyProtection.deactivate(_emergencyProtection); + + assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), false); + } + + function testFuzz_check_activation_committee(address committee, address stranger) external { + vm.assume(committee != address(0)); + vm.assume(stranger != address(0) && stranger != committee); + + vm.expectRevert( + abi.encodeWithSelector( + EmergencyProtection.NotEmergencyActivator.selector, + [stranger] + ) + ); + EmergencyProtection.checkActivationCommittee(_emergencyProtection, stranger); + EmergencyProtection.checkActivationCommittee(_emergencyProtection, address(0)); + + EmergencyProtection.setup(_emergencyProtection, committee, address(0x2), 100, 100); + + EmergencyProtection.checkActivationCommittee(_emergencyProtection, committee); + + vm.expectRevert( + abi.encodeWithSelector( + EmergencyProtection.NotEmergencyActivator.selector, + [stranger] + ) + ); + EmergencyProtection.checkActivationCommittee(_emergencyProtection, stranger); + } + + function testFuzz_check_execution_committee(address committee, address stranger) external { + vm.assume(committee != address(0)); + vm.assume(stranger != address(0) && stranger != committee); + + vm.expectRevert( + abi.encodeWithSelector( + EmergencyProtection.NotEmergencyEnactor.selector, + [stranger] + ) + ); + EmergencyProtection.checkExecutionCommittee(_emergencyProtection, stranger); + EmergencyProtection.checkExecutionCommittee(_emergencyProtection, address(0)); + + EmergencyProtection.setup(_emergencyProtection, address(0x1), committee, 100, 100); + + EmergencyProtection.checkExecutionCommittee(_emergencyProtection, committee); + + vm.expectRevert( + abi.encodeWithSelector( + EmergencyProtection.NotEmergencyEnactor.selector, + [stranger] + ) + ); + EmergencyProtection.checkExecutionCommittee(_emergencyProtection, stranger); + } + + function test_check_emergency_mode_active() external { + vm.expectRevert( + abi.encodeWithSelector( + EmergencyProtection.InvalidEmergencyModeActiveValue.selector, + [false, true] + ) + ); + EmergencyProtection.checkEmergencyModeActive(_emergencyProtection, true); + EmergencyProtection.checkEmergencyModeActive(_emergencyProtection, false); + + EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + EmergencyProtection.activate(_emergencyProtection); + + EmergencyProtection.checkEmergencyModeActive(_emergencyProtection, true); + vm.expectRevert( + abi.encodeWithSelector( + EmergencyProtection.InvalidEmergencyModeActiveValue.selector, + [true, false] + ) + ); + } +} From 11ef654400d9b6a01f96e89855e4c719b84437a4 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Fri, 3 May 2024 00:33:57 +0700 Subject: [PATCH 6/8] feat: add tests for Proposal library --- contracts/libraries/Proposals.sol | 11 +- test/unit/libraries/Proposals.t.sol | 417 ++++++++++++++++++++++++++++ 2 files changed, 422 insertions(+), 6 deletions(-) create mode 100644 test/unit/libraries/Proposals.t.sol diff --git a/contracts/libraries/Proposals.sol b/contracts/libraries/Proposals.sol index 7b0e1ee0..dc63086b 100644 --- a/contracts/libraries/Proposals.sol +++ b/contracts/libraries/Proposals.sol @@ -57,7 +57,7 @@ library Proposals { function submit( State storage self, address executor, - ExecutorCall[] calldata calls + ExecutorCall[] memory calls ) internal returns (uint256 newProposalId) { if (calls.length == 0) { revert EmptyCalls(); @@ -67,9 +67,8 @@ library Proposals { self.proposals.push(); ProposalPacked storage newProposal = self.proposals[newProposalIndex]; - newProposal.executor = executor; - newProposal.executedAt = 0; + newProposal.executor = executor; newProposal.submittedAt = TimeUtils.timestamp(); // copying of arrays of custom types from calldata to storage has not been supported by the @@ -136,7 +135,7 @@ library Proposals { && block.timestamp >= _packed(self, proposalId).submittedAt + afterSubmitDelay; } - function _executeProposal(State storage self, uint256 proposalId) private returns (bytes[] memory results) { + function _executeProposal(State storage self, uint256 proposalId) private { ProposalPacked storage packed = _packed(self, proposalId); packed.executedAt = TimeUtils.timestamp(); @@ -146,7 +145,7 @@ library Proposals { assert(callsCount > 0); address executor = packed.executor; - results = new bytes[](callsCount); + bytes[] memory results = new bytes[](callsCount); for (uint256 i = 0; i < callsCount; ++i) { results[i] = IExecutor(payable(executor)).execute(calls[i].target, calls[i].value, calls[i].payload); } @@ -197,7 +196,7 @@ library Proposals { } } - function _getProposalStatus(State storage self, uint256 proposalId) private view returns (Status) { + function _getProposalStatus(State storage self, uint256 proposalId) private view returns (Status status) { if (proposalId < PROPOSAL_ID_OFFSET || proposalId > self.proposals.length) return Status.NotExist; ProposalPacked storage packed = _packed(self, proposalId); diff --git a/test/unit/libraries/Proposals.t.sol b/test/unit/libraries/Proposals.t.sol new file mode 100644 index 00000000..2f61e0e4 --- /dev/null +++ b/test/unit/libraries/Proposals.t.sol @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {Vm} from "forge-std/Test.sol"; + +import {Executor} from "contracts/Executor.sol"; +import {Proposals, ExecutorCall, Proposal, Status} from "contracts/libraries/Proposals.sol"; + +import {TargetMock} from "test/utils/utils.sol"; +import {UnitTest} from "test/utils/unit-test.sol"; +import {IDangerousContract} from "test/utils/interfaces.sol"; +import {ExecutorCallHelpers} from "test/utils/executor-calls.sol"; + +contract ProposalsUnitTests is UnitTest { + using Proposals for Proposals.State; + + TargetMock private _targetMock; + Proposals.State internal _proposals; + Executor private _executor; + + uint256 private constant PROPOSAL_ID_OFFSET = 1; + + function setUp() external { + _targetMock = new TargetMock(); + _executor = new Executor(address(this)); + } + + function test_submit_reverts_if_empty_proposals() external { + vm.expectRevert(Proposals.EmptyCalls.selector); + Proposals.submit(_proposals, address(0), new ExecutorCall[](0)); + } + + function test_submit_proposal() external { + uint256 proposalsCount = _proposals.count(); + + ExecutorCall[] memory calls = _getTargetRegularStaffCalls(); + + vm.expectEmit(); + emit Proposals.ProposalSubmitted(proposalsCount + PROPOSAL_ID_OFFSET, address(_executor), calls); + + vm.recordLogs(); + + Proposals.submit(_proposals, address(_executor), calls); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 1); + + proposalsCount = _proposals.count(); + + Proposals.ProposalPacked memory proposal = _proposals.proposals[proposalsCount - PROPOSAL_ID_OFFSET]; + + assertEq(proposal.executor, address(_executor)); + assertEq(proposal.submittedAt, block.timestamp); + assertEq(proposal.executedAt, 0); + assertEq(proposal.scheduledAt, 0); + assertEq(proposal.calls.length, 1); + + for (uint256 i = 0; i < proposal.calls.length; i++) { + assertEq(proposal.calls[i].target, address(_targetMock)); + assertEq(proposal.calls[i].value, calls[i].value); + assertEq(proposal.calls[i].payload, calls[i].payload); + } + } + + function testFuzz_schedule_proposal(uint256 delay) external { + vm.assume(delay > 0 && delay < type(uint40).max); + + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.ProposalPacked memory proposal = _proposals.proposals[0]; + + uint256 submittedAt = block.timestamp; + + assertEq(proposal.submittedAt, submittedAt); + assertEq(proposal.scheduledAt, 0); + assertEq(proposal.executedAt, 0); + + uint256 proposalId = _proposals.count(); + + _wait(delay); + + vm.expectEmit(); + emit Proposals.ProposalScheduled(proposalId); + Proposals.schedule(_proposals, proposalId, delay); + + proposal = _proposals.proposals[0]; + + assertEq(proposal.submittedAt, submittedAt); + assertEq(proposal.scheduledAt, block.timestamp); + assertEq(proposal.executedAt, 0); + } + + function testFuzz_cannot_schedule_unsubmitted_proposal(uint256 proposalId) external { + vm.assume(proposalId > 0); + + vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotSubmitted.selector, proposalId)); + Proposals.schedule(_proposals, proposalId, 0); + } + + function test_cannot_schedule_proposal_twice() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = 1; + Proposals.schedule(_proposals, proposalId, 0); + + vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotSubmitted.selector, proposalId)); + Proposals.schedule(_proposals, proposalId, 0); + } + + function testFuzz_cannot_schedule_proposal_before_delay_passed(uint256 delay) external { + vm.assume(delay > 0 && delay < type(uint40).max); + + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + + _wait(delay - 1); + + vm.expectRevert(abi.encodeWithSelector(Proposals.AfterSubmitDelayNotPassed.selector, PROPOSAL_ID_OFFSET)); + Proposals.schedule(_proposals, PROPOSAL_ID_OFFSET, delay); + } + + function test_cannot_schedule_cancelled_proposal() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.cancelAll(_proposals); + + uint256 proposalId = _proposals.count(); + + vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotSubmitted.selector, proposalId)); + Proposals.schedule(_proposals, proposalId, 0); + } + + function testFuzz_execute_proposal(uint256 delay) external { + vm.assume(delay > 0 && delay < type(uint40).max); + + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + Proposals.schedule(_proposals, proposalId, 0); + + uint256 submittedAndScheduledAt = block.timestamp; + + assertEq(_proposals.proposals[0].submittedAt, submittedAndScheduledAt); + assertEq(_proposals.proposals[0].scheduledAt, submittedAndScheduledAt); + assertEq(_proposals.proposals[0].executedAt, 0); + + _wait(delay); + + // TODO: figure out why event is not emitted + // vm.expectEmit(); + // emit Proposals.ProposalExecuted(); + Proposals.execute(_proposals, proposalId, delay); + + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 0); + + Proposals.ProposalPacked memory proposal = _proposals.proposals[0]; + + assertEq(_proposals.proposals[0].submittedAt, submittedAndScheduledAt); + assertEq(_proposals.proposals[0].scheduledAt, submittedAndScheduledAt); + assertEq(proposal.executedAt, block.timestamp); + } + + function testFuzz_cannot_execute_unsubmitted_proposal(uint256 proposalId) external { + vm.assume(proposalId > 0); + vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotScheduled.selector, proposalId)); + Proposals.execute(_proposals, proposalId, 0); + } + + function test_cannot_execute_unscheduled_proposal() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + + vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotScheduled.selector, proposalId)); + Proposals.execute(_proposals, proposalId, 0); + } + + function test_cannot_execute_twice() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + Proposals.schedule(_proposals, proposalId, 0); + Proposals.execute(_proposals, proposalId, 0); + + vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotScheduled.selector, proposalId)); + Proposals.execute(_proposals, proposalId, 0); + } + + function test_cannot_execute_cancelled_proposal() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + Proposals.schedule(_proposals, proposalId, 0); + Proposals.cancelAll(_proposals); + + vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotScheduled.selector, proposalId)); + Proposals.execute(_proposals, proposalId, 0); + } + + function testFuzz_cannot_execute_before_delay_passed(uint256 delay) external { + vm.assume(delay > 0 && delay < type(uint40).max); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + Proposals.schedule(_proposals, proposalId, 0); + + _wait(delay - 1); + + vm.expectRevert(abi.encodeWithSelector(Proposals.AfterScheduleDelayNotPassed.selector, proposalId)); + Proposals.execute(_proposals, proposalId, delay); + } + + function test_cancel_all_proposals() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + + uint256 proposalsCount = _proposals.count(); + + Proposals.schedule(_proposals, proposalsCount, 0); + + vm.expectEmit(); + emit Proposals.ProposalsCancelledTill(proposalsCount); + Proposals.cancelAll(_proposals); + + assertEq(_proposals.lastCancelledProposalId, proposalsCount); + } + + function test_get_proposal() external { + ExecutorCall[] memory calls = _getTargetRegularStaffCalls(); + Proposals.submit(_proposals, address(_executor), calls); + uint256 proposalId = _proposals.count(); + + Proposal memory proposal = _proposals.get(proposalId); + + uint256 submittedAt = block.timestamp; + + assertEq(proposal.id, proposalId); + assertEq(proposal.executor, address(_executor)); + assertEq(proposal.submittedAt, submittedAt); + assertEq(proposal.scheduledAt, 0); + assertEq(proposal.executedAt, 0); + assertEq(proposal.calls.length, 1); + assert(proposal.status == Status.Submitted); + + for (uint256 i = 0; i < proposal.calls.length; i++) { + assertEq(proposal.calls[i].target, address(_targetMock)); + assertEq(proposal.calls[i].value, calls[i].value); + assertEq(proposal.calls[i].payload, calls[i].payload); + } + + Proposals.schedule(_proposals, proposalId, 0); + + uint256 scheduledAt = block.timestamp; + + proposal = _proposals.get(proposalId); + + assertEq(proposal.id, proposalId); + assertEq(proposal.executor, address(_executor)); + assertEq(proposal.submittedAt, submittedAt); + assertEq(proposal.scheduledAt, scheduledAt); + assertEq(proposal.executedAt, 0); + assertEq(proposal.calls.length, 1); + assert(proposal.status == Status.Scheduled); + + for (uint256 i = 0; i < proposal.calls.length; i++) { + assertEq(proposal.calls[i].target, address(_targetMock)); + assertEq(proposal.calls[i].value, calls[i].value); + assertEq(proposal.calls[i].payload, calls[i].payload); + } + + Proposals.execute(_proposals, proposalId, 0); + + uint256 executedAt = block.timestamp; + + proposal = _proposals.get(proposalId); + + assertEq(proposal.id, proposalId); + assertEq(proposal.executor, address(_executor)); + assertEq(proposal.submittedAt, submittedAt); + assertEq(proposal.scheduledAt, scheduledAt); + assertEq(proposal.executedAt, executedAt); + assertEq(proposal.calls.length, 1); + assert(proposal.status == Status.Executed); + + for (uint256 i = 0; i < proposal.calls.length; i++) { + assertEq(proposal.calls[i].target, address(_targetMock)); + assertEq(proposal.calls[i].value, calls[i].value); + assertEq(proposal.calls[i].payload, calls[i].payload); + } + } + + function test_get_cancelled_proposal() external { + ExecutorCall[] memory calls = _getTargetRegularStaffCalls(); + Proposals.submit(_proposals, address(_executor), calls); + uint256 proposalId = _proposals.count(); + + Proposal memory proposal = _proposals.get(proposalId); + + uint256 submittedAt = block.timestamp; + + assertEq(proposal.id, proposalId); + assertEq(proposal.executor, address(_executor)); + assertEq(proposal.submittedAt, submittedAt); + assertEq(proposal.scheduledAt, 0); + assertEq(proposal.executedAt, 0); + assertEq(proposal.calls.length, 1); + assert(proposal.status == Status.Submitted); + + for (uint256 i = 0; i < proposal.calls.length; i++) { + assertEq(proposal.calls[i].target, address(_targetMock)); + assertEq(proposal.calls[i].value, calls[i].value); + assertEq(proposal.calls[i].payload, calls[i].payload); + } + + Proposals.cancelAll(_proposals); + + proposal = _proposals.get(proposalId); + + assertEq(proposal.id, proposalId); + assertEq(proposal.executor, address(_executor)); + assertEq(proposal.submittedAt, submittedAt); + assertEq(proposal.scheduledAt, 0); + assertEq(proposal.executedAt, 0); + assertEq(proposal.calls.length, 1); + assert(proposal.status == Status.Cancelled); + + for (uint256 i = 0; i < proposal.calls.length; i++) { + assertEq(proposal.calls[i].target, address(_targetMock)); + assertEq(proposal.calls[i].value, calls[i].value); + assertEq(proposal.calls[i].payload, calls[i].payload); + } + } + + function testFuzz_get_not_existing_proposal(uint256 proposalId) external { + vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotFound.selector, proposalId)); + _proposals.get(proposalId); + } + + function test_count_proposals() external { + assertEq(_proposals.count(), 0); + + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + assertEq(_proposals.count(), 1); + + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + assertEq(_proposals.count(), 2); + + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + assertEq(_proposals.count(), 3); + + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + assertEq(_proposals.count(), 4); + + Proposals.schedule(_proposals, 1, 0); + assertEq(_proposals.count(), 4); + + Proposals.schedule(_proposals, 2, 0); + assertEq(_proposals.count(), 4); + + Proposals.execute(_proposals, 1, 0); + assertEq(_proposals.count(), 4); + + Proposals.cancelAll(_proposals); + assertEq(_proposals.count(), 4); + } + + function test_can_execute_proposal() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + + assert(!_proposals.canExecute(proposalId, 0)); + + Proposals.schedule(_proposals, proposalId, 0); + + assert(!_proposals.canExecute(proposalId, 100)); + + _wait(100); + + assert(_proposals.canExecute(proposalId, 100)); + + Proposals.execute(_proposals, proposalId, 0); + + assert(!_proposals.canExecute(proposalId, 100)); + } + + function test_can_not_execute_cancelled_proposal() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + Proposals.schedule(_proposals, proposalId, 0); + + assert(_proposals.canExecute(proposalId, 0)); + Proposals.cancelAll(_proposals); + + assert(!_proposals.canExecute(proposalId, 0)); + } + + function test_can_schedule_proposal() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + assert(!_proposals.canSchedule(proposalId, 100)); + + _wait(100); + + assert(_proposals.canSchedule(proposalId, 100)); + + Proposals.schedule(_proposals, proposalId, 100); + Proposals.execute(_proposals, proposalId, 0); + + assert(!_proposals.canSchedule(proposalId, 100)); + } + + function test_can_not_schedule_cancelled_proposal() external { + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + uint256 proposalId = _proposals.count(); + assert(_proposals.canSchedule(proposalId, 0)); + + Proposals.cancelAll(_proposals); + + assert(!_proposals.canSchedule(proposalId, 0)); + } + + function _getTargetRegularStaffCalls() internal view returns (ExecutorCall[] memory) { + return ExecutorCallHelpers.create(address(_targetMock), abi.encodeCall(IDangerousContract.doRegularStaff, (42))); + } +} From 2715c44adbc1e2c47cef0b1dd00fed1efba894a6 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 7 May 2024 19:32:15 +0700 Subject: [PATCH 7/8] feat: add unit tests for SingleGovernance --- contracts/SingleGovernance.sol | 4 + test/unit/EmergencyProtectedTimelock.t.sol | 12 +- test/unit/SingleGovernance.t.sol | 126 +++++++++++++++++++++ test/unit/libraries/Proposals.t.sol | 48 ++++---- test/unit/mocks/TimelockMock.sol | 55 +++++++++ test/utils/unit-test.sol | 7 ++ 6 files changed, 218 insertions(+), 34 deletions(-) create mode 100644 test/unit/SingleGovernance.t.sol create mode 100644 test/unit/mocks/TimelockMock.sol diff --git a/contracts/SingleGovernance.sol b/contracts/SingleGovernance.sol index 93da9847..c1034658 100644 --- a/contracts/SingleGovernance.sol +++ b/contracts/SingleGovernance.sol @@ -26,6 +26,10 @@ contract SingleGovernance is IGovernance, ConfigurationProvider { TIMELOCK.schedule(proposalId); } + function executeProposal(uint256 proposalId) external { + TIMELOCK.execute(proposalId); + } + function canSchedule(uint256 proposalId) external view returns (bool) { return TIMELOCK.canSchedule(proposalId); } diff --git a/test/unit/EmergencyProtectedTimelock.t.sol b/test/unit/EmergencyProtectedTimelock.t.sol index a3a97f15..7216c248 100644 --- a/test/unit/EmergencyProtectedTimelock.t.sol +++ b/test/unit/EmergencyProtectedTimelock.t.sol @@ -64,7 +64,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { function test_governance_can_submit_proposal() external { vm.prank(_dualGovernance); - _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls()); + _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); assertEq(_timelock.getProposalsCount(), 1); @@ -317,7 +317,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { function test_cannot_emergency_execute_proposal_if_mode_not_activated() external { vm.startPrank(_dualGovernance); - _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls()); + _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); assertEq(_timelock.getProposalsCount(), 1); @@ -686,7 +686,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { assertEq(_timelock.getProposalsCount(), 0); vm.startPrank(_dualGovernance); - ExecutorCall[] memory executorCalls = _getTargetRegularStaffCalls(); + ExecutorCall[] memory executorCalls = _getTargetRegularStaffCalls(address(_targetMock)); _timelock.submit(_adminExecutor, executorCalls); _timelock.submit(_adminExecutor, executorCalls); @@ -829,7 +829,7 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { function _submitProposal() internal { vm.prank(_dualGovernance); - _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls()); + _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); } function _scheduleProposal(uint256 proposalId) internal { @@ -853,8 +853,4 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { _timelock.deactivateEmergencyMode(); assertEq(_isEmergencyStateActivated(), false); } - - function _getTargetRegularStaffCalls() internal view returns (ExecutorCall[] memory) { - return ExecutorCallHelpers.create(address(_targetMock), abi.encodeCall(IDangerousContract.doRegularStaff, (42))); - } } diff --git a/test/unit/SingleGovernance.t.sol b/test/unit/SingleGovernance.t.sol new file mode 100644 index 00000000..489370bf --- /dev/null +++ b/test/unit/SingleGovernance.t.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {Vm} from "forge-std/Test.sol"; + +import {Executor} from "contracts/Executor.sol"; +import {SingleGovernance} from "contracts/SingleGovernance.sol"; +import {IConfiguration, Configuration} from "contracts/Configuration.sol"; +import {ExecutorCall} from "contracts/libraries/Proposals.sol"; + +import {UnitTest} from "test/utils/unit-test.sol"; +import {TargetMock} from "test/utils/utils.sol"; + +import {TimelockMock} from "./mocks/TimelockMock.sol"; + +contract SingleGovernanceUnitTests is UnitTest { + TimelockMock private _timelock; + SingleGovernance private _singleGovernance; + Configuration private _config; + + address private _emergencyGovernance = makeAddr("EMERGENCY_GOVERNANCE"); + address private _governance = makeAddr("GOVERNANCE"); + + function setUp() external { + Executor _executor = new Executor(address(this)); + _config = new Configuration(address(_executor), _emergencyGovernance, new address[](0)); + _timelock = new TimelockMock(); + _singleGovernance = new SingleGovernance(address(_config), _governance, address(_timelock)); + } + + function testFuzz_constructor(address governance, address timelock) external { + SingleGovernance instance = new SingleGovernance(address(_config), governance, timelock); + + assertEq(instance.GOVERNANCE(), governance); + assertEq(address(instance.TIMELOCK()), address(timelock)); + } + + function test_submit_proposal() external { + assertEq(_timelock.getSubmittedProposals().length, 0); + + vm.prank(_governance); + _singleGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + + assertEq(_timelock.getSubmittedProposals().length, 1); + } + + function testFuzz_stranger_cannot_submit_proposal(address stranger) external { + vm.assume(stranger != address(0) && stranger != _governance); + + assertEq(_timelock.getSubmittedProposals().length, 0); + + vm.startPrank(stranger); + vm.expectRevert( + abi.encodeWithSelector(SingleGovernance.NotGovernance.selector, [stranger]) + ); + _singleGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + + assertEq(_timelock.getSubmittedProposals().length, 0); + } + + function test_schedule_proposal() external { + assertEq(_timelock.getScheduledProposals().length, 0); + + vm.prank(_governance); + _singleGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + + _timelock.setSchedule(1); + _singleGovernance.scheduleProposal(1); + + assertEq(_timelock.getScheduledProposals().length, 1); + } + + function test_execute_proposal() external { + assertEq(_timelock.getExecutedProposals().length, 0); + + vm.prank(_governance); + _singleGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + + _timelock.setSchedule(1); + _singleGovernance.scheduleProposal(1); + + _singleGovernance.executeProposal(1); + + assertEq(_timelock.getExecutedProposals().length, 1); + } + + function test_cancel_all_pending_proposals() external { + assertEq(_timelock.getLastCancelledProposalId(), 0); + + vm.startPrank(_governance); + _singleGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + _singleGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + + _timelock.setSchedule(1); + _singleGovernance.scheduleProposal(1); + + _singleGovernance.cancelAllPendingProposals(); + + assertEq(_timelock.getLastCancelledProposalId(), 2); + } + + function testFuzz_stranger_cannot_cancel_all_pending_proposals(address stranger) external { + vm.assume(stranger != address(0) && stranger != _governance); + + assertEq(_timelock.getLastCancelledProposalId(), 0); + + vm.startPrank(stranger); + vm.expectRevert( + abi.encodeWithSelector(SingleGovernance.NotGovernance.selector, [stranger]) + ); + _singleGovernance.cancelAllPendingProposals(); + + assertEq(_timelock.getLastCancelledProposalId(), 0); + } + + function test_can_schedule() external { + vm.prank(_governance); + _singleGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + + assertFalse(_singleGovernance.canSchedule(1)); + + _timelock.setSchedule(1); + + assertTrue(_singleGovernance.canSchedule(1)); + } +} \ No newline at end of file diff --git a/test/unit/libraries/Proposals.t.sol b/test/unit/libraries/Proposals.t.sol index 2f61e0e4..3b4ed280 100644 --- a/test/unit/libraries/Proposals.t.sol +++ b/test/unit/libraries/Proposals.t.sol @@ -33,7 +33,7 @@ contract ProposalsUnitTests is UnitTest { function test_submit_proposal() external { uint256 proposalsCount = _proposals.count(); - ExecutorCall[] memory calls = _getTargetRegularStaffCalls(); + ExecutorCall[] memory calls = _getTargetRegularStaffCalls(address(_targetMock)); vm.expectEmit(); emit Proposals.ProposalSubmitted(proposalsCount + PROPOSAL_ID_OFFSET, address(_executor), calls); @@ -65,7 +65,7 @@ contract ProposalsUnitTests is UnitTest { function testFuzz_schedule_proposal(uint256 delay) external { vm.assume(delay > 0 && delay < type(uint40).max); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); Proposals.ProposalPacked memory proposal = _proposals.proposals[0]; uint256 submittedAt = block.timestamp; @@ -97,7 +97,7 @@ contract ProposalsUnitTests is UnitTest { } function test_cannot_schedule_proposal_twice() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = 1; Proposals.schedule(_proposals, proposalId, 0); @@ -108,7 +108,7 @@ contract ProposalsUnitTests is UnitTest { function testFuzz_cannot_schedule_proposal_before_delay_passed(uint256 delay) external { vm.assume(delay > 0 && delay < type(uint40).max); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); _wait(delay - 1); @@ -117,7 +117,7 @@ contract ProposalsUnitTests is UnitTest { } function test_cannot_schedule_cancelled_proposal() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); Proposals.cancelAll(_proposals); uint256 proposalId = _proposals.count(); @@ -129,7 +129,7 @@ contract ProposalsUnitTests is UnitTest { function testFuzz_execute_proposal(uint256 delay) external { vm.assume(delay > 0 && delay < type(uint40).max); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); Proposals.schedule(_proposals, proposalId, 0); @@ -163,7 +163,7 @@ contract ProposalsUnitTests is UnitTest { } function test_cannot_execute_unscheduled_proposal() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); vm.expectRevert(abi.encodeWithSelector(Proposals.ProposalNotScheduled.selector, proposalId)); @@ -171,7 +171,7 @@ contract ProposalsUnitTests is UnitTest { } function test_cannot_execute_twice() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); Proposals.schedule(_proposals, proposalId, 0); Proposals.execute(_proposals, proposalId, 0); @@ -181,7 +181,7 @@ contract ProposalsUnitTests is UnitTest { } function test_cannot_execute_cancelled_proposal() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); Proposals.schedule(_proposals, proposalId, 0); Proposals.cancelAll(_proposals); @@ -192,7 +192,7 @@ contract ProposalsUnitTests is UnitTest { function testFuzz_cannot_execute_before_delay_passed(uint256 delay) external { vm.assume(delay > 0 && delay < type(uint40).max); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); Proposals.schedule(_proposals, proposalId, 0); @@ -203,8 +203,8 @@ contract ProposalsUnitTests is UnitTest { } function test_cancel_all_proposals() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalsCount = _proposals.count(); @@ -218,7 +218,7 @@ contract ProposalsUnitTests is UnitTest { } function test_get_proposal() external { - ExecutorCall[] memory calls = _getTargetRegularStaffCalls(); + ExecutorCall[] memory calls = _getTargetRegularStaffCalls(address(_targetMock)); Proposals.submit(_proposals, address(_executor), calls); uint256 proposalId = _proposals.count(); @@ -282,7 +282,7 @@ contract ProposalsUnitTests is UnitTest { } function test_get_cancelled_proposal() external { - ExecutorCall[] memory calls = _getTargetRegularStaffCalls(); + ExecutorCall[] memory calls = _getTargetRegularStaffCalls(address(_targetMock)); Proposals.submit(_proposals, address(_executor), calls); uint256 proposalId = _proposals.count(); @@ -331,16 +331,16 @@ contract ProposalsUnitTests is UnitTest { function test_count_proposals() external { assertEq(_proposals.count(), 0); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); assertEq(_proposals.count(), 1); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); assertEq(_proposals.count(), 2); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); assertEq(_proposals.count(), 3); - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); assertEq(_proposals.count(), 4); Proposals.schedule(_proposals, 1, 0); @@ -357,7 +357,7 @@ contract ProposalsUnitTests is UnitTest { } function test_can_execute_proposal() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); assert(!_proposals.canExecute(proposalId, 0)); @@ -376,7 +376,7 @@ contract ProposalsUnitTests is UnitTest { } function test_can_not_execute_cancelled_proposal() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); Proposals.schedule(_proposals, proposalId, 0); @@ -387,7 +387,7 @@ contract ProposalsUnitTests is UnitTest { } function test_can_schedule_proposal() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); assert(!_proposals.canSchedule(proposalId, 100)); @@ -402,7 +402,7 @@ contract ProposalsUnitTests is UnitTest { } function test_can_not_schedule_cancelled_proposal() external { - Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls()); + Proposals.submit(_proposals, address(_executor), _getTargetRegularStaffCalls(address(_targetMock))); uint256 proposalId = _proposals.count(); assert(_proposals.canSchedule(proposalId, 0)); @@ -410,8 +410,4 @@ contract ProposalsUnitTests is UnitTest { assert(!_proposals.canSchedule(proposalId, 0)); } - - function _getTargetRegularStaffCalls() internal view returns (ExecutorCall[] memory) { - return ExecutorCallHelpers.create(address(_targetMock), abi.encodeCall(IDangerousContract.doRegularStaff, (42))); - } } diff --git a/test/unit/mocks/TimelockMock.sol b/test/unit/mocks/TimelockMock.sol new file mode 100644 index 00000000..1ff6f631 --- /dev/null +++ b/test/unit/mocks/TimelockMock.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.23; + +import {ExecutorCall} from "contracts/libraries/Proposals.sol"; + +contract TimelockMock { + uint8 public constant OFFSET = 1; + + mapping(uint256 => bool) public canScheduleProposal; + + uint256[] public submittedProposals; + uint256[] public scheduledProposals; + uint256[] public executedProposals; + + uint256 public lastCancelledProposalId; + + function submit(address, ExecutorCall[] calldata) external returns (uint256 newProposalId) { + newProposalId = submittedProposals.length + OFFSET; + submittedProposals.push(newProposalId); + canScheduleProposal[newProposalId] = false; + return newProposalId; + } + function schedule(uint256 proposalId) external { + if (canScheduleProposal[proposalId] == false) { + revert(); + } + + scheduledProposals.push(proposalId); + } + function execute(uint256 proposalId) external { + executedProposals.push(proposalId); + } + function canSchedule(uint256 proposalId) external view returns (bool) { + return canScheduleProposal[proposalId]; + } + function cancelAllNonExecutedProposals() external { + lastCancelledProposalId = submittedProposals[submittedProposals.length - 1]; + } + function setSchedule(uint256 proposalId) external { + canScheduleProposal[proposalId] = true; + } + + function getSubmittedProposals() external view returns (uint256[] memory) { + return submittedProposals; + } + function getScheduledProposals() external view returns (uint256[] memory) { + return scheduledProposals; + } + function getExecutedProposals() external view returns (uint256[] memory) { + return executedProposals; + } + function getLastCancelledProposalId() external view returns (uint256) { + return lastCancelledProposalId; + } +} \ No newline at end of file diff --git a/test/utils/unit-test.sol b/test/utils/unit-test.sol index 6fdb5cfd..6ff279a4 100644 --- a/test/utils/unit-test.sol +++ b/test/utils/unit-test.sol @@ -3,9 +3,16 @@ pragma solidity 0.8.23; // solhint-disable-next-line import {Test} from "forge-std/Test.sol"; +import {ExecutorCall} from "contracts/libraries/Proposals.sol"; +import {ExecutorCallHelpers} from "test/utils/executor-calls.sol"; +import {IDangerousContract} from "test/utils/interfaces.sol"; contract UnitTest is Test { function _wait(uint256 duration) internal { vm.warp(block.timestamp + duration); } + + function _getTargetRegularStaffCalls(address targetMock) internal pure returns (ExecutorCall[] memory) { + return ExecutorCallHelpers.create(address(targetMock), abi.encodeCall(IDangerousContract.doRegularStaff, (42))); + } } \ No newline at end of file From 37388fae092dcc58f9d5af535a54f2e3de3d6798 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 28 May 2024 15:02:07 +0300 Subject: [PATCH 8/8] fix: review fixes --- test/unit/libraries/EmergencyProtection.t.sol | 180 ++++++++---------- 1 file changed, 76 insertions(+), 104 deletions(-) diff --git a/test/unit/libraries/EmergencyProtection.t.sol b/test/unit/libraries/EmergencyProtection.t.sol index 8f05b257..442c708f 100644 --- a/test/unit/libraries/EmergencyProtection.t.sol +++ b/test/unit/libraries/EmergencyProtection.t.sol @@ -8,6 +8,8 @@ import {EmergencyProtection, EmergencyState} from "contracts/libraries/Emergency import {UnitTest} from "test/utils/unit-test.sol"; contract EmergencyProtectionUnitTests is UnitTest { + using EmergencyProtection for EmergencyProtection.State; + EmergencyProtection.State internal _emergencyProtection; function testFuzz_setup_emergency_protection( @@ -32,9 +34,7 @@ contract EmergencyProtectionUnitTests is UnitTest { vm.recordLogs(); - EmergencyProtection.setup( - _emergencyProtection, activationCommittee, executionCommittee, protectedTill, duration - ); + _emergencyProtection.setup(activationCommittee, executionCommittee, protectedTill, duration); Vm.Log[] memory entries = vm.getRecordedLogs(); assertEq(entries.length, 4); @@ -49,17 +49,17 @@ contract EmergencyProtectionUnitTests is UnitTest { function test_setup_same_activation_committee() external { address activationCommittee = makeAddr("activationCommittee"); - EmergencyProtection.setup(_emergencyProtection, activationCommittee, address(0x2), 100, 100); + _emergencyProtection.setup(activationCommittee, address(0x2), 100, 100); vm.expectEmit(); emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x3)); vm.expectEmit(); emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(block.timestamp + 200); vm.expectEmit(); - emit EmergencyProtection.EmergencyModeDurationSet(200); + emit EmergencyProtection.EmergencyModeDurationSet(300); vm.recordLogs(); - EmergencyProtection.setup(_emergencyProtection, activationCommittee, address(0x3), 200, 200); + _emergencyProtection.setup(activationCommittee, address(0x3), 200, 300); Vm.Log[] memory entries = vm.getRecordedLogs(); assertEq(entries.length, 3); @@ -67,24 +67,24 @@ contract EmergencyProtectionUnitTests is UnitTest { assertEq(_emergencyProtection.activationCommittee, activationCommittee); assertEq(_emergencyProtection.executionCommittee, address(0x3)); assertEq(_emergencyProtection.protectedTill, block.timestamp + 200); - assertEq(_emergencyProtection.emergencyModeDuration, 200); + assertEq(_emergencyProtection.emergencyModeDuration, 300); assertEq(_emergencyProtection.emergencyModeEndsAfter, 0); } function test_setup_same_execution_committee() external { address executionCommittee = makeAddr("executionCommittee"); - EmergencyProtection.setup(_emergencyProtection, address(0x1), executionCommittee, 100, 100); + _emergencyProtection.setup(address(0x1), executionCommittee, 100, 100); vm.expectEmit(); emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x2)); vm.expectEmit(); emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(block.timestamp + 200); vm.expectEmit(); - emit EmergencyProtection.EmergencyModeDurationSet(200); + emit EmergencyProtection.EmergencyModeDurationSet(300); vm.recordLogs(); - EmergencyProtection.setup(_emergencyProtection, address(0x2), executionCommittee, 200, 200); + _emergencyProtection.setup(address(0x2), executionCommittee, 200, 300); Vm.Log[] memory entries = vm.getRecordedLogs(); assertEq(entries.length, 3); @@ -92,12 +92,12 @@ contract EmergencyProtectionUnitTests is UnitTest { assertEq(_emergencyProtection.activationCommittee, address(0x2)); assertEq(_emergencyProtection.executionCommittee, executionCommittee); assertEq(_emergencyProtection.protectedTill, block.timestamp + 200); - assertEq(_emergencyProtection.emergencyModeDuration, 200); + assertEq(_emergencyProtection.emergencyModeDuration, 300); assertEq(_emergencyProtection.emergencyModeEndsAfter, 0); } function test_setup_same_protected_till() external { - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + _emergencyProtection.setup(address(0x1), address(0x2), 100, 100); vm.expectEmit(); emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x3)); @@ -107,7 +107,7 @@ contract EmergencyProtectionUnitTests is UnitTest { emit EmergencyProtection.EmergencyModeDurationSet(200); vm.recordLogs(); - EmergencyProtection.setup(_emergencyProtection, address(0x3), address(0x4), 100, 200); + _emergencyProtection.setup(address(0x3), address(0x4), 100, 200); Vm.Log[] memory entries = vm.getRecordedLogs(); assertEq(entries.length, 3); @@ -120,7 +120,7 @@ contract EmergencyProtectionUnitTests is UnitTest { } function test_setup_same_emergency_mode_duration() external { - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + _emergencyProtection.setup(address(0x1), address(0x2), 100, 100); vm.expectEmit(); emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x3)); @@ -130,7 +130,7 @@ contract EmergencyProtectionUnitTests is UnitTest { emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(block.timestamp + 200); vm.recordLogs(); - EmergencyProtection.setup(_emergencyProtection, address(0x3), address(0x4), 200, 100); + _emergencyProtection.setup(address(0x3), address(0x4), 200, 100); Vm.Log[] memory entries = vm.getRecordedLogs(); assertEq(entries.length, 3); @@ -143,14 +143,14 @@ contract EmergencyProtectionUnitTests is UnitTest { } function test_activate_emergency_mode() external { - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + _emergencyProtection.setup(address(0x1), address(0x2), 100, 100); vm.expectEmit(); emit EmergencyProtection.EmergencyModeActivated(block.timestamp); vm.recordLogs(); - EmergencyProtection.activate(_emergencyProtection); + _emergencyProtection.activate(); Vm.Log[] memory entries = vm.getRecordedLogs(); @@ -160,7 +160,7 @@ contract EmergencyProtectionUnitTests is UnitTest { function test_cannot_activate_emergency_mode_if_protected_till_expired() external { uint256 protectedTill = 100; - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), protectedTill, 100); + _emergencyProtection.setup(address(0x1), address(0x2), protectedTill, 100); _wait(protectedTill + 1); @@ -170,7 +170,7 @@ contract EmergencyProtectionUnitTests is UnitTest { [block.timestamp, _emergencyProtection.protectedTill] ) ); - EmergencyProtection.activate(_emergencyProtection); + _emergencyProtection.activate(); } function testFuzz_deactivate_emergency_mode( @@ -184,17 +184,15 @@ contract EmergencyProtectionUnitTests is UnitTest { vm.assume(activationCommittee != address(0)); vm.assume(executionCommittee != address(0)); - EmergencyProtection.setup( - _emergencyProtection, activationCommittee, executionCommittee, protectedTill, duration - ); - EmergencyProtection.activate(_emergencyProtection); + _emergencyProtection.setup(activationCommittee, executionCommittee, protectedTill, duration); + _emergencyProtection.activate(); vm.expectEmit(); emit EmergencyProtection.EmergencyModeDeactivated(block.timestamp); vm.recordLogs(); - EmergencyProtection.deactivate(_emergencyProtection); + _emergencyProtection.deactivate(); Vm.Log[] memory entries = vm.getRecordedLogs(); assertEq(entries.length, 1); @@ -207,7 +205,7 @@ contract EmergencyProtectionUnitTests is UnitTest { } function test_get_emergency_state() external { - EmergencyState memory state = EmergencyProtection.getEmergencyState(_emergencyProtection); + EmergencyState memory state = _emergencyProtection.getEmergencyState(); assertEq(state.activationCommittee, address(0)); assertEq(state.executionCommittee, address(0)); @@ -216,31 +214,31 @@ contract EmergencyProtectionUnitTests is UnitTest { assertEq(state.emergencyModeEndsAfter, 0); assertEq(state.isEmergencyModeActivated, false); - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + _emergencyProtection.setup(address(0x1), address(0x2), 100, 200); - state = EmergencyProtection.getEmergencyState(_emergencyProtection); + state = _emergencyProtection.getEmergencyState(); assertEq(state.activationCommittee, address(0x1)); assertEq(state.executionCommittee, address(0x2)); assertEq(state.protectedTill, block.timestamp + 100); - assertEq(state.emergencyModeDuration, 100); + assertEq(state.emergencyModeDuration, 200); assertEq(state.emergencyModeEndsAfter, 0); assertEq(state.isEmergencyModeActivated, false); - EmergencyProtection.activate(_emergencyProtection); + _emergencyProtection.activate(); - state = EmergencyProtection.getEmergencyState(_emergencyProtection); + state = _emergencyProtection.getEmergencyState(); assertEq(state.activationCommittee, address(0x1)); assertEq(state.executionCommittee, address(0x2)); assertEq(state.protectedTill, block.timestamp + 100); - assertEq(state.emergencyModeDuration, 100); - assertEq(state.emergencyModeEndsAfter, block.timestamp + 100); + assertEq(state.emergencyModeDuration, 200); + assertEq(state.emergencyModeEndsAfter, block.timestamp + 200); assertEq(state.isEmergencyModeActivated, true); - EmergencyProtection.deactivate(_emergencyProtection); + _emergencyProtection.deactivate(); - state = EmergencyProtection.getEmergencyState(_emergencyProtection); + state = _emergencyProtection.getEmergencyState(); assertEq(state.activationCommittee, address(0)); assertEq(state.executionCommittee, address(0)); @@ -251,52 +249,52 @@ contract EmergencyProtectionUnitTests is UnitTest { } function test_is_emergency_mode_activated() external { - assertEq(EmergencyProtection.isEmergencyModeActivated(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyModeActivated(), false); - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); + _emergencyProtection.setup(address(0x1), address(0x2), 100, 100); - assertEq(EmergencyProtection.isEmergencyModeActivated(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyModeActivated(), false); - EmergencyProtection.activate(_emergencyProtection); + _emergencyProtection.activate(); - assertEq(EmergencyProtection.isEmergencyModeActivated(_emergencyProtection), true); + assertEq(_emergencyProtection.isEmergencyModeActivated(), true); - EmergencyProtection.deactivate(_emergencyProtection); + _emergencyProtection.deactivate(); - assertEq(EmergencyProtection.isEmergencyModeActivated(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyModeActivated(), false); } function test_is_emergency_mode_passed() external { - assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyModePassed(), false); - uint256 duration = 100; + uint256 duration = 200; - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, duration); + _emergencyProtection.setup(address(0x1), address(0x2), 100, duration); - assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyModePassed(), false); - EmergencyProtection.activate(_emergencyProtection); + _emergencyProtection.activate(); - assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyModePassed(), false); _wait(duration + 1); - assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), true); + assertEq(_emergencyProtection.isEmergencyModePassed(), true); - EmergencyProtection.deactivate(_emergencyProtection); + _emergencyProtection.deactivate(); - assertEq(EmergencyProtection.isEmergencyModePassed(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyModePassed(), false); } function test_is_emergency_protection_enabled() external { uint256 protectedTill = 100; - uint256 duration = 100; + uint256 duration = 200; - assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), false); - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), protectedTill, duration); + _emergencyProtection.setup(address(0x1), address(0x2), protectedTill, duration); - assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), true); + assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); _wait(protectedTill - block.timestamp); @@ -304,88 +302,62 @@ contract EmergencyProtectionUnitTests is UnitTest { _wait(duration); - assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), true); + assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); _wait(100); - assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), true); + assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); EmergencyProtection.deactivate(_emergencyProtection); - assertEq(EmergencyProtection.isEmergencyProtectionEnabled(_emergencyProtection), false); + assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), false); } function testFuzz_check_activation_committee(address committee, address stranger) external { vm.assume(committee != address(0)); vm.assume(stranger != address(0) && stranger != committee); - vm.expectRevert( - abi.encodeWithSelector( - EmergencyProtection.NotEmergencyActivator.selector, - [stranger] - ) - ); - EmergencyProtection.checkActivationCommittee(_emergencyProtection, stranger); - EmergencyProtection.checkActivationCommittee(_emergencyProtection, address(0)); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyActivator.selector, [stranger])); + _emergencyProtection.checkActivationCommittee(stranger); + _emergencyProtection.checkActivationCommittee(address(0)); - EmergencyProtection.setup(_emergencyProtection, committee, address(0x2), 100, 100); + _emergencyProtection.setup(committee, address(0x2), 100, 100); - EmergencyProtection.checkActivationCommittee(_emergencyProtection, committee); + _emergencyProtection.checkActivationCommittee(committee); - vm.expectRevert( - abi.encodeWithSelector( - EmergencyProtection.NotEmergencyActivator.selector, - [stranger] - ) - ); - EmergencyProtection.checkActivationCommittee(_emergencyProtection, stranger); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyActivator.selector, [stranger])); + _emergencyProtection.checkActivationCommittee(stranger); } function testFuzz_check_execution_committee(address committee, address stranger) external { vm.assume(committee != address(0)); vm.assume(stranger != address(0) && stranger != committee); - vm.expectRevert( - abi.encodeWithSelector( - EmergencyProtection.NotEmergencyEnactor.selector, - [stranger] - ) - ); - EmergencyProtection.checkExecutionCommittee(_emergencyProtection, stranger); - EmergencyProtection.checkExecutionCommittee(_emergencyProtection, address(0)); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, [stranger])); + _emergencyProtection.checkExecutionCommittee(stranger); + _emergencyProtection.checkExecutionCommittee(address(0)); - EmergencyProtection.setup(_emergencyProtection, address(0x1), committee, 100, 100); + _emergencyProtection.setup(address(0x1), committee, 100, 100); - EmergencyProtection.checkExecutionCommittee(_emergencyProtection, committee); + _emergencyProtection.checkExecutionCommittee(committee); - vm.expectRevert( - abi.encodeWithSelector( - EmergencyProtection.NotEmergencyEnactor.selector, - [stranger] - ) - ); - EmergencyProtection.checkExecutionCommittee(_emergencyProtection, stranger); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, [stranger])); + _emergencyProtection.checkExecutionCommittee(stranger); } function test_check_emergency_mode_active() external { vm.expectRevert( - abi.encodeWithSelector( - EmergencyProtection.InvalidEmergencyModeActiveValue.selector, - [false, true] - ) + abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeActiveValue.selector, [false, true]) ); - EmergencyProtection.checkEmergencyModeActive(_emergencyProtection, true); - EmergencyProtection.checkEmergencyModeActive(_emergencyProtection, false); + _emergencyProtection.checkEmergencyModeActive(true); + _emergencyProtection.checkEmergencyModeActive(false); - EmergencyProtection.setup(_emergencyProtection, address(0x1), address(0x2), 100, 100); - EmergencyProtection.activate(_emergencyProtection); + _emergencyProtection.setup(address(0x1), address(0x2), 100, 100); + _emergencyProtection.activate(); - EmergencyProtection.checkEmergencyModeActive(_emergencyProtection, true); + _emergencyProtection.checkEmergencyModeActive(true); vm.expectRevert( - abi.encodeWithSelector( - EmergencyProtection.InvalidEmergencyModeActiveValue.selector, - [true, false] - ) + abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeActiveValue.selector, [true, false]) ); } }