Skip to content

Commit

Permalink
LockToApprove lifecycle adapted
Browse files Browse the repository at this point in the history
  • Loading branch information
brickpop committed Jan 20, 2025
1 parent b37d0de commit 55d6f62
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 58 deletions.
93 changes: 49 additions & 44 deletions src/LockToApprovePlugin.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13;

import {ILockManager} from "./interfaces/ILockManager.sol";
import {ILockManager, UnlockMode} from "./interfaces/ILockManager.sol";
import {ILockToVoteBase} from "./interfaces/ILockToVote.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {LockToVoteBase} from "./base/LockToVoteBase.sol";
Expand Down Expand Up @@ -169,6 +169,8 @@ contract LockToApprovePlugin is
revert NoVotingPower();
}

/// @dev `minProposerVotingPower` will be checked by the permission condition behind auth(CREATE_PROPOSAL_PERMISSION_ID)

(_startDate, _endDate) = _validateProposalDates(_startDate, _endDate);

proposalId = _createProposalId(keccak256(abi.encode(_actions, _metadata)));
Expand Down Expand Up @@ -247,25 +249,35 @@ contract LockToApprovePlugin is
}

/// @inheritdoc ILockToApprove
function approve(uint256 _proposalId, address _voter, uint256 _newVotingPower)
external
auth(LOCK_MANAGER_PERMISSION_ID)
{
ProposalApproval storage proposal_ = proposals[_proposalId];
function approve(
uint256 _proposalId,
address _voter,
uint256 _currentVotingPower
) external auth(LOCK_MANAGER_PERMISSION_ID) {
Proposal storage proposal_ = proposals[_proposalId];

if (!_canVote(proposal_, _voter, _newVotingPower)) {
if (!_canApprove(proposal_, _voter, _currentVotingPower)) {
revert ApprovalForbidden(_proposalId, _voter);
}

// Add the difference between the new voting power and the current one

uint256 diff = _newVotingPower - proposal_.approvals[_voter];
uint256 diff = _currentVotingPower - proposal_.approvals[_voter];
proposal_.approvalTally += diff;
proposal_.approvals[_voter] += diff;

emit Approved(_proposalId, _voter, _newVotingPower);
emit ApprovalCast(_proposalId, _voter, _currentVotingPower);

_checkEarlyExecution(_proposalId, proposal_, _voter);
// Check if we may execute early
(UnlockMode unlockMode, ) = lockManager.settings();
if (unlockMode == UnlockMode.STRICT) {
if (
_canExecute(proposal_) &&
dao().hasPermission(address(this), _msgSender(), EXECUTE_PROPOSAL_PERMISSION_ID, _msgData())
) {
_execute(_proposalId, proposal_);
}
}
}

/// @inheritdoc ILockToApprove
Expand All @@ -289,47 +301,40 @@ contract LockToApprovePlugin is
}

/// @inheritdoc IProposal
function hasSucceeded(uint256 _proposalId) external view returns (bool) {
function canExecute(uint256 _proposalId) external view returns (bool) {
if (!_proposalExists(_proposalId)) {
revert NonexistentProposal(_proposalId);
}

Proposal storage proposal_ = proposals[_proposalId];
return _hasSucceeded(proposal_);
return _canExecute(proposal_);
}

/// @inheritdoc IProposal
function canExecute(uint256 _proposalId) external view returns (bool) {
ProposalApproval storage proposal_ = proposals[_proposalId];
return _canExecute(proposal_);
function hasSucceeded(uint256 _proposalId) external view returns (bool) {
if (!_proposalExists(_proposalId)) {
revert NonexistentProposal(_proposalId);
}

Proposal storage proposal_ = proposals[_proposalId];
return _hasSucceeded(proposal_);
}

/// @inheritdoc IProposal
function execute(uint256 _proposalId) external auth(EXECUTE_PROPOSAL_PERMISSION_ID) {
ProposalApproval storage proposal_ = proposals[_proposalId];
Proposal storage proposal_ = proposals[_proposalId];

if (!_canExecute(proposal_)) {
revert ExecutionForbidden(_proposalId);
revert ProposalExecutionForbidden(_proposalId);
}

_execute(_proposalId, proposal_);
}

/// @inheritdoc ILockToVoteBase
function underlyingToken() external view returns (IERC20) {
return lockManager.underlyingToken();
}

/// @inheritdoc ILockToVoteBase
function token() external view returns (IERC20) {
return lockManager.token();
}

/// @inheritdoc ILockToApprove
function updatePluginSettings(LockToApproveSettings calldata _newSettings)
external
auth(UPDATE_VOTING_SETTINGS_PERMISSION_ID)
{
function updatePluginSettings(
ApprovalSettings calldata _newSettings
) external auth(UPDATE_VOTING_SETTINGS_PERMISSION_ID) {
_updatePluginSettings(_newSettings);
}

Expand Down Expand Up @@ -436,28 +441,28 @@ contract LockToApprovePlugin is
}
}

function _checkEarlyExecution(uint256 _proposalId, ProposalApproval storage proposal_, address _voter) internal {
if (!_canExecute(proposal_)) {
return;
} else if (!dao().hasPermission(address(this), _voter, EXECUTE_PROPOSAL_PERMISSION_ID, _msgData())) {
return;
}

_execute(_proposalId, proposal_);
}

function _execute(uint256 _proposalId, ProposalApproval storage proposal_) internal {
function _execute(uint256 _proposalId, Proposal storage proposal_) internal {
proposal_.executed = true;

// IProposal's target execution
_execute(bytes32(_proposalId), proposal_.actions, proposal_.allowFailureMap);
_execute(
proposal_.targetConfig.target,
bytes32(_proposalId),
proposal_.actions,
proposal_.allowFailureMap,
proposal_.targetConfig.operation
);

emit Executed(_proposalId);
emit ProposalExecuted(_proposalId);

// Notify the LockManager to stop tracking this proposal ID
lockManager.proposalEnded(_proposalId);
}

function _updatePluginSettings(ApprovalSettings memory _newSettings) internal {
settings.minApprovalRatio = _newSettings.minApprovalRatio;
settings.minProposalDuration = _newSettings.minProposalDuration;
}

/// @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
Expand Down
23 changes: 13 additions & 10 deletions src/LockToVotePlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ contract LockToVotePlugin is ILockToVote, MajorityVotingBase, LockToVoteBase {
revert NoVotingPower();
}

/// @dev `minProposerVotingPower` will be checked by the permission condition behind auth(CREATE_PROPOSAL_PERMISSION_ID)

(_startDate, _endDate) = _validateProposalDates(_startDate, _endDate);

proposalId = _createProposalId(
Expand Down Expand Up @@ -185,22 +187,23 @@ contract LockToVotePlugin is ILockToVote, MajorityVotingBase, LockToVoteBase {
uint256 _proposalId,
address _voter,
VoteOption _voteOption,
uint256 _votingPower
uint256 _currentVotingPower
) public override auth(LOCK_MANAGER_PERMISSION_ID) {
Proposal storage proposal_ = proposals[_proposalId];

if (!_canVote(proposal_, _voter, _voteOption, _votingPower)) {
if (!_canVote(proposal_, _voter, _voteOption, _currentVotingPower)) {
revert VoteCastForbidden(_proposalId, _voter);
}

// Same vote
if (_voteOption == proposal_.votes[_voter].voteOption) {
// Same balance, nothing to do
if (_votingPower == proposal_.votes[_voter].votingPower) return;
if (_currentVotingPower == proposal_.votes[_voter].votingPower) return;

// More balance
uint256 diff = _votingPower - proposal_.votes[_voter].votingPower;
proposal_.votes[_voter].votingPower = _votingPower;
/// @dev Positive diff is guaranteed, as _canVote() above will return false and revert otherwise
uint256 diff = _currentVotingPower - proposal_.votes[_voter].votingPower;
proposal_.votes[_voter].votingPower = _currentVotingPower;

if (proposal_.votes[_voter].voteOption == VoteOption.Yes) {
proposal_.tally.yes += diff;
Expand Down Expand Up @@ -230,17 +233,17 @@ contract LockToVotePlugin is ILockToVote, MajorityVotingBase, LockToVoteBase {

// Register the new vote
if (_voteOption == VoteOption.Yes) {
proposal_.tally.yes += _votingPower;
proposal_.tally.yes += _currentVotingPower;
} else if (_voteOption == VoteOption.No) {
proposal_.tally.no += _votingPower;
proposal_.tally.no += _currentVotingPower;
} else {
proposal_.tally.abstain += _votingPower;
proposal_.tally.abstain += _currentVotingPower;
}
proposal_.votes[_voter].voteOption = _voteOption;
proposal_.votes[_voter].votingPower = _votingPower;
proposal_.votes[_voter].votingPower = _currentVotingPower;
}

emit VoteCast(_proposalId, _voter, _voteOption, _votingPower);
emit VoteCast(_proposalId, _voter, _voteOption, _currentVotingPower);

if (proposal_.parameters.votingMode == VotingMode.EarlyExecution) {
_checkEarlyExecution(_proposalId, _msgSender());
Expand Down
8 changes: 4 additions & 4 deletions src/interfaces/ILockManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ struct LockManagerSettings {
/// @author Aragon X 2024
/// @notice Helper contract acting as the vault for locked tokens used to vote on multiple plugins and proposals.
interface ILockManager {
/// @notice Returns the current settings of the LockManager.
function settings() external view returns (UnlockMode unlockMode, PluginMode pluginMode);

/// @notice Returns the address of the voting plugin.
/// @return The LockToVote plugin address.
function plugin() external view returns (ILockToVoteBase);
Expand All @@ -54,10 +57,7 @@ interface ILockManager {

/// @notice Locks the balance currently allowed by msg.sender on this contract and registers the given vote on the target plugin
/// @param proposalId The ID of the proposal where the vote will be registered
function lockAndVote(
uint256 proposalId,
IMajorityVoting.VoteOption vote
) external;
function lockAndVote(uint256 proposalId, IMajorityVoting.VoteOption vote) external;

/// @notice Uses the locked balance to place an approval on the given proposal for the registered plugin
/// @param proposalId The ID of the proposal where the approval will be registered
Expand Down

0 comments on commit 55d6f62

Please sign in to comment.