diff --git a/packages/contracts/src/ExecutionMultisig.sol b/packages/contracts/src/ExecutionMultisig.sol new file mode 100644 index 00000000..e0e1d793 --- /dev/null +++ b/packages/contracts/src/ExecutionMultisig.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.17; + +import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; + +import {IDAO} from "@aragon/osx/core/dao/IDAO.sol"; +import {PluginUUPSUpgradeable} from "@aragon/osx/core/plugin/PluginUUPSUpgradeable.sol"; +import {Addresslist} from "@aragon/osx/plugins/utils/Addresslist.sol"; + +import {IExecutionMultisig} from "./IExecutionMultisig.sol"; + +abstract contract ExecutionMultisig is + IExecutionMultisig, + Initializable, + ERC165Upgradeable, + PluginUUPSUpgradeable, + Addresslist +{ + using SafeCastUpgradeable for uint256; + + /// @notice The ID of the permission required to add/remove executionMultisig members. + bytes32 public constant UPDATE_PLUGIN_EXECUTION_MULTISIG_PERMISSION_ID = + keccak256("UPDATE_PLUGIN_EXECUTION_MULTISIG_PERMISSION"); + + /// @notice Keeps track at which block number the executionMultisig has been changed the last time. + uint64 internal lastExecutionMultisigChange; + + /// @notice Initializes the component to be used by inheriting contracts. + /// @dev This method is required to support [ERC-1822](https://eips.ethereum.org/EIPS/eip-1822). + /// @param _dao The IDAO interface of the associated DAO. + function __ExecutionMultisig_init(IDAO _dao) internal onlyInitializing { + __PluginUUPSUpgradeable_init(_dao); + } + + /// @notice Checks if this or the parent contract supports an interface by its ID. + /// @param _interfaceId The ID of the interface. + /// @return Returns `true` if the interface is supported. + function supportsInterface( + bytes4 _interfaceId + ) public view virtual override(ERC165Upgradeable, PluginUUPSUpgradeable) returns (bool) { + return + _interfaceId == type(IExecutionMultisig).interfaceId || + _interfaceId == type(Addresslist).interfaceId || + super.supportsInterface(_interfaceId); + } + + /// @inheritdoc IExecutionMultisig + function addExecutionMultisigMembers( + address[] calldata _members + ) external override auth(UPDATE_PLUGIN_EXECUTION_MULTISIG_PERMISSION_ID) { + _addExecutionMultisigMembers(_members); + } + + /// @notice Private function for adding execution multisig members. + /// @param _members The addresses to add. + function _addExecutionMultisigMembers(address[] calldata _members) internal { + _guardExecutionMultisig(); + if (_members.length == 0) { + revert InvalidListLength({length: _members.length}); + } + + uint256 newAddresslistLength = addresslistLength() + _members.length; + + // Check if the new address list length would be greater than `type(uint16).max`, the maximal number of approvals. + if (newAddresslistLength > type(uint16).max) { + revert AddresslistLengthOutOfBounds({ + limit: type(uint16).max, + actual: newAddresslistLength + }); + } + + _addAddresses(_members); + lastExecutionMultisigChange = uint64(block.number); + + emit ExecutionMultisigMembersAdded({newMembers: _members}); + } + + /// @inheritdoc IExecutionMultisig + function removeExecutionMultisigMembers(address[] calldata _members) external virtual {} + + /// @inheritdoc IExecutionMultisig + function isExecutionMultisigMember(address _member) public view override returns (bool) { + return _isExecutionMultisigMember(_member); + } + + /// @notice Internal function for checking whether an address is a executionMultisig member. + /// @param _member The address to check. + /// @return Whether the address is a executionMultisig member. + function _isExecutionMultisigMember(address _member) internal view returns (bool) { + return isListed(_member); + } + + /// @notice Returns true if msg.sender has approved the given proposal tally + /// @param _proposalId The ID of the proposal. + /// @return Whether the msg.sender has approved the proposal tally. + function hasApprovedTally( + uint256 _proposalId, + address _member + ) external view virtual returns (bool); + + /// @notice Returns the block number of the last executionMultisig change. + /// @return The block number of the last executionMultisig change. + function getLastExecutionMultisigChange() external view returns (uint64) { + return lastExecutionMultisigChange; + } + + /// @notice Guard checks that processes key updates are not executed in the same block + /// where the executionMultisig changed. + function _guardExecutionMultisig() internal view { + if (lastExecutionMultisigChange == uint64(block.number)) { + revert ExecutionMultisigUpdatedTooRecently({lastUpdate: lastExecutionMultisigChange}); + } + } + + /// @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[49] private __gap; +} diff --git a/packages/contracts/src/IExecutionMultisig.sol b/packages/contracts/src/IExecutionMultisig.sol new file mode 100644 index 00000000..b1e557b0 --- /dev/null +++ b/packages/contracts/src/IExecutionMultisig.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +pragma solidity 0.8.17; + +interface IExecutionMultisig { + /// @notice Emitted when one or more execution multisig members are added. + /// @param newMembers The addresses of the new execution multisig members. + event ExecutionMultisigMembersAdded(address[] indexed newMembers); + + /// @notice Emitted when one or more execution multisig member are removed. + /// @param removedMembers The addresses of the removed execution multisig members. + event ExecutionMultisigMembersRemoved(address[] indexed removedMembers); + + /// @notice Thrown if the address list length is out of bounds. + /// @param limit The limit value. + /// @param actual The actual value. + error AddresslistLengthOutOfBounds(uint16 limit, uint256 actual); + + /// @notice Thrown if invalid list length + /// @param length The actual length + error InvalidListLength(uint256 length); + + /// @notice Thrown if the minimal approvals value is out of bounds (less than 1 or greater than the number of members in the address list). + /// @param limit The maximal value. + /// @param actual The actual value. + error MinApprovalsOutOfBounds(uint16 limit, uint16 actual); + + /// @notice Thrown if the execution multisig is updated too recently. + /// @param lastUpdate The block number of the last update. + error ExecutionMultisigUpdatedTooRecently(uint64 lastUpdate); + + /// @notice Adds members to the execution multisig. + /// @param _members The addresses to add. + function addExecutionMultisigMembers(address[] calldata _members) external; + + /// @notice Removes members from the execution multisig. + /// @param _members The addresses to remove. + function removeExecutionMultisigMembers(address[] calldata _members) external; + + /// @notice Checks if an address is a member of the execution multisig. + /// @param _member The address to check. + /// @return Whether the address is a member of the execution multisig. + function isExecutionMultisigMember(address _member) external view returns (bool); + + /// @notice Returns true if msg.sender has approved the given proposal tally + /// @param _proposalId The ID of the proposal. + /// @return Whether the msg.sender has approved the proposal tally. + function hasApprovedTally(uint256 _proposalId, address _member) external view returns (bool); + + /// @notice Returns the block number of the last executionMultisig change. + /// @return The block number of the last executionMultisig change. + function getLastExecutionMultisigChange() external view returns (uint64); +} diff --git a/packages/contracts/src/IVocdoniProposal.sol b/packages/contracts/src/IVocdoniProposal.sol index b981525d..b496aab7 100644 --- a/packages/contracts/src/IVocdoniProposal.sol +++ b/packages/contracts/src/IVocdoniProposal.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity 0.8.17; import {IDAO} from "@aragon/osx/core/dao/IDAO.sol"; diff --git a/packages/contracts/src/IVocdoniVoting.sol b/packages/contracts/src/IVocdoniVoting.sol index 2c0dedc4..23a5f6e4 100644 --- a/packages/contracts/src/IVocdoniVoting.sol +++ b/packages/contracts/src/IVocdoniVoting.sol @@ -1,19 +1,11 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity 0.8.17; /// @title IVocdoniVoting /// @author Vocdoni /// @notice The Vocdoni gasless voting contract interface for the OSX plugin. /// @notice The voting Proposal is managed gasless on the Vocdoni blockchain. interface IVocdoniVoting { - /// @notice Emitted when one or more execution multisig members are added. - /// @param newMembers The addresses of the new execution multisig members. - event ExecutionMultisigMembersAdded(address[] newMembers); - - /// @notice Emitted when one or more execution multisig member are removed. - /// @param removedMembers The addresses of the removed execution multisig members. - event ExecutionMultisigMembersRemoved(address[] removedMembers); - /// @notice Emitted when the tally of a proposal is set. /// @param proposalId The ID of the proposal. /// @param tally The tally. @@ -23,16 +15,6 @@ interface IVocdoniVoting { /// @param proposalId The ID of the proposal. event TallyApproval(uint256 indexed proposalId, address indexed approver); - /// @notice Thrown if the address list length is out of bounds. - /// @param limit The limit value. - /// @param actual The actual value. - error AddresslistLengthOutOfBounds(uint16 limit, uint256 actual); - - /// @notice Thrown if the minimal approvals value is out of bounds (less than 1 or greater than the number of members in the address list). - /// @param limit The maximal value. - /// @param actual The actual value. - error MinApprovalsOutOfBounds(uint16 limit, uint16 actual); - /// @notice Thrown if the vote phase duration is out of bounds (more than 1 year or less than 1 hour). /// @param limit The limit value. /// @param actual The actual value. @@ -62,10 +44,6 @@ interface IVocdoniVoting { /// @param lastUpdate The block number of the last update. error PluginSettingsUpdatedTooRecently(uint64 lastUpdate); - /// @notice Thrown if the execution multisig is updated too recently. - /// @param lastUpdate The block number of the last update. - error ExecutionMultisigUpdatedTooRecently(uint64 lastUpdate); - /// @notice Thrown if the proposal is already executed. /// @param proposalId The ID of the proposal. error ProposalAlreadyExecuted(uint256 proposalId); @@ -124,23 +102,6 @@ interface IVocdoniVoting { /// @param totalVotingPower The total voting power error InvalidTotalVotingPower(uint256 totalVotingPower); - /// @notice Thrown if invalid list length - /// @param length The actual length - error InvalidListLength(uint256 length); - - /// @notice Adds new execution multisig members. - /// @param _members The addresses of the new execution multisig members. - function addExecutionMultisigMembers(address[] calldata _members) external; - - /// @notice Removes execution multisig members. - /// @param _members The addresses of the execution multisig members to remove. - function removeExecutionMultisigMembers(address[] calldata _members) external; - - /// @notice Returns whether an address is a execution ultisig member. - /// @param _member The address to check. - /// @return Whether the address is a execution multisig member. - function isExecutionMultisigMember(address _member) external view returns (bool); - /// @notice Sets the tally of a given proposal. /// @param _proposalId The ID of the proposal to set the tally of. /// @param _tally The tally to set. diff --git a/packages/contracts/src/VocdoniProposalUpgradeable.sol b/packages/contracts/src/VocdoniProposalUpgradeable.sol index 40391b5d..915b5d25 100644 --- a/packages/contracts/src/VocdoniProposalUpgradeable.sol +++ b/packages/contracts/src/VocdoniProposalUpgradeable.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity 0.8.17; import {CountersUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; @@ -13,6 +13,44 @@ import {IDAO} from "@aragon/osx/core/dao/IDAO.sol"; abstract contract VocdoniProposalUpgradeable is IVocdoniProposal, ERC165Upgradeable { using CountersUpgradeable for CountersUpgradeable.Counter; + /// @notice A container for the proposal parameters. + /// @param securityBlock Block number used for limiting contract usage when plugin settings are updated + /// @param startDate The timestamp when the proposal starts. + /// @param voteEndDate The timestamp when the proposal ends. At this point the tally can be set. + /// @param tallyEndDate The timestamp when the proposal expires. Proposal can't be executed after. + /// @param totalVotingPower The total voting power of the proposal. + /// @param censusURI The URI of the census. + /// @param censusRoot The root of the census. + struct ProposalParameters { + uint64 securityBlock; + uint64 startDate; + uint64 voteEndDate; + uint64 tallyEndDate; + uint256 totalVotingPower; + string censusURI; + bytes32 censusRoot; + } + + /// @notice A container for proposal-related information. + /// @param executed Whether the proposal is executed or not. + /// @param vochainProposalId The ID of the proposal in the Vochain. + /// @param allowFailureMap A bitmap allowing the proposal to succeed, even if individual actions might revert. If the bit at index `i` is 1, + // the proposal succeeds even if the nth action reverts. A failure map value of 0 requires every action to not revert. + /// @param parameters The parameters of the proposal. + /// @param tally The tally of the proposal. + /// @dev tally only supports [[Yes, No, Abstain]] schema in this order. i.e [[10, 5, 2]] means 10 Yes, 5 No, 2 Abstain. + /// @param approvers The approvers of the tally. + /// @param actions The actions to be executed when the proposal passes. + struct Proposal { + bool executed; + bytes32 vochainProposalId; + uint256 allowFailureMap; + ProposalParameters parameters; + uint256[][] tally; + address[] approvers; + IDAO.Action[] actions; + } + /// @notice The incremental ID for proposals and executions. CountersUpgradeable.Counter private proposalCounter; diff --git a/packages/contracts/src/VocdoniVoting.sol b/packages/contracts/src/VocdoniVoting.sol index 37826f20..b729f5d2 100644 --- a/packages/contracts/src/VocdoniVoting.sol +++ b/packages/contracts/src/VocdoniVoting.sol @@ -1,28 +1,22 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity 0.8.17; import {SafeCastUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import {IVotesUpgradeable} from "@openzeppelin/contracts-upgradeable/governance/utils/IVotesUpgradeable.sol"; import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import {IDAO} from "@aragon/osx/core/dao/IDAO.sol"; -import {PluginUUPSUpgradeable} from "@aragon/osx/core/plugin/PluginUUPSUpgradeable.sol"; import {RATIO_BASE, _applyRatioCeiled, RatioOutOfBounds} from "@aragon/osx/plugins/utils/Ratio.sol"; -import {Addresslist} from "@aragon/osx/plugins/utils/Addresslist.sol"; import {VocdoniProposalUpgradeable} from "./VocdoniProposalUpgradeable.sol"; import {IVocdoniVoting} from "./IVocdoniVoting.sol"; +import {ExecutionMultisig} from "./ExecutionMultisig.sol"; /// @title VocdoniVoting /// @author Vocdoni /// @notice The Vocdoni gasless voting data contract for the OSX plugin. /// @notice The voting Proposal is managed gasless on the Vocdoni blockchain. -contract VocdoniVoting is - IVocdoniVoting, - PluginUUPSUpgradeable, - VocdoniProposalUpgradeable, - Addresslist -{ +contract VocdoniVoting is IVocdoniVoting, VocdoniProposalUpgradeable, ExecutionMultisig { using SafeCastUpgradeable for uint256; /// @notice The [ERC-165](https://eips.ethereum.org/EIPS/eip-165) interface ID of the contract. @@ -33,10 +27,6 @@ contract VocdoniVoting is bytes32 public constant UPDATE_PLUGIN_SETTINGS_PERMISSION_ID = keccak256("UPDATE_PLUGIN_SETTINGS_PERMISSION"); - /// @notice The ID of the permission required to add/remove executionMultisig members. - bytes32 public constant UPDATE_PLUGIN_EXECUTION_MULTISIG_PERMISSION_ID = - keccak256("UPDATE_PLUGIN_EXECUTION_MULTISIG_PERMISSION"); - /// @notice Emitted when the plugin settings are updated. /// @param onlyExecutionMultisigProposalCreation If true, only executionMultisig members can create proposals. /// @param minTallyApprovals The minimum number of approvals required for a tally to be considered accepted. @@ -81,50 +71,9 @@ contract VocdoniVoting is string censusStrategyURI; } - /// @notice A container for the proposal parameters. - /// @param securityBlock Block number used for limiting contract usage when plugin settings are updated - /// @param startDate The timestamp when the proposal starts. - /// @param voteEndDate The timestamp when the proposal ends. At this point the tally can be set. - /// @param tallyEndDate The timestamp when the proposal expires. Proposal can't be executed after. - /// @param totalVotingPower The total voting power of the proposal. - /// @param censusURI The URI of the census. - /// @param censusRoot The root of the census. - struct ProposalParameters { - uint64 securityBlock; - uint64 startDate; - uint64 voteEndDate; - uint64 tallyEndDate; - uint256 totalVotingPower; - string censusURI; - bytes32 censusRoot; - } - - /// @notice A container for proposal-related information. - /// @param executed Whether the proposal is executed or not. - /// @param vochainProposalId The ID of the proposal in the Vochain. - /// @param allowFailureMap A bitmap allowing the proposal to succeed, even if individual actions might revert. If the bit at index `i` is 1, - // the proposal succeeds even if the nth action reverts. A failure map value of 0 requires every action to not revert. - /// @param parameters The parameters of the proposal. - /// @param tally The tally of the proposal. - /// @dev tally only supports [[Yes, No, Abstain]] schema in this order. i.e [[10, 5, 2]] means 10 Yes, 5 No, 2 Abstain. - /// @param approvers The approvers of the tally. - /// @param actions The actions to be executed when the proposal passes. - struct Proposal { - bool executed; - bytes32 vochainProposalId; - uint256 allowFailureMap; - ProposalParameters parameters; - uint256[][] tally; - address[] approvers; - IDAO.Action[] actions; - } - /// @notice Keeps track at which block number the plugin settings have been changed the last time. uint64 private lastPluginSettingsChange; - /// @notice Keeps track at which block number the executionMultisig has been changed the last time. - uint64 private lastExecutionMultisigChange; - /// @notice A mapping between proposal IDs and proposal information. mapping(uint256 => Proposal) private proposals; @@ -140,7 +89,7 @@ contract VocdoniVoting is address[] calldata _executionMultisigAddresses, PluginSettings memory _pluginSettings ) external initializer { - __PluginUUPSUpgradeable_init(_dao); + __ExecutionMultisig_init(_dao); _addExecutionMultisigMembers(_executionMultisigAddresses); _updatePluginSettings(_pluginSettings); } @@ -150,52 +99,15 @@ contract VocdoniVoting is /// @return Returns `true` if the interface is supported. function supportsInterface( bytes4 _interfaceId - ) - public - view - virtual - override(PluginUUPSUpgradeable, VocdoniProposalUpgradeable) - returns (bool) - { + ) public view virtual override(ExecutionMultisig, VocdoniProposalUpgradeable) returns (bool) { return _interfaceId == VOCDONI_INTERFACE_ID || _interfaceId == type(IVocdoniVoting).interfaceId || - _interfaceId == type(Addresslist).interfaceId || super.supportsInterface(_interfaceId); } - /// @inheritdoc IVocdoniVoting - function addExecutionMultisigMembers( - address[] calldata _members - ) external override auth(UPDATE_PLUGIN_EXECUTION_MULTISIG_PERMISSION_ID) { - _addExecutionMultisigMembers(_members); - } - - /// @notice Private function for adding execution multisig members. - /// @param _members The addresses to add. - function _addExecutionMultisigMembers(address[] calldata _members) private { - _guardExecutionMultisig(); - if (_members.length == 0) { - revert InvalidListLength({length: _members.length}); - } - - uint256 newAddresslistLength = addresslistLength() + _members.length; - - // Check if the new address list length would be greater than `type(uint16).max`, the maximal number of approvals. - if (newAddresslistLength > type(uint16).max) { - revert AddresslistLengthOutOfBounds({ - limit: type(uint16).max, - actual: newAddresslistLength - }); - } - - _addAddresses(_members); - lastExecutionMultisigChange = uint64(block.number); - - emit ExecutionMultisigMembersAdded({newMembers: _members}); - } - - /// @inheritdoc IVocdoniVoting + /// @inheritdoc ExecutionMultisig + /// @dev Overriden where for having access to minTallyApprovals function removeExecutionMultisigMembers( address[] calldata _members ) external override auth(UPDATE_PLUGIN_EXECUTION_MULTISIG_PERMISSION_ID) { @@ -226,16 +138,32 @@ contract VocdoniVoting is emit ExecutionMultisigMembersRemoved({removedMembers: _members}); } - /// @inheritdoc IVocdoniVoting - function isExecutionMultisigMember(address _member) public view override returns (bool) { - return _isExecutionMultisigMember(_member); + /// @inheritdoc ExecutionMultisig + function hasApprovedTally( + uint256 _proposalId, + address _member + ) external view override returns (bool) { + return _hasApprovedTally(proposals[_proposalId], _member); } - /// @notice Internal function for checking whether an address is a executionMultisig member. - /// @param _member The address to check. - /// @return Whether the address is a executionMultisig member. - function _isExecutionMultisigMember(address _member) internal view returns (bool) { - return isListed(_member); + /// @notice Internal function for checking if a member has approved a proposal tally. + /// @param _proposal The proposal to check. + /// @param _member The member to check. + /// @return Whether the member has approved the proposal tally. + function _hasApprovedTally( + Proposal memory _proposal, + address _member + ) internal pure returns (bool) { + uint approversLength = _proposal.approvers.length; + for (uint256 i = 0; i < approversLength; ) { + if (_proposal.approvers[i] == _member) { + return true; + } + unchecked { + i++; + } + } + return false; } /// @notice Updates the plugin settings. @@ -258,6 +186,7 @@ contract VocdoniVoting is actual: _pluginSettings.supportThreshold }); } + // Require the minimum participation value to be in the interval [0, 10^6], because `>=` comparision is used in the participation criterion. if (_pluginSettings.minParticipation > RATIO_BASE) { revert RatioOutOfBounds({limit: RATIO_BASE, actual: _pluginSettings.minParticipation}); @@ -270,7 +199,7 @@ contract VocdoniVoting is }); } - if (_pluginSettings.minVoteDuration < 5 minutes) { + if (_pluginSettings.minVoteDuration < 60 minutes) { revert VoteDurationOutOfBounds({ limit: 60 minutes, actual: _pluginSettings.minVoteDuration @@ -284,7 +213,7 @@ contract VocdoniVoting is }); } - if (_pluginSettings.minTallyDuration < 5 minutes) { + if (_pluginSettings.minTallyDuration < 60 minutes) { revert TallyDurationOutOfBounds({ limit: 60 minutes, actual: _pluginSettings.minTallyDuration @@ -407,6 +336,7 @@ contract VocdoniVoting is proposal.parameters.censusRoot = _parameters.censusRoot; proposal.parameters.securityBlock = block.number.toUint64(); proposal.allowFailureMap = _allowFailureMap; + for (uint256 i = 0; i < _actions.length; ) { proposal.actions.push(_actions[i]); unchecked { @@ -534,7 +464,8 @@ contract VocdoniVoting is address[] memory newApprovers = new address[](0); // newApprovers are the oldApprovers list without the non executionMultisig members at the current block uint8 newApproversCount = 0; - for (uint256 i = 0; i < proposal.approvers.length; ) { + uint approversLength = proposal.approvers.length; + for (uint256 i = 0; i < approversLength; ) { address oldApprover = proposal.approvers[i]; if ( _isExecutionMultisigMember(oldApprover) && @@ -586,8 +517,14 @@ contract VocdoniVoting is return false; } - /// @notice Internal function to check the tally and execute a proposal if the tally - /// number of YES votes is greater than the tally number of NO votes. + /// @notice Internal function to check the tally and execute a proposal if: + /// - The support threshold is reached + /// - The minimum participation is reached. + /// - Enough execution multisig members have approved the tally. + /// - Proposal is not already executed. + /// - The tally is valid. + /// - The proposal is in the tally phase. + /// @param _proposalId The ID of the proposal to check. function _checkTallyAndExecute(uint256 _proposalId) internal { Proposal memory proposal = proposals[_proposalId]; @@ -666,7 +603,7 @@ contract VocdoniVoting is uint64 _startDate, uint64 _voteEndDate, uint64 _tallyEndDate - ) internal view virtual returns (uint64 startDate, uint64 voteEndDate, uint64 tallyEndDate) { + ) internal view returns (uint64 startDate, uint64 voteEndDate, uint64 tallyEndDate) { uint64 currentBlockTimestamp = block.timestamp.toUint64(); // check proposal start date and set it to the current block timestamp if it is 0 if (_startDate == 0) { @@ -708,40 +645,6 @@ contract VocdoniVoting is return pluginSettings; } - /// @notice Returns true if the provided _member has approved the given proposal tally - /// @param _proposalId The ID of the proposal. - /// @return Whether the msg.sender has approved the proposal tally. - function hasApprovedTally(uint256 _proposalId, address _member) external view returns (bool) { - return _hasApprovedTally(proposals[_proposalId], _member); - } - - /// @notice Internal function for checking if a member has approved a proposal tally. - /// @param _proposal The proposal to check. - /// @param _member The member to check. - /// @return Whether the member has approved the proposal tally. - function _hasApprovedTally( - Proposal memory _proposal, - address _member - ) internal pure returns (bool) { - for (uint256 i = 0; i < _proposal.approvers.length; ) { - if (_proposal.approvers[i] == _member) { - return true; - } - unchecked { - i++; - } - } - return false; - } - - /// @notice Guard checks that processes key updates are not executed in the same block - /// where the executionMultisig changed. - function _guardExecutionMultisig() internal view { - if (lastExecutionMultisigChange == uint64(block.number)) { - revert ExecutionMultisigUpdatedTooRecently({lastUpdate: lastExecutionMultisigChange}); - } - } - /// @notice Guard checks that processes key updates are not executed in the same block /// where the plugin settings changed. function _guardPluginSettings() internal view { @@ -750,12 +653,8 @@ contract VocdoniVoting is } } - // get last executionMultisig change block number - function getLastExecutionMultisigChange() external view returns (uint64) { - return lastExecutionMultisigChange; - } - - // get last plugin settings change block number + /// @notice Returns the last block number where the plugin settings changed. + /// @return The last block number where the plugin settings changed. function getLastPluginSettingsChange() external view returns (uint64) { return lastPluginSettingsChange; } diff --git a/packages/contracts/src/VocdoniVotingSetup.sol b/packages/contracts/src/VocdoniVotingSetup.sol index 1c28994c..ae38fd31 100644 --- a/packages/contracts/src/VocdoniVotingSetup.sol +++ b/packages/contracts/src/VocdoniVotingSetup.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity 0.8.17; import {IDAO} from "@aragon/osx/core/dao/IDAO.sol"; import {DAO} from "@aragon/osx/core/dao/DAO.sol"; diff --git a/packages/contracts/src/dependencies/dependencies.sol b/packages/contracts/src/dependencies/dependencies.sol index 1d5770b4..bfbe02ca 100644 --- a/packages/contracts/src/dependencies/dependencies.sol +++ b/packages/contracts/src/dependencies/dependencies.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -pragma solidity ^0.8.17; +pragma solidity 0.8.17; import {IDAO} from "@aragon/osx/core/dao/IDAO.sol"; import {DAO} from "@aragon/osx/core/dao/DAO.sol";