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-alpha.1 release #109

Merged
merged 61 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
e8a7a9f
Prevent executed proposals from changing state after cancelAll is called
Psirex Aug 13, 2024
25014bc
Fix the order of DG.activateNextState() calls in Escrow lock/unlock m…
Psirex Aug 13, 2024
1a17265
Update quorumAt after execution quorum changing
bulbozaur Aug 14, 2024
a50a1a4
fix: make tiebreakerScheduleProposal calling activateNextState
rkolpakov Aug 14, 2024
f2c9b56
feat: change timelockstate errors handling
rkolpakov Aug 16, 2024
92e9d49
feat: tiebreaker improvements and unit tests
rkolpakov Aug 14, 2024
06e1db2
feat: change escrowstate errors handling
rkolpakov Aug 16, 2024
ba51f14
feat: change tiebreaker error handling
rkolpakov Aug 16, 2024
97671b9
feat: change emergency protection errors handling
rkolpakov Aug 16, 2024
1c38dd0
feat: change emergency protected timelock errors handling
rkolpakov Aug 16, 2024
e27afc4
feat: change hash consensus errors handling
rkolpakov Aug 16, 2024
781a48d
fix: solidity warnings fixes
rkolpakov Aug 16, 2024
04a34ed
Align cancelAllPendingProposals method with specification
Psirex Aug 19, 2024
022d9d1
Merge pull request #97 from lidofinance/fix/dg-tiebreaker-schedule-pr…
Psirex Aug 19, 2024
48f8438
Merge pull request #99 from lidofinance/fix/solidity-warnings
rkolpakov Aug 20, 2024
962ccb9
fix: review fixes
rkolpakov Aug 20, 2024
a16055f
Merge branch 'develop' into feature/failure-models-of-setters
rkolpakov Aug 20, 2024
7e1094c
Merge pull request #98 from lidofinance/feature/failure-models-of-set…
rkolpakov Aug 20, 2024
3c3faa8
fix: sealable calls resume return value fix
rkolpakov Aug 20, 2024
39283d8
Merge pull request #102 from lidofinance/fix/sealable-calls-resume
bulbozaur Aug 20, 2024
0f7ca63
fix: withdrawETH reverts on empty array
rkolpakov Aug 21, 2024
4739b5a
fix: lockUnsteth reverts on empty array
rkolpakov Aug 21, 2024
44ce534
fix: requestNextWithdrawalBatch now closes when balance lower than mi…
rkolpakov Aug 21, 2024
405fc20
Merge pull request #103 from lidofinance/fix/audit-fixes-for-escrow
bulbozaur Aug 21, 2024
c7cebcb
Merge branch 'develop' into fix/escrow-activate-next-state-call-order
bulbozaur Aug 21, 2024
5f1f107
Merge pull request #95 from lidofinance/fix/escrow-activate-next-stat…
bulbozaur Aug 21, 2024
9dfcfbd
fix: proposers lib fix
rkolpakov Aug 22, 2024
3515294
fix: withdrawal batches fix
rkolpakov Aug 22, 2024
78131cd
Merge pull request #105 from lidofinance/fix/wq-fix
rkolpakov Aug 22, 2024
cb0f532
Merge pull request #104 from lidofinance/fix/proposal-spam-fix
bulbozaur Aug 23, 2024
86e12c1
Merge remote-tracking branch 'origin/develop' into feature/hashconsen…
bulbozaur Aug 23, 2024
e1018c4
proposal scheduling
bulbozaur Aug 23, 2024
a013a29
fix: check caller and typo
bulbozaur Aug 23, 2024
4d017c1
rename HashConsensus schedule
bulbozaur Aug 23, 2024
35acfba
natspec update
bulbozaur Aug 23, 2024
dc8444d
revert on schedule proposal
bulbozaur Aug 23, 2024
6c08364
fix typo
bulbozaur Aug 23, 2024
34b2b44
tests naming
bulbozaur Aug 23, 2024
c40cd6d
tests_naming
bulbozaur Aug 23, 2024
dc75ebd
using Timestamps and Durations
bulbozaur Aug 23, 2024
ecc2106
Add tests for EscrowState library
sandstone-ag Aug 9, 2024
a400f97
Fix AssetsAccounting tests after renaming
sandstone-ag Aug 12, 2024
d5e6b02
Use Duration/Timestamp instead of primitive type as a fuzzing parameter
sandstone-ag Aug 19, 2024
a06b67a
Pass named parameters to checkContext()
sandstone-ag Aug 19, 2024
b1103f1
Use assertEq() instead of regular assert()
sandstone-ag Aug 19, 2024
4e4982e
Use _wait() instead of vm.warp()
sandstone-ag Aug 19, 2024
ba395fa
Remove excessive type casts in AssetsAccounting tests
sandstone-ag Aug 19, 2024
3b9eb9d
Fix EscrowState tests
sandstone-ag Aug 20, 2024
4108d4f
Merge pull request #92 from lidofinance/feature/escrow-state-tests
rkolpakov Aug 26, 2024
6bf5739
Merge pull request #93 from lidofinance/feature/hashconsensus-quorum
rkolpakov Aug 26, 2024
f18fec6
Merge branch 'develop' into feature/tiebreaker-tests
rkolpakov Aug 26, 2024
8831953
fix: review fixes
rkolpakov Aug 27, 2024
81e23ec
Add unit tests for cancelAllPendingProposals()
Psirex Aug 27, 2024
dab02d6
Fix typo in the unstETHLockedShares value of getVetoerState method
Psirex Aug 27, 2024
1663175
Fix the overflow of the rage quit round
Psirex Aug 28, 2024
dd3bd6e
Merge pull request #108 from lidofinance/fix/get-vetoer-state-typo
Psirex Aug 28, 2024
10c2c2c
Merge pull request #100 from lidofinance/fix/cancell-all-pending-prop…
Psirex Aug 28, 2024
72a994a
Merge pull request #94 from lidofinance/fix/executed-proposals-canceling
Psirex Aug 28, 2024
a251e6e
Merge pull request #96 from lidofinance/feature/tiebreaker-tests
Psirex Aug 28, 2024
51ad0ef
Add check for rageQuitRound into the unit test
Psirex Aug 28, 2024
64ea7a9
Merge pull request #107 from lidofinance/fix/rage-quit-round-overflow
Psirex Aug 28, 2024
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
19 changes: 19 additions & 0 deletions contracts/DualGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ contract DualGovernance is IDualGovernance {
// Events
// ---

event CancelAllPendingProposalsSkipped();
event CancelAllPendingProposalsExecuted();
event EscrowMasterCopyDeployed(address escrowMasterCopy);
event ConfigProviderSet(IDualGovernanceConfigProvider newConfigProvider);

Expand Down Expand Up @@ -138,11 +140,27 @@ contract DualGovernance is IDualGovernance {
}

function cancelAllPendingProposals() external {
_stateMachine.activateNextState(_configProvider.getDualGovernanceConfig(), ESCROW_MASTER_COPY);

Proposers.Proposer memory proposer = _proposers.getProposer(msg.sender);
if (proposer.executor != TIMELOCK.getAdminExecutor()) {
revert NotAdminProposer();
}

State currentState = _stateMachine.getCurrentState();
if (currentState != State.VetoSignalling && currentState != State.VetoSignallingDeactivation) {
/// @dev Some proposer contracts, like Aragon Voting, may not support canceling decisions that have already
/// 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
/// due to an unsuitable Dual Governance state.
emit CancelAllPendingProposalsSkipped();
return;
}

TIMELOCK.cancelAllNonExecutedProposals();
emit CancelAllPendingProposalsExecuted();
}

function canSubmitProposal() public view returns (bool) {
Expand Down Expand Up @@ -267,6 +285,7 @@ contract DualGovernance is IDualGovernance {

function tiebreakerScheduleProposal(uint256 proposalId) external {
_tiebreaker.checkCallerIsTiebreakerCommittee();
_stateMachine.activateNextState(_configProvider.getDualGovernanceConfig(), ESCROW_MASTER_COPY);
_tiebreaker.checkTie(_stateMachine.getCurrentState(), _stateMachine.getNormalOrVetoCooldownStateExitedAt());
TIMELOCK.schedule(proposalId);
}
Expand Down
41 changes: 30 additions & 11 deletions contracts/EmergencyProtectedTimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ contract EmergencyProtectedTimelock is ITimelock {
_timelockState.setGovernance(newGovernance);
}

function setDelays(Duration afterSubmitDelay, Duration afterScheduleDelay) external {
function setupDelays(Duration afterSubmitDelay, Duration afterScheduleDelay) external {
_checkCallerIsAdminExecutor();
_timelockState.setAfterSubmitDelay(afterSubmitDelay, MAX_AFTER_SUBMIT_DELAY);
_timelockState.setAfterScheduleDelay(afterScheduleDelay, MAX_AFTER_SCHEDULE_DELAY);
Expand All @@ -128,22 +128,41 @@ contract EmergencyProtectedTimelock is ITimelock {
// Emergency Protection Functionality
// ---

function setupEmergencyProtection(
address emergencyGovernance,
address emergencyActivationCommittee,
address emergencyExecutionCommittee,
Timestamp emergencyProtectionEndDate,
Duration emergencyModeDuration
) external {
/// @dev Sets the emergency activation committee address.
/// @param emergencyActivationCommittee The address of the emergency activation committee.
function setEmergencyProtectionActivationCommittee(address emergencyActivationCommittee) external {
_checkCallerIsAdminExecutor();

_emergencyProtection.setEmergencyGovernance(emergencyGovernance);
_emergencyProtection.setEmergencyActivationCommittee(emergencyActivationCommittee);
}

/// @dev Sets the emergency execution committee address.
/// @param emergencyExecutionCommittee The address of the emergency execution committee.
function setEmergencyProtectionExecutionCommittee(address emergencyExecutionCommittee) external {
_checkCallerIsAdminExecutor();
_emergencyProtection.setEmergencyExecutionCommittee(emergencyExecutionCommittee);
}

/// @dev Sets the emergency protection end date.
/// @param emergencyProtectionEndDate The timestamp of the emergency protection end date.
function setEmergencyProtectionEndDate(Timestamp emergencyProtectionEndDate) external {
_checkCallerIsAdminExecutor();
_emergencyProtection.setEmergencyProtectionEndDate(
emergencyProtectionEndDate, MAX_EMERGENCY_PROTECTION_DURATION
);
}

/// @dev Sets the emergency mode duration.
/// @param emergencyModeDuration The duration of the emergency mode.
function setEmergencyModeDuration(Duration emergencyModeDuration) external {
_checkCallerIsAdminExecutor();
_emergencyProtection.setEmergencyModeDuration(emergencyModeDuration, MAX_EMERGENCY_MODE_DURATION);
_emergencyProtection.setEmergencyExecutionCommittee(emergencyExecutionCommittee);
}

/// @dev Sets the emergency governance address.
/// @param emergencyGovernance The address of the emergency governance.
function setEmergencyGovernance(address emergencyGovernance) external {
_checkCallerIsAdminExecutor();
_emergencyProtection.setEmergencyGovernance(emergencyGovernance);
}

/// @dev Activates the emergency mode.
Expand Down
34 changes: 24 additions & 10 deletions contracts/Escrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ contract Escrow is IEscrow {
error UnfinalizedUnstETHIds();
error NonProxyCallsForbidden();
error BatchesQueueIsNotClosed();
error EmptyUnstETHIds();
error InvalidBatchSize(uint256 size);
error CallerIsNotDualGovernance(address caller);
error InvalidHintsLength(uint256 actual, uint256 expected);
Expand Down Expand Up @@ -131,8 +132,8 @@ contract Escrow is IEscrow {
// ---

function lockStETH(uint256 amount) external returns (uint256 lockedStETHShares) {
_escrowState.checkSignallingEscrow();
DUAL_GOVERNANCE.activateNextState();
_escrowState.checkSignallingEscrow();

lockedStETHShares = ST_ETH.getSharesByPooledEth(amount);
_accounting.accountStETHSharesLock(msg.sender, SharesValues.from(lockedStETHShares));
Expand All @@ -142,10 +143,10 @@ contract Escrow is IEscrow {
}

function unlockStETH() external returns (uint256 unlockedStETHShares) {
_escrowState.checkSignallingEscrow();

DUAL_GOVERNANCE.activateNextState();
_escrowState.checkSignallingEscrow();
_accounting.checkMinAssetsLockDurationPassed(msg.sender, _escrowState.minAssetsLockDuration);

unlockedStETHShares = _accounting.accountStETHSharesUnlock(msg.sender).toUint256();
ST_ETH.transferShares(msg.sender, unlockedStETHShares);

Expand All @@ -157,8 +158,8 @@ contract Escrow is IEscrow {
// ---

function lockWstETH(uint256 amount) external returns (uint256 lockedStETHShares) {
_escrowState.checkSignallingEscrow();
DUAL_GOVERNANCE.activateNextState();
_escrowState.checkSignallingEscrow();

WST_ETH.transferFrom(msg.sender, address(this), amount);
lockedStETHShares = ST_ETH.getSharesByPooledEth(WST_ETH.unwrap(amount));
Expand All @@ -168,10 +169,10 @@ contract Escrow is IEscrow {
}

function unlockWstETH() external returns (uint256 unlockedStETHShares) {
_escrowState.checkSignallingEscrow();
DUAL_GOVERNANCE.activateNextState();

_escrowState.checkSignallingEscrow();
_accounting.checkMinAssetsLockDurationPassed(msg.sender, _escrowState.minAssetsLockDuration);

SharesValue wstETHUnlocked = _accounting.accountStETHSharesUnlock(msg.sender);
unlockedStETHShares = WST_ETH.wrap(ST_ETH.getPooledEthByShares(wstETHUnlocked.toUint256()));
WST_ETH.transfer(msg.sender, unlockedStETHShares);
Expand All @@ -183,8 +184,12 @@ contract Escrow is IEscrow {
// Lock & unlock unstETH
// ---
function lockUnstETH(uint256[] memory unstETHIds) external {
_escrowState.checkSignallingEscrow();
if (unstETHIds.length == 0) {
revert EmptyUnstETHIds();
}

DUAL_GOVERNANCE.activateNextState();
_escrowState.checkSignallingEscrow();

WithdrawalRequestStatus[] memory statuses = WITHDRAWAL_QUEUE.getWithdrawalStatus(unstETHIds);
_accounting.accountUnstETHLock(msg.sender, unstETHIds, statuses);
Expand All @@ -197,10 +202,10 @@ contract Escrow is IEscrow {
}

function unlockUnstETH(uint256[] memory unstETHIds) external {
_escrowState.checkSignallingEscrow();
DUAL_GOVERNANCE.activateNextState();

_escrowState.checkSignallingEscrow();
_accounting.checkMinAssetsLockDurationPassed(msg.sender, _escrowState.minAssetsLockDuration);

_accounting.accountUnstETHUnlock(msg.sender, unstETHIds);
uint256 unstETHIdsCount = unstETHIds.length;
for (uint256 i = 0; i < unstETHIdsCount; ++i) {
Expand Down Expand Up @@ -271,6 +276,12 @@ contract Escrow is IEscrow {
});

_batchesQueue.addUnstETHIds(WITHDRAWAL_QUEUE.requestWithdrawals(requestAmounts, address(this)));

stETHRemaining = ST_ETH.balanceOf(address(this));

if (stETHRemaining < minStETHWithdrawalRequestAmount) {
_batchesQueue.close();
}
}

// ---
Expand Down Expand Up @@ -364,6 +375,9 @@ contract Escrow is IEscrow {
}

function withdrawETH(uint256[] calldata unstETHIds) external {
if (unstETHIds.length == 0) {
revert EmptyUnstETHIds();
}
_escrowState.checkRageQuitEscrow();
_escrowState.checkWithdrawalsTimelockPassed();
ETHValue ethToWithdraw = _accounting.accountUnstETHWithdraw(msg.sender, unstETHIds);
Expand All @@ -389,7 +403,7 @@ contract Escrow is IEscrow {

state.unstETHIdsCount = assets.unstETHIds.length;
state.stETHLockedShares = assets.stETHLockedShares.toUint256();
state.unstETHLockedShares = assets.stETHLockedShares.toUint256();
state.unstETHLockedShares = assets.unstETHLockedShares.toUint256();
state.lastAssetsLockTimestamp = assets.lastAssetsLockTimestamp.toSeconds();
}

Expand Down
9 changes: 6 additions & 3 deletions contracts/committees/EmergencyActivationCommittee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol";

import {HashConsensus} from "./HashConsensus.sol";
import {ITimelock} from "../interfaces/ITimelock.sol";
import {Duration, Durations} from "../types/Duration.sol";
import {Timestamp} from "../types/Timestamp.sol";

/// @title Emergency Activation Committee Contract
/// @notice This contract allows a committee to approve and execute an emergency activation
Expand All @@ -19,7 +21,7 @@ contract EmergencyActivationCommittee is HashConsensus {
address[] memory committeeMembers,
uint256 executionQuorum,
address emergencyProtectedTimelock
) HashConsensus(owner, 0) {
) HashConsensus(owner, Durations.from(0)) {
EMERGENCY_PROTECTED_TIMELOCK = emergencyProtectedTimelock;

_addMembers(committeeMembers, executionQuorum);
Expand All @@ -34,12 +36,13 @@ contract EmergencyActivationCommittee is HashConsensus {

/// @notice Gets the current state of the emergency activation vote
/// @return support The number of votes in support of the activation
/// @return execuitionQuorum The required number of votes for execution
/// @return executionQuorum The required number of votes for execution
/// @return quorumAt The timestamp when the quorum was reached
/// @return isExecuted Whether the activation has been executed
function getActivateEmergencyModeState()
public
view
returns (uint256 support, uint256 execuitionQuorum, bool isExecuted)
returns (uint256 support, uint256 executionQuorum, Timestamp quorumAt, bool isExecuted)
{
return _getHashState(EMERGENCY_ACTIVATION_HASH);
}
Expand Down
16 changes: 10 additions & 6 deletions contracts/committees/EmergencyExecutionCommittee.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {HashConsensus} from "./HashConsensus.sol";
import {ProposalsList} from "./ProposalsList.sol";
import {ITimelock} from "../interfaces/ITimelock.sol";
import {Timestamp} from "../types/Timestamp.sol";
import {Durations} from "../types/Duration.sol";

enum ProposalType {
EmergencyExecute,
Expand All @@ -22,7 +24,7 @@ contract EmergencyExecutionCommittee is HashConsensus, ProposalsList {
address[] memory committeeMembers,
uint256 executionQuorum,
address emergencyProtectedTimelock
) HashConsensus(owner, 0) {
) HashConsensus(owner, Durations.from(0)) {
EMERGENCY_PROTECTED_TIMELOCK = emergencyProtectedTimelock;

_addMembers(committeeMembers, executionQuorum);
Expand All @@ -46,12 +48,13 @@ contract EmergencyExecutionCommittee is HashConsensus, ProposalsList {
/// @notice Gets the current state of an emergency execution proposal
/// @param proposalId The ID of the proposal
/// @return support The number of votes in support of the proposal
/// @return execuitionQuorum The required number of votes for execution
/// @return executionQuorum The required number of votes for execution
/// @return quorumAt The timestamp when the quorum was reached
/// @return isExecuted Whether the proposal has been executed
function getEmergencyExecuteState(uint256 proposalId)
public
view
returns (uint256 support, uint256 execuitionQuorum, bool isExecuted)
returns (uint256 support, uint256 executionQuorum, Timestamp quorumAt, bool isExecuted)
{
(, bytes32 key) = _encodeEmergencyExecute(proposalId);
return _getHashState(key);
Expand Down Expand Up @@ -93,14 +96,15 @@ contract EmergencyExecutionCommittee is HashConsensus, ProposalsList {
_pushProposal(proposalKey, uint256(ProposalType.EmergencyReset), bytes(""));
}

/// @notice Gets the current state of an emergency reset opprosal
/// @notice Gets the current state of an emergency reset proposal
/// @return support The number of votes in support of the proposal
/// @return execuitionQuorum The required number of votes for execution
/// @return executionQuorum The required number of votes for execution
/// @return quorumAt The timestamp when the quorum was reached
/// @return isExecuted Whether the proposal has been executed
function getEmergencyResetState()
public
view
returns (uint256 support, uint256 execuitionQuorum, bool isExecuted)
returns (uint256 support, uint256 executionQuorum, Timestamp quorumAt, bool isExecuted)
{
bytes32 proposalKey = _encodeEmergencyResetProposalKey();
return _getHashState(proposalKey);
Expand Down
Loading
Loading