Skip to content

Commit

Permalink
Merge pull request lidofinance#54 from lidofinance/fix/docs-inaccuracy
Browse files Browse the repository at this point in the history
Cumulative Update: The specification and mechanism inaccuracies
  • Loading branch information
Psirex authored Jul 8, 2024
2 parents fd84dc5 + 250b9af commit 3bb434b
Show file tree
Hide file tree
Showing 36 changed files with 1,970 additions and 1,264 deletions.
53 changes: 29 additions & 24 deletions contracts/Configuration.sol
Original file line number Diff line number Diff line change
@@ -1,46 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Durations, Duration} from "./types/Duration.sol";
import {IConfiguration, DualGovernanceConfig} 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;

uint256 public immutable DYNAMIC_TIMELOCK_MIN_DURATION = 3 days;
uint256 public immutable DYNAMIC_TIMELOCK_MAX_DURATION = 30 days;
Duration public immutable DYNAMIC_TIMELOCK_MIN_DURATION = Durations.from(3 days);
Duration public immutable DYNAMIC_TIMELOCK_MAX_DURATION = Durations.from(30 days);

uint256 public immutable VETO_SIGNALLING_MIN_ACTIVE_DURATION = 5 hours;
uint256 public immutable VETO_SIGNALLING_DEACTIVATION_MAX_DURATION = 5 days;
uint256 public immutable RAGE_QUIT_ACCUMULATION_MAX_DURATION = 3 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);

uint256 public immutable VETO_COOLDOWN_DURATION = 4 days;
Duration public immutable VETO_COOLDOWN_DURATION = Durations.from(4 days);

uint256 public immutable RAGE_QUIT_EXTENSION_DELAY = 7 days;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_MIN_TIMELOCK = 60 days;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_START_SEQ_NUMBER = 2;
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_CLAIM_TIMELOCK_GROWTH_COEFF_A = 0;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_B = 0;
uint256 public immutable RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_C = 0;
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;

uint256 public immutable AFTER_SUBMIT_DELAY = 3 days;
uint256 public immutable AFTER_SCHEDULE_DELAY = 2 days;
Duration public immutable AFTER_SUBMIT_DELAY = Durations.from(3 days);
Duration public immutable AFTER_SCHEDULE_DELAY = Durations.from(2 days);

uint256 public immutable SIGNALLING_ESCROW_MIN_LOCK_TIME = 5 hours;
Duration public immutable SIGNALLING_ESCROW_MIN_LOCK_TIME = Durations.from(5 hours);

uint256 public immutable TIE_BREAK_ACTIVATION_TIMEOUT = 365 days;
Duration public immutable TIE_BREAK_ACTIVATION_TIMEOUT = Durations.from(365 days);

// Sealables Array Representation
uint256 private immutable MAX_SELABLES_COUNT = 5;
Expand Down Expand Up @@ -84,8 +88,8 @@ contract Configuration is IConfiguration {
returns (
uint256 firstSealRageQuitSupport,
uint256 secondSealRageQuitSupport,
uint256 dynamicTimelockMinDuration,
uint256 dynamicTimelockMaxDuration
Duration dynamicTimelockMinDuration,
Duration dynamicTimelockMaxDuration
)
{
firstSealRageQuitSupport = FIRST_SEAL_RAGE_QUIT_SUPPORT;
Expand All @@ -103,12 +107,13 @@ contract Configuration is IConfiguration {
config.vetoSignallingDeactivationMaxDuration = VETO_SIGNALLING_DEACTIVATION_MAX_DURATION;
config.vetoCooldownDuration = VETO_COOLDOWN_DURATION;
config.rageQuitExtensionDelay = RAGE_QUIT_EXTENSION_DELAY;
config.rageQuitEthClaimMinTimelock = RAGE_QUIT_ETH_CLAIM_MIN_TIMELOCK;
config.rageQuitEthClaimTimelockGrowthStartSeqNumber = RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_START_SEQ_NUMBER;
config.rageQuitEthClaimTimelockGrowthCoeffs = [
RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_A,
RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_B,
RAGE_QUIT_ETH_CLAIM_TIMELOCK_GROWTH_COEFF_C
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
];
}
}
20 changes: 13 additions & 7 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Duration} from "./types/Duration.sol";
import {Timestamp} from "./types/Timestamp.sol";
import {ITimelock, IGovernance} from "./interfaces/ITimelock.sol";
import {ISealable} from "./interfaces/ISealable.sol";
import {IResealManager} from "./interfaces/IResealManager.sol";
Expand Down Expand Up @@ -52,8 +54,12 @@ contract DualGovernance is IGovernance, ConfigurationProvider {

function scheduleProposal(uint256 proposalId) external {
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
uint256 proposalSubmissionTime = TIMELOCK.schedule(proposalId);

Timestamp proposalSubmissionTime = TIMELOCK.getProposalSubmissionTime(proposalId);
_dgState.checkCanScheduleProposal(proposalSubmissionTime);

TIMELOCK.schedule(proposalId);

emit ProposalScheduled(proposalId);
}

Expand All @@ -62,11 +68,11 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
TIMELOCK.cancelAllNonExecutedProposals();
}

