Skip to content

Commit

Permalink
Merge pull request #68 from lidofinance/refactor/dual-governance-state
Browse files Browse the repository at this point in the history
DualGovernanceState library refactor
  • Loading branch information
Psirex authored Aug 1, 2024
2 parents 181991c + 1f5e088 commit d0a536f
Show file tree
Hide file tree
Showing 11 changed files with 477 additions and 519 deletions.
27 changes: 8 additions & 19 deletions contracts/Configuration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity 0.8.26;

import {Durations, Duration} from "./types/Duration.sol";
import {IConfiguration, DualGovernanceConfig} from "./interfaces/IConfiguration.sol";
import {IConfiguration, DualGovernanceStateMachine, TiebreakConfig} from "./interfaces/IConfiguration.sol";

uint256 constant PERCENT = 10 ** 16;

Expand Down Expand Up @@ -73,7 +73,7 @@ contract Configuration is IConfiguration {
if (SEALABLES_COUNT > 4) SEALABLE_4 = sealableWithdrawalBlockers_[4];
}

function sealableWithdrawalBlockers() external view returns (address[] memory sealableWithdrawalBlockers_) {
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;
Expand All @@ -82,23 +82,7 @@ contract Configuration is IConfiguration {
if (SEALABLES_COUNT > 4) sealableWithdrawalBlockers_[4] = SEALABLE_4;
}

function getSignallingThresholdData()
external
view
returns (
uint256 firstSealRageQuitSupport,
uint256 secondSealRageQuitSupport,
Duration dynamicTimelockMinDuration,
Duration dynamicTimelockMaxDuration
)
{
firstSealRageQuitSupport = FIRST_SEAL_RAGE_QUIT_SUPPORT;
secondSealRageQuitSupport = SECOND_SEAL_RAGE_QUIT_SUPPORT;
dynamicTimelockMinDuration = DYNAMIC_TIMELOCK_MIN_DURATION;
dynamicTimelockMaxDuration = DYNAMIC_TIMELOCK_MAX_DURATION;
}

function getDualGovernanceConfig() external view returns (DualGovernanceConfig memory config) {
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;
Expand All @@ -116,4 +100,9 @@ contract Configuration is IConfiguration {
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();
}
}
96 changes: 51 additions & 45 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {Duration} from "./types/Duration.sol";
import {Timestamp} from "./types/Timestamp.sol";
import {ITimelock, IGovernance} from "./interfaces/ITimelock.sol";
import {IResealManager} from "./interfaces/IResealManager.sol";

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

import {ConfigurationProvider} from "./ConfigurationProvider.sol";
import {Proposers, Proposer} from "./libraries/Proposers.sol";
import {ExternalCall} from "./libraries/ExternalCalls.sol";
import {EmergencyProtection} from "./libraries/EmergencyProtection.sol";
import {State, DualGovernanceState} from "./libraries/DualGovernanceState.sol";
import {State, DualGovernanceStateMachine} from "./libraries/DualGovernanceStateMachine.sol";
import {TiebreakerProtection} from "./libraries/TiebreakerProtection.sol";

contract DualGovernance is IGovernance, ConfigurationProvider {
using Proposers for Proposers.State;
using DualGovernanceState for DualGovernanceState.Store;
using TiebreakerProtection for TiebreakerProtection.Tiebreaker;
using DualGovernanceStateMachine for DualGovernanceStateMachine.Context;

event ProposalScheduled(uint256 proposalId);

error NotDeadlock();
error NotResealCommitttee(address account);
error ProposalSubmissionBlocked();
error ProposalSchedulingBlocked(uint256 proposalId);
error ResealIsNotAllowedInNormalState();

ITimelock public immutable TIMELOCK;

TiebreakerProtection.Tiebreaker internal _tiebreaker;
Proposers.State internal _proposers;
DualGovernanceState.Store internal _dgState;
DualGovernanceStateMachine.Context internal _stateMachine;
EmergencyProtection.State internal _emergencyProtection;
address internal _resealCommittee;
IResealManager internal _resealManager;
TiebreakerProtection.Tiebreaker internal _tiebreaker;

constructor(
address config,
Expand All @@ -39,78 +42,75 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
) ConfigurationProvider(config) {
TIMELOCK = ITimelock(timelock);

_dgState.initialize(escrowMasterCopy);
_stateMachine.initialize(escrowMasterCopy);
_proposers.register(adminProposer, CONFIG.ADMIN_EXECUTOR());
}

// ---
// Proposals Flow
// ---

function submitProposal(ExternalCall[] calldata calls) external returns (uint256 proposalId) {
_proposers.checkProposer(msg.sender);
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
_dgState.checkProposalsCreationAllowed();
_stateMachine.activateNextState(CONFIG.getDualGovernanceConfig());
if (!_stateMachine.canSubmitProposal()) {
revert ProposalSubmissionBlocked();
}
Proposer memory proposer = _proposers.get(msg.sender);
proposalId = TIMELOCK.submit(proposer.executor, calls);
}

function scheduleProposal(uint256 proposalId) external {
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
_stateMachine.activateNextState(CONFIG.getDualGovernanceConfig());
( /* id */ , /* status */, /* executor */, Timestamp submittedAt, /* scheduledAt */ ) =
TIMELOCK.getProposalInfo(proposalId);
_dgState.checkCanScheduleProposal(submittedAt);
if (!_stateMachine.canScheduleProposal(submittedAt)) {
revert ProposalSchedulingBlocked(proposalId);
}
TIMELOCK.schedule(proposalId);
emit ProposalScheduled(proposalId);
}

function cancelAllPendingProposals() external {
_proposers.checkAdminProposer(CONFIG, msg.sender);
TIMELOCK.cancelAllNonExecutedProposals();
}

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

function getRageQuitEscrow() external view returns (address) {
return address(_dgState.rageQuitEscrow);
function canSubmitProposal() public view returns (bool) {
return _stateMachine.canSubmitProposal();
}

function canSchedule(uint256 proposalId) external view returns (bool) {
return _dgState.isProposalsAdoptionAllowed() && TIMELOCK.canSchedule(proposalId);
function canScheduleProposal(uint256 proposalId) external view returns (bool) {
( /* id */ , /* status */, /* executor */, Timestamp submittedAt, /* scheduledAt */ ) =
TIMELOCK.getProposalInfo(proposalId);
return _stateMachine.canScheduleProposal(submittedAt) && TIMELOCK.canSchedule(proposalId);
}

// ---
// Dual Governance State
// ---

function activateNextState() external {
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
function getVetoSignallingEscrow() external view returns (address) {
return address(_stateMachine.signallingEscrow);
}

function getCurrentState() external view returns (State) {
return _dgState.currentState();
function getRageQuitEscrow() external view returns (address) {
return address(_stateMachine.rageQuitEscrow);
}

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

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

function getVetoSignallingDuration() external view returns (Duration) {
return _dgState.getVetoSignallingDuration(CONFIG.getDualGovernanceConfig());
function getCurrentStateContext() external view returns (DualGovernanceStateMachine.Context memory) {
return _stateMachine.getCurrentContext();
}

function isSchedulingEnabled() external view returns (bool) {
return _dgState.isProposalsAdoptionAllowed();
function getDynamicDelayDuration() external view returns (Duration) {
return _stateMachine.getDynamicDelayDuration(CONFIG.getDualGovernanceConfig());
}

// ---
Expand Down Expand Up @@ -149,13 +149,17 @@ contract DualGovernance is IGovernance, ConfigurationProvider {

function tiebreakerResumeSealable(address sealable) external {
_tiebreaker.checkTiebreakerCommittee(msg.sender);
_dgState.checkTiebreak(CONFIG);
if (!_stateMachine.isDeadlock(CONFIG.getTiebreakConfig())) {
revert NotDeadlock();
}
_tiebreaker.resumeSealable(sealable);
}

function tiebreakerScheduleProposal(uint256 proposalId) external {
_tiebreaker.checkTiebreakerCommittee(msg.sender);
_dgState.checkTiebreak(CONFIG);
if (!_stateMachine.isDeadlock(CONFIG.getTiebreakConfig())) {
revert NotDeadlock();
}
TIMELOCK.schedule(proposalId);
}

Expand All @@ -172,7 +176,9 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
if (msg.sender != _resealCommittee) {
revert NotResealCommitttee(msg.sender);
}
_dgState.checkResealState();
if (_stateMachine.getCurrentState() == State.Normal) {
revert ResealIsNotAllowedInNormalState();
}
_resealManager.reseal(sealables);
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/TimelockedGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ contract TimelockedGovernance is IGovernance, ConfigurationProvider {
/// @dev Checks if a proposal can be scheduled.
/// @param proposalId The ID of the proposal to check.
/// @return A boolean indicating whether the proposal can be scheduled.
function canSchedule(uint256 proposalId) external view returns (bool) {
function canScheduleProposal(uint256 proposalId) external view returns (bool) {
return TIMELOCK.canSchedule(proposalId);
}

Expand Down
31 changes: 4 additions & 27 deletions contracts/interfaces/IConfiguration.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,7 @@
pragma solidity 0.8.26;

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

struct DualGovernanceConfig {
uint256 firstSealRageQuitSupport;
uint256 secondSealRageQuitSupport;
Duration dynamicTimelockMaxDuration;
Duration dynamicTimelockMinDuration;
Duration vetoSignallingMinActiveDuration;
Duration vetoSignallingDeactivationMaxDuration;
Duration vetoCooldownDuration;
Duration rageQuitExtraTimelock;
Duration rageQuitExtensionDelay;
Duration rageQuitEthWithdrawalsMinTimelock;
uint256 rageQuitEthWithdrawalsTimelockGrowthStartSeqNumber;
uint256[3] rageQuitEthWithdrawalsTimelockGrowthCoeffs;
}
import {DualGovernanceStateMachine, TiebreakConfig} from "../libraries/DualGovernanceStateMachine.sol";

interface IEscrowConfigration {
function MIN_WITHDRAWALS_BATCH_SIZE() external view returns (uint256);
Expand Down Expand Up @@ -57,19 +43,10 @@ interface IDualGovernanceConfiguration {
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 sealableWithdrawalBlockers() external view returns (address[] memory);

function getSignallingThresholdData()
external
view
returns (
uint256 firstSealThreshold,
uint256 secondSealThreshold,
Duration signallingMinDuration,
Duration signallingMaxDuration
);
function getSealableWithdrawalBlockers() external view returns (address[] memory);

function getDualGovernanceConfig() external view returns (DualGovernanceConfig memory config);
function getDualGovernanceConfig() external view returns (DualGovernanceStateMachine.Config memory config);
function getTiebreakConfig() external view returns (TiebreakConfig memory config);
}

interface IConfiguration is
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/ITimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface IGovernance {
function scheduleProposal(uint256 proposalId) external;
function cancelAllPendingProposals() external;

function canSchedule(uint256 proposalId) external view returns (bool);
function canScheduleProposal(uint256 proposalId) external view returns (bool);
}

interface ITimelock {
Expand Down
Loading

0 comments on commit d0a536f

Please sign in to comment.