From 9d0f355abf2e015896f86844dddfa8609bcd22cb Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 2 Jul 2024 15:04:40 +0300 Subject: [PATCH 1/3] refactor: new proposals getter --- contracts/DualGovernance.sol | 6 +++++- contracts/EmergencyProtectedTimelock.sol | 8 ++++++-- contracts/interfaces/ITimelock.sol | 4 +++- contracts/libraries/Proposals.sol | 10 ++++++++-- test/unit/EmergencyProtectedTimelock.t.sol | 9 +++++++++ test/unit/mocks/TimelockMock.sol | 7 +++++-- 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/contracts/DualGovernance.sol b/contracts/DualGovernance.sol index 3a6c7a5a..8223330b 100644 --- a/contracts/DualGovernance.sol +++ b/contracts/DualGovernance.sol @@ -49,8 +49,12 @@ contract DualGovernance is IGovernance, ConfigurationProvider { function scheduleProposal(uint256 proposalId) external { _dgState.activateNextState(CONFIG.getDualGovernanceConfig()); - uint256 proposalSubmissionTime = TIMELOCK.schedule(proposalId); + + uint256 proposalSubmissionTime = TIMELOCK.getProposalSubmissionTime(proposalId); _dgState.checkCanScheduleProposal(proposalSubmissionTime); + + TIMELOCK.schedule(proposalId); + emit ProposalScheduled(proposalId); } diff --git a/contracts/EmergencyProtectedTimelock.sol b/contracts/EmergencyProtectedTimelock.sol index 019feb72..0f9ca933 100644 --- a/contracts/EmergencyProtectedTimelock.sol +++ b/contracts/EmergencyProtectedTimelock.sol @@ -30,9 +30,9 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { newProposalId = _proposals.submit(executor, calls); } - function schedule(uint256 proposalId) external returns (uint256 submittedAt) { + function schedule(uint256 proposalId) external { _checkGovernance(msg.sender); - submittedAt = _proposals.schedule(proposalId, CONFIG.AFTER_SUBMIT_DELAY()); + _proposals.schedule(proposalId, CONFIG.AFTER_SUBMIT_DELAY()); } function execute(uint256 proposalId) external { @@ -122,6 +122,10 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { count = _proposals.count(); } + function getProposalSubmissionTime(uint256 proposalId) external view returns (uint256 submittedAt) { + submittedAt = _proposals.getProposalSubmissionTime(proposalId); + } + // --- // Proposals Lifecycle View Methods // --- diff --git a/contracts/interfaces/ITimelock.sol b/contracts/interfaces/ITimelock.sol index 0f080e8d..bf8cbae7 100644 --- a/contracts/interfaces/ITimelock.sol +++ b/contracts/interfaces/ITimelock.sol @@ -13,10 +13,12 @@ interface IGovernance { interface ITimelock { function submit(address executor, ExecutorCall[] calldata calls) external returns (uint256 newProposalId); - function schedule(uint256 proposalId) external returns (uint256 submittedAt); + function schedule(uint256 proposalId) external; function execute(uint256 proposalId) external; function cancelAllNonExecutedProposals() external; function canSchedule(uint256 proposalId) external view returns (bool); function canExecute(uint256 proposalId) external view returns (bool); + + function getProposalSubmissionTime(uint256 proposalId) external view returns (uint256 submittedAt); } diff --git a/contracts/libraries/Proposals.sol b/contracts/libraries/Proposals.sol index e404a93f..56a8a589 100644 --- a/contracts/libraries/Proposals.sol +++ b/contracts/libraries/Proposals.sol @@ -85,12 +85,11 @@ library Proposals { State storage self, uint256 proposalId, uint256 afterSubmitDelay - ) internal returns (uint256 submittedAt) { + ) internal { _checkProposalSubmitted(self, proposalId); _checkAfterSubmitDelayPassed(self, proposalId, afterSubmitDelay); ProposalPacked storage proposal = _packed(self, proposalId); - submittedAt = proposal.submittedAt; proposal.scheduledAt = TimeUtils.timestamp(); emit ProposalScheduled(proposalId); @@ -121,6 +120,13 @@ library Proposals { proposal.calls = packed.calls; } + function getProposalSubmissionTime(State storage self, uint256 proposalId) internal view returns (uint256 submittedAt) { + _checkProposalExists(self, proposalId); + ProposalPacked storage packed = _packed(self, proposalId); + + submittedAt = packed.submittedAt; + } + function count(State storage self) internal view returns (uint256 count_) { count_ = self.proposals.length; } diff --git a/test/unit/EmergencyProtectedTimelock.t.sol b/test/unit/EmergencyProtectedTimelock.t.sol index 7216c248..987f8fb5 100644 --- a/test/unit/EmergencyProtectedTimelock.t.sol +++ b/test/unit/EmergencyProtectedTimelock.t.sol @@ -825,6 +825,15 @@ contract EmergencyProtectedTimelockUnitTests is UnitTest { assertEq(_timelock.canSchedule(1), false); } + // EmergencyProtectedTimelock.getProposalSubmissionTime() + + function test_get_proposal_submission_time() external { + _submitProposal(); + uint256 submitTimestamp = block.timestamp; + + assertEq(_timelock.getProposalSubmissionTime(1), submitTimestamp); + } + // Utils function _submitProposal() internal { diff --git a/test/unit/mocks/TimelockMock.sol b/test/unit/mocks/TimelockMock.sol index 63f58cc9..467a02b1 100644 --- a/test/unit/mocks/TimelockMock.sol +++ b/test/unit/mocks/TimelockMock.sol @@ -22,13 +22,12 @@ contract TimelockMock is ITimelock { return newProposalId; } - function schedule(uint256 proposalId) external returns (uint256 submittedAt) { + function schedule(uint256 proposalId) external { if (canScheduleProposal[proposalId] == false) { revert(); } scheduledProposals.push(proposalId); - return 0; } function execute(uint256 proposalId) external { @@ -66,4 +65,8 @@ contract TimelockMock is ITimelock { function getLastCancelledProposalId() external view returns (uint256) { return lastCancelledProposalId; } + + function getProposalSubmissionTime(uint256 proposalId) external view returns (uint256 submittedAt) { + revert("Not Implemented"); + } } From fa1b3f12403f9040ebab67b285c5856276ac96ea Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Thu, 4 Jul 2024 13:48:10 +0300 Subject: [PATCH 2/3] feat: add natspec for timelock --- contracts/EmergencyProtectedTimelock.sol | 125 ++++++++++++++++++++--- 1 file changed, 109 insertions(+), 16 deletions(-) diff --git a/contracts/EmergencyProtectedTimelock.sol b/contracts/EmergencyProtectedTimelock.sol index 0f9ca933..d68a3e96 100644 --- a/contracts/EmergencyProtectedTimelock.sol +++ b/contracts/EmergencyProtectedTimelock.sol @@ -9,6 +9,14 @@ import {EmergencyProtection, EmergencyState} from "./libraries/EmergencyProtecti import {ConfigurationProvider} from "./ConfigurationProvider.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 { using Proposals for Proposals.State; using EmergencyProtection for EmergencyProtection.State; @@ -25,52 +33,93 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { constructor(address config) ConfigurationProvider(config) {} + /** + * @dev Submits a new proposal to execute a series of calls through an executor. + * Only the governance contract can call this function. + * @param executor The address of the executor contract that will execute the calls. + * @param calls An array of `ExecutorCall` structs representing the calls to be executed. + * @return newProposalId The ID of the newly created proposal. + */ function submit(address executor, ExecutorCall[] calldata calls) external returns (uint256 newProposalId) { _checkGovernance(msg.sender); newProposalId = _proposals.submit(executor, calls); } + /** + * @dev Schedules a proposal for execution after a specified delay. + * 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()); } + /** + * @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.checkEmergencyModeActive(false); _proposals.execute(proposalId, CONFIG.AFTER_SCHEDULE_DELAY()); } + /** + * @dev Cancels all non-executed proposals. + * Only the governance contract can call this function. + */ function cancelAllNonExecutedProposals() external { _checkGovernance(msg.sender); _proposals.cancelAll(); } + /** + * @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. + * @param owner The address of the new owner. + */ function transferExecutorOwnership(address executor, address owner) external { _checkAdminExecutor(msg.sender); 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 - // --- - + /** + * @dev Activates the emergency mode. + * Only the activation committee can call this function. + */ function activateEmergencyMode() external { _emergencyProtection.checkActivationCommittee(msg.sender); _emergencyProtection.checkEmergencyModeActive(false); _emergencyProtection.activate(); } + /** + * @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.checkEmergencyModeActive(true); _emergencyProtection.checkExecutionCommittee(msg.sender); _proposals.execute(proposalId, /* afterScheduleDelay */ 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.checkEmergencyModeActive(true); if (!_emergencyProtection.isEmergencyModePassed()) { @@ -80,6 +129,10 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { _proposals.cancelAll(); } + /** + * @dev Resets the system after entering the emergency mode. + * Only the execution committee can call this function. + */ function emergencyReset() external { _emergencyProtection.checkEmergencyModeActive(true); _emergencyProtection.checkExecutionCommittee(msg.sender); @@ -88,6 +141,14 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { _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, @@ -98,51 +159,79 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { _emergencyProtection.setup(activator, enactor, protectionDuration, emergencyModeDuration); } + /** + * @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) { 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(); } - // --- - // Timelock View Methods - // --- - + /** + * @dev Retrieves the address of the current governance contract. + * @return The address of the current governance contract. + */ function getGovernance() external view returns (address) { return _governance; } + /** + * @dev Retrieves the details of a proposal. + * @param proposalId The ID of the proposal. + * @return proposal The Proposal struct containing the details of the proposal. + */ function getProposal(uint256 proposalId) external view returns (Proposal memory proposal) { proposal = _proposals.get(proposalId); } + /** + * @dev Retrieves the total number of proposals. + * @return count The total number of proposals. + */ function getProposalsCount() external view returns (uint256 count) { count = _proposals.count(); } + /** + * @dev Retrieves the submission time of a proposal. + * @param proposalId The ID of the proposal. + * @return submittedAt The submission time of the proposal. + */ function getProposalSubmissionTime(uint256 proposalId) external view returns (uint256 submittedAt) { submittedAt = _proposals.getProposalSubmissionTime(proposalId); } - // --- - // Proposals Lifecycle View Methods - // --- - + /** + * @dev Checks if a proposal can be executed. + * @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()); } + /** + * @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)) { @@ -152,6 +241,10 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { emit GovernanceSet(newGovernance); } + /** + * @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); From cca2785b37ab7d1053a1cbefed85624a31f4213b Mon Sep 17 00:00:00 2001 From: Bogdan Kovtun Date: Mon, 8 Jul 2024 03:13:10 +0400 Subject: [PATCH 3/3] Update the NatSpec comments format in the EmergencyProtectedTimelock --- contracts/EmergencyProtectedTimelock.sol | 188 ++++++++++------------- 1 file changed, 80 insertions(+), 108 deletions(-) diff --git a/contracts/EmergencyProtectedTimelock.sol b/contracts/EmergencyProtectedTimelock.sol index b4be7020..e8a84dc3 100644 --- a/contracts/EmergencyProtectedTimelock.sol +++ b/contracts/EmergencyProtectedTimelock.sol @@ -12,13 +12,11 @@ import {EmergencyProtection, EmergencyState} from "./libraries/EmergencyProtecti import {ConfigurationProvider} from "./ConfigurationProvider.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. - */ +/// @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 { using Proposals for Proposals.State; using EmergencyProtection for EmergencyProtection.State; @@ -35,93 +33,83 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { constructor(address config) ConfigurationProvider(config) {} - /** - * @dev Submits a new proposal to execute a series of calls through an executor. - * Only the governance contract can call this function. - * @param executor The address of the executor contract that will execute the calls. - * @param calls An array of `ExecutorCall` structs representing the calls to be executed. - * @return newProposalId The ID of the newly created proposal. - */ + // --- + // Main Timelock Functionality + // --- + + /// @dev Submits a new proposal to execute a series of calls through an executor. + /// Only the governance contract can call this function. + /// @param executor The address of the executor contract that will execute the calls. + /// @param calls An array of `ExecutorCall` structs representing the calls to be executed. + /// @return newProposalId The ID of the newly created proposal. function submit(address executor, ExecutorCall[] calldata calls) external returns (uint256 newProposalId) { _checkGovernance(msg.sender); newProposalId = _proposals.submit(executor, calls); } - /** - * @dev Schedules a proposal for execution after a specified delay. - * Only the governance contract can call this function. - * @param proposalId The ID of the proposal to be scheduled. - */ + /// @dev Schedules a proposal for execution after a specified delay. + /// 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()); } - /** - * @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. - */ + /// @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.checkEmergencyModeActive(false); _proposals.execute(proposalId, CONFIG.AFTER_SCHEDULE_DELAY()); } - /** - * @dev Cancels all non-executed proposals. - * Only the governance contract can call this function. - */ + /// @dev Cancels all non-executed proposals. + /// Only the governance contract can call this function. function cancelAllNonExecutedProposals() external { _checkGovernance(msg.sender); _proposals.cancelAll(); } - /** - * @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. - * @param owner The address of the new owner. - */ + /// @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. + /// @param owner The address of the new owner. function transferExecutorOwnership(address executor, address owner) external { _checkAdminExecutor(msg.sender); 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. - */ + /// @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); } - /** - * @dev Activates the emergency mode. - * Only the activation committee can call this function. - */ + // --- + // Emergency Protection Functionality + // --- + + /// @dev Activates the emergency mode. + /// Only the activation committee can call this function. function activateEmergencyMode() external { _emergencyProtection.checkActivationCommittee(msg.sender); _emergencyProtection.checkEmergencyModeActive(false); _emergencyProtection.activate(); } - /** - * @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. - */ + /// @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.checkEmergencyModeActive(true); _emergencyProtection.checkExecutionCommittee(msg.sender); _proposals.execute(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. - */ + /// @dev Deactivates the emergency mode. + /// If the emergency mode has not passed, only the admin executor can call this function. function deactivateEmergencyMode() external { _emergencyProtection.checkEmergencyModeActive(true); if (!_emergencyProtection.isEmergencyModePassed()) { @@ -131,10 +119,8 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { _proposals.cancelAll(); } - /** - * @dev Resets the system after entering the emergency mode. - * Only the execution committee can call this function. - */ + /// @dev Resets the system after entering the emergency mode. + /// Only the execution committee can call this function. function emergencyReset() external { _emergencyProtection.checkEmergencyModeActive(true); _emergencyProtection.checkExecutionCommittee(msg.sender); @@ -143,14 +129,12 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { _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. - */ + /// @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, @@ -161,79 +145,69 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { _emergencyProtection.setup(activator, enactor, protectionDuration, emergencyModeDuration); } - /** - * @dev Checks if the emergency protection functionality is enabled. - * @return A boolean indicating if the emergency protection is enabled. - */ + /// @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) { return _emergencyProtection.isEmergencyProtectionEnabled(); } - /** - * @dev Retrieves the current emergency state. - * @return res The EmergencyState struct containing the current emergency state. - */ + /// @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(); } - /** - * @dev Retrieves the address of the current governance contract. - * @return The address of the current governance contract. - */ + // --- + // Timelock View Methods + // --- + + /// @dev Retrieves the address of the current governance contract. + /// @return The address of the current governance contract. function getGovernance() external view returns (address) { return _governance; } - /** - * @dev Retrieves the details of a proposal. - * @param proposalId The ID of the proposal. - * @return proposal The Proposal struct containing the details of the proposal. - */ + /// @dev Retrieves the details of a proposal. + /// @param proposalId The ID of the proposal. + /// @return proposal The Proposal struct containing the details of the proposal. function getProposal(uint256 proposalId) external view returns (Proposal memory proposal) { proposal = _proposals.get(proposalId); } - /** - * @dev Retrieves the total number of proposals. - * @return count The total number of proposals. - */ + /// @dev Retrieves the total number of proposals. + /// @return count The total number of proposals. function getProposalsCount() external view returns (uint256 count) { count = _proposals.count(); } - /** - * @dev Retrieves the submission time of a proposal. - * @param proposalId The ID of the proposal. - * @return submittedAt The submission time of the proposal. - */ + /// @dev Retrieves the submission time of a proposal. + /// @param proposalId The ID of the proposal. + /// @return submittedAt The submission time of the proposal. function getProposalSubmissionTime(uint256 proposalId) external view returns (Timestamp submittedAt) { submittedAt = _proposals.getProposalSubmissionTime(proposalId); } - /** - * @dev Checks if a proposal can be executed. - * @param proposalId The ID of the proposal. - * @return A boolean indicating if the proposal can be executed. - */ + /// @dev Checks if a proposal can be executed. + /// @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()); } - /** - * @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. - */ + /// @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()); } - /** - * @dev Internal function to set the governance contract address. - * @param newGovernance The address of the new governance contract. - */ + // --- + // 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)) { @@ -243,10 +217,8 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider { emit GovernanceSet(newGovernance); } - /** - * @dev Internal function to check if the caller is the governance contract. - * @param account The address to check. - */ + /// @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);