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

Exit window #41

Merged
merged 3 commits into from
Oct 16, 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
32 changes: 27 additions & 5 deletions src/OptimisticTokenVotingPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ contract OptimisticTokenVotingPlugin is
bytes32 public constant UPDATE_OPTIMISTIC_GOVERNANCE_SETTINGS_PERMISSION_ID =
keccak256("UPDATE_OPTIMISTIC_GOVERNANCE_SETTINGS_PERMISSION");

/// @notice The time gap between a proposal passing and execution being unlocked
uint64 public constant EXIT_WINDOW = 7 days;

/// @notice An [OpenZeppelin `Votes`](https://docs.openzeppelin.com/contracts/4.x/api/governance#Votes) compatible contract referencing the token being used for voting.
IVotesUpgradeable public votingToken;

Expand All @@ -94,7 +97,7 @@ contract OptimisticTokenVotingPlugin is
ITaikoL1 public taikoL1;

/// @notice The struct storing the governance settings.
/// @dev Takes 1 storage slot (32+64+64+64)
/// @dev Takes 1 storage slot (32+64+64+64+8 < 256)
OptimisticGovernanceSettings public governanceSettings;

/// @notice A mapping between proposal IDs and proposal information.
Expand Down Expand Up @@ -294,6 +297,10 @@ contract OptimisticTokenVotingPlugin is
else if (isMinVetoRatioReached(_proposalId)) {
return false;
}
// Check that the proposal has completed the exit window period
else if (_proposalInExitWindow(proposal_)) {
return false;
}

return true;
}
Expand Down Expand Up @@ -364,7 +371,7 @@ contract OptimisticTokenVotingPlugin is
revert DurationOutOfBounds({limit: governanceSettings.minDuration, actual: _duration});
}
uint64 _now = block.timestamp.toUint64();
uint64 _vetoEndDate = _now + _duration; // Since `minDuration` will be less than 1 year, `startDate + minDuration` can only overflow if the `startDate` is after `type(uint64).max - minDuration`. In this case, the proposal creation will revert and another date can be picked.
uint64 _vetoEndDate = _now + _duration; // Since `minDuration` will be less than 1 year, `startDate + minDuration` can only overflow if the `startDate` is after `type(uint64).max - minDuration`.

proposalId = _createProposal({
_creator: _msgSender(),
Expand Down Expand Up @@ -401,9 +408,11 @@ contract OptimisticTokenVotingPlugin is
}
}

// For emergency multisig: execute if already possible
if (canExecute(proposalId)) {
execute(proposalId);
// Emergency proposal shortcut: Execute right away.
// Bypass the veto phase, the L2 grace period and the Exit Window
if (_duration == 0) {
proposals[proposalId].executed = true;
_executeProposal(dao(), proposalId, proposals[proposalId].actions, proposals[proposalId].allowFailureMap);
}
}

Expand Down Expand Up @@ -567,6 +576,19 @@ contract OptimisticTokenVotingPlugin is
block.timestamp.toUint64() < proposal_.parameters.vetoEndDate + governanceSettings.l2AggregationGracePeriod;
}

/// @notice Internal function to check if a passed proposal is on the exit window perios.
/// @param proposal_ The proposal struct.
/// @return True if the proposal cannot be executed because the exit window hasn't elapsed yet
function _proposalInExitWindow(Proposal storage proposal_) internal view virtual returns (bool) {
uint64 exitWindowTimestamp = proposal_.parameters.vetoEndDate + EXIT_WINDOW;

if (!proposal_.parameters.unavailableL2) {
exitWindowTimestamp += governanceSettings.l2AggregationGracePeriod;
}

return block.timestamp.toUint64() < exitWindowTimestamp;
}

/// @notice This empty reserved space is put in place to allow future versions to add new variables without shifting down storage in the inheritance chain (see [OpenZeppelin's guide about storage gaps](https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps)).
uint256[44] private __gap;
}
2 changes: 1 addition & 1 deletion src/interfaces/IOptimisticTokenVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ interface IOptimisticTokenVoting {
/// @param _metadata The metadata of the proposal.
/// @param _actions The actions that will be executed after the proposal passes.
/// @param _allowFailureMap Allows proposal to succeed even if an action reverts. Uses bitmap representation. If the bit at index `x` is 1, the tx succeeds even if the action at `x` failed. Passing 0 will be treated as atomic execution.
/// @param _duration The amount of seconds to allow token holders to veto.
/// @param _duration The amount of seconds to allow for token holders to veto. NOTE: If the supplied value is zero, the proposal will be treated as an emergency one.
/// @return proposalId The ID of the proposal.
function createProposal(
bytes calldata _metadata,
Expand Down
3 changes: 0 additions & 3 deletions test/Multisig.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1320,9 +1320,6 @@ contract MultisigTest is AragonTest {
assertEq(multisig.canApprove(pid, randomWallet), false, "Should be false");
}

// static ko
assertEq(multisig.canApprove(pid, randomWallet), false, "Should be false");

// static ok
assertEq(multisig.canApprove(pid, alice), true, "Should be true");
}
Expand Down
Loading