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

Fix: align cancelAllPendingProposals() with specification #100

Merged
merged 2 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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