Skip to content

Commit

Permalink
Merge pull request lidofinance#21 from lidofinance/feature/tiebreaker
Browse files Browse the repository at this point in the history
Tiebraker and Emergency execution committees
  • Loading branch information
Psirex authored Jul 8, 2024
2 parents 1695bdb + 01943be commit fd84dc5
Show file tree
Hide file tree
Showing 29 changed files with 1,192 additions and 415 deletions.
46 changes: 29 additions & 17 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,33 @@
pragma solidity 0.8.23;

import {ITimelock, IGovernance} from "./interfaces/ITimelock.sol";
import {ISealable} from "./interfaces/ISealable.sol";
import {IResealManager} from "./interfaces/IResealManager.sol";

import {ConfigurationProvider} from "./ConfigurationProvider.sol";
import {Proposers, Proposer} from "./libraries/Proposers.sol";
import {ExecutorCall} from "./libraries/Proposals.sol";
import {EmergencyProtection} from "./libraries/EmergencyProtection.sol";
import {State, DualGovernanceState} from "./libraries/DualGovernanceState.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;

event TiebreakerSet(address tiebreakCommittee);
event ProposalScheduled(uint256 proposalId);

error ProposalNotExecutable(uint256 proposalId);
error NotTiebreaker(address account, address tiebreakCommittee);
error NotResealCommitttee(address account);

ITimelock public immutable TIMELOCK;

address internal _tiebreaker;

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

constructor(
address config,
Expand Down Expand Up @@ -141,29 +144,38 @@ contract DualGovernance is IGovernance, ConfigurationProvider {
// Tiebreaker Protection
// ---

function tiebreakerResumeSealable(address sealable) external {
_tiebreaker.checkTiebreakerCommittee(msg.sender);
_dgState.checkTiebreak(CONFIG);
_tiebreaker.resumeSealable(sealable);
}

function tiebreakerScheduleProposal(uint256 proposalId) external {
_checkTiebreakerCommittee(msg.sender);
_dgState.activateNextState(CONFIG.getDualGovernanceConfig());
_tiebreaker.checkTiebreakerCommittee(msg.sender);
_dgState.checkTiebreak(CONFIG);
TIMELOCK.schedule(proposalId);
}

function setTiebreakerCommittee(address newTiebreaker) external {
function setTiebreakerProtection(address newTiebreaker, address resealManager) external {
_checkAdminExecutor(msg.sender);
address oldTiebreaker = _tiebreaker;
if (newTiebreaker != oldTiebreaker) {
_tiebreaker = newTiebreaker;
emit TiebreakerSet(newTiebreaker);
}
_tiebreaker.setTiebreaker(newTiebreaker, resealManager);
}

// ---
// Internal Helper Methods
// Reseal executor
// ---

function _checkTiebreakerCommittee(address account) internal view {
if (account != _tiebreaker) {
revert NotTiebreaker(account, _tiebreaker);
function resealSealables(address[] memory sealables) external {
if (msg.sender != _resealCommittee) {
revert NotResealCommitttee(msg.sender);
}
_dgState.checkResealState();
_resealManager.reseal(sealables);
}

function setReseal(address resealManager, address resealCommittee) external {
_checkAdminExecutor(msg.sender);
_resealCommittee = resealCommittee;
_resealManager = IResealManager(resealManager);
}
}
128 changes: 0 additions & 128 deletions contracts/GateSealBreaker.sol

This file was deleted.

49 changes: 49 additions & 0 deletions contracts/ResealManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ISealable} from "./interfaces/ISealable.sol";

interface IEmergencyProtectedTimelock {
function getGovernance() external view returns (address);
}

contract ResealManager {
error SealableWrongPauseState();
error SenderIsNotGovernance();
error NotAllowed();

uint256 public constant PAUSE_INFINITELY = type(uint256).max;
address public immutable EMERGENCY_PROTECTED_TIMELOCK;

constructor(address emergencyProtectedTimelock) {
EMERGENCY_PROTECTED_TIMELOCK = emergencyProtectedTimelock;
}

function reseal(address[] memory sealables) public onlyGovernance {
for (uint256 i = 0; i < sealables.length; ++i) {
uint256 sealableResumeSinceTimestamp = ISealable(sealables[i]).getResumeSinceTimestamp();
if (sealableResumeSinceTimestamp < block.timestamp || sealableResumeSinceTimestamp == PAUSE_INFINITELY) {
revert SealableWrongPauseState();
}
Address.functionCall(sealables[i], abi.encodeWithSelector(ISealable.resume.selector));
Address.functionCall(sealables[i], abi.encodeWithSelector(ISealable.pauseFor.selector, PAUSE_INFINITELY));
}
}

function resume(address sealable) public onlyGovernance {
uint256 sealableResumeSinceTimestamp = ISealable(sealable).getResumeSinceTimestamp();
if (sealableResumeSinceTimestamp < block.timestamp) {
revert SealableWrongPauseState();
}
Address.functionCall(sealable, abi.encodeWithSelector(ISealable.resume.selector));
}

modifier onlyGovernance() {
address governance = IEmergencyProtectedTimelock(EMERGENCY_PROTECTED_TIMELOCK).getGovernance();
if (msg.sender != governance) {
revert SenderIsNotGovernance();
}
_;
}
}
43 changes: 43 additions & 0 deletions contracts/committees/EmergencyActivationCommittee.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {HashConsensus} from "./HashConsensus.sol";

interface IEmergencyProtectedTimelock {
function emergencyActivate() external;
}

contract EmergencyActivationCommittee is HashConsensus {
address public immutable EMERGENCY_PROTECTED_TIMELOCK;

bytes32 private constant EMERGENCY_ACTIVATION_HASH = keccak256("EMERGENCY_ACTIVATE");

constructor(
address owner,
address[] memory committeeMembers,
uint256 executionQuorum,
address emergencyProtectedTimelock
) HashConsensus(owner, committeeMembers, executionQuorum, 0) {
EMERGENCY_PROTECTED_TIMELOCK = emergencyProtectedTimelock;
}

function approveEmergencyActivate() public onlyMember {
_vote(EMERGENCY_ACTIVATION_HASH, true);
}

function getEmergencyActivateState()
public
view
returns (uint256 support, uint256 execuitionQuorum, bool isExecuted)
{
return _getHashState(EMERGENCY_ACTIVATION_HASH);
}

function executeEmergencyActivate() external {
_markUsed(EMERGENCY_ACTIVATION_HASH);
Address.functionCall(
EMERGENCY_PROTECTED_TIMELOCK, abi.encodeWithSelector(IEmergencyProtectedTimelock.emergencyActivate.selector)
);
}
}
Loading

0 comments on commit fd84dc5

Please sign in to comment.