Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0.0-rc3 #267

Merged
merged 27 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1b0980a
Add validation for the second seal and eliminate the overflow risk in…
Psirex Jan 31, 2025
8337ce6
feat: handle properly empty unstETHIds array in markUnstETHFinalized
rkolpakov Jan 31, 2025
ca3ea75
fix: dual governance config comment fix
rkolpakov Jan 31, 2025
d03d9d1
fix: timelocked governance add docstring return value
rkolpakov Jan 31, 2025
d4fde93
fix: change IDualGovernance interface
rkolpakov Jan 31, 2025
99ae382
checking for sealable is paused on tiebreaker proposal submission
bulbozaur Jan 31, 2025
f3093cd
sealable zero address check
bulbozaur Feb 3, 2025
2e4e01a
Merge pull request #262 from lidofinance/fix/incorrect-interfaces
Psirex Feb 3, 2025
3692ea2
Merge pull request #261 from lidofinance/fix/incomplete-docstrings-ti…
Psirex Feb 3, 2025
68a9680
Merge pull request #260 from lidofinance/fix/dual-governance-config-c…
Psirex Feb 3, 2025
f721d2f
fix: remove redundant getter from setProposerExecutor
rkolpakov Jan 31, 2025
96567e7
fix: canceled wording
rkolpakov Feb 3, 2025
dca6fb3
Check minExecutionDelay has passed before proposal execution
Psirex Feb 3, 2025
0a8ecd9
Update spec for DualGovernance.setConfigProvider
Psirex Feb 3, 2025
ab8c459
Update plan-b & tweak spec documents
Psirex Feb 4, 2025
c19a82b
fix: wording
rkolpakov Feb 4, 2025
ff31ec0
documentation update
bulbozaur Feb 4, 2025
b45ef4e
spec update
bulbozaur Feb 4, 2025
c5a270c
Merge pull request #265 from lidofinance/fix/canceled-wording
Psirex Feb 4, 2025
b273969
Merge pull request #264 from lidofinance/feature/tiebreaker-extra-check
Psirex Feb 4, 2025
bcd0ff6
Merge pull request #258 from lidofinance/feature/code-quality-improve…
Psirex Feb 4, 2025
30015d3
feat: use setMinAssetsLockDuration on escrow initialize
rkolpakov Jan 31, 2025
08c5dbc
Merge pull request #266 from lidofinance/feature/min-execution-delay-…
Psirex Feb 4, 2025
005955d
fix: escrowState test fix
rkolpakov Feb 4, 2025
b741221
Merge pull request #256 from lidofinance/feat/escrow-initialize-min-a…
Psirex Feb 4, 2025
937699a
Comments and assume fixes for DualGovernanceConfig test
Psirex Feb 4, 2025
cc369df
Merge pull request #257 from lidofinance/feature/dg-config-constraints
Psirex Feb 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ contract DualGovernance is IDualGovernance {
/// or `VetoSignallingDeactivation` state.
/// @dev If the Dual Governance state is not `VetoSignalling` or `VetoSignallingDeactivation`, the function will
/// exit early, emitting the `CancelAllPendingProposalsSkipped` event without canceling any proposals.
/// @return isProposalsCancelled A boolean indicating whether the proposals were successfully canceled (`true`)
/// @return isProposalsCancelled A boolean indicating whether the proposals were successfully cancelled (`true`)
/// or the cancellation was skipped due to an inappropriate state (`false`).
function cancelAllPendingProposals() external returns (bool) {
_stateMachine.activateNextState();
Expand All @@ -240,7 +240,7 @@ contract DualGovernance is IDualGovernance {
/// reached consensus. This could lead to a situation where a proposer’s cancelAllPendingProposals() call
/// becomes unexecutable if the Dual Governance state changes. However, it might become executable again if
/// the system state shifts back to VetoSignalling or VetoSignallingDeactivation.
/// To avoid such a scenario, an early return is used instead of a revert when proposals cannot be canceled
/// To avoid such a scenario, an early return is used instead of a revert when proposals cannot be cancelled
/// due to an unsuitable Dual Governance state.
emit CancelAllPendingProposalsSkipped();
return false;
Expand All @@ -265,7 +265,7 @@ contract DualGovernance is IDualGovernance {
/// - The Dual Governance system is in the `Normal` or `VetoCooldown` state.
/// - If the system is in the `VetoCooldown` state, the proposal must have been submitted before the system
/// last entered the `VetoSignalling` state.
/// - The proposal has not already been scheduled, canceled, or executed.
/// - The proposal has not already been scheduled, cancelled, or executed.
/// - The required delay period, as defined by `ITimelock.getAfterSubmitDelay()`, has elapsed since the proposal
/// was submitted.
/// @param proposalId The unique identifier of the proposal to check.
Expand All @@ -282,9 +282,9 @@ contract DualGovernance is IDualGovernance {
/// `DualGovernance.cancelAllPendingProposals()` method.
/// @dev Proposal cancellation is only allowed when the Dual Governance system is in the `VetoSignalling` or
/// `VetoSignallingDeactivation` states. In any other state, the cancellation will be skipped and no proposals
/// will be canceled.
/// will be cancelled.
/// @return canCancelAllPendingProposals A boolean value indicating whether the pending proposals can be
/// canceled (`true`) or not (`false`) based on the current `effective` state of the Dual Governance system.
/// cancelled (`true`) or not (`false`) based on the current `effective` state of the Dual Governance system.
function canCancelAllPendingProposals() external view returns (bool) {
return _stateMachine.canCancelAllPendingProposals({useEffectiveState: true});
}
Expand Down Expand Up @@ -397,7 +397,7 @@ contract DualGovernance is IDualGovernance {
_proposers.setProposerExecutor(proposerAccount, newExecutor);

/// @dev after update of the proposer, check that admin executor still belongs to some proposer
_proposers.checkRegisteredExecutor(TIMELOCK.getAdminExecutor());
_proposers.checkRegisteredExecutor(msg.sender);
}

/// @notice Unregisters a proposer from the system.
Expand Down
8 changes: 6 additions & 2 deletions contracts/EmergencyProtectedTimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ contract EmergencyProtectedTimelock is IEmergencyProtectedTimelock {
/// @param proposalId The id of the proposal to be executed.
function execute(uint256 proposalId) external {
_emergencyProtection.checkEmergencyMode({isActive: false});
_proposals.execute(proposalId, _timelockState.getAfterScheduleDelay());
_proposals.execute(proposalId, _timelockState.getAfterScheduleDelay(), MIN_EXECUTION_DELAY);
}

/// @notice Cancels all non-executed proposals, preventing them from being executed in the future.
Expand Down Expand Up @@ -237,7 +237,11 @@ contract EmergencyProtectedTimelock is IEmergencyProtectedTimelock {
function emergencyExecute(uint256 proposalId) external {
_emergencyProtection.checkEmergencyMode({isActive: true});
_emergencyProtection.checkCallerIsEmergencyExecutionCommittee();
_proposals.execute({proposalId: proposalId, afterScheduleDelay: Duration.wrap(0)});
_proposals.execute({
proposalId: proposalId,
afterScheduleDelay: Durations.ZERO,
minExecutionDelay: Durations.ZERO
});
}

/// @notice Deactivates the emergency mode.
Expand Down
6 changes: 5 additions & 1 deletion contracts/Escrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ contract Escrow is ISignallingEscrow, IRageQuitEscrow {
}
_checkCallerIsDualGovernance();

_escrowState.initialize(minAssetsLockDuration);
_escrowState.initialize(minAssetsLockDuration, MAX_MIN_ASSETS_LOCK_DURATION);

ST_ETH.approve(address(WST_ETH), type(uint256).max);
ST_ETH.approve(address(WITHDRAWAL_QUEUE), type(uint256).max);
Expand Down Expand Up @@ -275,6 +275,9 @@ contract Escrow is ISignallingEscrow, IRageQuitEscrow {
/// @param hints An array of hints required by the WithdrawalQueue to efficiently retrieve
/// the claimable amounts for the unstETH NFTs.
function markUnstETHFinalized(uint256[] memory unstETHIds, uint256[] calldata hints) external {
if (unstETHIds.length == 0) {
revert EmptyUnstETHIds();
}
DUAL_GOVERNANCE.activateNextState();
_escrowState.checkSignallingEscrow();

Expand Down Expand Up @@ -310,6 +313,7 @@ contract Escrow is ISignallingEscrow, IRageQuitEscrow {
/// @param newMinAssetsLockDuration The new minimum lock duration to be set.
function setMinAssetsLockDuration(Duration newMinAssetsLockDuration) external {
_checkCallerIsDualGovernance();
_escrowState.checkSignallingEscrow();
_escrowState.setMinAssetsLockDuration(newMinAssetsLockDuration, MAX_MIN_ASSETS_LOCK_DURATION);
}

Expand Down
1 change: 1 addition & 0 deletions contracts/TimelockedGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ contract TimelockedGovernance is IGovernance {
}

/// @notice Cancels all pending proposals that have not been executed.
/// @return A boolean indicating whether the operation was successful.
function cancelAllPendingProposals() external returns (bool) {
_checkCallerIsGovernance();
TIMELOCK.cancelAllNonExecutedProposals();
Expand Down
21 changes: 16 additions & 5 deletions contracts/committees/TiebreakerCoreCommittee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {ITimelock} from "../interfaces/ITimelock.sol";
import {ITiebreaker} from "../interfaces/ITiebreaker.sol";
import {IDualGovernance} from "../interfaces/IDualGovernance.sol";
import {ITiebreakerCoreCommittee} from "../interfaces/ITiebreakerCoreCommittee.sol";
import {ISealable} from "../interfaces/ISealable.sol";

import {HashConsensus} from "./HashConsensus.sol";
import {ProposalsList} from "./ProposalsList.sol";
Expand All @@ -26,6 +27,7 @@ enum ProposalType {
contract TiebreakerCoreCommittee is ITiebreakerCoreCommittee, HashConsensus, ProposalsList {
error ResumeSealableNonceMismatch();
error ProposalDoesNotExist(uint256 proposalId);
error SealableIsNotPaused(address sealable);
error InvalidSealable(address sealable);

address public immutable DUAL_GOVERNANCE;
Expand Down Expand Up @@ -112,15 +114,12 @@ contract TiebreakerCoreCommittee is ITiebreakerCoreCommittee, HashConsensus, Pro

/// @notice Votes on a proposal to resume a sealable address
/// @dev Allows committee members to vote on resuming a sealable address
/// reverts if the sealable address is the zero address
/// reverts if the sealable address is the zero address or if the sealable address is not paused
/// @param sealable The address to resume
/// @param nonce The nonce for the resume proposal
function sealableResume(address sealable, uint256 nonce) external {
_checkCallerIsMember();

if (sealable == address(0)) {
revert InvalidSealable(sealable);
}
checkSealableIsPaused(sealable);

if (nonce != _sealableResumeNonces[sealable]) {
revert ResumeSealableNonceMismatch();
Expand All @@ -147,6 +146,18 @@ contract TiebreakerCoreCommittee is ITiebreakerCoreCommittee, HashConsensus, Pro
return _getHashState(key);
}

/// @notice Checks if a sealable address is paused
/// @dev Checks if the sealable address is paused by calling the getResumeSinceTimestamp function on the ISealable contract
/// @param sealable The address to check
function checkSealableIsPaused(address sealable) public view {
if (sealable == address(0)) {
revert InvalidSealable(sealable);
}
if (ISealable(sealable).getResumeSinceTimestamp() <= block.timestamp) {
revert SealableIsNotPaused(sealable);
}
}

/// @notice Executes an approved resume sealable proposal
/// @dev Executes the resume sealable proposal by calling the tiebreakerResumeSealable function on the Dual Governance contract
/// @param sealable The address to resume
Expand Down
8 changes: 2 additions & 6 deletions contracts/committees/TiebreakerSubCommittee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ enum ProposalType {
/// @notice This contract allows a subcommittee to vote on and execute proposals for scheduling and resuming sealable addresses
/// @dev Inherits from HashConsensus for voting mechanisms and ProposalsList for proposal management
contract TiebreakerSubCommittee is HashConsensus, ProposalsList {
error InvalidSealable(address sealable);

address public immutable TIEBREAKER_CORE_COMMITTEE;

constructor(
Expand Down Expand Up @@ -95,14 +93,12 @@ contract TiebreakerSubCommittee is HashConsensus, ProposalsList {

/// @notice Votes on a proposal to resume a sealable address
/// @dev Allows committee members to vote on resuming a sealable address
/// reverts if the sealable address is the zero address
/// reverts if the sealable address is the zero address or if the sealable address is not paused
/// @param sealable The address to resume
function sealableResume(address sealable) external {
_checkCallerIsMember();
ITiebreakerCoreCommittee(TIEBREAKER_CORE_COMMITTEE).checkSealableIsPaused(sealable);

if (sealable == address(0)) {
revert InvalidSealable(sealable);
}
(bytes memory proposalData, bytes32 key,) = _encodeSealableResume(sealable);
_vote(key, true);
_pushProposal(key, uint256(ProposalType.ResumeSealable), proposalData);
Expand Down
13 changes: 8 additions & 5 deletions contracts/interfaces/IDualGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,20 @@ interface IDualGovernance is IGovernance, ITiebreaker {
function getEffectiveState() external view returns (State effectiveState);
function getStateDetails() external view returns (StateDetails memory stateDetails);

function registerProposer(address proposer, address executor) external;
function registerProposer(address proposerAccount, address executor) external;
function setProposerExecutor(address proposerAccount, address newExecutor) external;
function unregisterProposer(address proposer) external;
function isProposer(address account) external view returns (bool);
function getProposer(address account) external view returns (Proposers.Proposer memory proposer);
function unregisterProposer(address proposerAccount) external;
function isProposer(address proposerAccount) external view returns (bool);
function getProposer(address proposerAccount) external view returns (Proposers.Proposer memory proposer);
function getProposers() external view returns (Proposers.Proposer[] memory proposers);
function isExecutor(address account) external view returns (bool);
function isExecutor(address executor) external view returns (bool);

function resealSealable(address sealable) external;
function setResealCommittee(address newResealCommittee) external;
function setResealManager(IResealManager newResealManager) external;
function getResealManager() external view returns (IResealManager);
function getResealCommittee() external view returns (address);

function setProposalsCanceller(address newProposalsCanceller) external;
function getProposalsCanceller() external view returns (address);
}
1 change: 1 addition & 0 deletions contracts/interfaces/ITiebreakerCoreCommittee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ interface ITiebreakerCoreCommittee {
function scheduleProposal(uint256 proposalId) external;
function sealableResume(address sealable, uint256 nonce) external;
function checkProposalExists(uint256 proposalId) external view;
function checkSealableIsPaused(address sealable) external view;
}
27 changes: 19 additions & 8 deletions contracts/libraries/DualGovernanceConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {PercentD16} from "../types/PercentD16.sol";
import {PercentD16, PercentsD16, HUNDRED_PERCENT_D16} from "../types/PercentD16.sol";
import {Duration, Durations} from "../types/Duration.sol";
import {Timestamp, Timestamps} from "../types/Timestamp.sol";

Expand All @@ -13,6 +13,7 @@ library DualGovernanceConfig {
// Errors
// ---

error InvalidSecondSealRageQuitSupport(PercentD16 secondSealRageQuitSupport);
error InvalidRageQuitSupportRange(PercentD16 firstSealRageQuitSupport, PercentD16 secondSealRageQuitSupport);
error InvalidRageQuitEthWithdrawalsDelayRange(
Duration rageQuitEthWithdrawalsMinDelay, Duration rageQuitEthWithdrawalsMaxDelay
Expand All @@ -26,7 +27,7 @@ library DualGovernanceConfig {

/// @notice Configuration values for Dual Governance.
/// @param firstSealRageQuitSupport The percentage of the total stETH supply that must be reached in the Signalling
/// Escrow to transition Dual Governance from the Normal state to the VetoSignalling state.
/// Escrow to transition Dual Governance from Normal, VetoCooldown and RageQuit states to the VetoSignalling state.
/// @param secondSealRageQuitSupport The percentage of the total stETH supply that must be reached in the
/// Signalling Escrow to transition Dual Governance into the RageQuit state.
///
Expand Down Expand Up @@ -64,6 +65,12 @@ library DualGovernanceConfig {
Duration rageQuitEthWithdrawalsDelayGrowth;
}

// ---
// Constants
// ---

uint256 internal constant MAX_SECOND_SEAL_RAGE_QUIT_SUPPORT = HUNDRED_PERCENT_D16;

// ---
// Main Functionality
// ---
Expand All @@ -72,6 +79,10 @@ library DualGovernanceConfig {
/// of the Dual Governance system.
/// @param self The configuration context.
function validate(Context memory self) internal pure {
if (self.secondSealRageQuitSupport > PercentsD16.from(MAX_SECOND_SEAL_RAGE_QUIT_SUPPORT)) {
revert InvalidSecondSealRageQuitSupport(self.secondSealRageQuitSupport);
}

if (self.firstSealRageQuitSupport >= self.secondSealRageQuitSupport) {
revert InvalidRageQuitSupportRange(self.firstSealRageQuitSupport, self.secondSealRageQuitSupport);
}
Expand Down Expand Up @@ -203,11 +214,11 @@ library DualGovernanceConfig {
Context memory self,
uint256 rageQuitRound
) internal pure returns (Duration) {
return Durations.min(
self.rageQuitEthWithdrawalsMinDelay.plusSeconds(
rageQuitRound * self.rageQuitEthWithdrawalsDelayGrowth.toSeconds()
),
self.rageQuitEthWithdrawalsMaxDelay
);
uint256 rageQuitWithdrawalsDelayInSeconds = self.rageQuitEthWithdrawalsMinDelay.toSeconds()
+ rageQuitRound * self.rageQuitEthWithdrawalsDelayGrowth.toSeconds();

return rageQuitWithdrawalsDelayInSeconds > self.rageQuitEthWithdrawalsMaxDelay.toSeconds()
? self.rageQuitEthWithdrawalsMaxDelay
: Durations.from(rageQuitWithdrawalsDelayInSeconds);
}
}
22 changes: 10 additions & 12 deletions contracts/libraries/EscrowState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,16 @@ library EscrowState {

/// @notice Initializes the Escrow state to SignallingEscrow.
/// @param self The context of the Escrow State library.
/// @param minAssetsLockDuration The minimum assets lock duration.
function initialize(Context storage self, Duration minAssetsLockDuration) internal {
/// @param minAssetsLockDuration The initial minimum assets lock duration.
/// @param maxMinAssetsLockDuration Sanity check upper bound for min assets lock duration.
function initialize(
Context storage self,
Duration minAssetsLockDuration,
Duration maxMinAssetsLockDuration
) internal {
_checkState(self, State.NotInitialized);
_setState(self, State.SignallingEscrow);
_setMinAssetsLockDuration(self, minAssetsLockDuration);
setMinAssetsLockDuration(self, minAssetsLockDuration, maxMinAssetsLockDuration);
}

/// @notice Starts the rage quit process.
Expand Down Expand Up @@ -116,7 +121,8 @@ library EscrowState {
) {
revert InvalidMinAssetsLockDuration(newMinAssetsLockDuration);
}
_setMinAssetsLockDuration(self, newMinAssetsLockDuration);
self.minAssetsLockDuration = newMinAssetsLockDuration;
emit MinAssetsLockDurationSet(newMinAssetsLockDuration);
}

// ---
Expand Down Expand Up @@ -205,12 +211,4 @@ library EscrowState {
self.state = newState;
emit EscrowStateChanged(prevState, newState);
}

/// @notice Sets the minimum assets lock duration.
/// @param self The context of the Escrow State library.
/// @param newMinAssetsLockDuration The new minimum assets lock duration.
function _setMinAssetsLockDuration(Context storage self, Duration newMinAssetsLockDuration) private {
self.minAssetsLockDuration = newMinAssetsLockDuration;
emit MinAssetsLockDurationSet(newMinAssetsLockDuration);
}
}
Loading
Loading