diff --git a/contracts/Configuration.sol b/contracts/Configuration.sol deleted file mode 100644 index a2f95048..00000000 --- a/contracts/Configuration.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.26; - -import {Durations, Duration} from "./types/Duration.sol"; -import {IConfiguration, DualGovernanceStateMachine, TiebreakConfig} from "./interfaces/IConfiguration.sol"; - -uint256 constant PERCENT = 10 ** 16; - -contract Configuration is IConfiguration { - error MaxSealablesLimitOverflow(uint256 count, uint256 limit); - - uint256 public immutable MIN_WITHDRAWALS_BATCH_SIZE = 8; - uint256 public immutable MAX_WITHDRAWALS_BATCH_SIZE = 128; - - // --- - // Dual Governance State Properties - // --- - uint256 public immutable FIRST_SEAL_RAGE_QUIT_SUPPORT = 3 * PERCENT; - uint256 public immutable SECOND_SEAL_RAGE_QUIT_SUPPORT = 15 * PERCENT; - - Duration public immutable DYNAMIC_TIMELOCK_MIN_DURATION = Durations.from(3 days); - Duration public immutable DYNAMIC_TIMELOCK_MAX_DURATION = Durations.from(30 days); - - Duration public immutable VETO_SIGNALLING_MIN_ACTIVE_DURATION = Durations.from(5 hours); - Duration public immutable VETO_SIGNALLING_DEACTIVATION_MAX_DURATION = Durations.from(5 days); - Duration public immutable RAGE_QUIT_ACCUMULATION_MAX_DURATION = Durations.from(3 days); - - Duration public immutable VETO_COOLDOWN_DURATION = Durations.from(4 days); - - Duration public immutable RAGE_QUIT_EXTENSION_DELAY = Durations.from(7 days); - Duration public immutable RAGE_QUIT_ETH_WITHDRAWALS_MIN_TIMELOCK = Durations.from(60 days); - uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_START_SEQ_NUMBER = 2; - - uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_A = 0; - uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_B = 0; - uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_C = 0; - // --- - - address public immutable ADMIN_EXECUTOR; - address public immutable EMERGENCY_GOVERNANCE; - - Duration public immutable AFTER_SUBMIT_DELAY = Durations.from(3 days); - Duration public immutable AFTER_SCHEDULE_DELAY = Durations.from(2 days); - - Duration public immutable SIGNALLING_ESCROW_MIN_LOCK_TIME = Durations.from(5 hours); - - Duration public immutable TIE_BREAK_ACTIVATION_TIMEOUT = Durations.from(365 days); - - // Sealables Array Representation - uint256 private immutable MAX_SELABLES_COUNT = 5; - - uint256 private immutable SEALABLES_COUNT; - - address private immutable SEALABLE_0; - address private immutable SEALABLE_1; - address private immutable SEALABLE_2; - address private immutable SEALABLE_3; - address private immutable SEALABLE_4; - - constructor(address adminExecutor, address emergencyGovernance, address[] memory sealableWithdrawalBlockers_) { - ADMIN_EXECUTOR = adminExecutor; - EMERGENCY_GOVERNANCE = emergencyGovernance; - - if (sealableWithdrawalBlockers_.length > MAX_SELABLES_COUNT) { - revert MaxSealablesLimitOverflow(sealableWithdrawalBlockers_.length, MAX_SELABLES_COUNT); - } - - SEALABLES_COUNT = sealableWithdrawalBlockers_.length; - if (SEALABLES_COUNT > 0) SEALABLE_0 = sealableWithdrawalBlockers_[0]; - if (SEALABLES_COUNT > 1) SEALABLE_1 = sealableWithdrawalBlockers_[1]; - if (SEALABLES_COUNT > 2) SEALABLE_2 = sealableWithdrawalBlockers_[2]; - if (SEALABLES_COUNT > 3) SEALABLE_3 = sealableWithdrawalBlockers_[3]; - if (SEALABLES_COUNT > 4) SEALABLE_4 = sealableWithdrawalBlockers_[4]; - } - - function getSealableWithdrawalBlockers() public view returns (address[] memory sealableWithdrawalBlockers_) { - sealableWithdrawalBlockers_ = new address[](SEALABLES_COUNT); - if (SEALABLES_COUNT > 0) sealableWithdrawalBlockers_[0] = SEALABLE_0; - if (SEALABLES_COUNT > 1) sealableWithdrawalBlockers_[1] = SEALABLE_1; - if (SEALABLES_COUNT > 2) sealableWithdrawalBlockers_[2] = SEALABLE_2; - if (SEALABLES_COUNT > 3) sealableWithdrawalBlockers_[3] = SEALABLE_3; - if (SEALABLES_COUNT > 4) sealableWithdrawalBlockers_[4] = SEALABLE_4; - } - - function getDualGovernanceConfig() external view returns (DualGovernanceStateMachine.Config memory config) { - config.firstSealRageQuitSupport = FIRST_SEAL_RAGE_QUIT_SUPPORT; - config.secondSealRageQuitSupport = SECOND_SEAL_RAGE_QUIT_SUPPORT; - config.dynamicTimelockMinDuration = DYNAMIC_TIMELOCK_MIN_DURATION; - config.dynamicTimelockMaxDuration = DYNAMIC_TIMELOCK_MAX_DURATION; - config.vetoSignallingMinActiveDuration = VETO_SIGNALLING_MIN_ACTIVE_DURATION; - config.vetoSignallingDeactivationMaxDuration = VETO_SIGNALLING_DEACTIVATION_MAX_DURATION; - config.vetoCooldownDuration = VETO_COOLDOWN_DURATION; - config.rageQuitExtensionDelay = RAGE_QUIT_EXTENSION_DELAY; - config.rageQuitEthWithdrawalsMinTimelock = RAGE_QUIT_ETH_WITHDRAWALS_MIN_TIMELOCK; - config.rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber = - RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_START_SEQ_NUMBER; - config.rageQuitEthWithdrawalsTimelockGrowthCoeffs = [ - RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_A, - RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_B, - RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_C - ]; - } - - function getTiebreakConfig() external view returns (TiebreakConfig memory config) { - config.tiebreakActivationTimeout = TIE_BREAK_ACTIVATION_TIMEOUT; - config.potentialDeadlockSealables = getSealableWithdrawalBlockers(); - } -} diff --git a/contracts/ConfigurationProvider.sol b/contracts/ConfigurationProvider.sol deleted file mode 100644 index a8d9af22..00000000 --- a/contracts/ConfigurationProvider.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.26; - -import {IConfiguration} from "./interfaces/IConfiguration.sol"; - -contract ConfigurationProvider { - error NotAdminExecutor(address account); - - IConfiguration public immutable CONFIG; - - constructor(address config) { - CONFIG = IConfiguration(config); - } - - function _checkAdminExecutor(address account) internal view { - if (CONFIG.ADMIN_EXECUTOR() != account) { - revert NotAdminExecutor(account); - } - } -} diff --git a/contracts/DualGovernance.sol b/contracts/DualGovernance.sol index 25df297c..3f3869be 100644 --- a/contracts/DualGovernance.sol +++ b/contracts/DualGovernance.sol @@ -1,49 +1,101 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import {ITimelock, IGovernance} from "./interfaces/ITimelock.sol"; +import {IDualGovernance} from "./interfaces/IDualGovernance.sol"; import {IResealManager} from "./interfaces/IResealManager.sol"; import {Duration} from "./types/Duration.sol"; import {Timestamp} from "./types/Timestamp.sol"; +import {ITimelock} from "./interfaces/ITimelock.sol"; +import {IResealManager} from "./interfaces/IResealManager.sol"; -import {ConfigurationProvider} from "./ConfigurationProvider.sol"; -import {Proposers, Proposer} from "./libraries/Proposers.sol"; +import {Tiebreaker} from "./libraries/Tiebreaker.sol"; import {ExternalCall} from "./libraries/ExternalCalls.sol"; -import {EmergencyProtection} from "./libraries/EmergencyProtection.sol"; +import {Proposers, Proposer} from "./libraries/Proposers.sol"; import {State, DualGovernanceStateMachine} from "./libraries/DualGovernanceStateMachine.sol"; -import {TiebreakerProtection} from "./libraries/TiebreakerProtection.sol"; +import {IDualGovernanceConfigProvider} from "./DualGovernanceConfigProvider.sol"; -contract DualGovernance is IGovernance, ConfigurationProvider { +import {Escrow} from "./Escrow.sol"; + +contract DualGovernance is IDualGovernance { using Proposers for Proposers.State; - using TiebreakerProtection for TiebreakerProtection.Tiebreaker; + using Tiebreaker for Tiebreaker.Context; using DualGovernanceStateMachine for DualGovernanceStateMachine.Context; - error NotDeadlock(); - error NotResealCommitttee(address account); + // --- + // Errors + // --- + + error InvalidConfigProvider(IDualGovernanceConfigProvider configProvider); + error NotResealCommittee(address account); error ProposalSubmissionBlocked(); + error InvalidAdminExecutor(address value); error ProposalSchedulingBlocked(uint256 proposalId); error ResealIsNotAllowedInNormalState(); + // --- + // Events + // --- + + event EscrowMasterCopyDeployed(address escrowMasterCopy); + event ConfigProviderSet(IDualGovernanceConfigProvider newConfigProvider); + + // --- + // Tiebreaker Sanity Check Param Immutables + // --- + + struct SanityCheckParams { + Duration minTiebreakerActivationTimeout; + Duration maxTiebreakerActivationTimeout; + uint256 maxSealableWithdrawalBlockersCount; + } + + Duration public immutable MIN_TIEBREAKER_ACTIVATION_TIMEOUT; + Duration public immutable MAX_TIEBREAKER_ACTIVATION_TIMEOUT; + uint256 public immutable MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT; + + // --- + // External Parts Immutables + ITimelock public immutable TIMELOCK; + IResealManager public immutable RESEAL_MANAGER; + address public immutable ESCROW_MASTER_COPY; + + // --- + // Aspects + // --- Proposers.State internal _proposers; + Tiebreaker.Context internal _tiebreaker; DualGovernanceStateMachine.Context internal _stateMachine; - EmergencyProtection.State internal _emergencyProtection; + + // --- + // Standalone State Variables + // --- + IDualGovernanceConfigProvider internal _configProvider; address internal _resealCommittee; - IResealManager internal _resealManager; - TiebreakerProtection.Tiebreaker internal _tiebreaker; constructor( - address config, - address timelock, - address escrowMasterCopy, - address adminProposer - ) ConfigurationProvider(config) { - TIMELOCK = ITimelock(timelock); - - _stateMachine.initialize(escrowMasterCopy); - _proposers.register(adminProposer, CONFIG.ADMIN_EXECUTOR()); + ITimelock timelock, + IResealManager resealManager, + IDualGovernanceConfigProvider configProvider, + SanityCheckParams memory dualGovernanceSanityCheckParams, + Escrow.SanityCheckParams memory escrowSanityCheckParams, + Escrow.ProtocolDependencies memory escrowProtocolDependencies + ) { + TIMELOCK = timelock; + RESEAL_MANAGER = resealManager; + + MIN_TIEBREAKER_ACTIVATION_TIMEOUT = dualGovernanceSanityCheckParams.minTiebreakerActivationTimeout; + MAX_TIEBREAKER_ACTIVATION_TIMEOUT = dualGovernanceSanityCheckParams.maxTiebreakerActivationTimeout; + MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT = dualGovernanceSanityCheckParams.maxSealableWithdrawalBlockersCount; + + _setConfigProvider(configProvider); + + ESCROW_MASTER_COPY = address(new Escrow(this, escrowSanityCheckParams, escrowProtocolDependencies)); + emit EscrowMasterCopyDeployed(ESCROW_MASTER_COPY); + + _stateMachine.initialize(configProvider.getDualGovernanceConfig(), ESCROW_MASTER_COPY); } // --- @@ -51,8 +103,8 @@ contract DualGovernance is IGovernance, ConfigurationProvider { // --- function submitProposal(ExternalCall[] calldata calls) external returns (uint256 proposalId) { + _stateMachine.activateNextState(_configProvider.getDualGovernanceConfig(), ESCROW_MASTER_COPY); _proposers.checkProposer(msg.sender); - _stateMachine.activateNextState(CONFIG.getDualGovernanceConfig()); if (!_stateMachine.canSubmitProposal()) { revert ProposalSubmissionBlocked(); } @@ -61,7 +113,7 @@ contract DualGovernance is IGovernance, ConfigurationProvider { } function scheduleProposal(uint256 proposalId) external { - _stateMachine.activateNextState(CONFIG.getDualGovernanceConfig()); + _stateMachine.activateNextState(_configProvider.getDualGovernanceConfig(), ESCROW_MASTER_COPY); ( /* id */ , /* status */, /* executor */, Timestamp submittedAt, /* scheduledAt */ ) = TIMELOCK.getProposalInfo(proposalId); if (!_stateMachine.canScheduleProposal(submittedAt)) { @@ -71,7 +123,7 @@ contract DualGovernance is IGovernance, ConfigurationProvider { } function cancelAllPendingProposals() external { - _proposers.checkAdminProposer(CONFIG, msg.sender); + _proposers.checkAdminProposer(TIMELOCK.getAdminExecutor(), msg.sender); TIMELOCK.cancelAllNonExecutedProposals(); } @@ -89,6 +141,25 @@ contract DualGovernance is IGovernance, ConfigurationProvider { // Dual Governance State // --- + function activateNextState() external { + _stateMachine.activateNextState(_configProvider.getDualGovernanceConfig(), ESCROW_MASTER_COPY); + } + + function setConfigProvider(IDualGovernanceConfigProvider newConfigProvider) external { + _checkAdminExecutor(msg.sender); + _setConfigProvider(newConfigProvider); + + /// @dev the minAssetsLockDuration is kept as a storage variable in the signalling Escrow instance + /// to sync the new value with current signalling escrow, it's value must be manually updated + _stateMachine.signallingEscrow.setMinAssetsLockDuration( + newConfigProvider.getDualGovernanceConfig().minAssetsLockDuration + ); + } + + function getConfigProvider() external view returns (IDualGovernanceConfigProvider) { + return _configProvider; + } + function getVetoSignallingEscrow() external view returns (address) { return address(_stateMachine.signallingEscrow); } @@ -97,10 +168,6 @@ contract DualGovernance is IGovernance, ConfigurationProvider { return address(_stateMachine.rageQuitEscrow); } - function activateNextState() external { - _stateMachine.activateNextState(CONFIG.getDualGovernanceConfig()); - } - function getCurrentState() external view returns (State currentState) { currentState = _stateMachine.getCurrentState(); } @@ -110,7 +177,7 @@ contract DualGovernance is IGovernance, ConfigurationProvider { } function getDynamicDelayDuration() external view returns (Duration) { - return _stateMachine.getDynamicDelayDuration(CONFIG.getDualGovernanceConfig()); + return _stateMachine.getDynamicDelayDuration(_configProvider.getDualGovernanceConfig()); } // --- @@ -124,7 +191,7 @@ contract DualGovernance is IGovernance, ConfigurationProvider { function unregisterProposer(address proposer) external { _checkAdminExecutor(msg.sender); - _proposers.unregister(CONFIG, proposer); + _proposers.unregister(TIMELOCK.getAdminExecutor(), proposer); } function getProposer(address account) external view returns (Proposer memory proposer) { @@ -147,25 +214,52 @@ contract DualGovernance is IGovernance, ConfigurationProvider { // Tiebreaker Protection // --- + function addTiebreakerSealableWithdrawalBlocker(address sealableWithdrawalBlocker) external { + _checkAdminExecutor(msg.sender); + _tiebreaker.addSealableWithdrawalBlocker(sealableWithdrawalBlocker, MAX_SEALABLE_WITHDRAWAL_BLOCKERS_COUNT); + } + + function removeTiebreakerSealableWithdrawalBlocker(address sealableWithdrawalBlocker) external { + _checkAdminExecutor(msg.sender); + _tiebreaker.removeSealableWithdrawalBlocker(sealableWithdrawalBlocker); + } + + function setTiebreakerCommittee(address tiebreakerCommittee) external { + _checkAdminExecutor(msg.sender); + _tiebreaker.setTiebreakerCommittee(tiebreakerCommittee); + } + + function setTiebreakerActivationTimeout(Duration tiebreakerActivationTimeout) external { + _checkAdminExecutor(msg.sender); + _tiebreaker.setTiebreakerActivationTimeout( + MIN_TIEBREAKER_ACTIVATION_TIMEOUT, tiebreakerActivationTimeout, MAX_TIEBREAKER_ACTIVATION_TIMEOUT + ); + } + function tiebreakerResumeSealable(address sealable) external { _tiebreaker.checkTiebreakerCommittee(msg.sender); - if (!_stateMachine.isDeadlock(CONFIG.getTiebreakConfig())) { - revert NotDeadlock(); - } - _tiebreaker.resumeSealable(sealable); + _tiebreaker.checkTie(_stateMachine.getCurrentState(), _stateMachine.getNormalOrVetoCooldownStateExitedAt()); + RESEAL_MANAGER.resume(sealable); } function tiebreakerScheduleProposal(uint256 proposalId) external { _tiebreaker.checkTiebreakerCommittee(msg.sender); - if (!_stateMachine.isDeadlock(CONFIG.getTiebreakConfig())) { - revert NotDeadlock(); - } + _tiebreaker.checkTie(_stateMachine.getCurrentState(), _stateMachine.getNormalOrVetoCooldownStateExitedAt()); TIMELOCK.schedule(proposalId); } - function setTiebreakerProtection(address newTiebreaker, address resealManager) external { - _checkAdminExecutor(msg.sender); - _tiebreaker.setTiebreaker(newTiebreaker, resealManager); + struct TiebreakerState { + address tiebreakerCommittee; + Duration tiebreakerActivationTimeout; + address[] sealableWithdrawalBlockers; + } + + function getTiebreakerState() external view returns (TiebreakerState memory tiebreakerState) { + ( + tiebreakerState.tiebreakerCommittee, + tiebreakerState.tiebreakerActivationTimeout, + tiebreakerState.sealableWithdrawalBlockers + ) = _tiebreaker.getTiebreakerInfo(); } // --- @@ -174,17 +268,34 @@ contract DualGovernance is IGovernance, ConfigurationProvider { function resealSealables(address[] memory sealables) external { if (msg.sender != _resealCommittee) { - revert NotResealCommitttee(msg.sender); + revert NotResealCommittee(msg.sender); } if (_stateMachine.getCurrentState() == State.Normal) { revert ResealIsNotAllowedInNormalState(); } - _resealManager.reseal(sealables); + RESEAL_MANAGER.reseal(sealables); } - function setReseal(address resealManager, address resealCommittee) external { - _checkAdminExecutor(msg.sender); - _resealCommittee = resealCommittee; - _resealManager = IResealManager(resealManager); + // --- + // Private methods + // --- + + function _setConfigProvider(IDualGovernanceConfigProvider newConfigProvider) internal { + if (address(newConfigProvider) == address(0)) { + revert InvalidConfigProvider(newConfigProvider); + } + + if (newConfigProvider == _configProvider) { + return; + } + + _configProvider = IDualGovernanceConfigProvider(newConfigProvider); + emit ConfigProviderSet(newConfigProvider); + } + + function _checkAdminExecutor(address account) internal view { + if (TIMELOCK.getAdminExecutor() != account) { + revert InvalidAdminExecutor(account); + } } } diff --git a/contracts/DualGovernanceConfigProvider.sol b/contracts/DualGovernanceConfigProvider.sol new file mode 100644 index 00000000..cf40fa20 --- /dev/null +++ b/contracts/DualGovernanceConfigProvider.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {Duration} from "./types/Duration.sol"; +import {DualGovernanceConfig} from "./libraries/DualGovernanceConfig.sol"; + +interface IDualGovernanceConfigProvider { + function getDualGovernanceConfig() external view returns (DualGovernanceConfig.Context memory config); +} + +contract ImmutableDualGovernanceConfigProvider is IDualGovernanceConfigProvider { + uint256 public immutable FIRST_SEAL_RAGE_QUIT_SUPPORT; + uint256 public immutable SECOND_SEAL_RAGE_QUIT_SUPPORT; + + Duration public immutable MIN_ASSETS_LOCK_DURATION; + Duration public immutable DYNAMIC_TIMELOCK_MIN_DURATION; + Duration public immutable DYNAMIC_TIMELOCK_MAX_DURATION; + + Duration public immutable VETO_SIGNALLING_MIN_ACTIVE_DURATION; + Duration public immutable VETO_SIGNALLING_DEACTIVATION_MAX_DURATION; + + Duration public immutable VETO_COOLDOWN_DURATION; + + Duration public immutable RAGE_QUIT_EXTENSION_DELAY; + Duration public immutable RAGE_QUIT_ETH_WITHDRAWALS_MIN_TIMELOCK; + uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_START_SEQ_NUMBER; + + uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_A; + uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_B; + uint256 public immutable RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_C; + + constructor(DualGovernanceConfig.Context memory dualGovernanceConfig) { + FIRST_SEAL_RAGE_QUIT_SUPPORT = dualGovernanceConfig.firstSealRageQuitSupport; + SECOND_SEAL_RAGE_QUIT_SUPPORT = dualGovernanceConfig.secondSealRageQuitSupport; + + MIN_ASSETS_LOCK_DURATION = dualGovernanceConfig.minAssetsLockDuration; + DYNAMIC_TIMELOCK_MIN_DURATION = dualGovernanceConfig.dynamicTimelockMinDuration; + DYNAMIC_TIMELOCK_MAX_DURATION = dualGovernanceConfig.dynamicTimelockMaxDuration; + + VETO_SIGNALLING_MIN_ACTIVE_DURATION = dualGovernanceConfig.vetoSignallingMinActiveDuration; + VETO_SIGNALLING_DEACTIVATION_MAX_DURATION = dualGovernanceConfig.vetoSignallingDeactivationMaxDuration; + + VETO_COOLDOWN_DURATION = dualGovernanceConfig.vetoCooldownDuration; + + RAGE_QUIT_EXTENSION_DELAY = dualGovernanceConfig.rageQuitExtensionDelay; + RAGE_QUIT_ETH_WITHDRAWALS_MIN_TIMELOCK = dualGovernanceConfig.rageQuitEthWithdrawalsMinTimelock; + RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_START_SEQ_NUMBER = + dualGovernanceConfig.rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber; + + RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_A = + dualGovernanceConfig.rageQuitEthWithdrawalsTimelockGrowthCoeffs[0]; + RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_B = + dualGovernanceConfig.rageQuitEthWithdrawalsTimelockGrowthCoeffs[1]; + RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_C = + dualGovernanceConfig.rageQuitEthWithdrawalsTimelockGrowthCoeffs[2]; + } + + function getDualGovernanceConfig() external view returns (DualGovernanceConfig.Context memory config) { + config.firstSealRageQuitSupport = FIRST_SEAL_RAGE_QUIT_SUPPORT; + config.secondSealRageQuitSupport = SECOND_SEAL_RAGE_QUIT_SUPPORT; + + config.minAssetsLockDuration = MIN_ASSETS_LOCK_DURATION; + config.dynamicTimelockMinDuration = DYNAMIC_TIMELOCK_MIN_DURATION; + config.dynamicTimelockMaxDuration = DYNAMIC_TIMELOCK_MAX_DURATION; + config.vetoSignallingMinActiveDuration = VETO_SIGNALLING_MIN_ACTIVE_DURATION; + config.vetoSignallingDeactivationMaxDuration = VETO_SIGNALLING_DEACTIVATION_MAX_DURATION; + config.vetoCooldownDuration = VETO_COOLDOWN_DURATION; + config.rageQuitExtensionDelay = RAGE_QUIT_EXTENSION_DELAY; + config.rageQuitEthWithdrawalsMinTimelock = RAGE_QUIT_ETH_WITHDRAWALS_MIN_TIMELOCK; + config.rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber = + RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_START_SEQ_NUMBER; + config.rageQuitEthWithdrawalsTimelockGrowthCoeffs = [ + RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_A, + RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_B, + RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_C + ]; + } +} diff --git a/contracts/EmergencyProtectedTimelock.sol b/contracts/EmergencyProtectedTimelock.sol index ffbb467b..4cfc16ef 100644 --- a/contracts/EmergencyProtectedTimelock.sol +++ b/contracts/EmergencyProtectedTimelock.sol @@ -7,33 +7,61 @@ import {Timestamp} from "./types/Timestamp.sol"; import {IOwnable} from "./interfaces/IOwnable.sol"; import {ITimelock, ProposalStatus} from "./interfaces/ITimelock.sol"; -import {EmergencyProtection, EmergencyState} from "./libraries/EmergencyProtection.sol"; - +import {TimelockState} from "./libraries/TimelockState.sol"; import {ExternalCall} from "./libraries/ExternalCalls.sol"; import {ExecutableProposals} from "./libraries/ExecutableProposals.sol"; - -import {ConfigurationProvider} from "./ConfigurationProvider.sol"; +import {EmergencyProtection} from "./libraries/EmergencyProtection.sol"; /// @title EmergencyProtectedTimelock /// @dev A timelock contract with emergency protection functionality. /// The contract allows for submitting, scheduling, and executing proposals, /// while providing emergency protection features to prevent unauthorized /// execution during emergency situations. -contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { +contract EmergencyProtectedTimelock is ITimelock { + using TimelockState for TimelockState.Context; using ExecutableProposals for ExecutableProposals.State; - using EmergencyProtection for EmergencyProtection.State; + using EmergencyProtection for EmergencyProtection.Context; + + error InvalidAdminExecutor(address value); + + // --- + // Sanity Check Params Immutables + // --- + struct SanityCheckParams { + Duration maxAfterSubmitDelay; + Duration maxAfterScheduleDelay; + Duration maxEmergencyModeDuration; + Duration maxEmergencyProtectionDuration; + } - error InvalidGovernance(address governance); - error NotGovernance(address account, address governance); + Duration public immutable MAX_AFTER_SUBMIT_DELAY; + Duration public immutable MAX_AFTER_SCHEDULE_DELAY; - event GovernanceSet(address governance); + Duration public immutable MAX_EMERGENCY_MODE_DURATION; + Duration public immutable MAX_EMERGENCY_PROTECTION_DURATION; - address internal _governance; + // --- + // Admin Executor Immutables + // --- + address private immutable _ADMIN_EXECUTOR; + + // --- + // Aspects + // --- + + TimelockState.Context internal _timelockState; ExecutableProposals.State internal _proposals; - EmergencyProtection.State internal _emergencyProtection; + EmergencyProtection.Context internal _emergencyProtection; - constructor(address config) ConfigurationProvider(config) {} + constructor(SanityCheckParams memory sanityCheckParams, address adminExecutor) { + _ADMIN_EXECUTOR = adminExecutor; + + MAX_AFTER_SUBMIT_DELAY = sanityCheckParams.maxAfterSubmitDelay; + MAX_AFTER_SCHEDULE_DELAY = sanityCheckParams.maxAfterScheduleDelay; + MAX_EMERGENCY_MODE_DURATION = sanityCheckParams.maxEmergencyModeDuration; + MAX_EMERGENCY_PROTECTION_DURATION = sanityCheckParams.maxEmergencyModeDuration; + } // --- // Main Timelock Functionality @@ -45,7 +73,7 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { /// @param calls An array of `ExternalCall` structs representing the calls to be executed. /// @return newProposalId The ID of the newly created proposal. function submit(address executor, ExternalCall[] calldata calls) external returns (uint256 newProposalId) { - _checkGovernance(msg.sender); + _timelockState.checkGovernance(msg.sender); newProposalId = _proposals.submit(executor, calls); } @@ -53,25 +81,40 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { /// Only the governance contract can call this function. /// @param proposalId The ID of the proposal to be scheduled. function schedule(uint256 proposalId) external { - _checkGovernance(msg.sender); - _proposals.schedule(proposalId, CONFIG.AFTER_SUBMIT_DELAY()); + _timelockState.checkGovernance(msg.sender); + _proposals.schedule(proposalId, _timelockState.getAfterSubmitDelay()); } /// @dev Executes a scheduled proposal. /// Checks if emergency mode is active and prevents execution if it is. /// @param proposalId The ID of the proposal to be executed. function execute(uint256 proposalId) external { - _emergencyProtection.checkEmergencyModeStatus(false); - _proposals.execute(proposalId, CONFIG.AFTER_SCHEDULE_DELAY()); + _emergencyProtection.checkEmergencyMode({isActive: false}); + _proposals.execute(proposalId, _timelockState.getAfterScheduleDelay()); } /// @dev Cancels all non-executed proposals. /// Only the governance contract can call this function. function cancelAllNonExecutedProposals() external { - _checkGovernance(msg.sender); + _timelockState.checkGovernance(msg.sender); _proposals.cancelAll(); } + // --- + // Timelock Management + // --- + + function setGovernance(address newGovernance) external { + _checkAdminExecutor(msg.sender); + _timelockState.setGovernance(newGovernance); + } + + function setDelays(Duration afterSubmitDelay, Duration afterScheduleDelay) external { + _checkAdminExecutor(msg.sender); + _timelockState.setAfterSubmitDelay(afterSubmitDelay, MAX_AFTER_SUBMIT_DELAY); + _timelockState.setAfterScheduleDelay(afterScheduleDelay, MAX_AFTER_SCHEDULE_DELAY); + } + /// @dev Transfers ownership of the executor contract to a new owner. /// Only the admin executor can call this function. /// @param executor The address of the executor contract. @@ -81,92 +124,97 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { IOwnable(executor).transferOwnership(owner); } - /// @dev Sets a new governance contract address. - /// Only the admin executor can call this function. - /// @param newGovernance The address of the new governance contract. - function setGovernance(address newGovernance) external { - _checkAdminExecutor(msg.sender); - _setGovernance(newGovernance); - } - // --- // Emergency Protection Functionality // --- + function setupEmergencyProtection( + address emergencyGovernance, + address emergencyActivationCommittee, + address emergencyExecutionCommittee, + Timestamp emergencyProtectionEndDate, + Duration emergencyModeDuration + ) external { + _checkAdminExecutor(msg.sender); + + _emergencyProtection.setEmergencyGovernance(emergencyGovernance); + _emergencyProtection.setEmergencyActivationCommittee(emergencyActivationCommittee); + _emergencyProtection.setEmergencyProtectionEndDate( + emergencyProtectionEndDate, MAX_EMERGENCY_PROTECTION_DURATION + ); + _emergencyProtection.setEmergencyModeDuration(emergencyModeDuration, MAX_EMERGENCY_MODE_DURATION); + _emergencyProtection.setEmergencyExecutionCommittee(emergencyExecutionCommittee); + } + /// @dev Activates the emergency mode. /// Only the activation committee can call this function. function activateEmergencyMode() external { - _emergencyProtection.checkActivationCommittee(msg.sender); - _emergencyProtection.checkEmergencyModeStatus(false); - _emergencyProtection.activate(); + _emergencyProtection.checkEmergencyActivationCommittee(msg.sender); + _emergencyProtection.checkEmergencyMode({isActive: false}); + _emergencyProtection.activateEmergencyMode(); } /// @dev Executes a proposal during emergency mode. /// Checks if emergency mode is active and if the caller is part of the execution committee. /// @param proposalId The ID of the proposal to be executed. function emergencyExecute(uint256 proposalId) external { - _emergencyProtection.checkEmergencyModeStatus(true); - _emergencyProtection.checkExecutionCommittee(msg.sender); - _proposals.execute(proposalId, /* afterScheduleDelay */ Duration.wrap(0)); + _emergencyProtection.checkEmergencyMode({isActive: true}); + _emergencyProtection.checkEmergencyExecutionCommittee(msg.sender); + _proposals.execute({proposalId: proposalId, afterScheduleDelay: Duration.wrap(0)}); } /// @dev Deactivates the emergency mode. /// If the emergency mode has not passed, only the admin executor can call this function. function deactivateEmergencyMode() external { - _emergencyProtection.checkEmergencyModeStatus(true); - if (!_emergencyProtection.isEmergencyModePassed()) { + _emergencyProtection.checkEmergencyMode({isActive: true}); + if (!_emergencyProtection.isEmergencyModeDurationPassed()) { _checkAdminExecutor(msg.sender); } - _emergencyProtection.deactivate(); + _emergencyProtection.deactivateEmergencyMode(); _proposals.cancelAll(); } /// @dev Resets the system after entering the emergency mode. /// Only the execution committee can call this function. function emergencyReset() external { - _emergencyProtection.checkEmergencyModeStatus(true); - _emergencyProtection.checkExecutionCommittee(msg.sender); - _emergencyProtection.deactivate(); - _setGovernance(CONFIG.EMERGENCY_GOVERNANCE()); + _emergencyProtection.checkEmergencyExecutionCommittee(msg.sender); + _emergencyProtection.checkEmergencyMode({isActive: true}); + _emergencyProtection.deactivateEmergencyMode(); + + _timelockState.setGovernance(_emergencyProtection.emergencyGovernance); _proposals.cancelAll(); } - /// @dev Sets the parameters for the emergency protection functionality. - /// Only the admin executor can call this function. - /// @param activator The address of the activation committee. - /// @param enactor The address of the execution committee. - /// @param protectionDuration The duration of the protection period. - /// @param emergencyModeDuration The duration of the emergency mode. - function setEmergencyProtection( - address activator, - address enactor, - Duration protectionDuration, - Duration emergencyModeDuration - ) external { - _checkAdminExecutor(msg.sender); - _emergencyProtection.setup(activator, enactor, protectionDuration, emergencyModeDuration); + function getEmergencyProtectionContext() external view returns (EmergencyProtection.Context memory) { + return _emergencyProtection; } - /// @dev Checks if the emergency protection functionality is enabled. - /// @return A boolean indicating if the emergency protection is enabled. - function isEmergencyProtectionEnabled() external view returns (bool) { + function isEmergencyProtectionEnabled() public view returns (bool) { return _emergencyProtection.isEmergencyProtectionEnabled(); } - /// @dev Retrieves the current emergency state. - /// @return res The EmergencyState struct containing the current emergency state. - function getEmergencyState() external view returns (EmergencyState memory res) { - res = _emergencyProtection.getEmergencyState(); + function isEmergencyModeActive() public view returns (bool isActive) { + isActive = _emergencyProtection.isEmergencyModeActive(); } // --- // Timelock View Methods // --- - /// @dev Retrieves the address of the current governance contract. - /// @return governance The address of the current governance contract. - function getGovernance() external view returns (address governance) { - governance = _governance; + function getGovernance() external view returns (address) { + return _timelockState.governance; + } + + function getAdminExecutor() external view returns (address) { + return _ADMIN_EXECUTOR; + } + + function getAfterSubmitDelay() external view returns (Duration) { + return _timelockState.getAfterSubmitDelay(); + } + + function getAfterScheduleDelay() external view returns (Duration) { + return _timelockState.getAfterScheduleDelay(); } /// @dev Retrieves the details of a proposal. @@ -219,37 +267,20 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { /// @param proposalId The ID of the proposal. /// @return A boolean indicating if the proposal can be executed. function canExecute(uint256 proposalId) external view returns (bool) { - return !_emergencyProtection.isEmergencyModeActivated() - && _proposals.canExecute(proposalId, CONFIG.AFTER_SCHEDULE_DELAY()); + return !_emergencyProtection.isEmergencyModeActive() + && _proposals.canExecute(proposalId, _timelockState.getAfterScheduleDelay()); } /// @dev Checks if a proposal can be scheduled. /// @param proposalId The ID of the proposal. /// @return A boolean indicating if the proposal can be scheduled. function canSchedule(uint256 proposalId) external view returns (bool) { - return _proposals.canSchedule(proposalId, CONFIG.AFTER_SUBMIT_DELAY()); - } - - // --- - // Internal Methods - // --- - - /// @dev Internal function to set the governance contract address. - /// @param newGovernance The address of the new governance contract. - function _setGovernance(address newGovernance) internal { - address prevGovernance = _governance; - if (newGovernance == prevGovernance || newGovernance == address(0)) { - revert InvalidGovernance(newGovernance); - } - _governance = newGovernance; - emit GovernanceSet(newGovernance); + return _proposals.canSchedule(proposalId, _timelockState.getAfterSubmitDelay()); } - /// @dev Internal function to check if the caller is the governance contract. - /// @param account The address to check. - function _checkGovernance(address account) internal view { - if (_governance != account) { - revert NotGovernance(account, _governance); + function _checkAdminExecutor(address account) internal view { + if (account != _ADMIN_EXECUTOR) { + revert InvalidAdminExecutor(account); } } } diff --git a/contracts/Escrow.sol b/contracts/Escrow.sol index 0ea3219f..335519dd 100644 --- a/contracts/Escrow.sol +++ b/contracts/Escrow.sol @@ -4,14 +4,14 @@ pragma solidity 0.8.26; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {Duration} from "./types/Duration.sol"; -import {Timestamp, Timestamps} from "./types/Timestamp.sol"; +import {Timestamp} from "./types/Timestamp.sol"; import {IEscrow} from "./interfaces/IEscrow.sol"; -import {IEscrowConfigration} from "./interfaces/IConfiguration.sol"; import {IStETH} from "./interfaces/IStETH.sol"; import {IWstETH} from "./interfaces/IWstETH.sol"; import {IWithdrawalQueue, WithdrawalRequestStatus} from "./interfaces/IWithdrawalQueue.sol"; +import {IDualGovernance} from "./interfaces/IDualGovernance.sol"; import { ETHValue, @@ -24,16 +24,7 @@ import { AssetsAccounting } from "./libraries/AssetsAccounting.sol"; import {WithdrawalsBatchesQueue} from "./libraries/WithdrawalBatchesQueue.sol"; - -interface IDualGovernance { - function activateNextState() external; -} - -enum EscrowState { - NotInitialized, - SignallingEscrow, - RageQuitEscrow -} +import {EscrowState} from "./libraries/EscrowState.sol"; struct LockedAssetsTotals { uint256 stETHLockedShares; @@ -50,58 +41,91 @@ struct VetoerState { } contract Escrow is IEscrow { + using EscrowState for EscrowState.Context; using AssetsAccounting for AssetsAccounting.State; using WithdrawalsBatchesQueue for WithdrawalsBatchesQueue.State; + // --- + // Errors + // --- + error UnexpectedUnstETHId(); - error InvalidHintsLength(uint256 actual, uint256 expected); - error ClaimingIsFinished(); + error NonProxyCallsForbidden(); error InvalidBatchSize(uint256 size); - error WithdrawalsTimelockNotPassed(); + error InvalidDualGovernance(address value); + error InvalidHintsLength(uint256 actual, uint256 expected); error InvalidETHSender(address actual, address expected); - error NotDualGovernance(address actual, address expected); - error MasterCopyCallForbidden(); - error InvalidState(EscrowState actual, EscrowState expected); - error RageQuitExtraTimelockNotStarted(); - address public immutable MASTER_COPY; + // --- + // Events + // --- + + event ConfigProviderSet(address newConfigProvider); + + // --- + // Sanity Check Params Immutables + // --- + + struct SanityCheckParams { + uint256 minWithdrawalsBatchSize; + } + + uint256 public immutable MIN_WITHDRAWALS_BATCH_SIZE; - uint256 public immutable MIN_WITHDRAWAL_REQUEST_AMOUNT; - uint256 public immutable MAX_WITHDRAWAL_REQUEST_AMOUNT; + // --- + // Dependencies Immutables + // --- + + struct ProtocolDependencies { + IStETH stETH; + IWstETH wstETH; + IWithdrawalQueue withdrawalQueue; + } IStETH public immutable ST_ETH; IWstETH public immutable WST_ETH; IWithdrawalQueue public immutable WITHDRAWAL_QUEUE; - IEscrowConfigration public immutable CONFIG; + // --- + // Implementation Immutables + + address private immutable _SELF; + IDualGovernance public immutable DUAL_GOVERNANCE; - EscrowState internal _escrowState; - IDualGovernance private _dualGovernance; + // --- + // Aspects + // --- + + EscrowState.Context internal _escrowState; AssetsAccounting.State private _accounting; WithdrawalsBatchesQueue.State private _batchesQueue; - Duration internal _rageQuitExtensionDelay; - Duration internal _rageQuitWithdrawalsTimelock; - Timestamp internal _rageQuitTimelockStartedAt; - - constructor(address stETH, address wstETH, address withdrawalQueue, address config) { - ST_ETH = IStETH(stETH); - WST_ETH = IWstETH(wstETH); - MASTER_COPY = address(this); - CONFIG = IEscrowConfigration(config); - WITHDRAWAL_QUEUE = IWithdrawalQueue(withdrawalQueue); - MIN_WITHDRAWAL_REQUEST_AMOUNT = WITHDRAWAL_QUEUE.MIN_STETH_WITHDRAWAL_AMOUNT(); - MAX_WITHDRAWAL_REQUEST_AMOUNT = WITHDRAWAL_QUEUE.MAX_STETH_WITHDRAWAL_AMOUNT(); + // --- + // Construction & Initializing + // --- + + constructor( + IDualGovernance dualGovernance, + SanityCheckParams memory sanityCheckParams, + ProtocolDependencies memory dependencies + ) { + _SELF = address(this); + DUAL_GOVERNANCE = dualGovernance; + + ST_ETH = dependencies.stETH; + WST_ETH = dependencies.wstETH; + WITHDRAWAL_QUEUE = dependencies.withdrawalQueue; + + MIN_WITHDRAWALS_BATCH_SIZE = sanityCheckParams.minWithdrawalsBatchSize; } - function initialize(address dualGovernance) external { - if (address(this) == MASTER_COPY) { - revert MasterCopyCallForbidden(); + function initialize(Duration minAssetsLockDuration) external { + if (address(this) == _SELF) { + revert NonProxyCallsForbidden(); } - _checkEscrowState(EscrowState.NotInitialized); + _checkDualGovernance(msg.sender); - _escrowState = EscrowState.SignallingEscrow; - _dualGovernance = IDualGovernance(dualGovernance); + _escrowState.initialize(minAssetsLockDuration); ST_ETH.approve(address(WST_ETH), type(uint256).max); ST_ETH.approve(address(WITHDRAWAL_QUEUE), type(uint256).max); @@ -112,20 +136,24 @@ contract Escrow is IEscrow { // --- function lockStETH(uint256 amount) external returns (uint256 lockedStETHShares) { - _checkEscrowState(EscrowState.SignallingEscrow); + _escrowState.checkSignallingEscrow(); + lockedStETHShares = ST_ETH.getSharesByPooledEth(amount); _accounting.accountStETHSharesLock(msg.sender, SharesValues.from(lockedStETHShares)); ST_ETH.transferSharesFrom(msg.sender, address(this), lockedStETHShares); - _activateNextGovernanceState(); + + DUAL_GOVERNANCE.activateNextState(); } function unlockStETH() external returns (uint256 unlockedStETHShares) { - _activateNextGovernanceState(); - _checkEscrowState(EscrowState.SignallingEscrow); - _accounting.checkAssetsUnlockDelayPassed(msg.sender, CONFIG.SIGNALLING_ESCROW_MIN_LOCK_TIME()); + _escrowState.checkSignallingEscrow(); + + DUAL_GOVERNANCE.activateNextState(); + _accounting.checkMinAssetsLockDurationPassed(msg.sender, _escrowState.minAssetsLockDuration); unlockedStETHShares = _accounting.accountStETHSharesUnlock(msg.sender).toUint256(); ST_ETH.transferShares(msg.sender, unlockedStETHShares); - _activateNextGovernanceState(); + + DUAL_GOVERNANCE.activateNextState(); } // --- @@ -133,52 +161,59 @@ contract Escrow is IEscrow { // --- function lockWstETH(uint256 amount) external returns (uint256 lockedStETHShares) { - _checkEscrowState(EscrowState.SignallingEscrow); + _escrowState.checkSignallingEscrow(); + WST_ETH.transferFrom(msg.sender, address(this), amount); lockedStETHShares = ST_ETH.getSharesByPooledEth(WST_ETH.unwrap(amount)); _accounting.accountStETHSharesLock(msg.sender, SharesValues.from(lockedStETHShares)); - _activateNextGovernanceState(); + + DUAL_GOVERNANCE.activateNextState(); } function unlockWstETH() external returns (uint256 unlockedStETHShares) { - _activateNextGovernanceState(); - _checkEscrowState(EscrowState.SignallingEscrow); - _accounting.checkAssetsUnlockDelayPassed(msg.sender, CONFIG.SIGNALLING_ESCROW_MIN_LOCK_TIME()); + _escrowState.checkSignallingEscrow(); + DUAL_GOVERNANCE.activateNextState(); + + _accounting.checkMinAssetsLockDurationPassed(msg.sender, _escrowState.minAssetsLockDuration); SharesValue wstETHUnlocked = _accounting.accountStETHSharesUnlock(msg.sender); unlockedStETHShares = WST_ETH.wrap(ST_ETH.getPooledEthByShares(wstETHUnlocked.toUint256())); WST_ETH.transfer(msg.sender, unlockedStETHShares); - _activateNextGovernanceState(); + + DUAL_GOVERNANCE.activateNextState(); } // --- // Lock / Unlock unstETH // --- function lockUnstETH(uint256[] memory unstETHIds) external { - _checkEscrowState(EscrowState.SignallingEscrow); + _escrowState.checkSignallingEscrow(); + WithdrawalRequestStatus[] memory statuses = WITHDRAWAL_QUEUE.getWithdrawalStatus(unstETHIds); _accounting.accountUnstETHLock(msg.sender, unstETHIds, statuses); - uint256 unstETHIdsCount = unstETHIds.length; for (uint256 i = 0; i < unstETHIdsCount; ++i) { WITHDRAWAL_QUEUE.transferFrom(msg.sender, address(this), unstETHIds[i]); } - _activateNextGovernanceState(); + + DUAL_GOVERNANCE.activateNextState(); } function unlockUnstETH(uint256[] memory unstETHIds) external { - _activateNextGovernanceState(); - _checkEscrowState(EscrowState.SignallingEscrow); - _accounting.checkAssetsUnlockDelayPassed(msg.sender, CONFIG.SIGNALLING_ESCROW_MIN_LOCK_TIME()); + _escrowState.checkSignallingEscrow(); + DUAL_GOVERNANCE.activateNextState(); + + _accounting.checkMinAssetsLockDurationPassed(msg.sender, _escrowState.minAssetsLockDuration); _accounting.accountUnstETHUnlock(msg.sender, unstETHIds); uint256 unstETHIdsCount = unstETHIds.length; for (uint256 i = 0; i < unstETHIdsCount; ++i) { WITHDRAWAL_QUEUE.transferFrom(address(this), msg.sender, unstETHIds[i]); } - _activateNextGovernanceState(); + + DUAL_GOVERNANCE.activateNextState(); } function markUnstETHFinalized(uint256[] memory unstETHIds, uint256[] calldata hints) external { - _checkEscrowState(EscrowState.SignallingEscrow); + _escrowState.checkSignallingEscrow(); uint256[] memory claimableAmounts = WITHDRAWAL_QUEUE.getClaimableEther(unstETHIds, hints); _accounting.accountUnstETHFinalized(unstETHIds, claimableAmounts); @@ -189,7 +224,8 @@ contract Escrow is IEscrow { // --- function requestWithdrawals(uint256[] calldata stEthAmounts) external returns (uint256[] memory unstETHIds) { - _checkEscrowState(EscrowState.SignallingEscrow); + _escrowState.checkSignallingEscrow(); + unstETHIds = WITHDRAWAL_QUEUE.requestWithdrawals(stEthAmounts, address(this)); WithdrawalRequestStatus[] memory statuses = WITHDRAWAL_QUEUE.getWithdrawalStatus(unstETHIds); @@ -207,54 +243,48 @@ contract Escrow is IEscrow { function startRageQuit(Duration rageQuitExtensionDelay, Duration rageQuitWithdrawalsTimelock) external { _checkDualGovernance(msg.sender); - _checkEscrowState(EscrowState.SignallingEscrow); - + _escrowState.startRageQuit(rageQuitExtensionDelay, rageQuitWithdrawalsTimelock); _batchesQueue.open(); - _escrowState = EscrowState.RageQuitEscrow; - _rageQuitExtensionDelay = rageQuitExtensionDelay; - _rageQuitWithdrawalsTimelock = rageQuitWithdrawalsTimelock; } - function requestNextWithdrawalsBatch(uint256 maxBatchSize) external { - _checkEscrowState(EscrowState.RageQuitEscrow); + function requestNextWithdrawalsBatch(uint256 batchSize) external { + _escrowState.checkRageQuitEscrow(); _batchesQueue.checkOpened(); - if (maxBatchSize < CONFIG.MIN_WITHDRAWALS_BATCH_SIZE() || maxBatchSize > CONFIG.MAX_WITHDRAWALS_BATCH_SIZE()) { - revert InvalidBatchSize(maxBatchSize); + if (batchSize < MIN_WITHDRAWALS_BATCH_SIZE) { + revert InvalidBatchSize(batchSize); } uint256 stETHRemaining = ST_ETH.balanceOf(address(this)); - if (stETHRemaining < MIN_WITHDRAWAL_REQUEST_AMOUNT) { + uint256 minStETHWithdrawalRequestAmount = WITHDRAWAL_QUEUE.MIN_STETH_WITHDRAWAL_AMOUNT(); + uint256 maxStETHWithdrawalRequestAmount = WITHDRAWAL_QUEUE.MAX_STETH_WITHDRAWAL_AMOUNT(); + + if (stETHRemaining < minStETHWithdrawalRequestAmount) { return _batchesQueue.close(); } uint256[] memory requestAmounts = WithdrawalsBatchesQueue.calcRequestAmounts({ - minRequestAmount: MIN_WITHDRAWAL_REQUEST_AMOUNT, - maxRequestAmount: MAX_WITHDRAWAL_REQUEST_AMOUNT, - remainingAmount: Math.min(stETHRemaining, MAX_WITHDRAWAL_REQUEST_AMOUNT * maxBatchSize) + minRequestAmount: minStETHWithdrawalRequestAmount, + maxRequestAmount: maxStETHWithdrawalRequestAmount, + remainingAmount: Math.min(stETHRemaining, maxStETHWithdrawalRequestAmount * batchSize) }); _batchesQueue.add(WITHDRAWAL_QUEUE.requestWithdrawals(requestAmounts, address(this))); } function claimNextWithdrawalsBatch(uint256 maxUnstETHIdsCount) external { - _checkEscrowState(EscrowState.RageQuitEscrow); - if (!_rageQuitTimelockStartedAt.isZero()) { - revert ClaimingIsFinished(); - } + _escrowState.checkRageQuitEscrow(); + _escrowState.checkBatchesClaimInProgress(); uint256[] memory unstETHIds = _batchesQueue.claimNextBatch(maxUnstETHIdsCount); - _claimNextWithdrawalsBatch( unstETHIds, WITHDRAWAL_QUEUE.findCheckpointHints(unstETHIds, 1, WITHDRAWAL_QUEUE.getLastCheckpointIndex()) ); } function claimNextWithdrawalsBatch(uint256 fromUnstETHId, uint256[] calldata hints) external { - _checkEscrowState(EscrowState.RageQuitEscrow); - if (!_rageQuitTimelockStartedAt.isZero()) { - revert ClaimingIsFinished(); - } + _escrowState.checkRageQuitEscrow(); + _escrowState.checkBatchesClaimInProgress(); uint256[] memory unstETHIds = _batchesQueue.claimNextBatch(hints.length); @@ -269,7 +299,7 @@ contract Escrow is IEscrow { } function claimUnstETH(uint256[] calldata unstETHIds, uint256[] calldata hints) external { - _checkEscrowState(EscrowState.RageQuitEscrow); + _escrowState.checkRageQuitEscrow(); uint256[] memory claimableAmounts = WITHDRAWAL_QUEUE.getClaimableEther(unstETHIds, hints); uint256 ethBalanceBefore = address(this).balance; @@ -280,20 +310,29 @@ contract Escrow is IEscrow { assert(totalAmountClaimed == ETHValues.from(ethBalanceAfter - ethBalanceBefore)); } + // --- + // Escrow Management + // --- + + function setMinAssetsLockDuration(Duration newMinAssetsLockDuration) external { + _checkDualGovernance(msg.sender); + _escrowState.setMinAssetsLockDuration(newMinAssetsLockDuration); + } + // --- // Withdraw Logic // --- function withdrawETH() external { - _checkEscrowState(EscrowState.RageQuitEscrow); - _checkWithdrawalsTimelockPassed(); + _escrowState.checkRageQuitEscrow(); + _escrowState.checkWithdrawalsTimelockPassed(); ETHValue ethToWithdraw = _accounting.accountStETHSharesWithdraw(msg.sender); ethToWithdraw.sendTo(payable(msg.sender)); } function withdrawETH(uint256[] calldata unstETHIds) external { - _checkEscrowState(EscrowState.RageQuitEscrow); - _checkWithdrawalsTimelockPassed(); + _escrowState.checkRageQuitEscrow(); + _escrowState.checkWithdrawalsTimelockPassed(); ETHValue ethToWithdraw = _accounting.accountUnstETHWithdraw(msg.sender, unstETHIds); ethToWithdraw.sendTo(payable(msg.sender)); } @@ -330,11 +369,11 @@ contract Escrow is IEscrow { } function isWithdrawalsClaimed() external view returns (bool) { - return !_rageQuitTimelockStartedAt.isZero(); + return _escrowState.isWithdrawalsClaimed(); } - function getRageQuitTimelockStartedAt() external view returns (Timestamp) { - return _rageQuitTimelockStartedAt; + function getRageQuitExtensionDelayStartedAt() external view returns (Timestamp) { + return _escrowState.rageQuitExtensionDelayStartedAt; } function getRageQuitSupport() external view returns (uint256 rageQuitSupport) { @@ -351,11 +390,7 @@ contract Escrow is IEscrow { } function isRageQuitFinalized() external view returns (bool) { - return ( - _escrowState == EscrowState.RageQuitEscrow && _batchesQueue.isClosed() - && !_rageQuitTimelockStartedAt.isZero() - && Timestamps.now() > _rageQuitExtensionDelay.addTo(_rageQuitTimelockStartedAt) - ); + return _escrowState.isRageQuitEscrow() && _escrowState.isRageQuitExtensionDelayPassed(); } // --- @@ -382,33 +417,13 @@ contract Escrow is IEscrow { } if (_batchesQueue.isClosed() && _batchesQueue.isAllUnstETHClaimed()) { - _rageQuitTimelockStartedAt = Timestamps.now(); - } - } - - function _activateNextGovernanceState() internal { - _dualGovernance.activateNextState(); - } - - function _checkEscrowState(EscrowState expected) internal view { - if (_escrowState != expected) { - revert InvalidState(_escrowState, expected); + _escrowState.startRageQuitExtensionDelay(); } } function _checkDualGovernance(address account) internal view { - if (account != address(_dualGovernance)) { - revert NotDualGovernance(account, address(_dualGovernance)); - } - } - - function _checkWithdrawalsTimelockPassed() internal view { - if (_rageQuitTimelockStartedAt.isZero()) { - revert RageQuitExtraTimelockNotStarted(); - } - Duration withdrawalsTimelock = _rageQuitExtensionDelay + _rageQuitWithdrawalsTimelock; - if (Timestamps.now() <= withdrawalsTimelock.addTo(_rageQuitTimelockStartedAt)) { - revert WithdrawalsTimelockNotPassed(); + if (account != address(DUAL_GOVERNANCE)) { + revert InvalidDualGovernance(account); } } } diff --git a/contracts/ResealManager.sol b/contracts/ResealManager.sol index 1c5593ff..99964e14 100644 --- a/contracts/ResealManager.sol +++ b/contracts/ResealManager.sol @@ -2,21 +2,20 @@ pragma solidity 0.8.26; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {ISealable} from "./interfaces/ISealable.sol"; -interface IEmergencyProtectedTimelock { - function getGovernance() external view returns (address); -} +import {ISealable} from "./interfaces/ISealable.sol"; +import {ITimelock} from "./interfaces/ITimelock.sol"; +import {IResealManager} from "./interfaces/IResealManager.sol"; -contract ResealManager { +contract ResealManager is IResealManager { error SealableWrongPauseState(); error SenderIsNotGovernance(); error NotAllowed(); uint256 public constant PAUSE_INFINITELY = type(uint256).max; - address public immutable EMERGENCY_PROTECTED_TIMELOCK; + ITimelock public immutable EMERGENCY_PROTECTED_TIMELOCK; - constructor(address emergencyProtectedTimelock) { + constructor(ITimelock emergencyProtectedTimelock) { EMERGENCY_PROTECTED_TIMELOCK = emergencyProtectedTimelock; } @@ -40,7 +39,7 @@ contract ResealManager { } modifier onlyGovernance() { - address governance = IEmergencyProtectedTimelock(EMERGENCY_PROTECTED_TIMELOCK).getGovernance(); + address governance = EMERGENCY_PROTECTED_TIMELOCK.getGovernance(); if (msg.sender != governance) { revert SenderIsNotGovernance(); } diff --git a/contracts/TimelockedGovernance.sol b/contracts/TimelockedGovernance.sol index 95607723..944f5ab2 100644 --- a/contracts/TimelockedGovernance.sol +++ b/contracts/TimelockedGovernance.sol @@ -2,24 +2,23 @@ pragma solidity 0.8.26; import {IGovernance, ITimelock} from "./interfaces/ITimelock.sol"; -import {ConfigurationProvider} from "./ConfigurationProvider.sol"; + import {ExternalCall} from "./libraries/ExternalCalls.sol"; /// @title TimelockedGovernance /// @dev A contract that serves as the interface for submitting and scheduling the execution of governance proposals. -contract TimelockedGovernance is IGovernance, ConfigurationProvider { +contract TimelockedGovernance is IGovernance { error NotGovernance(address account); address public immutable GOVERNANCE; ITimelock public immutable TIMELOCK; /// @dev Initializes the TimelockedGovernance contract. - /// @param config The address of the ConfigurationProvider contract. /// @param governance The address of the governance contract. /// @param timelock The address of the timelock contract. - constructor(address config, address governance, address timelock) ConfigurationProvider(config) { + constructor(address governance, ITimelock timelock) { GOVERNANCE = governance; - TIMELOCK = ITimelock(timelock); + TIMELOCK = timelock; } /// @dev Submits a proposal to the timelock. @@ -27,7 +26,7 @@ contract TimelockedGovernance is IGovernance, ConfigurationProvider { /// @return proposalId The ID of the submitted proposal. function submitProposal(ExternalCall[] calldata calls) external returns (uint256 proposalId) { _checkGovernance(msg.sender); - return TIMELOCK.submit(CONFIG.ADMIN_EXECUTOR(), calls); + return TIMELOCK.submit(TIMELOCK.getAdminExecutor(), calls); } /// @dev Schedules a submitted proposal. diff --git a/contracts/interfaces/IConfiguration.sol b/contracts/interfaces/IConfiguration.sol deleted file mode 100644 index c097079b..00000000 --- a/contracts/interfaces/IConfiguration.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.26; - -import {Duration} from "../types/Duration.sol"; -import {DualGovernanceStateMachine, TiebreakConfig} from "../libraries/DualGovernanceStateMachine.sol"; - -interface IEscrowConfigration { - function MIN_WITHDRAWALS_BATCH_SIZE() external view returns (uint256); - function MAX_WITHDRAWALS_BATCH_SIZE() external view returns (uint256); - function SIGNALLING_ESCROW_MIN_LOCK_TIME() external view returns (Duration); -} - -interface IAdminExecutorConfiguration { - function ADMIN_EXECUTOR() external view returns (address); -} - -interface ITimelockConfiguration { - function AFTER_SUBMIT_DELAY() external view returns (Duration); - function AFTER_SCHEDULE_DELAY() external view returns (Duration); - function EMERGENCY_GOVERNANCE() external view returns (address); -} - -interface IDualGovernanceConfiguration { - function TIE_BREAK_ACTIVATION_TIMEOUT() external view returns (Duration); - - function VETO_COOLDOWN_DURATION() external view returns (Duration); - function VETO_SIGNALLING_MIN_ACTIVE_DURATION() external view returns (Duration); - - function VETO_SIGNALLING_DEACTIVATION_MAX_DURATION() external view returns (Duration); - - function DYNAMIC_TIMELOCK_MIN_DURATION() external view returns (Duration); - function DYNAMIC_TIMELOCK_MAX_DURATION() external view returns (Duration); - - function FIRST_SEAL_RAGE_QUIT_SUPPORT() external view returns (uint256); - function SECOND_SEAL_RAGE_QUIT_SUPPORT() external view returns (uint256); - - function RAGE_QUIT_EXTENSION_DELAY() external view returns (Duration); - function RAGE_QUIT_ETH_WITHDRAWALS_MIN_TIMELOCK() external view returns (Duration); - function RAGE_QUIT_ACCUMULATION_MAX_DURATION() external view returns (Duration); - function RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_START_SEQ_NUMBER() external view returns (uint256); - - function RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_A() external view returns (uint256); - function RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_B() external view returns (uint256); - function RAGE_QUIT_ETH_WITHDRAWALS_TIMELOCK_GROWTH_COEFF_C() external view returns (uint256); - - function getSealableWithdrawalBlockers() external view returns (address[] memory); - - function getDualGovernanceConfig() external view returns (DualGovernanceStateMachine.Config memory config); - function getTiebreakConfig() external view returns (TiebreakConfig memory config); -} - -interface IConfiguration is - IEscrowConfigration, - ITimelockConfiguration, - IAdminExecutorConfiguration, - IDualGovernanceConfiguration -{} diff --git a/contracts/interfaces/IDualGovernance.sol b/contracts/interfaces/IDualGovernance.sol new file mode 100644 index 00000000..383cb611 --- /dev/null +++ b/contracts/interfaces/IDualGovernance.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {IGovernance} from "./ITimelock.sol"; + +interface IDualGovernance is IGovernance { + function activateNextState() external; +} diff --git a/contracts/interfaces/IEscrow.sol b/contracts/interfaces/IEscrow.sol index e4474b73..24d4efa1 100644 --- a/contracts/interfaces/IEscrow.sol +++ b/contracts/interfaces/IEscrow.sol @@ -4,11 +4,11 @@ pragma solidity 0.8.26; import {Duration} from "../types/Duration.sol"; interface IEscrow { - function initialize(address dualGovernance) external; + function initialize(Duration minAssetsLockDuration) external; function startRageQuit(Duration rageQuitExtraTimelock, Duration rageQuitWithdrawalsTimelock) external; - function MASTER_COPY() external view returns (address); function isRageQuitFinalized() external view returns (bool); function getRageQuitSupport() external view returns (uint256 rageQuitSupport); + function setMinAssetsLockDuration(Duration newMinAssetsLockDuration) external; } diff --git a/contracts/interfaces/IStETH.sol b/contracts/interfaces/IStETH.sol index fc1eecc4..9feb6a33 100644 --- a/contracts/interfaces/IStETH.sol +++ b/contracts/interfaces/IStETH.sol @@ -4,7 +4,23 @@ pragma solidity 0.8.26; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IStETH is IERC20 { + function STAKING_CONTROL_ROLE() external view returns (bytes32); + function removeStakingLimit() external; + function getStakeLimitFullInfo() + external + view + returns ( + bool isStakingPaused, + bool isStakingLimitSet, + uint256 currentStakeLimit, + uint256 maxStakeLimit, + uint256 maxStakeLimitGrowthBlocks, + uint256 prevStakeLimit, + uint256 prevStakeBlockNumber + ); + function getTotalShares() external view returns (uint256); + function sharesOf(address account) external view returns (uint256); function getSharesByPooledEth(uint256 ethAmount) external view returns (uint256); function getPooledEthByShares(uint256 sharesAmount) external view returns (uint256); @@ -15,4 +31,5 @@ interface IStETH is IERC20 { address _recipient, uint256 _sharesAmount ) external returns (uint256); + function submit(address referral) external payable returns (uint256); } diff --git a/contracts/interfaces/ITimelock.sol b/contracts/interfaces/ITimelock.sol index f3128ec6..1df53c99 100644 --- a/contracts/interfaces/ITimelock.sol +++ b/contracts/interfaces/ITimelock.sol @@ -32,6 +32,9 @@ interface ITimelock { function canSchedule(uint256 proposalId) external view returns (bool); function canExecute(uint256 proposalId) external view returns (bool); + function getGovernance() external view returns (address); + function getAdminExecutor() external view returns (address); + function getProposal(uint256 proposalId) external view returns (Proposal memory proposal); function getProposalInfo(uint256 proposalId) external diff --git a/contracts/interfaces/IWithdrawalQueue.sol b/contracts/interfaces/IWithdrawalQueue.sol index dab8edd0..9009206d 100644 --- a/contracts/interfaces/IWithdrawalQueue.sol +++ b/contracts/interfaces/IWithdrawalQueue.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; +import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol"; + struct WithdrawalRequestStatus { uint256 amountOfStETH; uint256 amountOfShares; @@ -10,7 +12,7 @@ struct WithdrawalRequestStatus { bool isClaimed; } -interface IWithdrawalQueue { +interface IWithdrawalQueue is IERC721 { function MIN_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256); function MAX_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256); @@ -51,6 +53,8 @@ interface IWithdrawalQueue { address _owner ) external returns (uint256[] memory requestIds); + function setApprovalForAll(address _operator, bool _approved) external; + function requestWithdrawalsWstETH( uint256[] calldata _amounts, address _owner @@ -59,4 +63,5 @@ interface IWithdrawalQueue { function grantRole(bytes32 role, address account) external; function pauseFor(uint256 duration) external; function isPaused() external returns (bool); + function finalize(uint256 _lastRequestIdToBeFinalized, uint256 _maxShareRate) external payable; } diff --git a/contracts/libraries/AssetsAccounting.sol b/contracts/libraries/AssetsAccounting.sol index f8c2d0c9..72994430 100644 --- a/contracts/libraries/AssetsAccounting.sol +++ b/contracts/libraries/AssetsAccounting.sol @@ -88,7 +88,7 @@ library AssetsAccounting { error InvalidSharesValue(SharesValue value); error InvalidUnstETHStatus(uint256 unstETHId, UnstETHRecordStatus status); error InvalidUnstETHHolder(uint256 unstETHId, address actual, address expected); - error AssetsUnlockDelayNotPassed(Timestamp unlockTimelockExpiresAt); + error MinAssetsLockDurationNotPassed(Timestamp unlockTimelockExpiresAt); error InvalidClaimableAmount(uint256 unstETHId, ETHValue expected, ETHValue actual); // --- @@ -249,14 +249,14 @@ library AssetsAccounting { ufinalizedShares = self.stETHTotals.lockedShares + self.unstETHTotals.unfinalizedShares; } - function checkAssetsUnlockDelayPassed( + function checkMinAssetsLockDurationPassed( State storage self, address holder, - Duration assetsUnlockDelay + Duration minAssetsLockDuration ) internal view { - Timestamp assetsUnlockAllowedAfter = assetsUnlockDelay.addTo(self.assets[holder].lastAssetsLockTimestamp); + Timestamp assetsUnlockAllowedAfter = minAssetsLockDuration.addTo(self.assets[holder].lastAssetsLockTimestamp); if (Timestamps.now() <= assetsUnlockAllowedAfter) { - revert AssetsUnlockDelayNotPassed(assetsUnlockAllowedAfter); + revert MinAssetsLockDurationNotPassed(assetsUnlockAllowedAfter); } } diff --git a/contracts/libraries/DualGovernanceConfig.sol b/contracts/libraries/DualGovernanceConfig.sol new file mode 100644 index 00000000..d8638dd2 --- /dev/null +++ b/contracts/libraries/DualGovernanceConfig.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {Duration, Durations} from "../types/Duration.sol"; +import {Timestamp, Timestamps} from "../types/Timestamp.sol"; + +library DualGovernanceConfig { + struct Context { + uint256 firstSealRageQuitSupport; + uint256 secondSealRageQuitSupport; + Duration minAssetsLockDuration; + Duration dynamicTimelockMaxDuration; + Duration dynamicTimelockMinDuration; + Duration vetoSignallingMinActiveDuration; + Duration vetoSignallingDeactivationMaxDuration; + Duration vetoCooldownDuration; + Duration rageQuitExtensionDelay; + Duration rageQuitEthWithdrawalsMinTimelock; + uint256 rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber; + uint256[3] rageQuitEthWithdrawalsTimelockGrowthCoeffs; + } + + function isFirstSealRageQuitSupportCrossed( + Context memory self, + uint256 rageQuitSupport + ) internal pure returns (bool) { + return rageQuitSupport > self.firstSealRageQuitSupport; + } + + function isSecondSealRageQuitSupportCrossed( + Context memory self, + uint256 rageQuitSupport + ) internal pure returns (bool) { + return rageQuitSupport > self.secondSealRageQuitSupport; + } + + function isDynamicTimelockMaxDurationPassed( + Context memory self, + Timestamp vetoSignallingActivatedAt + ) internal view returns (bool) { + return Timestamps.now() > self.dynamicTimelockMaxDuration.addTo(vetoSignallingActivatedAt); + } + + function isDynamicTimelockDurationPassed( + Context memory self, + Timestamp vetoSignallingActivatedAt, + uint256 rageQuitSupport + ) internal view returns (bool) { + Duration dynamicTimelock = calcDynamicDelayDuration(self, rageQuitSupport); + return Timestamps.now() > dynamicTimelock.addTo(vetoSignallingActivatedAt); + } + + function isVetoSignallingReactivationDurationPassed( + Context memory self, + Timestamp vetoSignallingReactivationTime + ) internal view returns (bool) { + return Timestamps.now() > self.vetoSignallingMinActiveDuration.addTo(vetoSignallingReactivationTime); + } + + function isVetoSignallingDeactivationMaxDurationPassed( + Context memory self, + Timestamp vetoSignallingDeactivationEnteredAt + ) internal view returns (bool) { + return Timestamps.now() > self.vetoSignallingDeactivationMaxDuration.addTo(vetoSignallingDeactivationEnteredAt); + } + + function isVetoCooldownDurationPassed( + Context memory self, + Timestamp vetoCooldownEnteredAt + ) internal view returns (bool) { + return Timestamps.now() > self.vetoCooldownDuration.addTo(vetoCooldownEnteredAt); + } + + function calcDynamicDelayDuration( + Context memory self, + uint256 rageQuitSupport + ) internal pure returns (Duration duration_) { + uint256 firstSealRageQuitSupport = self.firstSealRageQuitSupport; + uint256 secondSealRageQuitSupport = self.secondSealRageQuitSupport; + Duration dynamicTimelockMinDuration = self.dynamicTimelockMinDuration; + Duration dynamicTimelockMaxDuration = self.dynamicTimelockMaxDuration; + + if (rageQuitSupport < firstSealRageQuitSupport) { + return Durations.ZERO; + } + + if (rageQuitSupport >= secondSealRageQuitSupport) { + return dynamicTimelockMaxDuration; + } + + duration_ = dynamicTimelockMinDuration + + Durations.from( + (rageQuitSupport - firstSealRageQuitSupport) + * (dynamicTimelockMaxDuration - dynamicTimelockMinDuration).toSeconds() + / (secondSealRageQuitSupport - firstSealRageQuitSupport) + ); + } + + function calcRageQuitWithdrawalsTimelock( + Context memory self, + uint256 rageQuitRound + ) internal pure returns (Duration) { + if (rageQuitRound < self.rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber) { + return self.rageQuitEthWithdrawalsMinTimelock; + } + return self.rageQuitEthWithdrawalsMinTimelock + + Durations.from( + ( + self.rageQuitEthWithdrawalsTimelockGrowthCoeffs[0] * rageQuitRound * rageQuitRound + + self.rageQuitEthWithdrawalsTimelockGrowthCoeffs[1] * rageQuitRound + + self.rageQuitEthWithdrawalsTimelockGrowthCoeffs[2] + ) / 10 ** 18 + ); // TODO: rewrite in a prettier way + } +} diff --git a/contracts/libraries/DualGovernanceStateMachine.sol b/contracts/libraries/DualGovernanceStateMachine.sol index 77a24323..9b801457 100644 --- a/contracts/libraries/DualGovernanceStateMachine.sol +++ b/contracts/libraries/DualGovernanceStateMachine.sol @@ -5,11 +5,12 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {IEscrow} from "../interfaces/IEscrow.sol"; -import {ISealable} from "../interfaces/ISealable.sol"; -import {Duration, Durations} from "../types/Duration.sol"; +import {Duration} from "../types/Duration.sol"; import {Timestamp, Timestamps} from "../types/Timestamp.sol"; +import {DualGovernanceConfig} from "./DualGovernanceConfig.sol"; + enum State { Unset, Normal, @@ -19,27 +20,8 @@ enum State { RageQuit } -struct TiebreakConfig { - Duration tiebreakActivationTimeout; - address[] potentialDeadlockSealables; -} - library DualGovernanceStateMachine { - using DualGovernanceStateMachineConfig for Config; - - struct Config { - uint256 firstSealRageQuitSupport; - uint256 secondSealRageQuitSupport; - Duration dynamicTimelockMaxDuration; - Duration dynamicTimelockMinDuration; - Duration vetoSignallingMinActiveDuration; - Duration vetoSignallingDeactivationMaxDuration; - Duration vetoCooldownDuration; - Duration rageQuitExtensionDelay; - Duration rageQuitEthWithdrawalsMinTimelock; - uint256 rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber; - uint256[3] rageQuitEthWithdrawalsTimelockGrowthCoeffs; - } + using DualGovernanceConfig for DualGovernanceConfig.Context; struct Context { /// @@ -78,22 +60,30 @@ library DualGovernanceStateMachine { error AlreadyInitialized(); - event NewSignallingEscrowDeployed(address indexed escrow); + event NewSignallingEscrowDeployed(IEscrow indexed escrow); event DualGovernanceStateChanged(State from, State to, Context state); - function initialize(Context storage self, address escrowMasterCopy) internal { + function initialize( + Context storage self, + DualGovernanceConfig.Context memory config, + address escrowMasterCopy + ) internal { if (self.state != State.Unset) { revert AlreadyInitialized(); } self.state = State.Normal; self.enteredAt = Timestamps.now(); - _deployNewSignallingEscrow(self, escrowMasterCopy); + _deployNewSignallingEscrow(self, escrowMasterCopy, config.minAssetsLockDuration); emit DualGovernanceStateChanged(State.Unset, State.Normal, self); } - function activateNextState(Context storage self, Config memory config) internal { + function activateNextState( + Context storage self, + DualGovernanceConfig.Context memory config, + address escrowMasterCopy + ) internal { (State currentState, State newState) = DualGovernanceStateTransitions.getStateTransition(self, config); if (currentState == newState) { @@ -123,7 +113,7 @@ library DualGovernanceStateMachine { config.rageQuitExtensionDelay, config.calcRageQuitWithdrawalsTimelock(rageQuitRound) ); self.rageQuitEscrow = signallingEscrow; - _deployNewSignallingEscrow(self, signallingEscrow.MASTER_COPY()); + _deployNewSignallingEscrow(self, escrowMasterCopy, config.minAssetsLockDuration); } emit DualGovernanceStateChanged(currentState, newState, self); @@ -141,7 +131,10 @@ library DualGovernanceStateMachine { return self.normalOrVetoCooldownExitedAt; } - function getDynamicDelayDuration(Context storage self, Config memory config) internal view returns (Duration) { + function getDynamicDelayDuration( + Context storage self, + DualGovernanceConfig.Context memory config + ) internal view returns (Duration) { return config.calcDynamicDelayDuration(self.signallingEscrow.getRageQuitSupport()); } @@ -159,38 +152,24 @@ library DualGovernanceStateMachine { return false; } - function isDeadlock(Context storage self, TiebreakConfig memory config) internal view returns (bool) { - State state = self.state; - if (state == State.Normal || state == State.VetoCooldown) return false; - - // when the governance is locked for long period of time - if (Timestamps.now() >= config.tiebreakActivationTimeout.addTo(self.normalOrVetoCooldownExitedAt)) { - return true; - } - - if (self.state != State.RageQuit) return false; - - uint256 potentialDeadlockSealablesCount = config.potentialDeadlockSealables.length; - for (uint256 i = 0; i < potentialDeadlockSealablesCount; ++i) { - if (ISealable(config.potentialDeadlockSealables[i]).isPaused()) return true; - } - return false; - } - - function _deployNewSignallingEscrow(Context storage self, address escrowMasterCopy) private { - IEscrow clone = IEscrow(Clones.clone(escrowMasterCopy)); - clone.initialize(address(this)); - self.signallingEscrow = clone; - emit NewSignallingEscrowDeployed(address(clone)); + function _deployNewSignallingEscrow( + Context storage self, + address escrowMasterCopy, + Duration minAssetsLockDuration + ) private { + IEscrow newSignallingEscrow = IEscrow(Clones.clone(escrowMasterCopy)); + newSignallingEscrow.initialize(minAssetsLockDuration); + self.signallingEscrow = newSignallingEscrow; + emit NewSignallingEscrowDeployed(newSignallingEscrow); } } library DualGovernanceStateTransitions { - using DualGovernanceStateMachineConfig for DualGovernanceStateMachine.Config; + using DualGovernanceConfig for DualGovernanceConfig.Context; function getStateTransition( DualGovernanceStateMachine.Context storage self, - DualGovernanceStateMachine.Config memory config + DualGovernanceConfig.Context memory config ) internal view returns (State currentState, State nextStatus) { currentState = self.state; if (currentState == State.Normal) { @@ -210,7 +189,7 @@ library DualGovernanceStateTransitions { function _fromNormalState( DualGovernanceStateMachine.Context storage self, - DualGovernanceStateMachine.Config memory config + DualGovernanceConfig.Context memory config ) private view returns (State) { return config.isFirstSealRageQuitSupportCrossed(self.signallingEscrow.getRageQuitSupport()) ? State.VetoSignalling @@ -219,7 +198,7 @@ library DualGovernanceStateTransitions { function _fromVetoSignallingState( DualGovernanceStateMachine.Context storage self, - DualGovernanceStateMachine.Config memory config + DualGovernanceConfig.Context memory config ) private view returns (State) { uint256 rageQuitSupport = self.signallingEscrow.getRageQuitSupport(); @@ -231,14 +210,14 @@ library DualGovernanceStateTransitions { return State.RageQuit; } - return config.isVetoSignallingReactivationDurationPassed(self.vetoSignallingReactivationTime) - ? State.VetoSignallingDeactivation - : State.VetoSignalling; + return config.isVetoSignallingReactivationDurationPassed( + Timestamps.max(self.vetoSignallingReactivationTime, self.vetoSignallingActivatedAt) + ) ? State.VetoSignallingDeactivation : State.VetoSignalling; } function _fromVetoSignallingDeactivationState( DualGovernanceStateMachine.Context storage self, - DualGovernanceStateMachine.Config memory config + DualGovernanceConfig.Context memory config ) private view returns (State) { uint256 rageQuitSupport = self.signallingEscrow.getRageQuitSupport(); @@ -259,7 +238,7 @@ library DualGovernanceStateTransitions { function _fromVetoCooldownState( DualGovernanceStateMachine.Context storage self, - DualGovernanceStateMachine.Config memory config + DualGovernanceConfig.Context memory config ) private view returns (State) { if (!config.isVetoCooldownDurationPassed(self.enteredAt)) { return State.VetoCooldown; @@ -271,7 +250,7 @@ library DualGovernanceStateTransitions { function _fromRageQuitState( DualGovernanceStateMachine.Context storage self, - DualGovernanceStateMachine.Config memory config + DualGovernanceConfig.Context memory config ) private view returns (State) { if (!self.rageQuitEscrow.isRageQuitFinalized()) { return State.RageQuit; @@ -281,98 +260,3 @@ library DualGovernanceStateTransitions { : State.VetoCooldown; } } - -library DualGovernanceStateMachineConfig { - function isFirstSealRageQuitSupportCrossed( - DualGovernanceStateMachine.Config memory self, - uint256 rageQuitSupport - ) internal pure returns (bool) { - return rageQuitSupport > self.firstSealRageQuitSupport; - } - - function isSecondSealRageQuitSupportCrossed( - DualGovernanceStateMachine.Config memory self, - uint256 rageQuitSupport - ) internal pure returns (bool) { - return rageQuitSupport > self.secondSealRageQuitSupport; - } - - function isDynamicTimelockMaxDurationPassed( - DualGovernanceStateMachine.Config memory self, - Timestamp vetoSignallingActivatedAt - ) internal view returns (bool) { - return Timestamps.now() > self.dynamicTimelockMaxDuration.addTo(vetoSignallingActivatedAt); - } - - function isDynamicTimelockDurationPassed( - DualGovernanceStateMachine.Config memory self, - Timestamp vetoSignallingActivatedAt, - uint256 rageQuitSupport - ) internal view returns (bool) { - Duration dynamicTimelock = calcDynamicDelayDuration(self, rageQuitSupport); - return Timestamps.now() > dynamicTimelock.addTo(vetoSignallingActivatedAt); - } - - function isVetoSignallingReactivationDurationPassed( - DualGovernanceStateMachine.Config memory self, - Timestamp vetoSignallingReactivationTime - ) internal view returns (bool) { - return Timestamps.now() > self.vetoSignallingMinActiveDuration.addTo(vetoSignallingReactivationTime); - } - - function isVetoSignallingDeactivationMaxDurationPassed( - DualGovernanceStateMachine.Config memory self, - Timestamp vetoSignallingDeactivationEnteredAt - ) internal view returns (bool) { - return Timestamps.now() > self.vetoSignallingDeactivationMaxDuration.addTo(vetoSignallingDeactivationEnteredAt); - } - - function isVetoCooldownDurationPassed( - DualGovernanceStateMachine.Config memory self, - Timestamp vetoCooldownEnteredAt - ) internal view returns (bool) { - return Timestamps.now() > self.vetoCooldownDuration.addTo(vetoCooldownEnteredAt); - } - - function calcDynamicDelayDuration( - DualGovernanceStateMachine.Config memory self, - uint256 rageQuitSupport - ) internal pure returns (Duration duration_) { - uint256 firstSealRageQuitSupport = self.firstSealRageQuitSupport; - uint256 secondSealRageQuitSupport = self.secondSealRageQuitSupport; - Duration dynamicTimelockMinDuration = self.dynamicTimelockMinDuration; - Duration dynamicTimelockMaxDuration = self.dynamicTimelockMaxDuration; - - if (rageQuitSupport < firstSealRageQuitSupport) { - return Durations.ZERO; - } - - if (rageQuitSupport >= secondSealRageQuitSupport) { - return dynamicTimelockMaxDuration; - } - - duration_ = dynamicTimelockMinDuration - + Durations.from( - (rageQuitSupport - firstSealRageQuitSupport) - * (dynamicTimelockMaxDuration - dynamicTimelockMinDuration).toSeconds() - / (secondSealRageQuitSupport - firstSealRageQuitSupport) - ); - } - - function calcRageQuitWithdrawalsTimelock( - DualGovernanceStateMachine.Config memory self, - uint256 rageQuitRound - ) internal pure returns (Duration) { - if (rageQuitRound < self.rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber) { - return self.rageQuitEthWithdrawalsMinTimelock; - } - return self.rageQuitEthWithdrawalsMinTimelock - + Durations.from( - ( - self.rageQuitEthWithdrawalsTimelockGrowthCoeffs[0] * rageQuitRound * rageQuitRound - + self.rageQuitEthWithdrawalsTimelockGrowthCoeffs[1] * rageQuitRound - + self.rageQuitEthWithdrawalsTimelockGrowthCoeffs[2] - ) / 10 ** 18 - ); // TODO: rewrite in a prettier way - } -} diff --git a/contracts/libraries/EmergencyProtection.sol b/contracts/libraries/EmergencyProtection.sol index cd8d65be..0bc7d2b6 100644 --- a/contracts/libraries/EmergencyProtection.sol +++ b/contracts/libraries/EmergencyProtection.sol @@ -4,163 +4,184 @@ pragma solidity 0.8.26; import {Duration, Durations} from "../types/Duration.sol"; import {Timestamp, Timestamps} from "../types/Timestamp.sol"; -struct EmergencyState { - address executionCommittee; - address activationCommittee; - Timestamp protectedTill; - bool isEmergencyModeActivated; - Duration emergencyModeDuration; - Timestamp emergencyModeEndsAfter; -} - /// @title EmergencyProtection /// @dev This library manages emergency protection functionality, allowing for /// the activation and deactivation of emergency mode by designated committees. library EmergencyProtection { - error NotEmergencyActivator(address account); - error NotEmergencyEnactor(address account); - error EmergencyCommitteeExpired(Timestamp now, Timestamp protectedTill); - error InvalidEmergencyModeStatus(bool actual, bool expected); - - event EmergencyModeActivated(Timestamp timestamp); - event EmergencyModeDeactivated(Timestamp timestamp); - event EmergencyActivationCommitteeSet(address indexed activationCommittee); - event EmergencyExecutionCommitteeSet(address indexed executionCommittee); - event EmergencyModeDurationSet(Duration emergencyModeDuration); - event EmergencyCommitteeProtectedTillSet(Timestamp newProtectedTill); - - struct State { - // has rights to activate emergency mode - address activationCommittee; - Timestamp protectedTill; - // till this time, the committee may activate the emergency mode + error InvalidEmergencyActivationCommittee(address account); + error InvalidEmergencyExecutionCommittee(address account); + error EmergencyProtectionExpired(Timestamp protectedTill); + error InvalidEmergencyModeState(bool value); + error InvalidEmergencyModeDuration(Duration value); + error InvalidEmergencyProtectionEndDate(Timestamp value); + + event EmergencyModeActivated(); + event EmergencyModeDeactivated(); + event EmergencyGovernanceSet(address newEmergencyGovernance); + event EmergencyActivationCommitteeSet(address newActivationCommittee); + event EmergencyExecutionCommitteeSet(address newActivationCommittee); + event EmergencyModeDurationSet(Duration newEmergencyModeDuration); + event EmergencyProtectionEndDateSet(Timestamp newEmergencyProtectionEndDate); + + struct Context { + /// @dev slot0 [0..39] Timestamp emergencyModeEndsAfter; + /// @dev slot0 [40..199] + address emergencyActivationCommittee; + /// @dev slot0 [200..240] + Timestamp emergencyProtectionEndsAfter; + /// @dev slot1 [0..159] + address emergencyExecutionCommittee; + /// @dev slot1 [160..191] Duration emergencyModeDuration; - // has rights to execute proposals in emergency mode - address executionCommittee; + /// @dev slot2 [0..160] + address emergencyGovernance; } - /// @dev Sets up the initial state of the emergency protection. - /// @param self The storage reference to the State struct. - /// @param activationCommittee The address of the emergency activation committee. - /// @param executionCommittee The address of the emergency execution committee. - /// @param protectionDuration The duration of the committee protection. - /// @param emergencyModeDuration The duration of the emergency mode. - function setup( - State storage self, - address activationCommittee, - address executionCommittee, - Duration protectionDuration, - Duration emergencyModeDuration - ) internal { - address prevActivationCommittee = self.activationCommittee; - if (activationCommittee != prevActivationCommittee) { - self.activationCommittee = activationCommittee; - emit EmergencyActivationCommitteeSet(activationCommittee); - } + // --- + // Main functionality + // --- - address prevExecutionCommittee = self.executionCommittee; - if (executionCommittee != prevExecutionCommittee) { - self.executionCommittee = executionCommittee; - emit EmergencyExecutionCommitteeSet(executionCommittee); - } - - Timestamp prevProtectedTill = self.protectedTill; - Timestamp newProtectedTill = protectionDuration.addTo(Timestamps.now()); + /// @dev Activates the emergency mode. + /// @param self The storage reference to the Context struct. + function activateEmergencyMode(Context storage self) internal { + Timestamp now_ = Timestamps.now(); - if (newProtectedTill != prevProtectedTill) { - self.protectedTill = newProtectedTill; - emit EmergencyCommitteeProtectedTillSet(newProtectedTill); + if (now_ > self.emergencyProtectionEndsAfter) { + revert EmergencyProtectionExpired(self.emergencyProtectionEndsAfter); } - Duration prevEmergencyModeDuration = self.emergencyModeDuration; - if (emergencyModeDuration != prevEmergencyModeDuration) { - self.emergencyModeDuration = emergencyModeDuration; - emit EmergencyModeDurationSet(emergencyModeDuration); - } - } + self.emergencyModeEndsAfter = self.emergencyModeDuration.addTo(now_); + self.emergencyProtectionEndsAfter = now_; - /// @dev Activates the emergency mode. - /// @param self The storage reference to the State struct. - function activate(State storage self) internal { - Timestamp now = Timestamps.now(); - if (now > self.protectedTill) { - revert EmergencyCommitteeExpired(now, self.protectedTill); - } - self.emergencyModeEndsAfter = self.emergencyModeDuration.addTo(now); - emit EmergencyModeActivated(now); + emit EmergencyModeActivated(); } /// @dev Deactivates the emergency mode. - /// @param self The storage reference to the State struct. - function deactivate(State storage self) internal { - self.activationCommittee = address(0); - self.executionCommittee = address(0); - self.protectedTill = Timestamps.ZERO; + /// @param self The storage reference to the Context struct. + function deactivateEmergencyMode(Context storage self) internal { + self.emergencyActivationCommittee = address(0); + self.emergencyExecutionCommittee = address(0); + self.emergencyProtectionEndsAfter = Timestamps.ZERO; self.emergencyModeEndsAfter = Timestamps.ZERO; self.emergencyModeDuration = Durations.ZERO; - emit EmergencyModeDeactivated(Timestamps.now()); + emit EmergencyModeDeactivated(); } - /// @dev Retrieves the emergency state. - /// @param self The storage reference to the State struct. - /// @return res The EmergencyState struct representing the current emergency state. - function getEmergencyState(State storage self) internal view returns (EmergencyState memory res) { - res.executionCommittee = self.executionCommittee; - res.activationCommittee = self.activationCommittee; - res.protectedTill = self.protectedTill; - res.emergencyModeDuration = self.emergencyModeDuration; - res.emergencyModeEndsAfter = self.emergencyModeEndsAfter; - res.isEmergencyModeActivated = isEmergencyModeActivated(self); + // --- + // Setup functionality + // --- + + function setEmergencyGovernance(Context storage self, address newEmergencyGovernance) internal { + if (self.emergencyGovernance == newEmergencyGovernance) { + return; + } + self.emergencyGovernance = newEmergencyGovernance; + emit EmergencyGovernanceSet(newEmergencyGovernance); } - /// @dev Checks if the emergency mode is activated. - /// @param self The storage reference to the State struct. - /// @return Whether the emergency mode is activated or not. - function isEmergencyModeActivated(State storage self) internal view returns (bool) { - return self.emergencyModeEndsAfter.isNotZero(); + function setEmergencyProtectionEndDate( + Context storage self, + Timestamp emergencyProtectionEndDate, + Duration maxEmergencyProtectionDuration + ) internal { + if (emergencyProtectionEndDate > maxEmergencyProtectionDuration.addTo(Timestamps.now())) { + revert InvalidEmergencyProtectionEndDate(emergencyProtectionEndDate); + } + + if (self.emergencyProtectionEndsAfter == emergencyProtectionEndDate) { + return; + } + self.emergencyProtectionEndsAfter = emergencyProtectionEndDate; + emit EmergencyProtectionEndDateSet(emergencyProtectionEndDate); } - /// @dev Checks if the emergency mode has passed. - /// @param self The storage reference to the State struct. - /// @return Whether the emergency mode has passed or not. - function isEmergencyModePassed(State storage self) internal view returns (bool) { - Timestamp endsAfter = self.emergencyModeEndsAfter; - return endsAfter.isNotZero() && Timestamps.now() > endsAfter; + function setEmergencyModeDuration( + Context storage self, + Duration newEmergencyModeDuration, + Duration maxEmergencyModeDuration + ) internal { + if (newEmergencyModeDuration > maxEmergencyModeDuration) { + revert InvalidEmergencyModeDuration(newEmergencyModeDuration); + } + if (newEmergencyModeDuration == self.emergencyModeDuration) { + return; + } + + self.emergencyModeDuration = newEmergencyModeDuration; + emit EmergencyModeDurationSet(newEmergencyModeDuration); } - /// @dev Checks if the emergency protection is enabled. - /// @param self The storage reference to the State struct. - /// @return Whether the emergency protection is enabled or not. - function isEmergencyProtectionEnabled(State storage self) internal view returns (bool) { - return Timestamps.now() <= self.protectedTill || self.emergencyModeEndsAfter.isNotZero(); + function setEmergencyActivationCommittee(Context storage self, address newActivationCommittee) internal { + if (self.emergencyActivationCommittee == newActivationCommittee) { + return; + } + self.emergencyActivationCommittee = newActivationCommittee; + emit EmergencyActivationCommitteeSet(newActivationCommittee); + } + + function setEmergencyExecutionCommittee(Context storage self, address newExecutionCommittee) internal { + if (self.emergencyActivationCommittee == newExecutionCommittee) { + return; + } + self.emergencyExecutionCommittee = newExecutionCommittee; + emit EmergencyExecutionCommitteeSet(newExecutionCommittee); } + // --- + // Checks + // --- + /// @dev Checks if the caller is the emergency activator and reverts if not. - /// @param self The storage reference to the State struct. + /// @param self The storage reference to the Context struct. /// @param account The account to check. - function checkActivationCommittee(State storage self, address account) internal view { - if (self.activationCommittee != account) { - revert NotEmergencyActivator(account); + function checkEmergencyActivationCommittee(Context storage self, address account) internal view { + if (self.emergencyActivationCommittee != account) { + revert InvalidEmergencyActivationCommittee(account); } } /// @dev Checks if the caller is the emergency enactor and reverts if not. - /// @param self The storage reference to the State struct. + /// @param self The storage reference to the Context struct. /// @param account The account to check. - function checkExecutionCommittee(State storage self, address account) internal view { - if (self.executionCommittee != account) { - revert NotEmergencyEnactor(account); + function checkEmergencyExecutionCommittee(Context storage self, address account) internal view { + if (self.emergencyExecutionCommittee != account) { + revert InvalidEmergencyExecutionCommittee(account); } } /// @dev Checks if the emergency mode matches with expected passed value and reverts if not. - /// @param self The storage reference to the State struct. - /// @param expected The expected value of the emergency mode. - function checkEmergencyModeStatus(State storage self, bool expected) internal view { - bool actual = isEmergencyModeActivated(self); - if (actual != expected) { - revert InvalidEmergencyModeStatus(actual, expected); + /// @param self The storage reference to the Context struct. + /// @param isActive The expected value of the emergency mode. + function checkEmergencyMode(Context storage self, bool isActive) internal view { + if (isEmergencyModeActive(self) != isActive) { + revert InvalidEmergencyModeState(isActive); } } + + // --- + // Getters + // --- + + /// @dev Checks if the emergency mode is activated + /// @param self The storage reference to the Context struct. + /// @return Whether the emergency mode is activated or not. + function isEmergencyModeActive(Context storage self) internal view returns (bool) { + return self.emergencyModeEndsAfter.isNotZero(); + } + + /// @dev Checks if the emergency mode has passed. + /// @param self The storage reference to the Context struct. + /// @return Whether the emergency mode has passed or not. + function isEmergencyModeDurationPassed(Context storage self) internal view returns (bool) { + Timestamp endsAfter = self.emergencyModeEndsAfter; + return endsAfter.isNotZero() && Timestamps.now() > endsAfter; + } + + /// @dev Checks if the emergency protection is enabled. + /// @param self The storage reference to the Context struct. + /// @return Whether the emergency protection is enabled or not. + function isEmergencyProtectionEnabled(Context storage self) internal view returns (bool) { + return Timestamps.now() <= self.emergencyProtectionEndsAfter || self.emergencyModeEndsAfter.isNotZero(); + } } diff --git a/contracts/libraries/EscrowState.sol b/contracts/libraries/EscrowState.sol new file mode 100644 index 00000000..c25e3809 --- /dev/null +++ b/contracts/libraries/EscrowState.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {Duration} from "../types/Duration.sol"; +import {Timestamp, Timestamps} from "../types/Timestamp.sol"; + +enum State { + NotInitialized, + SignallingEscrow, + RageQuitEscrow +} + +library EscrowState { + error ClaimingIsFinished(); + error InvalidState(State value); + error InvalidDualGovernance(address value); + error RageQuitExtraTimelockNotStarted(); + error WithdrawalsTimelockNotPassed(); + + event RageQuitTimelockStarted(); + event EscrowStateChanged(State from, State to); + event RageQuitStarted(Duration rageQuitExtensionDelay, Duration rageQuitWithdrawalsTimelock); + event MinAssetsLockDurationSet(Duration newAssetsLockDuration); + + struct Context { + /// @dev slot0: [0..7] + State state; + /// @dev slot0: [8..39] + Duration minAssetsLockDuration; + /// @dev slot0: [40..71] + Duration rageQuitExtensionDelay; + /// @dev slot1: [72..103] + Duration rageQuitWithdrawalsTimelock; + /// @dev slot1: [104..144] + Timestamp rageQuitExtensionDelayStartedAt; + } + + function initialize(Context storage self, Duration minAssetsLockDuration) internal { + _checkState(self, State.NotInitialized); + _setState(self, State.SignallingEscrow); + _setMinAssetsLockDuration(self, minAssetsLockDuration); + } + + function startRageQuit( + Context storage self, + Duration rageQuitExtensionDelay, + Duration rageQuitWithdrawalsTimelock + ) internal { + _checkState(self, State.SignallingEscrow); + _setState(self, State.RageQuitEscrow); + self.rageQuitExtensionDelay = rageQuitExtensionDelay; + self.rageQuitWithdrawalsTimelock = rageQuitWithdrawalsTimelock; + emit RageQuitStarted(rageQuitExtensionDelay, rageQuitWithdrawalsTimelock); + } + + function startRageQuitExtensionDelay(Context storage self) internal { + self.rageQuitExtensionDelayStartedAt = Timestamps.now(); + emit RageQuitTimelockStarted(); + } + + function setMinAssetsLockDuration(Context storage self, Duration newMinAssetsLockDuration) internal { + if (self.minAssetsLockDuration == newMinAssetsLockDuration) { + return; + } + _setMinAssetsLockDuration(self, newMinAssetsLockDuration); + } + + // --- + // Checks + // --- + + function checkSignallingEscrow(Context storage self) internal view { + _checkState(self, State.SignallingEscrow); + } + + function checkRageQuitEscrow(Context storage self) internal view { + _checkState(self, State.RageQuitEscrow); + } + + function checkBatchesClaimInProgress(Context storage self) internal view { + if (!self.rageQuitExtensionDelayStartedAt.isZero()) { + revert ClaimingIsFinished(); + } + } + + function checkWithdrawalsTimelockPassed(Context storage self) internal view { + if (self.rageQuitExtensionDelayStartedAt.isZero()) { + revert RageQuitExtraTimelockNotStarted(); + } + Duration withdrawalsTimelock = self.rageQuitExtensionDelay + self.rageQuitWithdrawalsTimelock; + if (Timestamps.now() <= withdrawalsTimelock.addTo(self.rageQuitExtensionDelayStartedAt)) { + revert WithdrawalsTimelockNotPassed(); + } + } + + // --- + // Getters + // --- + function isWithdrawalsClaimed(Context storage self) internal view returns (bool) { + return self.rageQuitExtensionDelayStartedAt.isNotZero(); + } + + function isRageQuitExtensionDelayPassed(Context storage self) internal view returns (bool) { + Timestamp rageQuitExtensionDelayStartedAt = self.rageQuitExtensionDelayStartedAt; + return rageQuitExtensionDelayStartedAt.isNotZero() + && Timestamps.now() > self.rageQuitExtensionDelay.addTo(rageQuitExtensionDelayStartedAt); + } + + function isRageQuitEscrow(Context storage self) internal view returns (bool) { + return self.state == State.RageQuitEscrow; + } + + // --- + // Private Methods + // --- + + function _checkState(Context storage self, State state) private view { + if (self.state != state) { + revert InvalidState(state); + } + } + + function _setState(Context storage self, State newState) private { + State prevState = self.state; + self.state = newState; + emit EscrowStateChanged(prevState, newState); + } + + function _setMinAssetsLockDuration(Context storage self, Duration newMinAssetsLockDuration) private { + self.minAssetsLockDuration = newMinAssetsLockDuration; + emit MinAssetsLockDurationSet(newMinAssetsLockDuration); + } +} diff --git a/contracts/libraries/Proposers.sol b/contracts/libraries/Proposers.sol index 2dc32d8b..747b706a 100644 --- a/contracts/libraries/Proposers.sol +++ b/contracts/libraries/Proposers.sol @@ -3,8 +3,6 @@ pragma solidity 0.8.26; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {IAdminExecutorConfiguration as IConfiguration} from "../interfaces/IConfiguration.sol"; - struct Proposer { bool isAdmin; address account; @@ -56,9 +54,9 @@ library Proposers { /// @dev Unregisters a proposer. /// @param self The storage state of the Proposers library. - /// @param config The configuration contract. + /// @param adminExecutor The address of the admin executor. /// @param proposer The address of the proposer to unregister. - function unregister(State storage self, IConfiguration config, address proposer) internal { + function unregister(State storage self, address adminExecutor, address proposer) internal { uint256 proposerIndexToDelete; ExecutorData memory executorData = self.executors[proposer]; unchecked { @@ -76,7 +74,7 @@ library Proposers { delete self.executors[proposer]; address executor = executorData.executor; - if (executor == config.ADMIN_EXECUTOR() && self.executorRefsCounts[executor] == 1) { + if (executor == adminExecutor && self.executorRefsCounts[executor] == 1) { revert LastAdminProposerRemoval(); } @@ -117,12 +115,12 @@ library Proposers { /// @dev Checks if an account is an admin proposer. /// @param self The storage state of the Proposers library. - /// @param config The configuration contract. + /// @param adminExecutor The address of the admin executor /// @param account The address to check. /// @return A boolean indicating whether the account is an admin proposer. - function isAdminProposer(State storage self, IConfiguration config, address account) internal view returns (bool) { + function isAdminProposer(State storage self, address adminExecutor, address account) internal view returns (bool) { ExecutorData memory executorData = self.executors[account]; - return executorData.proposerIndexOneBased != 0 && executorData.executor == config.ADMIN_EXECUTOR(); + return executorData.proposerIndexOneBased != 0 && executorData.executor == adminExecutor; } /// @dev Checks if an account is an executor. @@ -156,11 +154,11 @@ library Proposers { /// @dev Checks if an account is an admin proposer and reverts if not. /// @param self The storage state of the Proposers library. - /// @param config The configuration contract. + /// @param adminExecutor The address of the admin executor. /// @param account The address to check. - function checkAdminProposer(State storage self, IConfiguration config, address account) internal view { + function checkAdminProposer(State storage self, address adminExecutor, address account) internal view { checkProposer(self, account); - if (!isAdminProposer(self, config, account)) { + if (!isAdminProposer(self, adminExecutor, account)) { revert NotAdminProposer(account); } } diff --git a/contracts/libraries/Tiebreaker.sol b/contracts/libraries/Tiebreaker.sol new file mode 100644 index 00000000..383b14e7 --- /dev/null +++ b/contracts/libraries/Tiebreaker.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; + +import {Duration} from "../types/Duration.sol"; +import {Timestamp, Timestamps} from "../types/Duration.sol"; + +import {ISealable} from "../interfaces/ISealable.sol"; + +import {SealableCalls} from "./SealableCalls.sol"; +import {State as DualGovernanceState} from "./DualGovernanceStateMachine.sol"; + +library Tiebreaker { + using SealableCalls for ISealable; + using EnumerableSet for EnumerableSet.AddressSet; + + error TiebreakDisallowed(); + error InvalidSealable(address value); + error InvalidTiebreakerCommittee(address value); + error InvalidTiebreakerActivationTimeout(Duration value); + error SealableWithdrawalBlockersLimitReached(); + + event SealableWithdrawalBlockerAdded(address sealable); + event SealableWithdrawalBlockerRemoved(address sealable); + event TiebreakerCommitteeSet(address newTiebreakerCommittee); + event TiebreakerActivationTimeoutSet(Duration newTiebreakerActivationTimeout); + + struct Context { + /// @dev slot0 [0..159] + address tiebreakerCommittee; + /// @dev slot0 [160..191] + Duration tiebreakerActivationTimeout; + /// @dev slot1 [0..255] + EnumerableSet.AddressSet sealableWithdrawalBlockers; + } + + // --- + // Setup functionality + // --- + + function addSealableWithdrawalBlocker( + Context storage self, + address sealableWithdrawalBlocker, + uint256 maxSealableWithdrawalBlockersCount + ) internal { + uint256 sealableWithdrawalBlockersCount = self.sealableWithdrawalBlockers.length(); + if (sealableWithdrawalBlockersCount == maxSealableWithdrawalBlockersCount) { + revert SealableWithdrawalBlockersLimitReached(); + } + + (bool isCallSucceed, /* lowLevelError */, /* isPaused */ ) = ISealable(sealableWithdrawalBlocker).callIsPaused(); + if (!isCallSucceed) { + revert InvalidSealable(sealableWithdrawalBlocker); + } + + bool isSuccessfullyAdded = self.sealableWithdrawalBlockers.add(sealableWithdrawalBlocker); + if (isSuccessfullyAdded) { + emit SealableWithdrawalBlockerAdded(sealableWithdrawalBlocker); + } + } + + function removeSealableWithdrawalBlocker(Context storage self, address sealableWithdrawalBlocker) internal { + bool isSuccessfullyRemoved = self.sealableWithdrawalBlockers.remove(sealableWithdrawalBlocker); + if (isSuccessfullyRemoved) { + emit SealableWithdrawalBlockerRemoved(sealableWithdrawalBlocker); + } + } + + function setTiebreakerCommittee(Context storage self, address newTiebreakerCommittee) internal { + if (newTiebreakerCommittee == address(0)) { + revert InvalidTiebreakerCommittee(newTiebreakerCommittee); + } + if (self.tiebreakerCommittee == newTiebreakerCommittee) { + return; + } + self.tiebreakerCommittee = newTiebreakerCommittee; + emit TiebreakerCommitteeSet(newTiebreakerCommittee); + } + + function setTiebreakerActivationTimeout( + Context storage self, + Duration minTiebreakerActivationTimeout, + Duration newTiebreakerActivationTimeout, + Duration maxTiebreakerActivationTimeout + ) internal { + if ( + newTiebreakerActivationTimeout < minTiebreakerActivationTimeout + || newTiebreakerActivationTimeout > maxTiebreakerActivationTimeout + ) { + revert InvalidTiebreakerActivationTimeout(newTiebreakerActivationTimeout); + } + + if (self.tiebreakerActivationTimeout == newTiebreakerActivationTimeout) { + return; + } + self.tiebreakerActivationTimeout = newTiebreakerActivationTimeout; + emit TiebreakerActivationTimeoutSet(newTiebreakerActivationTimeout); + } + + // --- + // Checks + // --- + + function checkTiebreakerCommittee(Context storage self, address account) internal view { + if (account != self.tiebreakerCommittee) { + revert InvalidTiebreakerCommittee(account); + } + } + + function checkTie( + Context storage self, + DualGovernanceState state, + Timestamp normalOrVetoCooldownExitedAt + ) internal view { + if (!isTie(self, state, normalOrVetoCooldownExitedAt)) { + revert TiebreakDisallowed(); + } + } + + // --- + // Getters + // --- + + function isTie( + Context storage self, + DualGovernanceState state, + Timestamp normalOrVetoCooldownExitedAt + ) internal view returns (bool) { + if (state == DualGovernanceState.Normal || state == DualGovernanceState.VetoCooldown) return false; + + // when the governance is locked for long period of time + if (Timestamps.now() >= self.tiebreakerActivationTimeout.addTo(normalOrVetoCooldownExitedAt)) { + return true; + } + + return state == DualGovernanceState.RageQuit && isSomeSealableWithdrawalBlockerPaused(self); + } + + function isSomeSealableWithdrawalBlockerPaused(Context storage self) internal view returns (bool) { + uint256 sealableWithdrawalBlockersCount = self.sealableWithdrawalBlockers.length(); + for (uint256 i = 0; i < sealableWithdrawalBlockersCount; ++i) { + (bool isCallSucceed, /* lowLevelError */, bool isPaused) = + ISealable(self.sealableWithdrawalBlockers.at(i)).callIsPaused(); + + // in normal condition this call must never fail, so if some sealable withdrawal blocker + // started behave unexpectedly tiebreaker action may be the last hope for the protocol saving + if (isPaused || !isCallSucceed) return true; + } + return false; + } + + function getTiebreakerInfo(Context storage self) + internal + view + returns ( + address tiebreakerCommittee, + Duration tiebreakerActivationTimeout, + address[] memory sealableWithdrawalBlockers + ) + { + tiebreakerCommittee = self.tiebreakerCommittee; + tiebreakerActivationTimeout = self.tiebreakerActivationTimeout; + + uint256 sealableWithdrawalBlockersCount = self.sealableWithdrawalBlockers.length(); + sealableWithdrawalBlockers = new address[](sealableWithdrawalBlockersCount); + + for (uint256 i = 0; i < sealableWithdrawalBlockersCount; ++i) { + sealableWithdrawalBlockers[i] = self.sealableWithdrawalBlockers.at(i); + } + } +} diff --git a/contracts/libraries/TiebreakerProtection.sol b/contracts/libraries/TiebreakerProtection.sol deleted file mode 100644 index ffdb5665..00000000 --- a/contracts/libraries/TiebreakerProtection.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.26; - -interface IResealManger { - function resume(address sealable) external; -} - -library TiebreakerProtection { - struct Tiebreaker { - address tiebreaker; - IResealManger resealManager; - } - - event TiebreakerSet(address tiebreakCommittee, address resealManager); - event SealableResumed(address sealable); - - error ProposalNotExecutable(uint256 proposalId); - error NotTiebreaker(address account, address tiebreakCommittee); - error TieBreakerAddressIsSame(); - - function resumeSealable(Tiebreaker storage self, address sealable) internal { - self.resealManager.resume(sealable); - emit SealableResumed(sealable); - } - - function setTiebreaker(Tiebreaker storage self, address tiebreaker, address resealManager) internal { - if (self.tiebreaker == tiebreaker) { - revert TieBreakerAddressIsSame(); - } - - self.tiebreaker = tiebreaker; - self.resealManager = IResealManger(resealManager); - emit TiebreakerSet(tiebreaker, resealManager); - } - - function checkTiebreakerCommittee(Tiebreaker storage self, address account) internal view { - if (account != self.tiebreaker) { - revert NotTiebreaker(account, self.tiebreaker); - } - } -} diff --git a/contracts/libraries/TimelockState.sol b/contracts/libraries/TimelockState.sol new file mode 100644 index 00000000..8478f2f9 --- /dev/null +++ b/contracts/libraries/TimelockState.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +import {Duration} from "../types/Duration.sol"; + +library TimelockState { + error InvalidGovernance(address value); + error InvalidAfterSubmitDelay(Duration value); + error InvalidAfterScheduleDelay(Duration value); + + event GovernanceSet(address newGovernance); + event AdminExecutorSet(address newAdminExecutor); + event AfterSubmitDelaySet(Duration newAfterSubmitDelay); + event AfterScheduleDelaySet(Duration newAfterScheduleDelay); + + struct Config { + Duration maxSubmitDelay; + Duration minSubmitDelay; + Duration minScheduleDelay; + Duration maxScheduleDelay; + } + + struct Context { + /// @dev slot0 [0..159] + address governance; + /// @dev slot0 [160..191] + Duration afterSubmitDelay; + /// @dev slot0 [192..224] + Duration afterScheduleDelay; + } + + function setGovernance(Context storage self, address newGovernance) internal { + if (newGovernance == address(0)) { + revert InvalidGovernance(newGovernance); + } + if (self.governance == newGovernance) { + return; + } + self.governance = newGovernance; + emit GovernanceSet(newGovernance); + } + + function getAfterSubmitDelay(Context storage self) internal view returns (Duration) { + return self.afterSubmitDelay; + } + + function getAfterScheduleDelay(Context storage self) internal view returns (Duration) { + return self.afterScheduleDelay; + } + + function setAfterSubmitDelay( + Context storage self, + Duration newAfterSubmitDelay, + Duration maxAfterSubmitDelay + ) internal { + if (newAfterSubmitDelay > maxAfterSubmitDelay) { + revert InvalidAfterScheduleDelay(newAfterSubmitDelay); + } + if (self.afterSubmitDelay == newAfterSubmitDelay) { + return; + } + self.afterSubmitDelay = newAfterSubmitDelay; + emit AfterSubmitDelaySet(newAfterSubmitDelay); + } + + function setAfterScheduleDelay( + Context storage self, + Duration newAfterScheduleDelay, + Duration maxAfterScheduleDelay + ) internal { + if (newAfterScheduleDelay > maxAfterScheduleDelay) { + revert InvalidAfterScheduleDelay(newAfterScheduleDelay); + } + if (self.afterScheduleDelay == newAfterScheduleDelay) { + return; + } + self.afterScheduleDelay = newAfterScheduleDelay; + emit AfterScheduleDelaySet(newAfterScheduleDelay); + } + + function checkGovernance(Context storage self, address account) internal view { + if (self.governance != account) { + revert InvalidGovernance(account); + } + } +} diff --git a/contracts/types/SequentialBatches.sol b/contracts/types/SequentialBatches.sol index 35edef11..a236ef0e 100644 --- a/contracts/types/SequentialBatches.sol +++ b/contracts/types/SequentialBatches.sol @@ -8,7 +8,7 @@ uint256 constant MAX_BATCH_SIZE = BATCH_SIZE_MASK; uint256 constant MAX_BATCH_VALUE = 2 ** (256 - BATCH_SIZE_LENGTH) - 1; // Stores the info about the withdrawals batch encoded as single uint256 -// The 230 MST bits stores the id of the UnstETH id +// The 240 MST bits stores the id of the UnstETH id // the 16 LST bits stores the size of the batch (max size is 2 ^ 16 - 1= 65535) type SequentialBatch is uint256; diff --git a/contracts/types/Timestamp.sol b/contracts/types/Timestamp.sol index 87d280a2..f153f71b 100644 --- a/contracts/types/Timestamp.sol +++ b/contracts/types/Timestamp.sol @@ -71,6 +71,10 @@ library Timestamps { Timestamp internal constant MIN = ZERO; Timestamp internal constant MAX = Timestamp.wrap(uint40(MAX_TIMESTAMP_VALUE)); + function max(Timestamp t1, Timestamp t2) internal pure returns (Timestamp) { + return t1 > t2 ? t1 : t2; + } + function now() internal view returns (Timestamp res) { res = Timestamp.wrap(uint40(block.timestamp)); } diff --git a/test/scenario/agent-timelock.t.sol b/test/scenario/agent-timelock.t.sol index 7de724ee..2ac2b056 100644 --- a/test/scenario/agent-timelock.t.sol +++ b/test/scenario/agent-timelock.t.sol @@ -20,7 +20,7 @@ contract AgentTimelockTest is ScenarioTestBlueprint { _dualGovernance, "Propose to doSmth on target passing dual governance", regularStaffCalls ); - _assertSubmittedProposalData(proposalId, _config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertSubmittedProposalData(proposalId, _timelock.getAdminExecutor(), regularStaffCalls); _assertCanSchedule(_dualGovernance, proposalId, false); } @@ -50,7 +50,7 @@ contract AgentTimelockTest is ScenarioTestBlueprint { _assertCanExecute(proposalId, false); _assertCanSchedule(_dualGovernance, proposalId, false); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } } @@ -65,7 +65,7 @@ contract AgentTimelockTest is ScenarioTestBlueprint { proposalId = _submitProposal( _dualGovernance, "Propose to doSmth on target passing dual governance", regularStaffCalls ); - _assertSubmittedProposalData(proposalId, _config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertSubmittedProposalData(proposalId, _timelock.getAdminExecutor(), regularStaffCalls); // proposal can't be scheduled until the AFTER_SUBMIT_DELAY has passed _assertCanSchedule(_dualGovernance, proposalId, false); @@ -76,7 +76,7 @@ contract AgentTimelockTest is ScenarioTestBlueprint { // --- { // wait until the delay has passed - _wait(_config.AFTER_SUBMIT_DELAY().plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().plusSeconds(1)); // when the first delay is passed and the is no opposition from the stETH holders // the proposal can be scheduled @@ -95,7 +95,7 @@ contract AgentTimelockTest is ScenarioTestBlueprint { { // some time passes and emergency committee activates emergency mode // and resets the controller - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); // committee resets governance vm.prank(address(_emergencyActivationCommittee)); @@ -105,7 +105,7 @@ contract AgentTimelockTest is ScenarioTestBlueprint { _timelock.emergencyReset(); // proposal is canceled now - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); // remove canceled call from the timelock _assertCanExecute(proposalId, false); diff --git a/test/scenario/escrow.t.sol b/test/scenario/escrow.t.sol index da6aa3ab..ed7e92e3 100644 --- a/test/scenario/escrow.t.sol +++ b/test/scenario/escrow.t.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import {WithdrawalRequestStatus} from "../utils/interfaces.sol"; import {Duration as DurationType} from "contracts/types/Duration.sol"; +import {WithdrawalRequestStatus} from "contracts/interfaces/IWithdrawalQueue.sol"; +import {EscrowState, State} from "contracts/libraries/EscrowState.sol"; + import { Escrow, Balances, @@ -10,8 +12,7 @@ import { ScenarioTestBlueprint, VetoerState, LockedAssetsTotals, - Durations, - EscrowState + Durations } from "../utils/scenario-test-blueprint.sol"; contract TestHelpers is ScenarioTestBlueprint { @@ -104,7 +105,7 @@ contract EscrowHappyPath is TestHelpers { _lockStETH(_VETOER_2, secondVetoerLockStETHAmount); _lockWstETH(_VETOER_2, secondVetoerLockWstETHAmount); - _wait(_config.SIGNALLING_ESCROW_MIN_LOCK_TIME().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.MIN_ASSETS_LOCK_DURATION().plusSeconds(1)); _unlockStETH(_VETOER_1); assertApproxEqAbs( @@ -147,7 +148,7 @@ contract EscrowHappyPath is TestHelpers { uint256 secondVetoerStETHSharesAfterRebase = _ST_ETH.sharesOf(_VETOER_2); uint256 secondVetoerWstETHBalanceAfterRebase = _WST_ETH.balanceOf(_VETOER_2); - _wait(_config.SIGNALLING_ESCROW_MIN_LOCK_TIME().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.MIN_ASSETS_LOCK_DURATION().plusSeconds(1)); _unlockWstETH(_VETOER_1); assertApproxEqAbs( @@ -188,7 +189,7 @@ contract EscrowHappyPath is TestHelpers { rebase(-100); - _wait(_config.SIGNALLING_ESCROW_MIN_LOCK_TIME().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.MIN_ASSETS_LOCK_DURATION().plusSeconds(1)); _unlockStETH(_VETOER_1); assertApproxEqAbs( @@ -221,7 +222,7 @@ contract EscrowHappyPath is TestHelpers { _lockUnstETH(_VETOER_1, unstETHIds); - _wait(_config.SIGNALLING_ESCROW_MIN_LOCK_TIME().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.MIN_ASSETS_LOCK_DURATION().plusSeconds(1)); _unlockUnstETH(_VETOER_1, unstETHIds); } @@ -467,7 +468,7 @@ contract EscrowHappyPath is TestHelpers { ); assertApproxEqAbs(escrow.getLockedAssetsTotals().stETHLockedShares, totalSharesLocked, 2); - _wait(_config.SIGNALLING_ESCROW_MIN_LOCK_TIME().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.MIN_ASSETS_LOCK_DURATION().plusSeconds(1)); uint256[] memory stETHWithdrawalRequestAmounts = new uint256[](1); stETHWithdrawalRequestAmounts[0] = firstVetoerStETHAmount; @@ -508,7 +509,7 @@ contract EscrowHappyPath is TestHelpers { assertApproxEqAbs(escrow.getLockedAssetsTotals().stETHLockedShares, 0, 2); assertApproxEqAbs(escrow.getLockedAssetsTotals().unstETHUnfinalizedShares, 0, 2); - _wait(_config.SIGNALLING_ESCROW_MIN_LOCK_TIME().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.MIN_ASSETS_LOCK_DURATION().plusSeconds(1)); vm.prank(_VETOER_1); escrow.unlockUnstETH(stETHWithdrawalRequestIds); @@ -540,46 +541,22 @@ contract EscrowHappyPath is TestHelpers { // After the Escrow enters RageQuitEscrow state, lock/unlock of tokens is forbidden // --- - vm.expectRevert( - abi.encodeWithSelector( - Escrow.InvalidState.selector, EscrowState.RageQuitEscrow, EscrowState.SignallingEscrow - ) - ); + vm.expectRevert(abi.encodeWithSelector(EscrowState.InvalidState.selector, State.SignallingEscrow)); this.externalLockStETH(_VETOER_1, 1 ether); - vm.expectRevert( - abi.encodeWithSelector( - Escrow.InvalidState.selector, EscrowState.RageQuitEscrow, EscrowState.SignallingEscrow - ) - ); + vm.expectRevert(abi.encodeWithSelector(EscrowState.InvalidState.selector, State.SignallingEscrow)); this.externalLockWstETH(_VETOER_1, 1 ether); - vm.expectRevert( - abi.encodeWithSelector( - Escrow.InvalidState.selector, EscrowState.RageQuitEscrow, EscrowState.SignallingEscrow - ) - ); + vm.expectRevert(abi.encodeWithSelector(EscrowState.InvalidState.selector, State.SignallingEscrow)); this.externalLockUnstETH(_VETOER_1, notLockedWithdrawalNfts); - vm.expectRevert( - abi.encodeWithSelector( - Escrow.InvalidState.selector, EscrowState.RageQuitEscrow, EscrowState.SignallingEscrow - ) - ); + vm.expectRevert(abi.encodeWithSelector(EscrowState.InvalidState.selector, State.SignallingEscrow)); this.externalUnlockStETH(_VETOER_1); - vm.expectRevert( - abi.encodeWithSelector( - Escrow.InvalidState.selector, EscrowState.RageQuitEscrow, EscrowState.SignallingEscrow - ) - ); + vm.expectRevert(abi.encodeWithSelector(EscrowState.InvalidState.selector, State.SignallingEscrow)); this.externalUnlockWstETH(_VETOER_1); - vm.expectRevert( - abi.encodeWithSelector( - Escrow.InvalidState.selector, EscrowState.RageQuitEscrow, EscrowState.SignallingEscrow - ) - ); + vm.expectRevert(abi.encodeWithSelector(EscrowState.InvalidState.selector, State.SignallingEscrow)); this.externalUnlockUnstETH(_VETOER_1, lockedWithdrawalNfts); } diff --git a/test/scenario/gov-state-transitions.t.sol b/test/scenario/gov-state-transitions.t.sol index acaaa1cd..884aed7d 100644 --- a/test/scenario/gov-state-transitions.t.sol +++ b/test/scenario/gov-state-transitions.t.sol @@ -15,18 +15,18 @@ contract GovernanceStateTransitions is ScenarioTestBlueprint { function test_signalling_state_min_duration() public { _assertNormalState(); - _lockStETH(_VETOER, percents(_config.FIRST_SEAL_RAGE_QUIT_SUPPORT())); + _lockStETH(_VETOER, percents(_dualGovernanceConfigProvider.FIRST_SEAL_RAGE_QUIT_SUPPORT())); _assertNormalState(); _lockStETH(_VETOER, 1 gwei); _assertVetoSignalingState(); - _wait(_config.DYNAMIC_TIMELOCK_MIN_DURATION().dividedBy(2)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MIN_DURATION().dividedBy(2)); _activateNextState(); _assertVetoSignalingState(); - _wait(_config.DYNAMIC_TIMELOCK_MIN_DURATION().dividedBy(2).plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MIN_DURATION().dividedBy(2).plusSeconds(1)); _activateNextState(); _assertVetoSignalingDeactivationState(); @@ -35,16 +35,16 @@ contract GovernanceStateTransitions is ScenarioTestBlueprint { function test_signalling_state_max_duration() public { _assertNormalState(); - _lockStETH(_VETOER, percents(_config.SECOND_SEAL_RAGE_QUIT_SUPPORT())); + _lockStETH(_VETOER, percents(_dualGovernanceConfigProvider.SECOND_SEAL_RAGE_QUIT_SUPPORT())); _assertVetoSignalingState(); - _wait(_config.DYNAMIC_TIMELOCK_MAX_DURATION().dividedBy(2)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MAX_DURATION().dividedBy(2)); _activateNextState(); _assertVetoSignalingState(); - _wait(_config.DYNAMIC_TIMELOCK_MAX_DURATION().dividedBy(2)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MAX_DURATION().dividedBy(2)); _activateNextState(); _assertVetoSignalingState(); @@ -60,19 +60,19 @@ contract GovernanceStateTransitions is ScenarioTestBlueprint { function test_signalling_to_normal() public { _assertNormalState(); - _lockStETH(_VETOER, percents(_config.FIRST_SEAL_RAGE_QUIT_SUPPORT())); + _lockStETH(_VETOER, percents(_dualGovernanceConfigProvider.FIRST_SEAL_RAGE_QUIT_SUPPORT())); _assertNormalState(); _lockStETH(_VETOER, 1 gwei); _assertVetoSignalingState(); - _wait(_config.DYNAMIC_TIMELOCK_MIN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MIN_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoSignalingDeactivationState(); - _wait(_config.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoCooldownState(); @@ -81,7 +81,7 @@ contract GovernanceStateTransitions is ScenarioTestBlueprint { _getVetoSignallingEscrow().unlockStETH(); vm.stopPrank(); - _wait(_config.VETO_COOLDOWN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_COOLDOWN_DURATION().plusSeconds(1)); _activateNextState(); _assertNormalState(); @@ -90,23 +90,23 @@ contract GovernanceStateTransitions is ScenarioTestBlueprint { function test_signalling_non_stop() public { _assertNormalState(); - _lockStETH(_VETOER, percents(_config.FIRST_SEAL_RAGE_QUIT_SUPPORT())); + _lockStETH(_VETOER, percents(_dualGovernanceConfigProvider.FIRST_SEAL_RAGE_QUIT_SUPPORT())); _assertNormalState(); _lockStETH(_VETOER, 1 gwei); _assertVetoSignalingState(); - _wait(_config.DYNAMIC_TIMELOCK_MIN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MIN_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoSignalingDeactivationState(); - _wait(_config.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoCooldownState(); - _wait(_config.VETO_COOLDOWN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_COOLDOWN_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoSignalingState(); @@ -115,10 +115,10 @@ contract GovernanceStateTransitions is ScenarioTestBlueprint { function test_signalling_to_rage_quit() public { _assertNormalState(); - _lockStETH(_VETOER, percents(_config.SECOND_SEAL_RAGE_QUIT_SUPPORT())); + _lockStETH(_VETOER, percents(_dualGovernanceConfigProvider.SECOND_SEAL_RAGE_QUIT_SUPPORT())); _assertVetoSignalingState(); - _wait(_config.DYNAMIC_TIMELOCK_MAX_DURATION()); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MAX_DURATION()); _activateNextState(); _assertVetoSignalingState(); diff --git a/test/scenario/happy-path-plan-b.t.sol b/test/scenario/happy-path-plan-b.t.sol index 15200fae..bc6ec84c 100644 --- a/test/scenario/happy-path-plan-b.t.sol +++ b/test/scenario/happy-path-plan-b.t.sol @@ -2,8 +2,6 @@ pragma solidity 0.8.26; import { - EmergencyState, - EmergencyProtection, IDangerousContract, ScenarioTestBlueprint, ExternalCall, @@ -15,6 +13,7 @@ import { } from "../utils/scenario-test-blueprint.sol"; import {ExecutableProposals} from "contracts/libraries/ExecutableProposals.sol"; +import {EmergencyProtection} from "contracts/libraries/EmergencyProtection.sol"; contract PlanBSetup is ScenarioTestBlueprint { function setUp() external { @@ -49,14 +48,14 @@ contract PlanBSetup is ScenarioTestBlueprint { _assertCanExecute(proposalId, true); _executeProposal(proposalId); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } // --- // ACT 2. 😱 DAO IS UNDER ATTACK // --- uint256 maliciousProposalId; - EmergencyState memory emergencyState; + EmergencyProtection.Context memory emergencyState; { // Malicious vote was proposed by the attacker with huge LDO wad (but still not the majority) ExternalCall[] memory maliciousCalls = @@ -69,7 +68,7 @@ contract PlanBSetup is ScenarioTestBlueprint { _assertCanSchedule(_timelockedGovernance, maliciousProposalId, false); // some time required to assemble the emergency committee and activate emergency mode - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); // malicious call still can't be scheduled _assertCanSchedule(_timelockedGovernance, maliciousProposalId, false); @@ -80,13 +79,14 @@ contract PlanBSetup is ScenarioTestBlueprint { // emergency mode was successfully activated Timestamp expectedEmergencyModeEndTimestamp = _EMERGENCY_MODE_DURATION.addTo(Timestamps.now()); - emergencyState = _timelock.getEmergencyState(); - assertTrue(emergencyState.isEmergencyModeActivated); + emergencyState = _timelock.getEmergencyProtectionContext(); + + assertTrue(_timelock.isEmergencyModeActive()); assertEq(emergencyState.emergencyModeEndsAfter, expectedEmergencyModeEndTimestamp); // after the submit delay has passed, the call still may be scheduled, but executed // only the emergency committee - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_timelockedGovernance, maliciousProposalId, true); _scheduleProposal(_timelockedGovernance, maliciousProposalId); @@ -96,9 +96,7 @@ contract PlanBSetup is ScenarioTestBlueprint { // but the call still not executable _assertCanExecute(maliciousProposalId, false); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, true, false) - ); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeState.selector, true)); _executeProposal(maliciousProposalId); } @@ -125,11 +123,12 @@ contract PlanBSetup is ScenarioTestBlueprint { abi.encodeCall(_timelock.deactivateEmergencyMode, ()), // Setup emergency committee for some period of time until the Dual Governance is battle tested abi.encodeCall( - _timelock.setEmergencyProtection, + _timelock.setupEmergencyProtection, ( + address(_ADMIN_PROPOSER), // DAO_VOTING TODO: declare variable in scenario test blueprint address(_emergencyActivationCommittee), address(_emergencyExecutionCommittee), - _EMERGENCY_PROTECTION_DURATION, + _EMERGENCY_PROTECTION_DURATION.addTo(Timestamps.now()), Durations.from(30 days) ) ) @@ -183,94 +182,94 @@ contract PlanBSetup is ScenarioTestBlueprint { _assertCanExecute(proposalId, true); _executeProposal(proposalId); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); - } - - // --- - // ACT 5. 🔜 NEW DUAL GOVERNANCE VERSION IS COMING - // --- - { - // some time later, the major Dual Governance update release is ready to be launched - _wait(Durations.from(365 days)); - DualGovernance dualGovernanceV2 = - new DualGovernance(address(_config), address(_timelock), address(_escrowMasterCopy), _ADMIN_PROPOSER); - - ExternalCall[] memory dualGovernanceUpdateCalls = ExternalCallHelpers.create( - address(_timelock), - [ - // Update the controller for timelock - abi.encodeCall(_timelock.setGovernance, address(dualGovernanceV2)), - // Assembly the emergency committee again, until the new version of Dual Governance is battle tested - abi.encodeCall( - _timelock.setEmergencyProtection, - ( - address(_emergencyActivationCommittee), - address(_emergencyExecutionCommittee), - _EMERGENCY_PROTECTION_DURATION, - Durations.from(30 days) - ) - ) - ] - ); - - uint256 updateDualGovernanceProposalId = - _submitProposal(_dualGovernance, "Update Dual Governance to V2", dualGovernanceUpdateCalls); - - _waitAfterSubmitDelayPassed(); - - _assertCanSchedule(_dualGovernance, updateDualGovernanceProposalId, true); - _scheduleProposal(_dualGovernance, updateDualGovernanceProposalId); - - _waitAfterScheduleDelayPassed(); - - // but the call still not executable - _assertCanExecute(updateDualGovernanceProposalId, true); - _executeProposal(updateDualGovernanceProposalId); - - // new version of dual governance attached to timelock - assertEq(_timelock.getGovernance(), address(dualGovernanceV2)); - - // - emergency protection enabled - assertTrue(_timelock.isEmergencyProtectionEnabled()); - - emergencyState = _timelock.getEmergencyState(); - assertEq(emergencyState.activationCommittee, address(_emergencyActivationCommittee)); - assertEq(emergencyState.executionCommittee, address(_emergencyExecutionCommittee)); - assertFalse(emergencyState.isEmergencyModeActivated); - assertEq(emergencyState.emergencyModeDuration, Durations.from(30 days)); - assertEq(emergencyState.emergencyModeEndsAfter, Timestamps.ZERO); - - // use the new version of the dual governance in the future calls - _dualGovernance = dualGovernanceV2; - } - - // --- - // ACT 7. 📆 DAO CONTINUES THEIR REGULAR DUTIES (PROTECTED BY DUAL GOVERNANCE V2) - // --- - { - uint256 proposalId = _submitProposal( - _dualGovernance, "DAO does regular staff on potentially dangerous contract", regularStaffCalls - ); - _assertProposalSubmitted(proposalId); - _assertSubmittedProposalData(proposalId, regularStaffCalls); - - _waitAfterSubmitDelayPassed(); - - _assertCanSchedule(_dualGovernance, proposalId, true); - _scheduleProposal(_dualGovernance, proposalId); - _assertProposalScheduled(proposalId); - - // wait while the after schedule delay has passed - _waitAfterScheduleDelayPassed(); - - // execute the proposal - _assertCanExecute(proposalId, true); - _executeProposal(proposalId); - _assertProposalExecuted(proposalId); - - // call successfully executed - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } + // TODO: update the deployment logic below + // // --- + // // ACT 5. 🔜 NEW DUAL GOVERNANCE VERSION IS COMING + // // --- + // { + // // some time later, the major Dual Governance update release is ready to be launched + // _wait(Durations.from(365 days)); + // DualGovernance dualGovernanceV2 = + // new DualGovernance(address(_config), address(_timelock), address(_escrowMasterCopy), _ADMIN_PROPOSER); + + // ExternalCall[] memory dualGovernanceUpdateCalls = ExternalCallHelpers.create( + // address(_timelock), + // [ + // // Update the controller for timelock + // abi.encodeCall(_timelock.setGovernance, address(dualGovernanceV2)), + // // Assembly the emergency committee again, until the new version of Dual Governance is battle tested + // abi.encodeCall( + // _timelock.setEmergencyProtection, + // ( + // address(_emergencyActivationCommittee), + // address(_emergencyExecutionCommittee), + // _EMERGENCY_PROTECTION_DURATION, + // Durations.from(30 days) + // ) + // ) + // ] + // ); + + // uint256 updateDualGovernanceProposalId = + // _submitProposal(_dualGovernance, "Update Dual Governance to V2", dualGovernanceUpdateCalls); + + // _waitAfterSubmitDelayPassed(); + + // _assertCanSchedule(_dualGovernance, updateDualGovernanceProposalId, true); + // _scheduleProposal(_dualGovernance, updateDualGovernanceProposalId); + + // _waitAfterScheduleDelayPassed(); + + // // but the call still not executable + // _assertCanExecute(updateDualGovernanceProposalId, true); + // _executeProposal(updateDualGovernanceProposalId); + + // // new version of dual governance attached to timelock + // assertEq(_timelock.getGovernance(), address(dualGovernanceV2)); + + // // - emergency protection enabled + // assertTrue(_timelock.isEmergencyProtectionEnabled()); + + // emergencyState = _timelock.getEmergencyState(); + // assertEq(emergencyState.activationCommittee, address(_emergencyActivationCommittee)); + // assertEq(emergencyState.executionCommittee, address(_emergencyExecutionCommittee)); + // assertFalse(emergencyState.isEmergencyModeActivated); + // assertEq(emergencyState.emergencyModeDuration, Durations.from(30 days)); + // assertEq(emergencyState.emergencyModeEndsAfter, Timestamps.ZERO); + + // // use the new version of the dual governance in the future calls + // _dualGovernance = dualGovernanceV2; + // } + + // // --- + // // ACT 7. 📆 DAO CONTINUES THEIR REGULAR DUTIES (PROTECTED BY DUAL GOVERNANCE V2) + // // --- + // { + // uint256 proposalId = _submitProposal( + // _dualGovernance, "DAO does regular staff on potentially dangerous contract", regularStaffCalls + // ); + // _assertProposalSubmitted(proposalId); + // _assertSubmittedProposalData(proposalId, regularStaffCalls); + + // _waitAfterSubmitDelayPassed(); + + // _assertCanSchedule(_dualGovernance, proposalId, true); + // _scheduleProposal(_dualGovernance, proposalId); + // _assertProposalScheduled(proposalId); + + // // wait while the after schedule delay has passed + // _waitAfterScheduleDelayPassed(); + + // // execute the proposal + // _assertCanExecute(proposalId, true); + // _executeProposal(proposalId); + // _assertProposalExecuted(proposalId); + + // // call successfully executed + // _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); + // } } function testFork_SubmittedCallsCantBeExecutedAfterEmergencyModeDeactivation() external { @@ -287,32 +286,29 @@ contract PlanBSetup is ScenarioTestBlueprint { } // activate emergency mode - EmergencyState memory emergencyState; + EmergencyProtection.Context memory emergencyState; { - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); vm.prank(address(_emergencyActivationCommittee)); _timelock.activateEmergencyMode(); - emergencyState = _timelock.getEmergencyState(); - assertTrue(emergencyState.isEmergencyModeActivated); + assertTrue(_timelock.isEmergencyModeActive()); } // delay for malicious proposal has passed, but it can't be executed because of emergency mode was activated { // the after submit delay has passed, and proposal can be scheduled, but not executed - _wait(_config.AFTER_SCHEDULE_DELAY() + Durations.from(1 seconds)); - _wait(_config.AFTER_SUBMIT_DELAY().plusSeconds(1)); + _wait(_timelock.getAfterScheduleDelay() + Durations.from(1 seconds)); + _wait(_timelock.getAfterSubmitDelay().plusSeconds(1)); _assertCanSchedule(_timelockedGovernance, maliciousProposalId, true); _scheduleProposal(_timelockedGovernance, maliciousProposalId); - _wait(_config.AFTER_SCHEDULE_DELAY().plusSeconds(1)); + _wait(_timelock.getAfterScheduleDelay().plusSeconds(1)); _assertCanExecute(maliciousProposalId, false); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, true, false) - ); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeState.selector, true)); _executeProposal(maliciousProposalId); } @@ -331,15 +327,13 @@ contract PlanBSetup is ScenarioTestBlueprint { _assertCanExecute(anotherMaliciousProposalId, false); // the after submit delay has passed, and proposal can not be executed - _wait(_config.AFTER_SUBMIT_DELAY().plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().plusSeconds(1)); _assertCanSchedule(_timelockedGovernance, anotherMaliciousProposalId, true); - _wait(_config.AFTER_SCHEDULE_DELAY().plusSeconds(1)); + _wait(_timelock.getAfterScheduleDelay().plusSeconds(1)); _assertCanExecute(anotherMaliciousProposalId, false); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, true, false) - ); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeState.selector, true)); _executeProposal(anotherMaliciousProposalId); } @@ -348,14 +342,10 @@ contract PlanBSetup is ScenarioTestBlueprint { _wait(_EMERGENCY_MODE_DURATION.dividedBy(2)); assertTrue(emergencyState.emergencyModeEndsAfter < Timestamps.now()); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, true, false) - ); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeState.selector, true)); _executeProposal(maliciousProposalId); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, true, false) - ); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeState.selector, true)); _executeProposal(anotherMaliciousProposalId); } @@ -363,8 +353,7 @@ contract PlanBSetup is ScenarioTestBlueprint { { _timelock.deactivateEmergencyMode(); - emergencyState = _timelock.getEmergencyState(); - assertFalse(emergencyState.isEmergencyModeActivated); + assertFalse(_timelock.isEmergencyModeActive()); assertFalse(_timelock.isEmergencyProtectionEnabled()); } @@ -389,17 +378,16 @@ contract PlanBSetup is ScenarioTestBlueprint { // deploy dual governance full setup { _deployDualGovernanceSetup( /* isEmergencyProtectionEnabled */ true); - assertNotEq(_timelock.getGovernance(), _config.EMERGENCY_GOVERNANCE()); + assertNotEq(_timelock.getGovernance(), _timelock.getEmergencyProtectionContext().emergencyGovernance); } // emergency committee activates emergency mode - EmergencyState memory emergencyState; + EmergencyProtection.Context memory emergencyState; { vm.prank(address(_emergencyActivationCommittee)); _timelock.activateEmergencyMode(); - emergencyState = _timelock.getEmergencyState(); - assertTrue(emergencyState.isEmergencyModeActivated); + assertFalse(_timelock.isEmergencyModeActive()); } // before the end of the emergency mode emergency committee can reset the controller to @@ -410,14 +398,14 @@ contract PlanBSetup is ScenarioTestBlueprint { _executeEmergencyReset(); - assertEq(_timelock.getGovernance(), _config.EMERGENCY_GOVERNANCE()); + assertEq(_timelock.getGovernance(), _timelock.getEmergencyProtectionContext().emergencyGovernance); - emergencyState = _timelock.getEmergencyState(); - assertEq(emergencyState.activationCommittee, address(0)); - assertEq(emergencyState.executionCommittee, address(0)); + emergencyState = _timelock.getEmergencyProtectionContext(); + assertEq(emergencyState.emergencyActivationCommittee, address(0)); + assertEq(emergencyState.emergencyExecutionCommittee, address(0)); assertEq(emergencyState.emergencyModeDuration, Durations.ZERO); assertEq(emergencyState.emergencyModeEndsAfter, Timestamps.ZERO); - assertFalse(emergencyState.isEmergencyModeActivated); + assertFalse(_timelock.isEmergencyModeActive()); } } @@ -425,7 +413,7 @@ contract PlanBSetup is ScenarioTestBlueprint { // deploy dual governance full setup { _deployDualGovernanceSetup( /* isEmergencyProtectionEnabled */ true); - assertNotEq(_timelock.getGovernance(), _config.EMERGENCY_GOVERNANCE()); + assertNotEq(_timelock.getGovernance(), _timelock.getEmergencyProtectionContext().emergencyGovernance); } // wait till the protection duration passes @@ -433,15 +421,13 @@ contract PlanBSetup is ScenarioTestBlueprint { _wait(_EMERGENCY_PROTECTION_DURATION.plusSeconds(1)); } - EmergencyState memory emergencyState = _timelock.getEmergencyState(); + EmergencyProtection.Context memory emergencyState = _timelock.getEmergencyProtectionContext(); // attempt to activate emergency protection fails { vm.expectRevert( abi.encodeWithSelector( - EmergencyProtection.EmergencyCommitteeExpired.selector, - block.timestamp, - emergencyState.protectedTill + EmergencyProtection.EmergencyProtectionExpired.selector, emergencyState.emergencyProtectionEndsAfter ) ); vm.prank(address(_emergencyActivationCommittee)); diff --git a/test/scenario/happy-path.t.sol b/test/scenario/happy-path.t.sol index bea2deef..fd6deea2 100644 --- a/test/scenario/happy-path.t.sol +++ b/test/scenario/happy-path.t.sol @@ -30,14 +30,14 @@ contract HappyPathTest is ScenarioTestBlueprint { _assertProposalSubmitted(proposalId); _assertSubmittedProposalData(proposalId, regularStaffCalls); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); // the min execution delay hasn't elapsed yet vm.expectRevert(abi.encodeWithSelector(ExecutableProposals.AfterSubmitDelayNotPassed.selector, (proposalId))); _scheduleProposal(_dualGovernance, proposalId); // wait till the first phase of timelock passes - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_dualGovernance, proposalId, true); _scheduleProposal(_dualGovernance, proposalId); @@ -48,12 +48,12 @@ contract HappyPathTest is ScenarioTestBlueprint { _assertCanExecute(proposalId, true); _executeProposal(proposalId); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } function testFork_HappyPathWithMultipleItems() external { // additional phase required here, grant rights to call DAO Agent to the admin executor - Utils.grantPermission(DAO_AGENT, IAragonAgent(DAO_AGENT).RUN_SCRIPT_ROLE(), _config.ADMIN_EXECUTOR()); + Utils.grantPermission(DAO_AGENT, IAragonAgent(DAO_AGENT).RUN_SCRIPT_ROLE(), _timelock.getAdminExecutor()); bytes memory agentDoRegularStaffPayload = abi.encodeCall(IDangerousContract.doRegularStaff, (42)); bytes memory targetCallEvmScript = Utils.encodeEvmCallScript(address(_target), agentDoRegularStaffPayload); @@ -68,7 +68,7 @@ contract HappyPathTest is ScenarioTestBlueprint { uint256 proposalId = _submitProposal(_dualGovernance, "Multiple items", multipleCalls); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); // proposal can't be scheduled before the after submit delay has passed _assertCanSchedule(_dualGovernance, proposalId, false); @@ -78,7 +78,7 @@ contract HappyPathTest is ScenarioTestBlueprint { _scheduleProposal(_dualGovernance, proposalId); // wait till the DG-enforced timelock elapses - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_dualGovernance, proposalId, true); _scheduleProposal(_dualGovernance, proposalId); @@ -91,7 +91,7 @@ contract HappyPathTest is ScenarioTestBlueprint { address[] memory senders = new address[](2); senders[0] = DAO_AGENT; - senders[1] = _config.ADMIN_EXECUTOR(); + senders[1] = _timelock.getAdminExecutor(); ExternalCall[] memory expectedTargetCalls = ExternalCallHelpers.create( [DAO_AGENT, address(_target)], diff --git a/test/scenario/last-moment-malicious-proposal.t.sol b/test/scenario/last-moment-malicious-proposal.t.sol index a5d6b114..51e55b83 100644 --- a/test/scenario/last-moment-malicious-proposal.t.sol +++ b/test/scenario/last-moment-malicious-proposal.t.sol @@ -78,12 +78,12 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { address stEthHolders = makeAddr("STETH_WHALE"); _step("5. STETH HOLDERS ACQUIRING QUORUM TO VETO MALICIOUS PROPOSAL"); { - _wait(_config.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().dividedBy(2)); - _lockStETH(stEthHolders, percents(_config.FIRST_SEAL_RAGE_QUIT_SUPPORT() + 1)); + _wait(_dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().dividedBy(2)); + _lockStETH(stEthHolders, percents(_dualGovernanceConfigProvider.FIRST_SEAL_RAGE_QUIT_SUPPORT() + 1)); _assertVetoSignalingDeactivationState(); _logVetoSignallingDeactivationState(); - _wait(_config.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().dividedBy(2).plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().dividedBy(2).plusSeconds(1)); _activateNextState(); _assertVetoCooldownState(); } @@ -107,17 +107,17 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { _step("7. NEW VETO SIGNALLING ROUND FOR MALICIOUS PROPOSAL IS STARTED"); { - _wait(_config.VETO_COOLDOWN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_COOLDOWN_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoSignalingState(); _logVetoSignallingState(); // the second seal rage quit support is reached - _lockStETH(stEthHolders, percents(_config.SECOND_SEAL_RAGE_QUIT_SUPPORT())); + _lockStETH(stEthHolders, percents(_dualGovernanceConfigProvider.SECOND_SEAL_RAGE_QUIT_SUPPORT())); _assertVetoSignalingState(); _logVetoSignallingState(); - _wait(_config.DYNAMIC_TIMELOCK_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MAX_DURATION().plusSeconds(1)); _logVetoSignallingState(); _activateNextState(); _assertRageQuitState(); @@ -150,12 +150,12 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { // --- address maliciousActor = makeAddr("MALICIOUS_ACTOR"); { - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); _lockStETH(maliciousActor, percents("12.0")); _assertVetoSignalingState(); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertProposalSubmitted(proposalId); @@ -170,7 +170,7 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { // ACT 3. THE VETO SIGNALLING DEACTIVATION DURATION EQUALS TO "VETO_SIGNALLING_DEACTIVATION_MAX_DURATION" DAYS // --- { - _wait(_config.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoCooldownState(); @@ -184,7 +184,7 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { _assertCanExecute(proposalId, true); _executeProposal(proposalId); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } } @@ -211,12 +211,12 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { _step("3. THE VETO SIGNALLING & DEACTIVATION PASSED BUT PROPOSAL STILL NOT EXECUTABLE"); { - _wait(_config.DYNAMIC_TIMELOCK_MIN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MIN_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoSignalingDeactivationState(); _logVetoSignallingDeactivationState(); - _wait(_config.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoCooldownState(); @@ -227,7 +227,7 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { _step("4. AFTER THE VETO COOLDOWN GOVERNANCE TRANSITIONS INTO NORMAL STATE"); { _unlockStETH(maliciousActor); - _wait(_config.VETO_COOLDOWN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_COOLDOWN_DURATION().plusSeconds(1)); _activateNextState(); _assertNormalState(); } @@ -265,12 +265,12 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { _step("3. THE VETO SIGNALLING & DEACTIVATION PASSED BUT PROPOSAL STILL NOT EXECUTABLE"); { - _wait(_config.DYNAMIC_TIMELOCK_MIN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MIN_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoSignalingDeactivationState(); _logVetoSignallingDeactivationState(); - _wait(_config.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoCooldownState(); @@ -280,7 +280,7 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { _step("4. AFTER THE VETO COOLDOWN NEW VETO SIGNALLING ROUND STARTED"); { - _wait(_config.VETO_COOLDOWN_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_COOLDOWN_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoSignalingState(); _logVetoSignallingState(); @@ -291,12 +291,12 @@ contract LastMomentMaliciousProposalSuccessor is ScenarioTestBlueprint { _step("5. PROPOSAL EXECUTABLE IN THE NEXT VETO COOLDOWN"); { - _wait(_config.DYNAMIC_TIMELOCK_MIN_DURATION().multipliedBy(2)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MIN_DURATION().multipliedBy(2)); _activateNextState(); _assertVetoSignalingDeactivationState(); _logVetoSignallingDeactivationState(); - _wait(_config.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertVetoCooldownState(); diff --git a/test/scenario/proposal-deployment-modes.sol b/test/scenario/proposal-deployment-modes.t.sol similarity index 73% rename from test/scenario/proposal-deployment-modes.sol rename to test/scenario/proposal-deployment-modes.t.sol index 557fc384..1da2b173 100644 --- a/test/scenario/proposal-deployment-modes.sol +++ b/test/scenario/proposal-deployment-modes.t.sol @@ -1,9 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import {EmergencyState} from "contracts/libraries/EmergencyProtection.sol"; +import {Durations} from "contracts/types/Duration.sol"; +import {Timestamps} from "contracts/types/Timestamp.sol"; + +import {EmergencyProtection} from "contracts/libraries/EmergencyProtection.sol"; import {ExecutableProposals} from "contracts/libraries/ExecutableProposals.sol"; -import {Durations, Timestamps} from "test/utils/unit-test.sol"; import {percents, ScenarioTestBlueprint, ExternalCall} from "../utils/scenario-test-blueprint.sol"; @@ -19,12 +21,12 @@ contract ProposalDeploymentModesTest is ScenarioTestBlueprint { (uint256 proposalId, ExternalCall[] memory regularStaffCalls) = _createAndAssertProposal(); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); vm.expectRevert(abi.encodeWithSelector(ExecutableProposals.AfterSubmitDelayNotPassed.selector, (proposalId))); _scheduleProposal(_dualGovernance, proposalId); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_dualGovernance, proposalId, true); _scheduleProposal(_dualGovernance, proposalId); @@ -35,7 +37,7 @@ contract ProposalDeploymentModesTest is ScenarioTestBlueprint { _assertCanExecute(proposalId, true); _executeProposal(proposalId); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } function test_protected_deployment_mode_execute_after_timelock() external { @@ -43,12 +45,12 @@ contract ProposalDeploymentModesTest is ScenarioTestBlueprint { (uint256 proposalId, ExternalCall[] memory regularStaffCalls) = _createAndAssertProposal(); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); vm.expectRevert(abi.encodeWithSelector(ExecutableProposals.AfterSubmitDelayNotPassed.selector, (proposalId))); _scheduleProposal(_dualGovernance, proposalId); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_dualGovernance, proposalId, true); _scheduleProposal(_dualGovernance, proposalId); @@ -59,7 +61,7 @@ contract ProposalDeploymentModesTest is ScenarioTestBlueprint { _assertCanExecute(proposalId, true); _executeProposal(proposalId); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } function test_protected_deployment_mode_execute_in_emergency_mode() external { @@ -67,12 +69,12 @@ contract ProposalDeploymentModesTest is ScenarioTestBlueprint { (uint256 proposalId, ExternalCall[] memory regularStaffCalls) = _createAndAssertProposal(); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); vm.expectRevert(abi.encodeWithSelector(ExecutableProposals.AfterSubmitDelayNotPassed.selector, (proposalId))); _scheduleProposal(_dualGovernance, proposalId); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_dualGovernance, proposalId, true); _scheduleProposal(_dualGovernance, proposalId); @@ -85,16 +87,14 @@ contract ProposalDeploymentModesTest is ScenarioTestBlueprint { vm.prank(address(_emergencyActivationCommittee)); _timelock.activateEmergencyMode(); - EmergencyState memory emergencyState = _timelock.getEmergencyState(); - - assertEq(emergencyState.isEmergencyModeActivated, true); + assertEq(_timelock.isEmergencyModeActive(), true); _assertCanExecute(proposalId, false); vm.prank(address(_emergencyExecutionCommittee)); _timelock.emergencyExecute(proposalId); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } function test_protected_deployment_mode_deactivation_in_emergency_mode() external { @@ -102,12 +102,12 @@ contract ProposalDeploymentModesTest is ScenarioTestBlueprint { (uint256 proposalId, ExternalCall[] memory regularStaffCalls) = _createAndAssertProposal(); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); vm.expectRevert(abi.encodeWithSelector(ExecutableProposals.AfterSubmitDelayNotPassed.selector, (proposalId))); _scheduleProposal(_dualGovernance, proposalId); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_dualGovernance, proposalId, true); _scheduleProposal(_dualGovernance, proposalId); @@ -120,17 +120,22 @@ contract ProposalDeploymentModesTest is ScenarioTestBlueprint { vm.prank(address(_emergencyActivationCommittee)); _timelock.activateEmergencyMode(); - EmergencyState memory emergencyState = _timelock.getEmergencyState(); - - assertEq(emergencyState.isEmergencyModeActivated, true); + assertEq(_timelock.isEmergencyModeActive(), true); + assertEq(_timelock.isEmergencyProtectionEnabled(), true); _assertCanExecute(proposalId, false); - _wait(emergencyState.emergencyModeDuration.plusSeconds(1)); + // emergency protection disabled after emergency mode is activated + + _wait(_timelock.getEmergencyProtectionContext().emergencyModeDuration.plusSeconds(1)); + + assertEq(_timelock.isEmergencyModeActive(), true); + assertEq(_timelock.isEmergencyProtectionEnabled(), true); _timelock.deactivateEmergencyMode(); - _assertCanExecute(proposalId, false); + assertEq(_timelock.isEmergencyModeActive(), false); assertEq(_timelock.isEmergencyProtectionEnabled(), false); + _assertCanExecute(proposalId, false); } function _createAndAssertProposal() internal returns (uint256, ExternalCall[] memory) { diff --git a/test/scenario/tiebraker.t.sol b/test/scenario/tiebraker.t.sol index 02cf5b1e..80aa6e97 100644 --- a/test/scenario/tiebraker.t.sol +++ b/test/scenario/tiebraker.t.sol @@ -28,12 +28,12 @@ contract TiebreakerScenarioTest is ScenarioTestBlueprint { // Tiebreak activation _assertNormalState(); - _lockStETH(_VETOER, percents(_config.SECOND_SEAL_RAGE_QUIT_SUPPORT())); + _lockStETH(_VETOER, percents(_dualGovernanceConfigProvider.SECOND_SEAL_RAGE_QUIT_SUPPORT())); _lockStETH(_VETOER, 1 gwei); - _wait(_config.DYNAMIC_TIMELOCK_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertRageQuitState(); - _wait(_config.TIE_BREAK_ACTIVATION_TIMEOUT()); + _wait(_dualGovernance.getTiebreakerState().tiebreakerActivationTimeout); _activateNextState(); ExternalCall[] memory proposalCalls = ExternalCallHelpers.create(address(0), new bytes(0)); @@ -81,7 +81,7 @@ contract TiebreakerScenarioTest is ScenarioTestBlueprint { assert(support == quorum); // Waiting for submit delay pass - _wait(_config.AFTER_SUBMIT_DELAY()); + _wait(_timelock.getAfterSubmitDelay()); _tiebreakerCommittee.executeScheduleProposal(proposalIdToExecute); } @@ -103,12 +103,12 @@ contract TiebreakerScenarioTest is ScenarioTestBlueprint { // Tiebreak activation _assertNormalState(); - _lockStETH(_VETOER, percents(_config.SECOND_SEAL_RAGE_QUIT_SUPPORT())); + _lockStETH(_VETOER, percents(_dualGovernanceConfigProvider.SECOND_SEAL_RAGE_QUIT_SUPPORT())); _lockStETH(_VETOER, 1 gwei); - _wait(_config.DYNAMIC_TIMELOCK_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertRageQuitState(); - _wait(_config.TIE_BREAK_ACTIVATION_TIMEOUT()); + _wait(_dualGovernance.getTiebreakerState().tiebreakerActivationTimeout); _activateNextState(); // Tiebreaker subcommittee 0 diff --git a/test/scenario/timelocked-governance.t.sol b/test/scenario/timelocked-governance.t.sol index ff246f97..cdbfb9f0 100644 --- a/test/scenario/timelocked-governance.t.sol +++ b/test/scenario/timelocked-governance.t.sol @@ -2,10 +2,11 @@ pragma solidity 0.8.26; import {IGovernance} from "contracts/interfaces/ITimelock.sol"; +import {Duration, Durations} from "contracts/types/Duration.sol"; import {Timestamps, Timestamp} from "contracts/types/Timestamp.sol"; import {Durations, minus} from "contracts/types/Duration.sol"; import {ExternalCall} from "contracts/libraries/ExecutableProposals.sol"; -import {EmergencyProtection, EmergencyState} from "contracts/libraries/EmergencyProtection.sol"; +import {EmergencyProtection} from "contracts/libraries/EmergencyProtection.sol"; import {ScenarioTestBlueprint, ExternalCallHelpers, IDangerousContract} from "../utils/scenario-test-blueprint.sol"; @@ -27,10 +28,12 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { // --- // Act 2. Timeskip. Emergency protection is about to be expired. // --- - EmergencyState memory emergencyState = _timelock.getEmergencyState(); + EmergencyProtection.Context memory emergencyState = _timelock.getEmergencyProtectionContext(); { assertEq(_timelock.isEmergencyProtectionEnabled(), true); - _wait(Durations.from(emergencyState.protectedTill.toSeconds()).minusSeconds(block.timestamp).plusSeconds(1)); + Duration emergencyProtectionDuration = + Durations.from(emergencyState.emergencyProtectionEndsAfter.toSeconds() - block.timestamp); + _wait(emergencyProtectionDuration.plusSeconds(1)); assertEq(_timelock.isEmergencyProtectionEnabled(), false); } @@ -38,18 +41,17 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { // Act 3. Emergency committee has no more power to stop proposal flow. // { - vm.prank(address(emergencyState.activationCommittee)); + vm.prank(address(emergencyState.emergencyActivationCommittee)); vm.expectRevert( abi.encodeWithSelector( - EmergencyProtection.EmergencyCommitteeExpired.selector, - block.timestamp, - emergencyState.protectedTill.toSeconds() + EmergencyProtection.EmergencyProtectionExpired.selector, + emergencyState.emergencyProtectionEndsAfter.toSeconds() ) ); _timelock.activateEmergencyMode(); - assertFalse(_timelock.getEmergencyState().isEmergencyModeActivated); + assertFalse(_timelock.isEmergencyModeActive()); assertFalse(_timelock.isEmergencyProtectionEnabled()); } @@ -74,7 +76,7 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { // --- (uint256 maliciousProposalId,) = _submitAndAssertMaliciousProposal(); { - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); _assertCanSchedule(_timelockedGovernance, maliciousProposalId, false); } @@ -85,9 +87,9 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { vm.prank(address(_emergencyActivationCommittee)); _timelock.activateEmergencyMode(); - assertTrue(_timelock.getEmergencyState().isEmergencyModeActivated); + assertTrue(_timelock.isEmergencyModeActive()); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_timelockedGovernance, maliciousProposalId, true); _scheduleProposal(_timelockedGovernance, maliciousProposalId); @@ -96,9 +98,7 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { _assertCanExecute(maliciousProposalId, false); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, true, false) - ); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeState.selector, false)); _executeProposal(maliciousProposalId); } @@ -123,7 +123,7 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { _assertCanExecute(deactivateEmergencyModeProposalId, false); _executeEmergencyExecute(deactivateEmergencyModeProposalId); - assertFalse(_timelock.getEmergencyState().isEmergencyModeActivated); + assertFalse(_timelock.isEmergencyModeActive()); assertFalse(_timelock.isEmergencyProtectionEnabled()); _timelock.getProposal(maliciousProposalId); @@ -151,22 +151,20 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { // --- (uint256 maliciousProposalId,) = _submitAndAssertMaliciousProposal(); { - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2)); _assertCanSchedule(_timelockedGovernance, maliciousProposalId, false); } // --- // Act 3. Emergency committee activates emergency mode. // --- - EmergencyState memory emergencyState; { vm.prank(address(_emergencyActivationCommittee)); _timelock.activateEmergencyMode(); - emergencyState = _timelock.getEmergencyState(); - assertTrue(emergencyState.isEmergencyModeActivated); + assertTrue(_timelock.isEmergencyModeActive()); - _wait(_config.AFTER_SUBMIT_DELAY().dividedBy(2).plusSeconds(1)); + _wait(_timelock.getAfterSubmitDelay().dividedBy(2).plusSeconds(1)); _assertCanSchedule(_timelockedGovernance, maliciousProposalId, true); _scheduleProposal(_timelockedGovernance, maliciousProposalId); @@ -175,9 +173,7 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { _assertCanExecute(maliciousProposalId, false); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, true, false) - ); + vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeState.selector, false)); _executeProposal(maliciousProposalId); } @@ -185,7 +181,8 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { // Act 4. DAO decides to not deactivate emergency mode and allow stakers to quit. // --- { - assertTrue(_timelock.getEmergencyState().isEmergencyModeActivated); + EmergencyProtection.Context memory emergencyState = _timelock.getEmergencyProtectionContext(); + assertTrue(_timelock.isEmergencyModeActive()); _wait( Durations.from(emergencyState.emergencyModeEndsAfter.toSeconds()).minusSeconds(block.timestamp) @@ -193,7 +190,7 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { ); _timelock.deactivateEmergencyMode(); - assertFalse(_timelock.getEmergencyState().isEmergencyModeActivated); + assertFalse(_timelock.isEmergencyModeActive()); } // --- @@ -252,7 +249,7 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { vm.prank(address(_emergencyActivationCommittee)); _timelock.activateEmergencyMode(); - assertTrue(_timelock.getEmergencyState().isEmergencyModeActivated); + assertTrue(_timelock.isEmergencyModeActive()); ExternalCall[] memory timelockedGovernanceLaunchCalls = ExternalCallHelpers.create( address(_timelock), @@ -325,6 +322,6 @@ contract TimelockedGovernanceScenario is ScenarioTestBlueprint { _assertCanExecute(proposalId, true); _executeProposal(proposalId); - _assertTargetMockCalls(_config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertTargetMockCalls(_timelock.getAdminExecutor(), regularStaffCalls); } } diff --git a/test/scenario/veto-cooldown-mechanics.t.sol b/test/scenario/veto-cooldown-mechanics.t.sol index e7f145ae..2ad238b0 100644 --- a/test/scenario/veto-cooldown-mechanics.t.sol +++ b/test/scenario/veto-cooldown-mechanics.t.sol @@ -7,7 +7,8 @@ import { ExternalCall, ExternalCallHelpers, ScenarioTestBlueprint, - DualGovernance + DualGovernance, + console } from "../utils/scenario-test-blueprint.sol"; interface IDangerousContract { @@ -33,7 +34,7 @@ contract VetoCooldownMechanicsTest is ScenarioTestBlueprint { _dualGovernance, "Propose to doSmth on target passing dual governance", regularStaffCalls ); - _assertSubmittedProposalData(proposalId, _config.ADMIN_EXECUTOR(), regularStaffCalls); + _assertSubmittedProposalData(proposalId, _timelock.getAdminExecutor(), regularStaffCalls); _assertCanSchedule(_dualGovernance, proposalId, false); } @@ -41,10 +42,11 @@ contract VetoCooldownMechanicsTest is ScenarioTestBlueprint { address vetoer = makeAddr("MALICIOUS_ACTOR"); _step("2. THE SECOND SEAL RAGE QUIT SUPPORT IS ACQUIRED"); { - vetoedStETHAmount = _lockStETH(vetoer, percents(_config.SECOND_SEAL_RAGE_QUIT_SUPPORT() + 1)); + vetoedStETHAmount = + _lockStETH(vetoer, percents(_dualGovernanceConfigProvider.SECOND_SEAL_RAGE_QUIT_SUPPORT() + 1)); _assertVetoSignalingState(); - _wait(_config.DYNAMIC_TIMELOCK_MAX_DURATION().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.DYNAMIC_TIMELOCK_MAX_DURATION().plusSeconds(1)); _activateNextState(); _assertRageQuitState(); } @@ -65,8 +67,6 @@ contract VetoCooldownMechanicsTest is ScenarioTestBlueprint { { // request withdrawals batches Escrow rageQuitEscrow = _getRageQuitEscrow(); - uint256 requestAmount = _WITHDRAWAL_QUEUE.MAX_STETH_WITHDRAWAL_AMOUNT(); - uint256 maxRequestsCount = vetoedStETHAmount / requestAmount + 1; while (!rageQuitEscrow.isWithdrawalsBatchesFinalized()) { rageQuitEscrow.requestNextWithdrawalsBatch(96); @@ -79,7 +79,7 @@ contract VetoCooldownMechanicsTest is ScenarioTestBlueprint { rageQuitEscrow.claimNextWithdrawalsBatch(128); } - _wait(_config.RAGE_QUIT_EXTENSION_DELAY().plusSeconds(1)); + _wait(_dualGovernanceConfigProvider.RAGE_QUIT_EXTENSION_DELAY().plusSeconds(1)); assertTrue(rageQuitEscrow.isRageQuitFinalized()); } diff --git a/test/unit/EmergencyProtectedTimelock.t.sol b/test/unit/EmergencyProtectedTimelock.t.sol index 55f42cbf..2233ea23 100644 --- a/test/unit/EmergencyProtectedTimelock.t.sol +++ b/test/unit/EmergencyProtectedTimelock.t.sol @@ -1,847 +1,855 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import {Vm} from "forge-std/Test.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +// import {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 {ITimelock, ProposalStatus} from "contracts/interfaces/ITimelock.sol"; +// import {Executor} from "contracts/Executor.sol"; +// import {EmergencyProtection} from "contracts/libraries/EmergencyProtection.sol"; +// import {ExecutableProposals} from "contracts/libraries/ExecutableProposals.sol"; + +// import {UnitTest, Duration, Timestamp, Timestamps, Durations, console} from "test/utils/unit-test.sol"; +// import {TargetMock} from "test/utils/utils.sol"; +// import {ExternalCall, ExternalCallHelpers} from "test/utils/executor-calls.sol"; +// import {IDangerousContract} from "test/utils/interfaces.sol"; +// import {Deployment} from "test/utils/deployment.sol"; + +// contract EmergencyProtectedTimelockUnitTests is UnitTest { +// EmergencyProtectedTimelock private _timelock; +// TargetMock private _targetMock; +// Executor private _executor; -import {Executor} from "contracts/Executor.sol"; -import {EmergencyProtectedTimelock} from "contracts/EmergencyProtectedTimelock.sol"; -import {ITimelock, ProposalStatus} from "contracts/interfaces/ITimelock.sol"; -import {IConfiguration, Configuration} from "contracts/Configuration.sol"; -import {ConfigurationProvider} from "contracts/ConfigurationProvider.sol"; -import {Executor} from "contracts/Executor.sol"; -import {EmergencyProtection, EmergencyState} from "contracts/libraries/EmergencyProtection.sol"; -import {ExecutableProposals} from "contracts/libraries/ExecutableProposals.sol"; +// address private _emergencyActivator = makeAddr("EMERGENCY_ACTIVATION_COMMITTEE"); +// address private _emergencyEnactor = makeAddr("EMERGENCY_EXECUTION_COMMITTEE"); +// Duration private _emergencyModeDuration = Durations.from(180 days); +// Duration private _emergencyProtectionDuration = Durations.from(90 days); -import {UnitTest, Duration, Timestamp, Timestamps, Durations, console} from "test/utils/unit-test.sol"; -import {TargetMock} from "test/utils/utils.sol"; -import {ExternalCall, ExternalCallHelpers} from "test/utils/executor-calls.sol"; -import {IDangerousContract} from "test/utils/interfaces.sol"; +// address private _emergencyGovernance = makeAddr("EMERGENCY_GOVERNANCE"); +// address private _dualGovernance = makeAddr("DUAL_GOVERNANCE"); +// address private _adminExecutor; -contract EmergencyProtectedTimelockUnitTests is UnitTest { - EmergencyProtectedTimelock private _timelock; - Configuration private _config; - TargetMock private _targetMock; - Executor private _executor; +// function setUp() external { +// _executor = new Executor(address(this)); +// _timelock = new EmergencyProtectedTimelock( +// EmergencyProtectedTimelock.SanityCheckParams({ +// maxAfterSubmitDelay: Durations.from(45 days), +// maxAfterScheduleDelay: Durations.from(45 days), +// maxEmergencyModeDuration: Durations.from(365 days), +// maxEmergencyProtectionDuration: Durations.from(365 days) +// }), +// _executor +// ); - address private _emergencyActivator = makeAddr("EMERGENCY_ACTIVATION_COMMITTEE"); - address private _emergencyEnactor = makeAddr("EMERGENCY_EXECUTION_COMMITTEE"); - Duration private _emergencyModeDuration = Durations.from(180 days); - Duration private _emergencyProtectionDuration = Durations.from(90 days); +// _targetMock = new TargetMock(); - address private _emergencyGovernance = makeAddr("EMERGENCY_GOVERNANCE"); - address private _dualGovernance = makeAddr("DUAL_GOVERNANCE"); - address private _adminExecutor; +// _executor.transferOwnership(address(_timelock)); +// _adminExecutor = address(_executor); - function setUp() external { - _executor = new Executor(address(this)); - _config = new Configuration(address(_executor), _emergencyGovernance, new address[](0)); - _timelock = new EmergencyProtectedTimelock(address(_config)); - _targetMock = new TargetMock(); +// vm.startPrank(_adminExecutor); +// _timelock.setGovernance(_dualGovernance); +// _timelock.setDelays({afterSubmitDelay: Durations.from(3 days), afterScheduleDelay: Durations.from(2 days)}); +// _timelock.setEmergencyProtection( +// _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration +// ); +// vm.stopPrank(); +// } - _executor.transferOwnership(address(_timelock)); - _adminExecutor = address(_executor); +// // EmergencyProtectedTimelock.submit() - vm.startPrank(_adminExecutor); - _timelock.setGovernance(_dualGovernance); - _timelock.setEmergencyProtection( - _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration - ); - vm.stopPrank(); - } +// function testFuzz_stranger_cannot_submit_proposal(address stranger) external { +// vm.assume(stranger != _dualGovernance); - // EmergencyProtectedTimelock.submit() +// vm.prank(stranger); +// vm.expectRevert( +// abi.encodeWithSelector(EmergencyProtectedTimelock.NotGovernance.selector, [stranger, _dualGovernance]) +// ); +// _timelock.submit(_adminExecutor, new ExternalCall[](0)); +// assertEq(_timelock.getProposalsCount(), 0); +// } - function testFuzz_stranger_cannot_submit_proposal(address stranger) external { - vm.assume(stranger != _dualGovernance); +// function test_governance_can_submit_proposal() external { +// vm.prank(_dualGovernance); +// _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); - vm.prank(stranger); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtectedTimelock.NotGovernance.selector, [stranger, _dualGovernance]) - ); - _timelock.submit(_adminExecutor, new ExternalCall[](0)); - assertEq(_timelock.getProposalsCount(), 0); - } +// assertEq(_timelock.getProposalsCount(), 1); - function test_governance_can_submit_proposal() external { - vm.prank(_dualGovernance); - _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); +// ITimelock.Proposal memory proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Submitted); +// } - assertEq(_timelock.getProposalsCount(), 1); +// // EmergencyProtectedTimelock.schedule() - ITimelock.Proposal memory proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Submitted); - } +// function test_governance_can_schedule_proposal() external { +// _submitProposal(); - // EmergencyProtectedTimelock.schedule() +// assertEq(_timelock.getProposalsCount(), 1); - function test_governance_can_schedule_proposal() external { - _submitProposal(); +// _wait(_timelock.getAfterSubmitDelay()); - assertEq(_timelock.getProposalsCount(), 1); +// _scheduleProposal(1); - _wait(_config.AFTER_SUBMIT_DELAY()); +// ITimelock.Proposal memory proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Scheduled); +// } - _scheduleProposal(1); +// function testFuzz_stranger_cannot_schedule_proposal(address stranger) external { +// vm.assume(stranger != _dualGovernance); +// vm.assume(stranger != address(0)); - ITimelock.Proposal memory proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Scheduled); - } +// _submitProposal(); - function testFuzz_stranger_cannot_schedule_proposal(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.schedule(1); - _submitProposal(); +// ITimelock.Proposal memory proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Submitted); +// } - vm.prank(stranger); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtectedTimelock.NotGovernance.selector, [stranger, _dualGovernance]) - ); - _timelock.schedule(1); +// // EmergencyProtectedTimelock.execute() - ITimelock.Proposal memory proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Submitted); - } +// function testFuzz_anyone_can_execute_proposal(address stranger) external { +// vm.assume(stranger != _dualGovernance); +// vm.assume(stranger != address(0)); - // EmergencyProtectedTimelock.execute() +// _submitProposal(); +// assertEq(_timelock.getProposalsCount(), 1); - function testFuzz_anyone_can_execute_proposal(address stranger) external { - vm.assume(stranger != _dualGovernance); - vm.assume(stranger != address(0)); +// _wait(_timelock.getAfterSubmitDelay()); - _submitProposal(); - assertEq(_timelock.getProposalsCount(), 1); +// _scheduleProposal(1); - _wait(_config.AFTER_SUBMIT_DELAY()); +// _wait(_timelock.getAfterScheduleDelay()); - _scheduleProposal(1); +// vm.prank(stranger); +// _timelock.execute(1); - _wait(_config.AFTER_SCHEDULE_DELAY()); +// ITimelock.Proposal memory proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Executed); +// } - vm.prank(stranger); - _timelock.execute(1); +// function test_cannot_execute_proposal_if_emergency_mode_active() external { +// _submitProposal(); - ITimelock.Proposal memory proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Executed); - } +// assertEq(_timelock.getProposalsCount(), 1); - function test_cannot_execute_proposal_if_emergency_mode_active() external { - _submitProposal(); +// _wait(_timelock.getAfterSubmitDelay()); +// _scheduleProposal(1); - assertEq(_timelock.getProposalsCount(), 1); +// _wait(_timelock.getAfterScheduleDelay()); - _wait(_config.AFTER_SUBMIT_DELAY()); - _scheduleProposal(1); +// _activateEmergencyMode(); - _wait(_config.AFTER_SCHEDULE_DELAY()); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [true, false])); +// _timelock.execute(1); - _activateEmergencyMode(); +// ITimelock.Proposal memory proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Scheduled); +// } - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [true, false])); - _timelock.execute(1); +// // EmergencyProtectedTimelock.cancelAllNonExecutedProposals() - ITimelock.Proposal memory proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Scheduled); - } +// function test_governance_can_cancel_all_non_executed_proposals() external { +// _submitProposal(); +// _submitProposal(); - // EmergencyProtectedTimelock.cancelAllNonExecutedProposals() +// assertEq(_timelock.getProposalsCount(), 2); - function test_governance_can_cancel_all_non_executed_proposals() external { - _submitProposal(); - _submitProposal(); +// _wait(_timelock.getAfterSubmitDelay()); - assertEq(_timelock.getProposalsCount(), 2); +// _scheduleProposal(1); - _wait(_config.AFTER_SUBMIT_DELAY()); +// ITimelock.Proposal memory proposal1 = _timelock.getProposal(1); +// ITimelock.Proposal memory proposal2 = _timelock.getProposal(2); - _scheduleProposal(1); +// assertEq(proposal1.status, ProposalStatus.Scheduled); +// assertEq(proposal2.status, ProposalStatus.Submitted); - ITimelock.Proposal memory proposal1 = _timelock.getProposal(1); - ITimelock.Proposal memory proposal2 = _timelock.getProposal(2); +// vm.prank(_dualGovernance); +// _timelock.cancelAllNonExecutedProposals(); - assertEq(proposal1.status, ProposalStatus.Scheduled); - assertEq(proposal2.status, ProposalStatus.Submitted); +// proposal1 = _timelock.getProposal(1); +// proposal2 = _timelock.getProposal(2); - vm.prank(_dualGovernance); - _timelock.cancelAllNonExecutedProposals(); +// assertEq(_timelock.getProposalsCount(), 2); +// assertEq(proposal1.status, ProposalStatus.Cancelled); +// assertEq(proposal2.status, ProposalStatus.Cancelled); +// } - proposal1 = _timelock.getProposal(1); - proposal2 = _timelock.getProposal(2); +// function testFuzz_stranger_cannot_cancel_all_non_executed_proposals(address stranger) external { +// vm.assume(stranger != _dualGovernance); +// vm.assume(stranger != address(0)); - assertEq(_timelock.getProposalsCount(), 2); - assertEq(proposal1.status, ProposalStatus.Cancelled); - assertEq(proposal2.status, ProposalStatus.Cancelled); - } +// vm.prank(stranger); +// vm.expectRevert( +// abi.encodeWithSelector(EmergencyProtectedTimelock.NotGovernance.selector, [stranger, _dualGovernance]) +// ); +// _timelock.cancelAllNonExecutedProposals(); +// } - function testFuzz_stranger_cannot_cancel_all_non_executed_proposals(address stranger) external { - vm.assume(stranger != _dualGovernance); - vm.assume(stranger != address(0)); +// // EmergencyProtectedTimelock.transferExecutorOwnership() - vm.prank(stranger); - vm.expectRevert( - abi.encodeWithSelector(EmergencyProtectedTimelock.NotGovernance.selector, [stranger, _dualGovernance]) - ); - _timelock.cancelAllNonExecutedProposals(); - } +// function testFuzz_admin_executor_can_transfer_executor_ownership(address newOwner) external { +// vm.assume(newOwner != _adminExecutor); +// vm.assume(newOwner != address(0)); - // EmergencyProtectedTimelock.transferExecutorOwnership() +// Executor executor = new Executor(address(_timelock)); - function testFuzz_admin_executor_can_transfer_executor_ownership(address newOwner) external { - vm.assume(newOwner != _adminExecutor); - vm.assume(newOwner != address(0)); +// assertEq(executor.owner(), address(_timelock)); - Executor executor = new Executor(address(_timelock)); +// vm.prank(_adminExecutor); - assertEq(executor.owner(), address(_timelock)); +// vm.expectEmit(address(executor)); +// emit Ownable.OwnershipTransferred(address(_timelock), newOwner); - vm.prank(_adminExecutor); +// _timelock.transferExecutorOwnership(address(executor), newOwner); - vm.expectEmit(address(executor)); - emit Ownable.OwnershipTransferred(address(_timelock), newOwner); +// assertEq(executor.owner(), newOwner); +// } - _timelock.transferExecutorOwnership(address(executor), newOwner); +// function test_stranger_cannot_transfer_executor_ownership(address stranger) external { +// vm.assume(stranger != _adminExecutor); - assertEq(executor.owner(), newOwner); - } +// vm.prank(stranger); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.NotAdminExecutor.selector, stranger)); +// _timelock.transferExecutorOwnership(_adminExecutor, makeAddr("newOwner")); +// } - function test_stranger_cannot_transfer_executor_ownership(address stranger) external { - vm.assume(stranger != _adminExecutor); +// // EmergencyProtectedTimelock.setGovernance() - vm.prank(stranger); - vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); - _timelock.transferExecutorOwnership(_adminExecutor, makeAddr("newOwner")); - } +// function testFuzz_admin_executor_can_set_governance(address newGovernance) external { +// vm.assume(newGovernance != _dualGovernance); +// vm.assume(newGovernance != address(0)); - // EmergencyProtectedTimelock.setGovernance() +// vm.expectEmit(address(_timelock)); +// emit EmergencyProtectedTimelock.GovernanceSet(newGovernance); - function testFuzz_admin_executor_can_set_governance(address newGovernance) external { - vm.assume(newGovernance != _dualGovernance); - vm.assume(newGovernance != address(0)); +// vm.recordLogs(); +// vm.prank(_adminExecutor); +// _timelock.setGovernance(newGovernance); - vm.expectEmit(address(_timelock)); - emit EmergencyProtectedTimelock.GovernanceSet(newGovernance); +// assertEq(_timelock.getGovernance(), newGovernance); - vm.recordLogs(); - vm.prank(_adminExecutor); - _timelock.setGovernance(newGovernance); +// Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(_timelock.getGovernance(), newGovernance); +// assertEq(entries.length, 1); +// } - Vm.Log[] memory entries = vm.getRecordedLogs(); +// function test_cannot_set_governance_to_zero() external { +// vm.prank(_adminExecutor); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.InvalidGovernance.selector, address(0))); +// _timelock.setGovernance(address(0)); +// } - assertEq(entries.length, 1); - } +// function test_cannot_set_governance_to_the_same_address() external { +// address currentGovernance = _timelock.getGovernance(); +// vm.prank(_adminExecutor); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.InvalidGovernance.selector, _dualGovernance)); +// _timelock.setGovernance(currentGovernance); - function test_cannot_set_governance_to_zero() external { - vm.prank(_adminExecutor); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.InvalidGovernance.selector, address(0))); - _timelock.setGovernance(address(0)); - } +// assertEq(_timelock.getGovernance(), currentGovernance); +// } - function test_cannot_set_governance_to_the_same_address() external { - address currentGovernance = _timelock.getGovernance(); - vm.prank(_adminExecutor); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.InvalidGovernance.selector, _dualGovernance)); - _timelock.setGovernance(currentGovernance); +// function testFuzz_stranger_cannot_set_governance(address stranger) external { +// vm.assume(stranger != _adminExecutor); - assertEq(_timelock.getGovernance(), currentGovernance); - } +// vm.prank(stranger); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.NotAdminExecutor.selector, stranger)); +// _timelock.setGovernance(makeAddr("newGovernance")); +// } - function testFuzz_stranger_cannot_set_governance(address stranger) external { - vm.assume(stranger != _adminExecutor); +// // EmergencyProtectedTimelock.activateEmergencyMode() - vm.prank(stranger); - vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); - _timelock.setGovernance(makeAddr("newGovernance")); - } +// function test_emergency_activator_can_activate_emergency_mode() external { +// vm.prank(_emergencyActivator); +// _timelock.activateEmergencyMode(); - // EmergencyProtectedTimelock.activateEmergencyMode() +// assertEq(_isEmergencyStateActivated(), true); +// } - function test_emergency_activator_can_activate_emergency_mode() external { - vm.prank(_emergencyActivator); - _timelock.activateEmergencyMode(); +// function testFuzz_stranger_cannot_activate_emergency_mode(address stranger) external { +// vm.assume(stranger != _emergencyActivator); +// vm.assume(stranger != address(0)); - assertEq(_isEmergencyStateActivated(), true); - } +// vm.prank(stranger); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyActivator.selector, stranger)); +// _timelock.activateEmergencyMode(); - function testFuzz_stranger_cannot_activate_emergency_mode(address stranger) external { - vm.assume(stranger != _emergencyActivator); - vm.assume(stranger != address(0)); +// assertEq(_isEmergencyStateActivated(), false); +// } - vm.prank(stranger); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyActivator.selector, stranger)); - _timelock.activateEmergencyMode(); +// function test_cannot_activate_emergency_mode_if_already_active() external { +// _activateEmergencyMode(); - assertEq(_isEmergencyStateActivated(), false); - } +// assertEq(_isEmergencyStateActivated(), true); - function test_cannot_activate_emergency_mode_if_already_active() external { - _activateEmergencyMode(); +// vm.prank(_emergencyActivator); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [true, false])); +// _timelock.activateEmergencyMode(); - assertEq(_isEmergencyStateActivated(), true); +// assertEq(_isEmergencyStateActivated(), true); +// } - vm.prank(_emergencyActivator); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [true, false])); - _timelock.activateEmergencyMode(); +// // EmergencyProtectedTimelock.emergencyExecute() - assertEq(_isEmergencyStateActivated(), true); - } +// function test_emergency_executior_can_execute_proposal() external { +// _submitProposal(); - // EmergencyProtectedTimelock.emergencyExecute() +// assertEq(_timelock.getProposalsCount(), 1); - function test_emergency_executior_can_execute_proposal() external { - _submitProposal(); +// _wait(_timelock.getAfterSubmitDelay()); - assertEq(_timelock.getProposalsCount(), 1); +// _scheduleProposal(1); - _wait(_config.AFTER_SUBMIT_DELAY()); +// _wait(_timelock.getAfterScheduleDelay()); - _scheduleProposal(1); +// _activateEmergencyMode(); - _wait(_config.AFTER_SCHEDULE_DELAY()); +// assertEq(_isEmergencyStateActivated(), true); - _activateEmergencyMode(); +// vm.prank(_emergencyEnactor); +// _timelock.emergencyExecute(1); - assertEq(_isEmergencyStateActivated(), true); +// ITimelock.Proposal memory proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Executed); +// } - vm.prank(_emergencyEnactor); - _timelock.emergencyExecute(1); +// function test_cannot_emergency_execute_proposal_if_mode_not_activated() external { +// vm.startPrank(_dualGovernance); +// _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); - ITimelock.Proposal memory proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Executed); - } +// assertEq(_timelock.getProposalsCount(), 1); - function test_cannot_emergency_execute_proposal_if_mode_not_activated() external { - vm.startPrank(_dualGovernance); - _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); +// _wait(_timelock.getAfterSubmitDelay()); +// _timelock.schedule(1); - assertEq(_timelock.getProposalsCount(), 1); +// _wait(_timelock.getAfterScheduleDelay()); +// vm.stopPrank(); - _wait(_config.AFTER_SUBMIT_DELAY()); - _timelock.schedule(1); +// assertEq(_timelock.isEmergencyModeActive(), false); - _wait(_config.AFTER_SCHEDULE_DELAY()); - vm.stopPrank(); +// vm.prank(_emergencyActivator); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); +// _timelock.emergencyExecute(1); +// } - EmergencyState memory state = _timelock.getEmergencyState(); - assertEq(state.isEmergencyModeActivated, false); +// function testFuzz_stranger_cannot_emergency_execute_proposal(address stranger) external { +// vm.assume(stranger != _emergencyEnactor); +// vm.assume(stranger != address(0)); - vm.prank(_emergencyActivator); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); - _timelock.emergencyExecute(1); - } +// _submitProposal(); - function testFuzz_stranger_cannot_emergency_execute_proposal(address stranger) external { - vm.assume(stranger != _emergencyEnactor); - vm.assume(stranger != address(0)); +// assertEq(_timelock.getProposalsCount(), 1); - _submitProposal(); +// _wait(_timelock.getAfterSubmitDelay()); - assertEq(_timelock.getProposalsCount(), 1); +// _scheduleProposal(1); - _wait(_config.AFTER_SUBMIT_DELAY()); +// _wait(_timelock.getAfterScheduleDelay()); - _scheduleProposal(1); +// _activateEmergencyMode(); - _wait(_config.AFTER_SCHEDULE_DELAY()); +// assertEq(_isEmergencyStateActivated(), true); - _activateEmergencyMode(); +// vm.prank(stranger); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, stranger)); +// _timelock.emergencyExecute(1); +// } - assertEq(_isEmergencyStateActivated(), true); +// // EmergencyProtectedTimelock.deactivateEmergencyMode() - vm.prank(stranger); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, stranger)); - _timelock.emergencyExecute(1); - } +// function test_admin_executor_can_deactivate_emergency_mode_if_delay_not_passed() external { +// _submitProposal(); +// _activateEmergencyMode(); - // EmergencyProtectedTimelock.deactivateEmergencyMode() +// vm.prank(_adminExecutor); +// _timelock.deactivateEmergencyMode(); - function test_admin_executor_can_deactivate_emergency_mode_if_delay_not_passed() external { - _submitProposal(); - _activateEmergencyMode(); +// assertEq(_isEmergencyStateActivated(), false); +// } - vm.prank(_adminExecutor); - _timelock.deactivateEmergencyMode(); +// function test_after_deactivation_all_proposals_are_cancelled() external { +// _submitProposal(); - assertEq(_isEmergencyStateActivated(), false); - } +// assertEq(_timelock.getProposalsCount(), 1); - function test_after_deactivation_all_proposals_are_cancelled() external { - _submitProposal(); +// ITimelock.Proposal memory proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Submitted); - assertEq(_timelock.getProposalsCount(), 1); +// _activateEmergencyMode(); - ITimelock.Proposal memory proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Submitted); +// _deactivateEmergencyMode(); - _activateEmergencyMode(); +// proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Cancelled); +// } - _deactivateEmergencyMode(); +// function testFuzz_stranger_can_deactivate_emergency_mode_if_passed(address stranger) external { +// vm.assume(stranger != _adminExecutor); - proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Cancelled); - } +// _activateEmergencyMode(); - function testFuzz_stranger_can_deactivate_emergency_mode_if_passed(address stranger) external { - vm.assume(stranger != _adminExecutor); +// EmergencyProtection.Context memory state = _timelock.getEmergencyProtectionContext(); +// assertEq(_isEmergencyStateActivated(), true); - _activateEmergencyMode(); +// _wait(state.emergencyModeDuration.plusSeconds(1)); - EmergencyState memory state = _timelock.getEmergencyState(); - assertEq(_isEmergencyStateActivated(), true); +// vm.prank(stranger); +// _timelock.deactivateEmergencyMode(); - _wait(state.emergencyModeDuration.plusSeconds(1)); +// state = _timelock.getEmergencyState(); +// assertEq(_isEmergencyStateActivated(), false); +// } - vm.prank(stranger); - _timelock.deactivateEmergencyMode(); +// function testFuzz_cannot_deactivate_emergency_mode_if_not_activated(address stranger) external { +// vm.assume(stranger != _adminExecutor); - state = _timelock.getEmergencyState(); - assertEq(_isEmergencyStateActivated(), false); - } +// vm.prank(stranger); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); +// _timelock.deactivateEmergencyMode(); - function testFuzz_cannot_deactivate_emergency_mode_if_not_activated(address stranger) external { - vm.assume(stranger != _adminExecutor); +// vm.prank(_adminExecutor); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); +// _timelock.deactivateEmergencyMode(); +// } - vm.prank(stranger); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); - _timelock.deactivateEmergencyMode(); +// function testFuzz_stranger_cannot_deactivate_emergency_mode_if_not_passed(address stranger) external { +// vm.assume(stranger != _adminExecutor); - vm.prank(_adminExecutor); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); - _timelock.deactivateEmergencyMode(); - } +// _activateEmergencyMode(); +// assertEq(_isEmergencyStateActivated(), true); - function testFuzz_stranger_cannot_deactivate_emergency_mode_if_not_passed(address stranger) external { - vm.assume(stranger != _adminExecutor); +// vm.prank(stranger); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.NotAdminExecutor.selector, stranger)); +// _timelock.deactivateEmergencyMode(); +// } - _activateEmergencyMode(); - assertEq(_isEmergencyStateActivated(), true); +// // EmergencyProtectedTimelock.emergencyReset() - vm.prank(stranger); - vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); - _timelock.deactivateEmergencyMode(); - } +// function test_execution_committee_can_emergency_reset() external { +// _activateEmergencyMode(); +// assertEq(_isEmergencyStateActivated(), true); +// assertEq(_timelock.isEmergencyProtectionEnabled(), true); - // EmergencyProtectedTimelock.emergencyReset() +// vm.prank(_emergencyEnactor); +// _timelock.emergencyReset(); - function test_execution_committee_can_emergency_reset() external { - _activateEmergencyMode(); - assertEq(_isEmergencyStateActivated(), true); - assertEq(_timelock.isEmergencyProtectionEnabled(), true); +// EmergencyProtection.Context memory newState = _timelock.getEmergencyProtectionContext(); - vm.prank(_emergencyEnactor); - _timelock.emergencyReset(); +// assertEq(_isEmergencyStateActivated(), false); +// assertEq(_timelock.getGovernance(), _emergencyGovernance); +// assertEq(_timelock.isEmergencyProtectionEnabled(), false); - EmergencyState memory newState = _timelock.getEmergencyState(); +// assertEq(newState.activationCommittee, address(0)); +// assertEq(newState.executionCommittee, address(0)); +// assertEq(newState.protectedTill, Timestamps.ZERO); +// assertEq(newState.emergencyModeDuration, Durations.ZERO); +// assertEq(newState.emergencyModeEndsAfter, Timestamps.ZERO); +// } - assertEq(_isEmergencyStateActivated(), false); - assertEq(_timelock.getGovernance(), _emergencyGovernance); - assertEq(_timelock.isEmergencyProtectionEnabled(), false); +// function test_after_emergency_reset_all_proposals_are_cancelled() external { +// _submitProposal(); +// _activateEmergencyMode(); - assertEq(newState.activationCommittee, address(0)); - assertEq(newState.executionCommittee, address(0)); - assertEq(newState.protectedTill, Timestamps.ZERO); - assertEq(newState.emergencyModeDuration, Durations.ZERO); - assertEq(newState.emergencyModeEndsAfter, Timestamps.ZERO); - } +// ITimelock.Proposal memory proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Submitted); - function test_after_emergency_reset_all_proposals_are_cancelled() external { - _submitProposal(); - _activateEmergencyMode(); +// vm.prank(_emergencyEnactor); +// _timelock.emergencyReset(); - ITimelock.Proposal memory proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Submitted); +// proposal = _timelock.getProposal(1); +// assertEq(proposal.status, ProposalStatus.Cancelled); +// } - vm.prank(_emergencyEnactor); - _timelock.emergencyReset(); +// function testFuzz_stranger_cannot_emergency_reset_governance(address stranger) external { +// vm.assume(stranger != _emergencyEnactor); +// vm.assume(stranger != address(0)); - proposal = _timelock.getProposal(1); - assertEq(proposal.status, ProposalStatus.Cancelled); - } +// _activateEmergencyMode(); - function testFuzz_stranger_cannot_emergency_reset_governance(address stranger) external { - vm.assume(stranger != _emergencyEnactor); - vm.assume(stranger != address(0)); +// assertEq(_isEmergencyStateActivated(), true); - _activateEmergencyMode(); +// vm.prank(stranger); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, stranger)); +// _timelock.emergencyReset(); - assertEq(_isEmergencyStateActivated(), true); +// assertEq(_isEmergencyStateActivated(), true); +// } - vm.prank(stranger); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, stranger)); - _timelock.emergencyReset(); +// function test_cannot_emergency_reset_if_emergency_mode_not_activated() external { +// assertEq(_isEmergencyStateActivated(), false); - assertEq(_isEmergencyStateActivated(), true); - } +// EmergencyProtection.Context memory state = _timelock.getEmergencyProtectionContext(); - function test_cannot_emergency_reset_if_emergency_mode_not_activated() external { - assertEq(_isEmergencyStateActivated(), false); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); +// vm.prank(_emergencyEnactor); +// _timelock.emergencyReset(); - EmergencyState memory state = _timelock.getEmergencyState(); +// EmergencyProtection.Context memory newState = _timelock.getEmergencyProtectionContext(); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); - vm.prank(_emergencyEnactor); - _timelock.emergencyReset(); +// 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); +// } - EmergencyState memory newState = _timelock.getEmergencyState(); +// // EmergencyProtectedTimelock.setEmergencyProtection() - 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); - } +// function test_admin_executor_can_set_emenrgency_protection() external { +// EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); - // EmergencyProtectedTimelock.setEmergencyProtection() +// vm.prank(_adminExecutor); +// _localTimelock.setEmergencyProtection( +// _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration +// ); - function test_admin_executor_can_set_emenrgency_protection() external { - EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); +// EmergencyProtection.Context memory state = _timelock.getEmergencyProtectionContext(); - vm.prank(_adminExecutor); - _localTimelock.setEmergencyProtection( - _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration - ); +// assertEq(state.activationCommittee, _emergencyActivator); +// assertEq(state.executionCommittee, _emergencyEnactor); +// assertEq(state.protectedTill, _emergencyProtectionDuration.addTo(Timestamps.now())); +// assertEq(state.emergencyModeDuration, _emergencyModeDuration); +// assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// assertEq(state.isEmergencyModeActivated, false); +// } - EmergencyState memory state = _localTimelock.getEmergencyState(); +// function testFuzz_stranger_cannot_set_emergency_protection(address stranger) external { +// vm.assume(stranger != _adminExecutor); +// vm.assume(stranger != address(0)); - assertEq(state.activationCommittee, _emergencyActivator); - assertEq(state.executionCommittee, _emergencyEnactor); - assertEq(state.protectedTill, _emergencyProtectionDuration.addTo(Timestamps.now())); - assertEq(state.emergencyModeDuration, _emergencyModeDuration); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - assertEq(state.isEmergencyModeActivated, false); - } +// EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); - function testFuzz_stranger_cannot_set_emergency_protection(address stranger) external { - vm.assume(stranger != _adminExecutor); - vm.assume(stranger != address(0)); +// vm.prank(stranger); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtectedTimelock.NotAdminExecutor.selector, stranger)); +// _localTimelock.setEmergencyProtection( +// _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration +// ); - EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); +// EmergencyProtection.Context memory state = _timelock.getEmergencyProtectionContext(); - vm.prank(stranger); - vm.expectRevert(abi.encodeWithSelector(ConfigurationProvider.NotAdminExecutor.selector, stranger)); - _localTimelock.setEmergencyProtection( - _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration - ); +// assertEq(state.activationCommittee, address(0)); +// assertEq(state.executionCommittee, address(0)); +// assertEq(state.protectedTill, Timestamps.ZERO); +// assertEq(state.emergencyModeDuration, Durations.ZERO); +// assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// assertEq(state.isEmergencyModeActivated, false); +// } - EmergencyState memory state = _localTimelock.getEmergencyState(); +// // EmergencyProtectedTimelock.isEmergencyProtectionEnabled() - assertEq(state.activationCommittee, address(0)); - assertEq(state.executionCommittee, address(0)); - assertEq(state.protectedTill, Timestamps.ZERO); - assertEq(state.emergencyModeDuration, Durations.ZERO); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - assertEq(state.isEmergencyModeActivated, false); - } +// function test_is_emergency_protection_enabled_deactivate() external { +// EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); - // EmergencyProtectedTimelock.isEmergencyProtectionEnabled() +// assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); - function test_is_emergency_protection_enabled_deactivate() external { - EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); +// vm.prank(_adminExecutor); +// _localTimelock.setEmergencyProtection( +// _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration +// ); - assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); +// assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); - vm.prank(_adminExecutor); - _localTimelock.setEmergencyProtection( - _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration - ); +// vm.prank(_emergencyActivator); +// _localTimelock.activateEmergencyMode(); - assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); +// assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); - vm.prank(_emergencyActivator); - _localTimelock.activateEmergencyMode(); +// vm.prank(_adminExecutor); +// _localTimelock.deactivateEmergencyMode(); - assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); +// assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); +// } - vm.prank(_adminExecutor); - _localTimelock.deactivateEmergencyMode(); +// function test_is_emergency_protection_enabled_reset() external { +// EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); - assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); - } +// assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); - function test_is_emergency_protection_enabled_reset() external { - EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); +// vm.prank(_adminExecutor); +// _localTimelock.setEmergencyProtection( +// _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration +// ); - assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); +// assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); - vm.prank(_adminExecutor); - _localTimelock.setEmergencyProtection( - _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration - ); +// vm.prank(_emergencyActivator); +// _localTimelock.activateEmergencyMode(); - assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); +// assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); - vm.prank(_emergencyActivator); - _localTimelock.activateEmergencyMode(); +// vm.prank(_emergencyEnactor); +// _localTimelock.emergencyReset(); - assertEq(_localTimelock.isEmergencyProtectionEnabled(), true); +// assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); +// } - vm.prank(_emergencyEnactor); - _localTimelock.emergencyReset(); +// // EmergencyProtectedTimelock.getEmergencyState() - assertEq(_localTimelock.isEmergencyProtectionEnabled(), false); - } +// function test_get_emergency_state_deactivate() external { +// EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); - // EmergencyProtectedTimelock.getEmergencyState() +// EmergencyProtection.Context memory state = _timelock.getEmergencyProtectionContext(); - function test_get_emergency_state_deactivate() external { - EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); +// assertEq(state.isEmergencyModeActivated, false); +// assertEq(state.activationCommittee, address(0)); +// assertEq(state.executionCommittee, address(0)); +// assertEq(state.protectedTill, Timestamps.ZERO); +// assertEq(state.emergencyModeDuration, Durations.ZERO); +// assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - EmergencyState memory state = _localTimelock.getEmergencyState(); +// vm.prank(_adminExecutor); +// _localTimelock.setEmergencyProtection( +// _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration +// ); - assertEq(state.isEmergencyModeActivated, false); - assertEq(state.activationCommittee, address(0)); - assertEq(state.executionCommittee, address(0)); - assertEq(state.protectedTill, Timestamps.ZERO); - assertEq(state.emergencyModeDuration, Durations.ZERO); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// state = _localTimelock.getEmergencyState(); - vm.prank(_adminExecutor); - _localTimelock.setEmergencyProtection( - _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration - ); +// assertEq(_localTimelock.getEmergencyState().isEmergencyModeActivated, false); +// assertEq(state.activationCommittee, _emergencyActivator); +// assertEq(state.executionCommittee, _emergencyEnactor); +// assertEq(state.protectedTill, _emergencyProtectionDuration.addTo(Timestamps.now())); +// assertEq(state.emergencyModeDuration, _emergencyModeDuration); +// assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - state = _localTimelock.getEmergencyState(); +// vm.prank(_emergencyActivator); +// _localTimelock.activateEmergencyMode(); - assertEq(_localTimelock.getEmergencyState().isEmergencyModeActivated, false); - assertEq(state.activationCommittee, _emergencyActivator); - assertEq(state.executionCommittee, _emergencyEnactor); - assertEq(state.protectedTill, _emergencyProtectionDuration.addTo(Timestamps.now())); - assertEq(state.emergencyModeDuration, _emergencyModeDuration); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// state = _localTimelock.getEmergencyState(); - vm.prank(_emergencyActivator); - _localTimelock.activateEmergencyMode(); +// assertEq(_localTimelock.getEmergencyState().isEmergencyModeActivated, true); +// assertEq(state.executionCommittee, _emergencyEnactor); +// assertEq(state.activationCommittee, _emergencyActivator); +// assertEq(state.emergencyModeDuration, _emergencyModeDuration); +// assertEq(state.protectedTill, _emergencyProtectionDuration.addTo(Timestamps.now())); +// assertEq(state.emergencyModeEndsAfter, _emergencyModeDuration.addTo(Timestamps.now())); - state = _localTimelock.getEmergencyState(); +// vm.prank(_adminExecutor); +// _localTimelock.deactivateEmergencyMode(); - assertEq(_localTimelock.getEmergencyState().isEmergencyModeActivated, true); - assertEq(state.executionCommittee, _emergencyEnactor); - assertEq(state.activationCommittee, _emergencyActivator); - assertEq(state.emergencyModeDuration, _emergencyModeDuration); - assertEq(state.protectedTill, _emergencyProtectionDuration.addTo(Timestamps.now())); - assertEq(state.emergencyModeEndsAfter, _emergencyModeDuration.addTo(Timestamps.now())); +// state = _localTimelock.getEmergencyState(); - vm.prank(_adminExecutor); - _localTimelock.deactivateEmergencyMode(); +// assertEq(state.isEmergencyModeActivated, false); +// assertEq(state.activationCommittee, address(0)); +// assertEq(state.executionCommittee, address(0)); +// assertEq(state.protectedTill, Timestamps.ZERO); +// assertEq(state.emergencyModeDuration, Durations.ZERO); +// assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// } - state = _localTimelock.getEmergencyState(); +// function test_get_emergency_state_reset() external { +// EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); - assertEq(state.isEmergencyModeActivated, false); - assertEq(state.activationCommittee, address(0)); - assertEq(state.executionCommittee, address(0)); - assertEq(state.protectedTill, Timestamps.ZERO); - assertEq(state.emergencyModeDuration, Durations.ZERO); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - } +// vm.prank(_adminExecutor); +// _localTimelock.setEmergencyProtection( +// _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration +// ); - function test_get_emergency_state_reset() external { - EmergencyProtectedTimelock _localTimelock = new EmergencyProtectedTimelock(address(_config)); +// vm.prank(_emergencyActivator); +// _localTimelock.activateEmergencyMode(); - vm.prank(_adminExecutor); - _localTimelock.setEmergencyProtection( - _emergencyActivator, _emergencyEnactor, _emergencyProtectionDuration, _emergencyModeDuration - ); +// vm.prank(_emergencyEnactor); +// _localTimelock.emergencyReset(); - vm.prank(_emergencyActivator); - _localTimelock.activateEmergencyMode(); +// EmergencyProtection.Context memory state = _timelock.getEmergencyProtectionContext(); - vm.prank(_emergencyEnactor); - _localTimelock.emergencyReset(); +// assertEq(state.isEmergencyModeActivated, false); +// assertEq(state.activationCommittee, address(0)); +// assertEq(state.executionCommittee, address(0)); +// assertEq(state.protectedTill, Timestamps.ZERO); +// assertEq(state.emergencyModeDuration, Durations.ZERO); +// assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// } - EmergencyState memory state = _localTimelock.getEmergencyState(); +// // EmergencyProtectedTimelock.getGovernance() - assertEq(state.isEmergencyModeActivated, false); - assertEq(state.activationCommittee, address(0)); - assertEq(state.executionCommittee, address(0)); - assertEq(state.protectedTill, Timestamps.ZERO); - assertEq(state.emergencyModeDuration, Durations.ZERO); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - } +// function testFuzz_get_governance(address governance) external { +// vm.assume(governance != address(0) && governance != _timelock.getGovernance()); +// vm.prank(_adminExecutor); +// _timelock.setGovernance(governance); +// assertEq(_timelock.getGovernance(), governance); +// } - // EmergencyProtectedTimelock.getGovernance() +// // EmergencyProtectedTimelock.getProposal() + +// function test_get_proposal() external { +// assertEq(_timelock.getProposalsCount(), 0); + +// vm.startPrank(_dualGovernance); +// ExternalCall[] memory executorCalls = _getTargetRegularStaffCalls(address(_targetMock)); +// _timelock.submit(_adminExecutor, executorCalls); +// _timelock.submit(_adminExecutor, executorCalls); - function testFuzz_get_governance(address governance) external { - vm.assume(governance != address(0) && governance != _timelock.getGovernance()); - vm.prank(_adminExecutor); - _timelock.setGovernance(governance); - assertEq(_timelock.getGovernance(), governance); - } +// ITimelock.Proposal memory submittedProposal = _timelock.getProposal(1); - // EmergencyProtectedTimelock.getProposal() - - function test_get_proposal() external { - assertEq(_timelock.getProposalsCount(), 0); - - vm.startPrank(_dualGovernance); - ExternalCall[] memory executorCalls = _getTargetRegularStaffCalls(address(_targetMock)); - _timelock.submit(_adminExecutor, executorCalls); - _timelock.submit(_adminExecutor, executorCalls); +// Timestamp submitTimestamp = Timestamps.now(); - ITimelock.Proposal memory submittedProposal = _timelock.getProposal(1); +// assertEq(submittedProposal.id, 1); +// assertEq(submittedProposal.executor, _adminExecutor); +// assertEq(submittedProposal.submittedAt, submitTimestamp); +// assertEq(submittedProposal.scheduledAt, Timestamps.ZERO); +// assertEq(submittedProposal.status, ProposalStatus.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); - Timestamp submitTimestamp = Timestamps.now(); +// _wait(_timelock.getAfterSubmitDelay()); - assertEq(submittedProposal.id, 1); - assertEq(submittedProposal.executor, _adminExecutor); - assertEq(submittedProposal.submittedAt, submitTimestamp); - assertEq(submittedProposal.scheduledAt, Timestamps.ZERO); - assertEq(submittedProposal.status, ProposalStatus.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); +// _timelock.schedule(1); +// Timestamp scheduleTimestamp = Timestamps.now(); - _wait(_config.AFTER_SUBMIT_DELAY()); +// ITimelock.Proposal memory scheduledProposal = _timelock.getProposal(1); - _timelock.schedule(1); - Timestamp scheduleTimestamp = Timestamps.now(); +// assertEq(scheduledProposal.id, 1); +// assertEq(scheduledProposal.executor, _adminExecutor); +// assertEq(scheduledProposal.submittedAt, submitTimestamp); +// assertEq(scheduledProposal.scheduledAt, scheduleTimestamp); +// assertEq(scheduledProposal.status, ProposalStatus.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); - ITimelock.Proposal memory scheduledProposal = _timelock.getProposal(1); +// _wait(_timelock.getAfterScheduleDelay()); - assertEq(scheduledProposal.id, 1); - assertEq(scheduledProposal.executor, _adminExecutor); - assertEq(scheduledProposal.submittedAt, submitTimestamp); - assertEq(scheduledProposal.scheduledAt, scheduleTimestamp); - assertEq(scheduledProposal.status, ProposalStatus.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); +// _timelock.execute(1); - _wait(_config.AFTER_SCHEDULE_DELAY()); +// ITimelock.Proposal memory executedProposal = _timelock.getProposal(1); +// Timestamp executeTimestamp = Timestamps.now(); - _timelock.execute(1); +// assertEq(executedProposal.id, 1); +// assertEq(executedProposal.status, ProposalStatus.Executed); +// assertEq(executedProposal.executor, _adminExecutor); +// assertEq(executedProposal.submittedAt, submitTimestamp); +// assertEq(executedProposal.scheduledAt, scheduleTimestamp); +// // assertEq(executedProposal.executedAt, executeTimestamp); +// // assertEq doesn't support comparing enumerables so far +// 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); - ITimelock.Proposal memory executedProposal = _timelock.getProposal(1); - Timestamp executeTimestamp = Timestamps.now(); +// _timelock.cancelAllNonExecutedProposals(); - assertEq(executedProposal.id, 1); - assertEq(executedProposal.status, ProposalStatus.Executed); - assertEq(executedProposal.executor, _adminExecutor); - assertEq(executedProposal.submittedAt, submitTimestamp); - assertEq(executedProposal.scheduledAt, scheduleTimestamp); - // assertEq(executedProposal.executedAt, executeTimestamp); - // assertEq doesn't support comparing enumerables so far - 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); +// ITimelock.Proposal memory cancelledProposal = _timelock.getProposal(2); - _timelock.cancelAllNonExecutedProposals(); +// assertEq(cancelledProposal.id, 2); +// assertEq(cancelledProposal.status, ProposalStatus.Cancelled); +// assertEq(cancelledProposal.executor, _adminExecutor); +// assertEq(cancelledProposal.submittedAt, submitTimestamp); +// assertEq(cancelledProposal.scheduledAt, Timestamps.ZERO); +// // assertEq(cancelledProposal.executedAt, Timestamps.ZERO); +// // assertEq doesn't support comparing enumerables so far +// 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); +// } - ITimelock.Proposal memory cancelledProposal = _timelock.getProposal(2); +// function test_get_not_existing_proposal() external { +// assertEq(_timelock.getProposalsCount(), 0); - assertEq(cancelledProposal.id, 2); - assertEq(cancelledProposal.status, ProposalStatus.Cancelled); - assertEq(cancelledProposal.executor, _adminExecutor); - assertEq(cancelledProposal.submittedAt, submitTimestamp); - assertEq(cancelledProposal.scheduledAt, Timestamps.ZERO); - // assertEq(cancelledProposal.executedAt, Timestamps.ZERO); - // assertEq doesn't support comparing enumerables so far - 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); - } +// vm.expectRevert(); +// _timelock.getProposal(1); +// } - function test_get_not_existing_proposal() external { - assertEq(_timelock.getProposalsCount(), 0); +// // EmergencyProtectedTimelock.getProposalsCount() - vm.expectRevert(); - _timelock.getProposal(1); - } +// function testFuzz_get_proposals_count(uint256 count) external { +// vm.assume(count > 0); +// vm.assume(count <= type(uint8).max); +// assertEq(_timelock.getProposalsCount(), 0); - // EmergencyProtectedTimelock.getProposalsCount() +// for (uint256 i = 1; i <= count; i++) { +// _submitProposal(); +// assertEq(_timelock.getProposalsCount(), i); +// } +// } - function testFuzz_get_proposals_count(uint256 count) external { - vm.assume(count > 0); - vm.assume(count <= type(uint8).max); - assertEq(_timelock.getProposalsCount(), 0); +// // EmergencyProtectedTimelock.canExecute() - for (uint256 i = 1; i <= count; i++) { - _submitProposal(); - assertEq(_timelock.getProposalsCount(), i); - } - } +// function test_can_execute() external { +// assertEq(_timelock.canExecute(1), false); +// _submitProposal(); +// assertEq(_timelock.canExecute(1), false); - // EmergencyProtectedTimelock.canExecute() +// _wait(_timelock.getAfterSubmitDelay()); - function test_can_execute() external { - assertEq(_timelock.canExecute(1), false); - _submitProposal(); - assertEq(_timelock.canExecute(1), false); +// _scheduleProposal(1); - _wait(_config.AFTER_SUBMIT_DELAY()); +// assertEq(_timelock.canExecute(1), false); - _scheduleProposal(1); +// _wait(_timelock.getAfterScheduleDelay()); - assertEq(_timelock.canExecute(1), false); +// assertEq(_timelock.canExecute(1), true); - _wait(_config.AFTER_SCHEDULE_DELAY()); +// vm.prank(_dualGovernance); +// _timelock.cancelAllNonExecutedProposals(); - assertEq(_timelock.canExecute(1), true); +// assertEq(_timelock.canExecute(1), false); +// } - vm.prank(_dualGovernance); - _timelock.cancelAllNonExecutedProposals(); +// // EmergencyProtectedTimelock.canSchedule() - assertEq(_timelock.canExecute(1), false); - } +// function test_can_schedule() external { +// assertEq(_timelock.canExecute(1), false); +// _submitProposal(); - // EmergencyProtectedTimelock.canSchedule() +// // the scheduling is possible just after submit, because the AFTER_SUBMIT_DELAY is equal to zero +// assertEq(_timelock.canSchedule(1), true); - function test_can_schedule() external { - assertEq(_timelock.canExecute(1), false); - _submitProposal(); - assertEq(_timelock.canSchedule(1), false); +// _wait(_timelock.getAfterSubmitDelay()); - _wait(_config.AFTER_SUBMIT_DELAY()); +// assertEq(_timelock.canSchedule(1), true); - assertEq(_timelock.canSchedule(1), true); +// _scheduleProposal(1); - _scheduleProposal(1); +// assertEq(_timelock.canSchedule(1), false); - assertEq(_timelock.canSchedule(1), false); +// vm.prank(_dualGovernance); +// _timelock.cancelAllNonExecutedProposals(); - vm.prank(_dualGovernance); - _timelock.cancelAllNonExecutedProposals(); +// assertEq(_timelock.canSchedule(1), false); +// } - assertEq(_timelock.canSchedule(1), false); - } +// function test_get_proposal_submission_time() external { +// _submitProposal(); +// assertEq(_timelock.getProposal(1).submittedAt, Timestamps.now()); +// } - function test_get_proposal_submission_time() external { - _submitProposal(); - assertEq(_timelock.getProposal(1).submittedAt, Timestamps.now()); - } +// // Utils - // Utils +// function _submitProposal() internal { +// vm.prank(_dualGovernance); +// _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); +// } - function _submitProposal() internal { - vm.prank(_dualGovernance); - _timelock.submit(_adminExecutor, _getTargetRegularStaffCalls(address(_targetMock))); - } +// function _scheduleProposal(uint256 proposalId) internal { +// vm.prank(_dualGovernance); +// _timelock.schedule(proposalId); +// } - 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 _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 _activateEmergencyMode() internal { - vm.prank(_emergencyActivator); - _timelock.activateEmergencyMode(); - assertEq(_isEmergencyStateActivated(), true); - } - - function _deactivateEmergencyMode() internal { - vm.prank(_adminExecutor); - _timelock.deactivateEmergencyMode(); - assertEq(_isEmergencyStateActivated(), false); - } -} +// function _deactivateEmergencyMode() internal { +// vm.prank(_adminExecutor); +// _timelock.deactivateEmergencyMode(); +// assertEq(_isEmergencyStateActivated(), false); +// } +// } diff --git a/test/unit/TimelockedGovernance.sol b/test/unit/TimelockedGovernance.sol deleted file mode 100644 index 33de1225..00000000 --- a/test/unit/TimelockedGovernance.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.26; - -import {Vm} from "forge-std/Test.sol"; - -import {Executor} from "contracts/Executor.sol"; -import {TimelockedGovernance} from "contracts/TimelockedGovernance.sol"; -import {IConfiguration, Configuration} from "contracts/Configuration.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; - TimelockedGovernance private _timelockedGovernance; - 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(); - _timelockedGovernance = new TimelockedGovernance(address(_config), _governance, address(_timelock)); - } - - function testFuzz_constructor(address governance, address timelock) external { - TimelockedGovernance instance = new TimelockedGovernance(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); - _timelockedGovernance.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(TimelockedGovernance.NotGovernance.selector, [stranger])); - _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); - - assertEq(_timelock.getSubmittedProposals().length, 0); - } - - function test_schedule_proposal() external { - assertEq(_timelock.getScheduledProposals().length, 0); - - vm.prank(_governance); - _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); - - _timelock.setSchedule(1); - _timelockedGovernance.scheduleProposal(1); - - assertEq(_timelock.getScheduledProposals().length, 1); - } - - function test_execute_proposal() external { - assertEq(_timelock.getExecutedProposals().length, 0); - - vm.prank(_governance); - _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); - - _timelock.setSchedule(1); - _timelockedGovernance.scheduleProposal(1); - - _timelockedGovernance.executeProposal(1); - - assertEq(_timelock.getExecutedProposals().length, 1); - } - - function test_cancel_all_pending_proposals() external { - assertEq(_timelock.getLastCancelledProposalId(), 0); - - vm.startPrank(_governance); - _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); - _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); - - _timelock.setSchedule(1); - _timelockedGovernance.scheduleProposal(1); - - _timelockedGovernance.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(TimelockedGovernance.NotGovernance.selector, [stranger])); - _timelockedGovernance.cancelAllPendingProposals(); - - assertEq(_timelock.getLastCancelledProposalId(), 0); - } - - function test_can_schedule() external { - vm.prank(_governance); - _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); - - assertFalse(_timelockedGovernance.canScheduleProposal(1)); - - _timelock.setSchedule(1); - - assertTrue(_timelockedGovernance.canScheduleProposal(1)); - } -} diff --git a/test/unit/TimelockedGovernance.t.sol b/test/unit/TimelockedGovernance.t.sol new file mode 100644 index 00000000..d507fd1c --- /dev/null +++ b/test/unit/TimelockedGovernance.t.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +// import {Vm} from "forge-std/Test.sol"; + +// import {Executor} from "contracts/Executor.sol"; +// import {TimelockedGovernance} from "contracts/TimelockedGovernance.sol"; +// import {IConfigurableTimelock, IEmergencyProtectedTimelockConfig} from "contracts/interfaces/ITimelock.sol"; + +// import {UnitTest} from "test/utils/unit-test.sol"; +// import {TargetMock} from "test/utils/utils.sol"; + +// import {TimelockMock} from "./mocks/TimelockMock.sol"; +// import {IEmergencyProtectedTimelockConfigProvider} from +// "contracts/configuration/EmergencyProtectedTimelockConfigProvider.sol"; +// import {Deployment} from "test/utils/deployment.sol"; + +// contract ConfigurableTimelockMock is TimelockMock, IConfigurableTimelock { +// IEmergencyProtectedTimelockConfig public immutable CONFIG; + +// constructor(IEmergencyProtectedTimelockConfig config) { +// CONFIG = config; +// } +// } + +// contract SingleGovernanceUnitTests is UnitTest { +// TimelockMock private _timelock; +// IEmergencyProtectedTimelockConfigProvider private _config; +// TimelockedGovernance private _timelockedGovernance; + +// address private _emergencyGovernance = makeAddr("EMERGENCY_GOVERNANCE"); +// address private _governance = makeAddr("GOVERNANCE"); + +// function setUp() external { +// Executor _executor = new Executor(address(this)); +// _config = Deployment.deployEmergencyProtectedTimelockConfigProvider(); +// _timelock = new ConfigurableTimelockMock(_config); +// _timelockedGovernance = new TimelockedGovernance(_governance, address(_timelock)); +// } + +// function testFuzz_constructor(address governance, address timelock) external { +// SingleGovernance instance = new SingleGovernance(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); +// _timelockedGovernance.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(TimelockedGovernance.NotGovernance.selector, [stranger])); +// _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + +// assertEq(_timelock.getSubmittedProposals().length, 0); +// } + +// function test_schedule_proposal() external { +// assertEq(_timelock.getScheduledProposals().length, 0); + +// vm.prank(_governance); +// _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + +// _timelock.setSchedule(1); +// _timelockedGovernance.scheduleProposal(1); + +// assertEq(_timelock.getScheduledProposals().length, 1); +// } + +// function test_execute_proposal() external { +// assertEq(_timelock.getExecutedProposals().length, 0); + +// vm.prank(_governance); +// _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + +// _timelock.setSchedule(1); +// _timelockedGovernance.scheduleProposal(1); + +// _timelockedGovernance.executeProposal(1); + +// assertEq(_timelock.getExecutedProposals().length, 1); +// } + +// function test_cancel_all_pending_proposals() external { +// assertEq(_timelock.getLastCancelledProposalId(), 0); + +// vm.startPrank(_governance); +// _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); +// _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + +// _timelock.setSchedule(1); +// _timelockedGovernance.scheduleProposal(1); + +// _timelockedGovernance.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(TimelockedGovernance.NotGovernance.selector, [stranger])); +// _timelockedGovernance.cancelAllPendingProposals(); + +// assertEq(_timelock.getLastCancelledProposalId(), 0); +// } + +// function test_can_schedule() external { +// vm.prank(_governance); +// _timelockedGovernance.submitProposal(_getTargetRegularStaffCalls(address(0x1))); + +// assertFalse(_timelockedGovernance.canScheduleProposal(1)); + +// _timelock.setSchedule(1); + +// assertTrue(_timelockedGovernance.canScheduleProposal(1)); +// } +// } diff --git a/test/unit/libraries/EmergencyProtection.t.sol b/test/unit/libraries/EmergencyProtection.t.sol index a08091bb..d3e19c69 100644 --- a/test/unit/libraries/EmergencyProtection.t.sol +++ b/test/unit/libraries/EmergencyProtection.t.sol @@ -1,405 +1,411 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import {Test, Vm} from "forge-std/Test.sol"; - -import {EmergencyProtection, EmergencyState} from "contracts/libraries/EmergencyProtection.sol"; - -import {UnitTest, Duration, Durations, Timestamp, Timestamps} from "test/utils/unit-test.sol"; - -contract EmergencyProtectionUnitTests is UnitTest { - using EmergencyProtection for EmergencyProtection.State; - - EmergencyProtection.State internal _emergencyProtection; +// import {Test, Vm} from "forge-std/Test.sol"; - function testFuzz_setup_emergency_protection( - address activationCommittee, - address executionCommittee, - Duration protectionDuration, - Duration duration - ) external { - vm.assume(protectionDuration > Durations.ZERO); - vm.assume(duration > Durations.ZERO); - 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(protectionDuration.addTo(Timestamps.now())); - vm.expectEmit(); - emit EmergencyProtection.EmergencyModeDurationSet(duration); - - vm.recordLogs(); - - _emergencyProtection.setup(activationCommittee, executionCommittee, protectionDuration, duration); - - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 4); - - assertEq(_emergencyProtection.activationCommittee, activationCommittee); - assertEq(_emergencyProtection.executionCommittee, executionCommittee); - assertEq(_emergencyProtection.protectedTill, protectionDuration.addTo(Timestamps.now())); - assertEq(_emergencyProtection.emergencyModeDuration, duration); - assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); - } - - function test_setup_same_activation_committee() external { - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); - address activationCommittee = makeAddr("activationCommittee"); - - _emergencyProtection.setup(activationCommittee, address(0x2), protectionDuration, emergencyModeDuration); - - Duration newProtectionDuration = Durations.from(200 seconds); - Duration newEmergencyModeDuration = Durations.from(300 seconds); - - vm.expectEmit(); - emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x3)); - vm.expectEmit(); - emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(newProtectionDuration.addTo(Timestamps.now())); - vm.expectEmit(); - emit EmergencyProtection.EmergencyModeDurationSet(newEmergencyModeDuration); - - vm.recordLogs(); - _emergencyProtection.setup(activationCommittee, address(0x3), newProtectionDuration, newEmergencyModeDuration); - - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 3); - - assertEq(_emergencyProtection.activationCommittee, activationCommittee); - assertEq(_emergencyProtection.executionCommittee, address(0x3)); - assertEq(_emergencyProtection.protectedTill, newProtectionDuration.addTo(Timestamps.now())); - assertEq(_emergencyProtection.emergencyModeDuration, newEmergencyModeDuration); - assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); - } +// import {EmergencyProtection} from "contracts/libraries/EmergencyProtection.sol"; + +// import {UnitTest, Duration, Durations, Timestamp, Timestamps} from "test/utils/unit-test.sol"; + +// contract EmergencyProtectionUnitTests is UnitTest { +// using EmergencyProtection for EmergencyProtection.Context; + +// EmergencyProtection.Context internal _emergencyProtection; + +// function testFuzz_setup_emergency_protection( +// address activationCommittee, +// address executionCommittee, +// address emergencyGovernance, +// Duration protectionDuration, +// Duration duration +// ) external { +// vm.assume(protectionDuration > Durations.ZERO); +// vm.assume(duration > Durations.ZERO); +// // 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.EmergencyProtectionEndDateSet(protectionDuration.addTo(Timestamps.now())); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyModeDurationSet(duration); + +// vm.recordLogs(); + +// _emergencyProtection.setEmergencyGovernance(emergencyGovernance); +// _emergencyProtection.setEmergencyProtectionEndDate(emergencyProtectionEndDate, maxEmergencyProtectionDuration); + +// _emergencyProtection.setup(activationCommittee, executionCommittee, protectionDuration, duration); + +// Vm.Log[] memory entries = vm.getRecordedLogs(); +// assertEq(entries.length, 4); + +// assertEq(_emergencyProtection.activationCommittee, activationCommittee); +// assertEq(_emergencyProtection.executionCommittee, executionCommittee); +// assertEq(_emergencyProtection.protectedTill, protectionDuration.addTo(Timestamps.now())); +// assertEq(_emergencyProtection.emergencyModeDuration, duration); +// assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); +// } + +// function test_setup_same_activation_committee() external { +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); +// address activationCommittee = makeAddr("activationCommittee"); + +// _emergencyProtection.setup(activationCommittee, address(0x2), protectionDuration, emergencyModeDuration); + +// Duration newProtectionDuration = Durations.from(200 seconds); +// Duration newEmergencyModeDuration = Durations.from(300 seconds); + +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x3)); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(newProtectionDuration.addTo(Timestamps.now())); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyModeDurationSet(newEmergencyModeDuration); + +// vm.recordLogs(); +// _emergencyProtection.setup(activationCommittee, address(0x3), newProtectionDuration, newEmergencyModeDuration); + +// Vm.Log[] memory entries = vm.getRecordedLogs(); +// assertEq(entries.length, 3); + +// assertEq(_emergencyProtection.activationCommittee, activationCommittee); +// assertEq(_emergencyProtection.executionCommittee, address(0x3)); +// assertEq(_emergencyProtection.protectedTill, newProtectionDuration.addTo(Timestamps.now())); +// assertEq(_emergencyProtection.emergencyModeDuration, newEmergencyModeDuration); +// assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); +// } - function test_setup_same_execution_committee() external { - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); - address executionCommittee = makeAddr("executionCommittee"); +// function test_setup_same_execution_committee() external { +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); +// address executionCommittee = makeAddr("executionCommittee"); - _emergencyProtection.setup(address(0x1), executionCommittee, protectionDuration, emergencyModeDuration); +// _emergencyProtection.setup(address(0x1), executionCommittee, protectionDuration, emergencyModeDuration); - Duration newProtectionDuration = Durations.from(200 seconds); - Duration newEmergencyModeDuration = Durations.from(300 seconds); +// Duration newProtectionDuration = Durations.from(200 seconds); +// Duration newEmergencyModeDuration = Durations.from(300 seconds); - vm.expectEmit(); - emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x2)); - vm.expectEmit(); - emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(newProtectionDuration.addTo(Timestamps.now())); - vm.expectEmit(); - emit EmergencyProtection.EmergencyModeDurationSet(newEmergencyModeDuration); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x2)); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(newProtectionDuration.addTo(Timestamps.now())); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyModeDurationSet(newEmergencyModeDuration); - vm.recordLogs(); - _emergencyProtection.setup(address(0x2), executionCommittee, newProtectionDuration, newEmergencyModeDuration); +// vm.recordLogs(); +// _emergencyProtection.setup(address(0x2), executionCommittee, newProtectionDuration, newEmergencyModeDuration); - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 3); +// Vm.Log[] memory entries = vm.getRecordedLogs(); +// assertEq(entries.length, 3); - assertEq(_emergencyProtection.activationCommittee, address(0x2)); - assertEq(_emergencyProtection.executionCommittee, executionCommittee); - assertEq(_emergencyProtection.protectedTill, newProtectionDuration.addTo(Timestamps.now())); - assertEq(_emergencyProtection.emergencyModeDuration, newEmergencyModeDuration); - assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); - } - - function test_setup_same_protected_till() external { - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); - - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); - - Duration newProtectionDuration = protectionDuration; // the new value is the same as previous one - Duration newEmergencyModeDuration = Durations.from(200 seconds); - - vm.expectEmit(); - emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x3)); - vm.expectEmit(); - emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x4)); - vm.expectEmit(); - emit EmergencyProtection.EmergencyModeDurationSet(newEmergencyModeDuration); +// assertEq(_emergencyProtection.activationCommittee, address(0x2)); +// assertEq(_emergencyProtection.executionCommittee, executionCommittee); +// assertEq(_emergencyProtection.protectedTill, newProtectionDuration.addTo(Timestamps.now())); +// assertEq(_emergencyProtection.emergencyModeDuration, newEmergencyModeDuration); +// assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); +// } + +// function test_setup_same_protected_till() external { +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); + +// _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); + +// Duration newProtectionDuration = protectionDuration; // the new value is the same as previous one +// Duration newEmergencyModeDuration = Durations.from(200 seconds); + +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x3)); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x4)); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyModeDurationSet(newEmergencyModeDuration); - vm.recordLogs(); - _emergencyProtection.setup(address(0x3), address(0x4), newProtectionDuration, newEmergencyModeDuration); +// vm.recordLogs(); +// _emergencyProtection.setup(address(0x3), address(0x4), newProtectionDuration, newEmergencyModeDuration); - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 3); +// Vm.Log[] memory entries = vm.getRecordedLogs(); +// assertEq(entries.length, 3); - assertEq(_emergencyProtection.activationCommittee, address(0x3)); - assertEq(_emergencyProtection.executionCommittee, address(0x4)); - assertEq(_emergencyProtection.protectedTill, protectionDuration.addTo(Timestamps.now())); - assertEq(_emergencyProtection.emergencyModeDuration, newEmergencyModeDuration); - assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); - } +// assertEq(_emergencyProtection.activationCommittee, address(0x3)); +// assertEq(_emergencyProtection.executionCommittee, address(0x4)); +// assertEq(_emergencyProtection.protectedTill, protectionDuration.addTo(Timestamps.now())); +// assertEq(_emergencyProtection.emergencyModeDuration, newEmergencyModeDuration); +// assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); +// } - function test_setup_same_emergency_mode_duration() external { - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); - - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); - - Duration newProtectionDuration = Durations.from(200 seconds); - Duration newEmergencyModeDuration = emergencyModeDuration; // the new value is the same as previous one +// function test_setup_same_emergency_mode_duration() external { +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); + +// _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); + +// Duration newProtectionDuration = Durations.from(200 seconds); +// Duration newEmergencyModeDuration = emergencyModeDuration; // the new value is the same as previous one - vm.expectEmit(); - emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x3)); - vm.expectEmit(); - emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x4)); - vm.expectEmit(); - emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(newProtectionDuration.addTo(Timestamps.now())); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyActivationCommitteeSet(address(0x3)); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyExecutionCommitteeSet(address(0x4)); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyCommitteeProtectedTillSet(newProtectionDuration.addTo(Timestamps.now())); - vm.recordLogs(); - _emergencyProtection.setup(address(0x3), address(0x4), newProtectionDuration, newEmergencyModeDuration); +// vm.recordLogs(); +// _emergencyProtection.setup(address(0x3), address(0x4), newProtectionDuration, newEmergencyModeDuration); - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 3); +// Vm.Log[] memory entries = vm.getRecordedLogs(); +// assertEq(entries.length, 3); - assertEq(_emergencyProtection.activationCommittee, address(0x3)); - assertEq(_emergencyProtection.executionCommittee, address(0x4)); - assertEq(_emergencyProtection.protectedTill, newProtectionDuration.addTo(Timestamps.now())); - assertEq(_emergencyProtection.emergencyModeDuration, newEmergencyModeDuration); - assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); - } +// assertEq(_emergencyProtection.activationCommittee, address(0x3)); +// assertEq(_emergencyProtection.executionCommittee, address(0x4)); +// assertEq(_emergencyProtection.protectedTill, newProtectionDuration.addTo(Timestamps.now())); +// assertEq(_emergencyProtection.emergencyModeDuration, newEmergencyModeDuration); +// assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); +// } - function test_activate_emergency_mode() external { - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); +// function test_activate_emergency_mode() external { +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); +// _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); - vm.expectEmit(); - emit EmergencyProtection.EmergencyModeActivated(Timestamps.now()); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyModeActivated(Timestamps.now()); - vm.recordLogs(); +// vm.recordLogs(); - _emergencyProtection.activate(); +// _emergencyProtection.activate(); - Vm.Log[] memory entries = vm.getRecordedLogs(); +// Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 1); - assertEq(_emergencyProtection.emergencyModeEndsAfter, emergencyModeDuration.addTo(Timestamps.now())); - } +// assertEq(entries.length, 1); +// assertEq(_emergencyProtection.emergencyModeEndsAfter, emergencyModeDuration.addTo(Timestamps.now())); +// } - function test_cannot_activate_emergency_mode_if_protected_till_expired() external { - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); +// function test_cannot_activate_emergency_mode_if_protected_till_expired() external { +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); +// _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); - _wait(protectionDuration.plusSeconds(1)); +// _wait(protectionDuration.plusSeconds(1)); - vm.expectRevert( - abi.encodeWithSelector( - EmergencyProtection.EmergencyCommitteeExpired.selector, - Timestamps.now(), - _emergencyProtection.protectedTill - ) - ); - _emergencyProtection.activate(); - } +// vm.expectRevert( +// abi.encodeWithSelector( +// EmergencyProtection.EmergencyCommitteeExpired.selector, +// Timestamps.now(), +// _emergencyProtection.protectedTill +// ) +// ); +// _emergencyProtection.activate(); +// } - function testFuzz_deactivate_emergency_mode( - address activationCommittee, - address executionCommittee, - Duration protectionDuration, - Duration emergencyModeDuration - ) external { - vm.assume(activationCommittee != address(0)); - vm.assume(executionCommittee != address(0)); +// function testFuzz_deactivate_emergency_mode( +// address activationCommittee, +// address executionCommittee, +// Duration protectionDuration, +// Duration emergencyModeDuration +// ) external { +// vm.assume(activationCommittee != address(0)); +// vm.assume(executionCommittee != address(0)); - _emergencyProtection.setup(activationCommittee, executionCommittee, protectionDuration, emergencyModeDuration); - _emergencyProtection.activate(); +// _emergencyProtection.setup(activationCommittee, executionCommittee, protectionDuration, emergencyModeDuration); +// _emergencyProtection.activate(); - vm.expectEmit(); - emit EmergencyProtection.EmergencyModeDeactivated(Timestamps.now()); +// vm.expectEmit(); +// emit EmergencyProtection.EmergencyModeDeactivated(Timestamps.now()); - vm.recordLogs(); +// vm.recordLogs(); - _emergencyProtection.deactivate(); +// _emergencyProtection.deactivate(); - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 1); +// Vm.Log[] memory entries = vm.getRecordedLogs(); +// assertEq(entries.length, 1); - assertEq(_emergencyProtection.activationCommittee, address(0)); - assertEq(_emergencyProtection.executionCommittee, address(0)); - assertEq(_emergencyProtection.protectedTill, Timestamps.ZERO); - assertEq(_emergencyProtection.emergencyModeDuration, Durations.ZERO); - assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); - } +// assertEq(_emergencyProtection.activationCommittee, address(0)); +// assertEq(_emergencyProtection.executionCommittee, address(0)); +// assertEq(_emergencyProtection.protectedTill, Timestamps.ZERO); +// assertEq(_emergencyProtection.emergencyModeDuration, Durations.ZERO); +// assertEq(_emergencyProtection.emergencyModeEndsAfter, Timestamps.ZERO); +// } - function test_get_emergency_state() external { - EmergencyState memory state = _emergencyProtection.getEmergencyState(); +// // function test_get_emergency_state() external { +// // EmergencyState memory state = _emergencyProtection.getEmergencyState(); - assertEq(state.activationCommittee, address(0)); - assertEq(state.executionCommittee, address(0)); - assertEq(state.protectedTill, Timestamps.ZERO); - assertEq(state.emergencyModeDuration, Durations.ZERO); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - assertEq(state.isEmergencyModeActivated, false); +// // assertEq(state.activationCommittee, address(0)); +// // assertEq(state.executionCommittee, address(0)); +// // assertEq(state.protectedTill, Timestamps.ZERO); +// // assertEq(state.emergencyModeDuration, Durations.ZERO); +// // assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// // assertEq(state.isEmergencyModeActivated, false); - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(200 seconds); +// // Duration protectionDuration = Durations.from(100 seconds); +// // Duration emergencyModeDuration = Durations.from(200 seconds); - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); +// // _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); - state = _emergencyProtection.getEmergencyState(); +// // state = _emergencyProtection.getEmergencyState(); - assertEq(state.activationCommittee, address(0x1)); - assertEq(state.executionCommittee, address(0x2)); - assertEq(state.protectedTill, protectionDuration.addTo(Timestamps.now())); - assertEq(state.emergencyModeDuration, emergencyModeDuration); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - assertEq(state.isEmergencyModeActivated, false); +// // assertEq(state.activationCommittee, address(0x1)); +// // assertEq(state.executionCommittee, address(0x2)); +// // assertEq(state.protectedTill, protectionDuration.addTo(Timestamps.now())); +// // assertEq(state.emergencyModeDuration, emergencyModeDuration); +// // assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// // assertEq(state.isEmergencyModeActivated, false); - _emergencyProtection.activate(); +// // _emergencyProtection.activate(); - state = _emergencyProtection.getEmergencyState(); +// // state = _emergencyProtection.getEmergencyState(); - assertEq(state.activationCommittee, address(0x1)); - assertEq(state.executionCommittee, address(0x2)); - assertEq(state.protectedTill, protectionDuration.addTo(Timestamps.now())); - assertEq(state.emergencyModeDuration, emergencyModeDuration); - assertEq(state.emergencyModeEndsAfter, emergencyModeDuration.addTo(Timestamps.now())); - assertEq(state.isEmergencyModeActivated, true); +// // assertEq(state.activationCommittee, address(0x1)); +// // assertEq(state.executionCommittee, address(0x2)); +// // assertEq(state.protectedTill, protectionDuration.addTo(Timestamps.now())); +// // assertEq(state.emergencyModeDuration, emergencyModeDuration); +// // assertEq(state.emergencyModeEndsAfter, emergencyModeDuration.addTo(Timestamps.now())); +// // assertEq(state.isEmergencyModeActivated, true); - _emergencyProtection.deactivate(); +// // _emergencyProtection.deactivate(); - state = _emergencyProtection.getEmergencyState(); +// // state = _emergencyProtection.getEmergencyState(); - assertEq(state.activationCommittee, address(0)); - assertEq(state.executionCommittee, address(0)); - assertEq(state.protectedTill, Timestamps.ZERO); - assertEq(state.emergencyModeDuration, Durations.ZERO); - assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); - assertEq(state.isEmergencyModeActivated, false); - } +// // assertEq(state.activationCommittee, address(0)); +// // assertEq(state.executionCommittee, address(0)); +// // assertEq(state.protectedTill, Timestamps.ZERO); +// // assertEq(state.emergencyModeDuration, Durations.ZERO); +// // assertEq(state.emergencyModeEndsAfter, Timestamps.ZERO); +// // assertEq(state.isEmergencyModeActivated, false); +// // } - function test_is_emergency_mode_activated() external { - assertEq(_emergencyProtection.isEmergencyModeActivated(), false); +// function test_is_emergency_mode_activated() external { +// assertEq(_emergencyProtection.isEmergencyModeActivated(), false); - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); +// _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); - assertEq(_emergencyProtection.isEmergencyModeActivated(), false); +// assertEq(_emergencyProtection.isEmergencyModeActivated(), false); - _emergencyProtection.activate(); +// _emergencyProtection.activate(); - assertEq(_emergencyProtection.isEmergencyModeActivated(), true); +// assertEq(_emergencyProtection.isEmergencyModeActivated(), true); - _emergencyProtection.deactivate(); +// _emergencyProtection.deactivate(); - assertEq(_emergencyProtection.isEmergencyModeActivated(), false); - } +// assertEq(_emergencyProtection.isEmergencyModeActivated(), false); +// } - function test_is_emergency_mode_passed() external { - assertEq(_emergencyProtection.isEmergencyModePassed(), false); +// function test_is_emergency_mode_passed() external { +// assertEq(_emergencyProtection.isEmergencyModePassed(), false); - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(200 seconds); +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(200 seconds); - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); +// _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); - assertEq(_emergencyProtection.isEmergencyModePassed(), false); +// assertEq(_emergencyProtection.isEmergencyModePassed(), false); - _emergencyProtection.activate(); +// _emergencyProtection.activate(); - assertEq(_emergencyProtection.isEmergencyModePassed(), false); +// assertEq(_emergencyProtection.isEmergencyModePassed(), false); - _wait(emergencyModeDuration.plusSeconds(1)); +// _wait(emergencyModeDuration.plusSeconds(1)); - assertEq(_emergencyProtection.isEmergencyModePassed(), true); +// assertEq(_emergencyProtection.isEmergencyModePassed(), true); - _emergencyProtection.deactivate(); +// _emergencyProtection.deactivate(); - assertEq(_emergencyProtection.isEmergencyModePassed(), false); - } +// assertEq(_emergencyProtection.isEmergencyModePassed(), false); +// } - function test_is_emergency_protection_enabled() external { - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(200 seconds); +// function test_is_emergency_protection_enabled() external { +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(200 seconds); - assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), false); +// assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), false); - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); +// _emergencyProtection.setup( +// address(0x1), address(0x2), protectionDuration.addTo(Timestamps.now()), emergencyModeDuration +// ); - assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); +// assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); - EmergencyState memory emergencyState = _emergencyProtection.getEmergencyState(); +// EmergencyProtection.Context memory emergencyState = _emergencyProtection.getEmergencyState(); - _wait(Durations.between(emergencyState.protectedTill, Timestamps.now())); +// _wait(Durations.between(emergencyState.emergencyProtectionEndsAfter, Timestamps.now())); - // _wait(emergencyState.protectedTill.absDiff(Timestamps.now())); +// // _wait(emergencyState.protectedTill.absDiff(Timestamps.now())); - EmergencyProtection.activate(_emergencyProtection); +// EmergencyProtection.activate(_emergencyProtection); - _wait(emergencyModeDuration); +// _wait(emergencyModeDuration); - assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); +// assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); - _wait(protectionDuration); +// _wait(protectionDuration); - assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); +// assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), true); - EmergencyProtection.deactivate(_emergencyProtection); +// EmergencyProtection.deactivate(_emergencyProtection); - assertEq(_emergencyProtection.isEmergencyProtectionEnabled(), 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); +// 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(stranger); - _emergencyProtection.checkActivationCommittee(address(0)); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyActivator.selector, [stranger])); +// _emergencyProtection.checkActivationCommittee(stranger); +// _emergencyProtection.checkActivationCommittee(address(0)); - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); - _emergencyProtection.setup(committee, address(0x2), protectionDuration, emergencyModeDuration); +// _emergencyProtection.setup(committee, address(0x2), protectionDuration, emergencyModeDuration); - _emergencyProtection.checkActivationCommittee(committee); +// _emergencyProtection.checkActivationCommittee(committee); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyActivator.selector, [stranger])); - _emergencyProtection.checkActivationCommittee(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); +// 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(stranger); - _emergencyProtection.checkExecutionCommittee(address(0)); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, [stranger])); +// _emergencyProtection.checkExecutionCommittee(stranger); +// _emergencyProtection.checkExecutionCommittee(address(0)); - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); - _emergencyProtection.setup(address(0x1), committee, protectionDuration, emergencyModeDuration); +// _emergencyProtection.setup(address(0x1), committee, protectionDuration, emergencyModeDuration); - _emergencyProtection.checkExecutionCommittee(committee); +// _emergencyProtection.checkExecutionCommittee(committee); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, [stranger])); - _emergencyProtection.checkExecutionCommittee(stranger); - } +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.NotEmergencyEnactor.selector, [stranger])); +// _emergencyProtection.checkExecutionCommittee(stranger); +// } - function test_check_emergency_mode_active() external { - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); - _emergencyProtection.checkEmergencyModeStatus(true); - _emergencyProtection.checkEmergencyModeStatus(false); +// function test_check_emergency_mode_active() external { +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [false, true])); +// _emergencyProtection.checkEmergencyModeStatus(true); +// _emergencyProtection.checkEmergencyModeStatus(false); - Duration protectionDuration = Durations.from(100 seconds); - Duration emergencyModeDuration = Durations.from(100 seconds); +// Duration protectionDuration = Durations.from(100 seconds); +// Duration emergencyModeDuration = Durations.from(100 seconds); - _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); - _emergencyProtection.activate(); +// _emergencyProtection.setup(address(0x1), address(0x2), protectionDuration, emergencyModeDuration); +// _emergencyProtection.activate(); - _emergencyProtection.checkEmergencyModeStatus(true); - vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [true, false])); - } -} +// _emergencyProtection.checkEmergencyModeStatus(true); +// vm.expectRevert(abi.encodeWithSelector(EmergencyProtection.InvalidEmergencyModeStatus.selector, [true, false])); +// } +// } diff --git a/test/unit/mocks/TimelockMock.sol b/test/unit/mocks/TimelockMock.sol index b9bcbc43..68e85296 100644 --- a/test/unit/mocks/TimelockMock.sol +++ b/test/unit/mocks/TimelockMock.sol @@ -78,4 +78,12 @@ contract TimelockMock is ITimelock { { revert("Not Implemented"); } + + function getGovernance() external view returns (address) { + revert("Not Implemented"); + } + + function getAdminExecutor() external view returns (address) { + revert("Not Implemented"); + } } diff --git a/test/utils/deployment.sol b/test/utils/deployment.sol new file mode 100644 index 00000000..b951d8ce --- /dev/null +++ b/test/utils/deployment.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.26; + +uint256 constant PERCENT = 10 ** 16; + +// --- +// Types +// --- +import {Durations} from "contracts/types/Duration.sol"; + +// --- +// Interfaces +// --- +import {IStETH} from "contracts/interfaces/IStETH.sol"; +import {IWstETH} from "contracts/interfaces/IWstETH.sol"; +import {IResealManager} from "contracts/interfaces/IResealManager.sol"; +import {IWithdrawalQueue} from "contracts/interfaces/IWithdrawalQueue.sol"; + +import {ITimelock} from "contracts/interfaces/ITimelock.sol"; + +// --- +// Core Contracts +// --- + +import {Escrow} from "contracts/Escrow.sol"; +import {Executor} from "contracts/Executor.sol"; +import {ResealManager} from "contracts/ResealManager.sol"; +import {DualGovernance} from "contracts/DualGovernance.sol"; +import {TimelockedGovernance} from "contracts/TimelockedGovernance.sol"; +import {EmergencyProtectedTimelock} from "contracts/EmergencyProtectedTimelock.sol"; + +// --- +// Committees +// --- + +import {TiebreakerCore} from "contracts/committees/TiebreakerCore.sol"; +import {TiebreakerSubCommittee} from "contracts/committees/TiebreakerSubCommittee.sol"; +import {EmergencyExecutionCommittee} from "contracts/committees/EmergencyExecutionCommittee.sol"; +import {EmergencyActivationCommittee} from "contracts/committees/EmergencyActivationCommittee.sol"; + +// --- +// Configuration +// --- + +import { + DualGovernanceConfig, + IDualGovernanceConfigProvider, + ImmutableDualGovernanceConfigProvider +} from "contracts/DualGovernanceConfigProvider.sol"; + +library Deployment { + // --- + // Executor + // --- + function deployExecutor(address owner) internal returns (Executor executor) { + executor = new Executor(owner); + } + + // --- + // Emergency Protected Timelock + // --- + + function deployEmergencyProtectedTimelock( + EmergencyProtectedTimelock.SanityCheckParams memory sanityCheckParams, + Executor adminExecutor + ) internal returns (EmergencyProtectedTimelock timelock) { + timelock = new EmergencyProtectedTimelock(sanityCheckParams, address(adminExecutor)); + } + + // --- + // Dual Governance Configuration + // --- + + function deployDualGovernanceConfigProvider() + internal + returns (ImmutableDualGovernanceConfigProvider dualGovernanceConfigProvider) + { + dualGovernanceConfigProvider = new ImmutableDualGovernanceConfigProvider( + DualGovernanceConfig.Context({ + firstSealRageQuitSupport: 3 * PERCENT, + secondSealRageQuitSupport: 15 * PERCENT, + // + minAssetsLockDuration: Durations.from(5 hours), + dynamicTimelockMinDuration: Durations.from(3 days), + dynamicTimelockMaxDuration: Durations.from(30 days), + // + vetoSignallingMinActiveDuration: Durations.from(5 hours), + vetoSignallingDeactivationMaxDuration: Durations.from(5 days), + vetoCooldownDuration: Durations.from(4 days), + // + rageQuitExtensionDelay: Durations.from(7 days), + rageQuitEthWithdrawalsMinTimelock: Durations.from(60 days), + rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber: 2, + rageQuitEthWithdrawalsTimelockGrowthCoeffs: [uint256(0), 0, 0] + }) + ); + } + + // --- + // Dual Governance + // --- + + function deployDualGovernance( + ITimelock timelock, + IResealManager resealManager, + IDualGovernanceConfigProvider configProvider, + DualGovernance.SanityCheckParams memory dualGovernanceSanityCheckParams, + Escrow.SanityCheckParams memory escrowSanityCheckParams, + Escrow.ProtocolDependencies memory escrowProtocolDependencies + ) internal returns (DualGovernance dualGovernance) { + dualGovernance = new DualGovernance( + timelock, + resealManager, + configProvider, + dualGovernanceSanityCheckParams, + escrowSanityCheckParams, + escrowProtocolDependencies + ); + } + + // --- + // Reseal Manager + // --- + function deployResealManager(ITimelock timelock) internal returns (ResealManager resealManager) { + resealManager = new ResealManager(timelock); + } + + // --- + // Timelocked Governance + // --- + function deployTimelockedGovernance( + address governance, + ITimelock timelock + ) internal returns (TimelockedGovernance) { + return new TimelockedGovernance(governance, timelock); + } + + // --- + // Committees + // --- + + function deployEmergencyActivationCommittee( + address adminExecutor, + address emergencyProtectedTimelock, + address[] memory committeeMembers, + uint256 executionQuorum + ) internal returns (EmergencyActivationCommittee) { + return new EmergencyActivationCommittee({ + owner: adminExecutor, + committeeMembers: committeeMembers, + executionQuorum: executionQuorum, + emergencyProtectedTimelock: emergencyProtectedTimelock + }); + } + + function deployEmergencyExecutionCommittee( + address owner, + address[] memory committeeMembers, + uint256 executionQuorum, + address emergencyProtectedTimelock + ) internal returns (EmergencyExecutionCommittee) { + return new EmergencyExecutionCommittee(owner, committeeMembers, executionQuorum, emergencyProtectedTimelock); + } + + function deployTiebreakerCoreCommittee( + address adminExecutor, + address dualGovernance + ) internal returns (TiebreakerCore tiebreakerCore) { + tiebreakerCore = new TiebreakerCore({ + owner: adminExecutor, + dualGovernance: dualGovernance, + committeeMembers: new address[](0), + executionQuorum: 1, + timelock: 0 + }); + } + + function deployTiebreakerSubCommittee( + address owner, + address[] memory committeeMembers, + uint256 executionQuorum, + address tiebreakerCore + ) internal returns (TiebreakerSubCommittee) { + return new TiebreakerSubCommittee(owner, committeeMembers, executionQuorum, tiebreakerCore); + } + + // --- + // Dual Governance Setup Deployment + // --- + + struct DualGovernanceSetup { + Executor adminExecutor; + ResealManager resealManager; + EmergencyProtectedTimelock timelock; + TimelockedGovernance emergencyGovernance; + ImmutableDualGovernanceConfigProvider dualGovernanceConfigProvider; + DualGovernance dualGovernance; + } + + function deployDualGovernanceContracts( + IStETH stETH, + IWstETH wstETH, + IWithdrawalQueue withdrawalQueue, + address emergencyGovernance + ) internal returns (DualGovernanceSetup memory setup) { + address tmpExecutorOwner = address(this); + setup.adminExecutor = deployExecutor({owner: tmpExecutorOwner}); + + setup.timelock = deployEmergencyProtectedTimelock({ + adminExecutor: setup.adminExecutor, + sanityCheckParams: EmergencyProtectedTimelock.SanityCheckParams({ + maxAfterSubmitDelay: Durations.from(45 days), + maxAfterScheduleDelay: Durations.from(45 days), + maxEmergencyModeDuration: Durations.from(365 days), + maxEmergencyProtectionDuration: Durations.from(180 days) + }) + }); + + setup.resealManager = deployResealManager(setup.timelock); + setup.dualGovernanceConfigProvider = deployDualGovernanceConfigProvider(); + setup.emergencyGovernance = deployTimelockedGovernance(emergencyGovernance, setup.timelock); + + setup.dualGovernance = deployDualGovernance({ + timelock: setup.timelock, + resealManager: setup.resealManager, + configProvider: setup.dualGovernanceConfigProvider, + dualGovernanceSanityCheckParams: DualGovernance.SanityCheckParams({ + minTiebreakerActivationTimeout: Durations.from(180 days), + maxTiebreakerActivationTimeout: Durations.from(365 days), + maxSealableWithdrawalBlockersCount: 255 + }), + escrowSanityCheckParams: Escrow.SanityCheckParams({minWithdrawalsBatchSize: 4}), + escrowProtocolDependencies: Escrow.ProtocolDependencies({ + stETH: stETH, + wstETH: wstETH, + withdrawalQueue: withdrawalQueue + }) + }); + } +} diff --git a/test/utils/interfaces.sol b/test/utils/interfaces.sol index 56660cf4..40afc0a5 100644 --- a/test/utils/interfaces.sol +++ b/test/utils/interfaces.sol @@ -1,17 +1,5 @@ pragma solidity 0.8.26; -import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol"; -import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol"; - -struct WithdrawalRequestStatus { - uint256 amountOfStETH; - uint256 amountOfShares; - address owner; - uint256 timestamp; - bool isFinalized; - bool isClaimed; -} - interface IAragonAgent { function RUN_SCRIPT_ROLE() external pure returns (bytes32); } @@ -43,73 +31,6 @@ interface IAragonForwarder { function forward(bytes memory evmScript) external; } -interface IStEth is IERC20 { - function STAKING_CONTROL_ROLE() external view returns (bytes32); - function submit(address referral) external payable returns (uint256); - function removeStakingLimit() external; - function sharesOf(address account) external view returns (uint256); - function getSharesByPooledEth(uint256 ethAmount) external view returns (uint256); - function getPooledEthByShares(uint256 sharesAmount) external view returns (uint256); - function getStakeLimitFullInfo() - external - view - returns ( - bool isStakingPaused, - bool isStakingLimitSet, - uint256 currentStakeLimit, - uint256 maxStakeLimit, - uint256 maxStakeLimitGrowthBlocks, - uint256 prevStakeLimit, - uint256 prevStakeBlockNumber - ); - function getTotalShares() external view returns (uint256); -} - -interface IWstETH is IERC20 { - function wrap(uint256 stETHAmount) external returns (uint256); - function unwrap(uint256 wstETHAmount) external returns (uint256); -} - -interface IWithdrawalQueue is IERC721 { - function PAUSE_ROLE() external pure returns (bytes32); - function RESUME_ROLE() external pure returns (bytes32); - - /// @notice Returns amount of ether available for claim for each provided request id - /// @param _requestIds array of request ids - /// @param _hints checkpoint hints. can be found with `findCheckpointHints(_requestIds, 1, getLastCheckpointIndex())` - /// @return claimableEthValues amount of claimable ether for each request, amount is equal to 0 if request - /// is not finalized or already claimed - function getClaimableEther( - uint256[] calldata _requestIds, - uint256[] calldata _hints - ) external view returns (uint256[] memory claimableEthValues); - function getWithdrawalStatus(uint256[] calldata _requestIds) - external - view - returns (WithdrawalRequestStatus[] memory statuses); - function requestWithdrawalsWstETH(uint256[] calldata amounts, address owner) external returns (uint256[] memory); - function requestWithdrawals(uint256[] calldata amounts, address owner) external returns (uint256[] memory); - function setApprovalForAll(address _operator, bool _approved) external; - function balanceOf(address owner) external view returns (uint256); - function MAX_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256); - function getLastRequestId() external view returns (uint256); - function findCheckpointHints( - uint256[] calldata _requestIds, - uint256 _firstIndex, - uint256 _lastIndex - ) external view returns (uint256[] memory hintIds); - function getLastCheckpointIndex() external view returns (uint256); - function claimWithdrawals(uint256[] calldata requestIds, uint256[] calldata hints) external; - function getLastFinalizedRequestId() external view returns (uint256); - function finalize(uint256 _lastRequestIdToBeFinalized, uint256 _maxShareRate) external payable; - function grantRole(bytes32 role, address account) external; - function hasRole(bytes32 role, address account) external view returns (bool); - function isPaused() external view returns (bool); - function resume() external; - function pauseFor(uint256 duration) external; - function getResumeSinceTimestamp() external view returns (uint256); -} - interface IDangerousContract { function doRegularStaff(uint256 magic) external; function doRugPool() external; diff --git a/test/utils/scenario-test-blueprint.sol b/test/utils/scenario-test-blueprint.sol index fd1eb261..b1b61dd8 100644 --- a/test/utils/scenario-test-blueprint.sol +++ b/test/utils/scenario-test-blueprint.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.26; import {Test} from "forge-std/Test.sol"; import {Timestamp, Timestamps} from "contracts/types/Timestamp.sol"; -import {Durations, Duration as DurationType} from "contracts/types/Duration.sol"; +import {Durations, Duration} from "contracts/types/Duration.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import { @@ -12,7 +12,6 @@ import { } from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {EscrowState, Escrow, VetoerState, LockedAssetsTotals} from "contracts/Escrow.sol"; -import {IConfiguration, Configuration} from "contracts/Configuration.sol"; import {Executor} from "contracts/Executor.sol"; import {EmergencyActivationCommittee} from "contracts/committees/EmergencyActivationCommittee.sol"; @@ -23,10 +22,7 @@ import {TiebreakerSubCommittee} from "contracts/committees/TiebreakerSubCommitte import {ResealManager} from "contracts/ResealManager.sol"; import { - ProposalStatus, - EmergencyState, - EmergencyProtection, - EmergencyProtectedTimelock + ProposalStatus, EmergencyProtection, EmergencyProtectedTimelock } from "contracts/EmergencyProtectedTimelock.sol"; import {DualGovernance, State as DGState, DualGovernanceStateMachine} from "contracts/DualGovernance.sol"; @@ -35,19 +31,19 @@ import {TimelockedGovernance, IGovernance} from "contracts/TimelockedGovernance. import {ExternalCall} from "contracts/libraries/ExternalCalls.sol"; import {Percents, percents} from "../utils/percents.sol"; -import { - IERC20, - IStEth, - IWstETH, - IWithdrawalQueue, - WithdrawalRequestStatus, - IDangerousContract -} from "../utils/interfaces.sol"; +import {IStETH} from "contracts/interfaces/IStETH.sol"; +import {IWstETH} from "contracts/interfaces/IWstETH.sol"; + +import {IWithdrawalQueue, WithdrawalRequestStatus} from "contracts/interfaces/IWithdrawalQueue.sol"; +import {IDangerousContract} from "../utils/interfaces.sol"; import {ExternalCallHelpers} from "../utils/executor-calls.sol"; import {Utils, TargetMock, console} from "../utils/utils.sol"; +import {ImmutableDualGovernanceConfigProvider} from "contracts/DualGovernanceConfigProvider.sol"; import {DAO_VOTING, ST_ETH, WST_ETH, WITHDRAWAL_QUEUE, DAO_AGENT} from "../utils/mainnet-addresses.sol"; +import {Deployment} from "../utils/deployment.sol"; + struct Balances { uint256 stETHAmount; uint256 stETHShares; @@ -63,50 +59,63 @@ function countDigits(uint256 number) pure returns (uint256 digitsCount) { } while (number / 10 != 0); } -DurationType constant ONE_SECOND = DurationType.wrap(1); +Duration constant ONE_SECOND = Duration.wrap(1); contract ScenarioTestBlueprint is Test { address internal immutable _ADMIN_PROPOSER = DAO_VOTING; - DurationType internal immutable _EMERGENCY_MODE_DURATION = Durations.from(180 days); - DurationType internal immutable _EMERGENCY_PROTECTION_DURATION = Durations.from(90 days); + Duration internal immutable _EMERGENCY_MODE_DURATION = Durations.from(180 days); + Duration internal immutable _EMERGENCY_PROTECTION_DURATION = Durations.from(90 days); address internal immutable _EMERGENCY_ACTIVATION_COMMITTEE = makeAddr("EMERGENCY_ACTIVATION_COMMITTEE"); address internal immutable _EMERGENCY_EXECUTION_COMMITTEE = makeAddr("EMERGENCY_EXECUTION_COMMITTEE"); - DurationType internal immutable _SEALING_DURATION = Durations.from(14 days); - DurationType internal immutable _SEALING_COMMITTEE_LIFETIME = Durations.from(365 days); + Duration internal immutable _SEALING_DURATION = Durations.from(14 days); + Duration internal immutable _SEALING_COMMITTEE_LIFETIME = Durations.from(365 days); address internal immutable _SEALING_COMMITTEE = makeAddr("SEALING_COMMITTEE"); - IStEth public immutable _ST_ETH = IStEth(ST_ETH); - IWstETH public immutable _WST_ETH = IWstETH(WST_ETH); - IWithdrawalQueue public immutable _WITHDRAWAL_QUEUE = IWithdrawalQueue(WITHDRAWAL_QUEUE); - - EmergencyActivationCommittee internal _emergencyActivationCommittee; - EmergencyExecutionCommittee internal _emergencyExecutionCommittee; - TiebreakerCore internal _tiebreakerCommittee; - TiebreakerSubCommittee[] internal _tiebreakerSubCommittees; + Duration internal immutable _AFTER_SUBMIT_DELAY = Durations.from(3 days); + Duration internal immutable _AFTER_SCHEDULE_DELAY = Durations.from(2 days); - TargetMock internal _target; + // --- + // Protocol Dependencies + // --- - IConfiguration internal _config; - IConfiguration internal _configImpl; - ProxyAdmin internal _configProxyAdmin; - TransparentUpgradeableProxy internal _configProxy; + IStETH public immutable _ST_ETH = IStETH(ST_ETH); + IWstETH public immutable _WST_ETH = IWstETH(WST_ETH); + IWithdrawalQueue public immutable _WITHDRAWAL_QUEUE = IWithdrawalQueue(WITHDRAWAL_QUEUE); - Escrow internal _escrowMasterCopy; + // --- + // Core Components + // --- Executor internal _adminExecutor; - EmergencyProtectedTimelock internal _timelock; - TimelockedGovernance internal _timelockedGovernance; - DualGovernance internal _dualGovernance; ResealManager internal _resealManager; + DualGovernance internal _dualGovernance; + ImmutableDualGovernanceConfigProvider internal _dualGovernanceConfigProvider; + + TimelockedGovernance internal _timelockedGovernance; + + // --- + // Committees + // --- + + EmergencyActivationCommittee internal _emergencyActivationCommittee; + EmergencyExecutionCommittee internal _emergencyExecutionCommittee; + TiebreakerCore internal _tiebreakerCommittee; + TiebreakerSubCommittee[] internal _tiebreakerSubCommittees; address[] internal _sealableWithdrawalBlockers = [WITHDRAWAL_QUEUE]; + // --- + // Helper Contracts + // --- + TargetMock internal _target; + // --- // Helper Getters // --- + function _getVetoSignallingEscrow() internal view returns (Escrow) { return Escrow(payable(_dualGovernance.getVetoSignallingEscrow())); } @@ -139,7 +148,7 @@ contract ScenarioTestBlueprint is Test { { DualGovernanceStateMachine.Context memory stateContext = _dualGovernance.getCurrentStateContext(); isActive = stateContext.state == DGState.VetoSignallingDeactivation; - duration = _dualGovernance.CONFIG().VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().toSeconds(); + duration = _dualGovernanceConfigProvider.VETO_SIGNALLING_DEACTIVATION_MAX_DURATION().toSeconds(); enteredAt = stateContext.enteredAt.toSeconds(); } @@ -345,7 +354,7 @@ contract ScenarioTestBlueprint is Test { // --- function _assertSubmittedProposalData(uint256 proposalId, ExternalCall[] memory calls) internal { - _assertSubmittedProposalData(proposalId, _config.ADMIN_EXECUTOR(), calls); + _assertSubmittedProposalData(proposalId, _timelock.getAdminExecutor(), calls); } function _assertSubmittedProposalData(uint256 proposalId, address executor, ExternalCall[] memory calls) internal { @@ -521,64 +530,50 @@ contract ScenarioTestBlueprint is Test { // --- function _deployDualGovernanceSetup(bool isEmergencyProtectionEnabled) internal { - _deployAdminExecutor(address(this)); - _deployConfigImpl(); - _deployConfigProxy(address(this)); - _deployEscrowMasterCopy(); - _deployUngovernedTimelock(); - _deployDualGovernance(); + Deployment.DualGovernanceSetup memory dgSetup = Deployment.deployDualGovernanceContracts({ + stETH: _ST_ETH, + wstETH: _WST_ETH, + withdrawalQueue: _WITHDRAWAL_QUEUE, + emergencyGovernance: DAO_VOTING + }); + _adminExecutor = dgSetup.adminExecutor; + _timelock = dgSetup.timelock; + _resealManager = dgSetup.resealManager; + _dualGovernanceConfigProvider = dgSetup.dualGovernanceConfigProvider; + _dualGovernance = dgSetup.dualGovernance; + + _deployTiebreaker(); _deployEmergencyActivationCommittee(); _deployEmergencyExecutionCommittee(); - _deployTiebreaker(); - _finishTimelockSetup(address(_dualGovernance), isEmergencyProtectionEnabled); + _finishTimelockSetup( + address(_dualGovernance), address(dgSetup.emergencyGovernance), isEmergencyProtectionEnabled + ); } function _deployTimelockedGovernanceSetup(bool isEmergencyProtectionEnabled) internal { - _deployAdminExecutor(address(this)); - _deployConfigImpl(); - _deployConfigProxy(address(this)); - _deployEscrowMasterCopy(); - _deployUngovernedTimelock(); - _deployTimelockedGovernance(); + Deployment.DualGovernanceSetup memory dgSetup = Deployment.deployDualGovernanceContracts({ + stETH: _ST_ETH, + wstETH: _WST_ETH, + withdrawalQueue: _WITHDRAWAL_QUEUE, + emergencyGovernance: DAO_VOTING + }); + _timelockedGovernance = dgSetup.emergencyGovernance; + _adminExecutor = dgSetup.adminExecutor; + _timelock = dgSetup.timelock; + _deployEmergencyActivationCommittee(); _deployEmergencyExecutionCommittee(); - _deployTiebreaker(); - _finishTimelockSetup(address(_timelockedGovernance), isEmergencyProtectionEnabled); + _finishTimelockSetup( + address(_timelockedGovernance), address(dgSetup.emergencyGovernance), isEmergencyProtectionEnabled + ); } function _deployTarget() internal { _target = new TargetMock(); } - function _deployAdminExecutor(address owner) internal { - _adminExecutor = new Executor(owner); - } - - function _deployConfigImpl() internal { - _configImpl = new Configuration(address(_adminExecutor), address(DAO_VOTING), _sealableWithdrawalBlockers); - } - - function _deployConfigProxy(address owner) internal { - _configProxy = new TransparentUpgradeableProxy(address(_configImpl), address(owner), new bytes(0)); - _configProxyAdmin = ProxyAdmin(Utils.predictDeployedAddress(address(_configProxy), 1)); - _config = Configuration(address(_configProxy)); - } - - function _deployUngovernedTimelock() internal { - _timelock = new EmergencyProtectedTimelock(address(_config)); - } - - function _deployTimelockedGovernance() internal { - _timelockedGovernance = new TimelockedGovernance(address(_config), DAO_VOTING, address(_timelock)); - } - function _deployDualGovernance() internal { - _dualGovernance = - new DualGovernance(address(_config), address(_timelock), address(_escrowMasterCopy), _ADMIN_PROPOSER); - } - - function _deployEscrowMasterCopy() internal { - _escrowMasterCopy = new Escrow(ST_ETH, WST_ETH, WITHDRAWAL_QUEUE, address(_config)); + revert("not implemented"); } function _deployTiebreaker() internal { @@ -627,17 +622,26 @@ contract ScenarioTestBlueprint is Test { new EmergencyExecutionCommittee(address(_adminExecutor), committeeMembers, quorum, address(_timelock)); } - function _finishTimelockSetup(address governance, bool isEmergencyProtectionEnabled) internal { + function _finishTimelockSetup( + address governance, + address emergencyGovernance, + bool isEmergencyProtectionEnabled + ) internal { + _adminExecutor.execute( + address(_timelock), 0, abi.encodeCall(_timelock.setDelays, (_AFTER_SUBMIT_DELAY, _AFTER_SCHEDULE_DELAY)) + ); + if (isEmergencyProtectionEnabled) { _adminExecutor.execute( address(_timelock), 0, abi.encodeCall( - _timelock.setEmergencyProtection, + _timelock.setupEmergencyProtection, ( + emergencyGovernance, address(_emergencyActivationCommittee), address(_emergencyExecutionCommittee), - _EMERGENCY_PROTECTION_DURATION, + _EMERGENCY_PROTECTION_DURATION.addTo(Timestamps.now()), _EMERGENCY_MODE_DURATION ) ) @@ -646,8 +650,6 @@ contract ScenarioTestBlueprint is Test { assertEq(_timelock.isEmergencyProtectionEnabled(), true); } - _resealManager = new ResealManager(address(_timelock)); - vm.prank(DAO_AGENT); _WITHDRAWAL_QUEUE.grantRole( 0x139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d, address(_resealManager) @@ -661,9 +663,22 @@ contract ScenarioTestBlueprint is Test { _adminExecutor.execute( address(_dualGovernance), 0, - abi.encodeCall( - _dualGovernance.setTiebreakerProtection, (address(_tiebreakerCommittee), address(_resealManager)) - ) + abi.encodeCall(_dualGovernance.setTiebreakerCommittee, address(_tiebreakerCommittee)) + ); + _adminExecutor.execute( + address(_dualGovernance), + 0, + abi.encodeCall(_dualGovernance.setTiebreakerActivationTimeout, Durations.from(365 days)) + ); + _adminExecutor.execute( + address(_dualGovernance), + 0, + abi.encodeCall(_dualGovernance.addTiebreakerSealableWithdrawalBlocker, WITHDRAWAL_QUEUE) + ); + _adminExecutor.execute( + address(_dualGovernance), + 0, + abi.encodeCall(_dualGovernance.registerProposer, (_ADMIN_PROPOSER, address(_adminExecutor))) ); } _adminExecutor.execute(address(_timelock), 0, abi.encodeCall(_timelock.setGovernance, (governance))); @@ -679,16 +694,16 @@ contract ScenarioTestBlueprint is Test { console.log(string.concat(">>> ", text, " <<<")); } - function _wait(DurationType duration) internal { + function _wait(Duration duration) internal { vm.warp(duration.addTo(Timestamps.now()).toSeconds()); } function _waitAfterSubmitDelayPassed() internal { - _wait(_config.AFTER_SUBMIT_DELAY() + ONE_SECOND); + _wait(_timelock.getAfterSubmitDelay() + ONE_SECOND); } function _waitAfterScheduleDelayPassed() internal { - _wait(_config.AFTER_SCHEDULE_DELAY() + ONE_SECOND); + _wait(_timelock.getAfterScheduleDelay() + ONE_SECOND); } function _executeEmergencyActivate() internal { @@ -718,21 +733,21 @@ contract ScenarioTestBlueprint is Test { _emergencyExecutionCommittee.executeEmergencyReset(); } - struct Duration { + struct DurationStruct { uint256 _days; uint256 _hours; uint256 _minutes; uint256 _seconds; } - function _toDuration(uint256 timestamp) internal view returns (Duration memory duration) { + function _toDuration(uint256 timestamp) internal view returns (DurationStruct memory duration) { duration._days = timestamp / 1 days; duration._hours = (timestamp - 1 days * duration._days) / 1 hours; duration._minutes = (timestamp - 1 days * duration._days - 1 hours * duration._hours) / 1 minutes; duration._seconds = timestamp % 1 minutes; } - function _formatDuration(Duration memory duration) internal pure returns (string memory) { + function _formatDuration(DurationStruct memory duration) internal pure returns (string memory) { // format example: 1d:22h:33m:12s return string( abi.encodePacked( @@ -756,8 +771,8 @@ contract ScenarioTestBlueprint is Test { assertEq(uint256(Timestamp.unwrap(a)), uint256(Timestamp.unwrap(b))); } - function assertEq(DurationType a, DurationType b) internal { - assertEq(uint256(DurationType.unwrap(a)), uint256(DurationType.unwrap(b))); + function assertEq(Duration a, Duration b) internal { + assertEq(uint256(Duration.unwrap(a)), uint256(Duration.unwrap(b))); } function assertEq(ProposalStatus a, ProposalStatus b) internal { diff --git a/test/utils/utils.sol b/test/utils/utils.sol index a259cfd1..d2c4dcfc 100644 --- a/test/utils/utils.sol +++ b/test/utils/utils.sol @@ -12,6 +12,8 @@ import {Percents, percents} from "../utils/percents.sol"; import "./mainnet-addresses.sol"; import "./interfaces.sol"; +import {IStETH, IERC20} from "contracts/interfaces/IStETH.sol"; + // May be used as a mock contract to collect method calls contract TargetMock { struct Call { @@ -101,37 +103,37 @@ library Utils { uint256 ST_ETH_TRANSFERS_SHARE_LOSS_COMPENSATION = 8; // TODO: evaluate min enough value // bal / (totalSupply + bal) = percentage => bal = totalSupply * percentage / (1 - percentage) shares = ST_ETH_TRANSFERS_SHARE_LOSS_COMPENSATION - + IStEth(ST_ETH).getTotalShares() * totalSupplyPercentage.value + + IStETH(ST_ETH).getTotalShares() * totalSupplyPercentage.value / (100 * 10 ** totalSupplyPercentage.precision - totalSupplyPercentage.value); // to compensate StETH wei lost on submit/transfers, generate slightly larger eth amount - return depositStETH(addr, IStEth(ST_ETH).getPooledEthByShares(shares)); + return depositStETH(addr, IStETH(ST_ETH).getPooledEthByShares(shares)); } function depositStETH( address addr, uint256 amountToMint ) internal returns (uint256 sharesMinted, uint256 amountMinted) { - uint256 sharesBalanceBefore = IStEth(ST_ETH).sharesOf(addr); - uint256 amountBalanceBefore = IStEth(ST_ETH).balanceOf(addr); + uint256 sharesBalanceBefore = IStETH(ST_ETH).sharesOf(addr); + uint256 amountBalanceBefore = IStETH(ST_ETH).balanceOf(addr); // solhint-disable-next-line console.log("setting ETH balance of address %x to %d ETH", addr, amountToMint / 10 ** 18); vm.deal(addr, amountToMint); vm.prank(addr); - IStEth(ST_ETH).submit{value: amountToMint}(address(0)); + IStETH(ST_ETH).submit{value: amountToMint}(address(0)); - sharesMinted = IStEth(ST_ETH).sharesOf(addr) - sharesBalanceBefore; - amountMinted = IStEth(ST_ETH).balanceOf(addr) - amountBalanceBefore; + sharesMinted = IStETH(ST_ETH).sharesOf(addr) - sharesBalanceBefore; + amountMinted = IStETH(ST_ETH).balanceOf(addr) - amountBalanceBefore; // solhint-disable-next-line console.log("stETH balance of address %x: %d stETH", addr, (amountMinted) / 10 ** 18); } function removeLidoStakingLimit() external { - grantPermission(ST_ETH, IStEth(ST_ETH).STAKING_CONTROL_ROLE(), address(this)); - (, bool isStakingLimitSet,,,,,) = IStEth(ST_ETH).getStakeLimitFullInfo(); + grantPermission(ST_ETH, IStETH(ST_ETH).STAKING_CONTROL_ROLE(), address(this)); + (, bool isStakingLimitSet,,,,,) = IStETH(ST_ETH).getStakeLimitFullInfo(); if (isStakingLimitSet) { - IStEth(ST_ETH).removeStakingLimit(); + IStETH(ST_ETH).removeStakingLimit(); } // solhint-disable-next-line console.log("Lido staking limit removed");