Skip to content

Commit

Permalink
Merge pull request #100 from lidofinance/fix/cancell-all-pending-prop…
Browse files Browse the repository at this point in the history
…osals

Fix: align `cancelAllPendingProposals()` with specification
  • Loading branch information
Psirex authored Aug 28, 2024
2 parents dd3bd6e + 81e23ec commit 10c2c2c
Show file tree
Hide file tree
Showing 5 changed files with 439 additions and 0 deletions.
18 changes: 18 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
40 changes: 40 additions & 0 deletions test/mocks/StETHMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol";
import {IStETH} from "contracts/interfaces/IStETH.sol";

/* solhint-disable no-unused-vars,custom-errors */
contract StETHMock is ERC20Mock, IStETH {
uint256 public __shareRate = 1 gwei;

constructor() {
/// @dev the total supply of the stETH always > 0
_mint(address(this), 100 wei);
}

function __setShareRate(uint256 newShareRate) public {
__shareRate = newShareRate;
}

function getSharesByPooledEth(uint256 ethAmount) external view returns (uint256) {
return ethAmount / __shareRate;
}

function getPooledEthByShares(uint256 sharesAmount) external view returns (uint256) {
return __shareRate * sharesAmount;
}

function transferShares(address to, uint256 sharesAmount) external {
transfer(to, sharesAmount * __shareRate);
}

function transferSharesFrom(
address _sender,
address _recipient,
uint256 _sharesAmount
) external returns (uint256 tokensAmount) {
tokensAmount = _sharesAmount * __shareRate;
transferFrom(_sender, _recipient, tokensAmount);
}
}
4 changes: 4 additions & 0 deletions test/mocks/TimelockMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ contract TimelockMock is ITimelock {
revert("Not Implemented");
}

function getProposalsCount() external view returns (uint256 count) {
return submittedProposals.length;
}

function getAdminExecutor() external view returns (address) {
return _ADMIN_EXECUTOR;
}
Expand Down
135 changes: 135 additions & 0 deletions test/mocks/WithdrawalQueueMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

// import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; /*, ERC721("test", "test")*/
import {IWithdrawalQueue, WithdrawalRequestStatus} from "contracts/interfaces/IWithdrawalQueue.sol";

/* solhint-disable no-unused-vars,custom-errors */
contract WithdrawalQueueMock is IWithdrawalQueue {
uint256 private _lastRequestId;
uint256 private _lastFinalizedRequestId;
uint256 private _minStETHWithdrawalAmount;
uint256 private _maxStETHWithdrawalAmount;
uint256[] private _requestWithdrawalsResult;

constructor() {}

function MIN_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256) {
return _minStETHWithdrawalAmount;
}

function MAX_STETH_WITHDRAWAL_AMOUNT() external view returns (uint256) {
return _maxStETHWithdrawalAmount;
}

function claimWithdrawals(uint256[] calldata requestIds, uint256[] calldata hints) external {
revert("Not Implemented");
}

function getLastRequestId() external view returns (uint256) {
return _lastRequestId;
}

function getLastFinalizedRequestId() external view returns (uint256) {
return _lastFinalizedRequestId;
}

function getWithdrawalStatus(uint256[] calldata _requestIds)
external
view
returns (WithdrawalRequestStatus[] memory statuses)
{
revert("Not Implemented");
}

/// @notice Returns amount of ether available for claim for each provided request id
/// @param _requestIds array of request ids
/// @param _hints checkpoint hints. can be found with `findCheckpointHints(_requestIds, 1, getLastCheckpointIndex())`
/// @return claimableEthValues amount of claimable ether for each request, amount is equal to 0 if request
/// is not finalized or already claimed
function getClaimableEther(
uint256[] calldata _requestIds,
uint256[] calldata _hints
) external view returns (uint256[] memory claimableEthValues) {
revert("Not Implemented");
}

function findCheckpointHints(
uint256[] calldata _requestIds,
uint256 _firstIndex,
uint256 _lastIndex
) external view returns (uint256[] memory hintIds) {
revert("Not Implemented");
}

function getLastCheckpointIndex() external view returns (uint256) {
revert("Not Implemented");
}

function requestWithdrawals(
uint256[] calldata _amounts,
address _owner
) external returns (uint256[] memory requestIds) {
return _requestWithdrawalsResult;
}

function balanceOf(address owner) external view returns (uint256 balance) {
revert("Not Implemented");
}

function ownerOf(uint256 tokenId) external view returns (address owner) {
revert("Not Implemented");
}

function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external {
revert("Not Implemented");
}

function safeTransferFrom(address from, address to, uint256 tokenId) external {
revert("Not Implemented");
}

function transferFrom(address from, address to, uint256 tokenId) external {
revert("Not Implemented");
}

function approve(address to, uint256 tokenId) external {
revert("Not Implemented");
}

function setApprovalForAll(address operator, bool approved) external {
revert("Not Implemented");
}

function getApproved(uint256 tokenId) external view returns (address operator) {
revert("Not Implemented");
}

function isApprovedForAll(address owner, address operator) external view returns (bool) {
revert("Not Implemented");
}

function supportsInterface(bytes4 interfaceId) external view returns (bool) {
revert("Not Implemented");
}

function setLastRequestId(uint256 id) public {
_lastRequestId = id;
}

function setLastFinalizedRequestId(uint256 id) public {
_lastFinalizedRequestId = id;
}

function setMinStETHWithdrawalAmount(uint256 amount) public {
_minStETHWithdrawalAmount = amount;
}

function setMaxStETHWithdrawalAmount(uint256 amount) public {
_maxStETHWithdrawalAmount = amount;
}

function setRequestWithdrawalsResult(uint256[] memory requestIds) public {
_requestWithdrawalsResult = requestIds;
}
}
Loading

0 comments on commit 10c2c2c

Please sign in to comment.