function vetoSignallingEscrow() external view returns (address) {
function getVetoSignallingEscrow() external view returns (address) {
return address(_dgState.signallingEscrow);
}

function rageQuitEscrow() external view returns (address) {
function getRageQuitEscrow() external view returns (address) {
return address(_dgState.rageQuitEscrow);
}

Expand All @@ -82,27 +88,27 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
}

function currentState() external view returns (State) {
function getCurrentState() external view returns (State) {
return _dgState.currentState();
}

function getVetoSignallingState()
external
view
returns (bool isActive, uint256 duration, uint256 activatedAt, uint256 enteredAt)
returns (bool isActive, Duration duration, Timestamp activatedAt, Timestamp enteredAt)
{
(isActive, duration, activatedAt, enteredAt) = _dgState.getVetoSignallingState(CONFIG.getDualGovernanceConfig());
}

function getVetoSignallingDeactivationState()
external
view
returns (bool isActive, uint256 duration, uint256 enteredAt)
returns (bool isActive, Duration duration, Timestamp enteredAt)
{
(isActive, duration, enteredAt) = _dgState.getVetoSignallingDeactivationState(CONFIG.getDualGovernanceConfig());
}

function getVetoSignallingDuration() external view returns (uint256) {
function getVetoSignallingDuration() external view returns (Duration) {
return _dgState.getVetoSignallingDuration(CONFIG.getDualGovernanceConfig());
}

Expand Down
87 changes: 79 additions & 8 deletions contracts/EmergencyProtectedTimelock.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Duration} from "./types/Duration.sol";
import {Timestamp} from "./types/Timestamp.sol";

import {IOwnable} from "./interfaces/IOwnable.sol";
import {ITimelock} from "./interfaces/ITimelock.sol";

Expand All @@ -9,6 +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.
contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider {
using Proposals for Proposals.State;
using EmergencyProtection for EmergencyProtection.State;
Expand All @@ -25,31 +33,55 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider {

constructor(address config) ConfigurationProvider(config) {}

// ---
// 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);
}

function schedule(uint256 proposalId) external returns (uint256 submittedAt) {
/// @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);
submittedAt = _proposals.schedule(proposalId, CONFIG.AFTER_SUBMIT_DELAY());
_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);
Expand All @@ -59,18 +91,25 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider {
// 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);
_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.
function deactivateEmergencyMode() external {
_emergencyProtection.checkEmergencyModeActive(true);
if (!_emergencyProtection.isEmergencyModePassed()) {
Expand All @@ -80,6 +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.
function emergencyReset() external {
_emergencyProtection.checkEmergencyModeActive(true);
_emergencyProtection.checkExecutionCommittee(msg.sender);
Expand All @@ -88,20 +129,30 @@ 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,
uint256 protectionDuration,
uint256 emergencyModeDuration
Duration protectionDuration,
Duration emergencyModeDuration
) external {
_checkAdminExecutor(msg.sender);
_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();
}
Expand All @@ -110,27 +161,43 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider {
// 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();
}

// ---
// Proposals Lifecycle View Methods
// ---
/// @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.
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());
}
Expand All @@ -139,6 +206,8 @@ contract EmergencyProtectedTimelock is ITimelock, ConfigurationProvider {
// 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)) {
Expand All @@ -148,6 +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.
function _checkGovernance(address account) internal view {
if (_governance != account) {
revert NotGovernance(account, _governance);
Expand Down
Loading

0 comments on commit 3bb434b

Please sign in to comment.