diff --git a/packages/contracts/src/MajorityVotingBase.sol b/packages/contracts/src/MajorityVotingBase.sol index ec6a3700..482d9fe3 100644 --- a/packages/contracts/src/MajorityVotingBase.sol +++ b/packages/contracts/src/MajorityVotingBase.sol @@ -176,6 +176,7 @@ abstract contract MajorityVotingBase is IDAO.Action[] actions; uint256 allowFailureMap; uint256 minApprovalPower; + TargetConfig targetConfig; // added in v1.3 } /// @notice A container for the proposal parameters at the time of proposal creation. @@ -220,6 +221,9 @@ abstract contract MajorityVotingBase is bytes32 public constant UPDATE_VOTING_SETTINGS_PERMISSION_ID = keccak256("UPDATE_VOTING_SETTINGS_PERMISSION"); + /// @notice The ID of the permission required to call the `addAddresses` and `removeAddresses` functions. + bytes32 public constant CREATE_PROPOSAL_PERMISSION_ID = keccak256("CREATE_PROPOSAL_PERMISSION"); + /// @notice A mapping between proposal IDs and proposal information. // solhint-disable-next-line named-parameters-mapping mapping(uint256 => Proposal) internal proposals; @@ -259,6 +263,10 @@ abstract contract MajorityVotingBase is /// @param proposalId The ID of the proposal. error ProposalExecutionForbidden(uint256 proposalId); + /// @notice Thrown if the proposal with same actions and metadata already exists. + /// @param proposalId The id of the proposal. + error ProposalAlreadyExists(uint256 proposalId); + /// @notice Emitted when the voting settings are updated. /// @param votingMode A parameter to select the vote mode. /// @param supportThreshold The support threshold value. @@ -285,11 +293,14 @@ abstract contract MajorityVotingBase is function __MajorityVotingBase_init( IDAO _dao, VotingSettings calldata _votingSettings, - uint256 _minApprovals + uint256 _minApprovals, + TargetConfig calldata _targetConfig ) internal onlyInitializing { __PluginUUPSUpgradeable_init(_dao); _updateVotingSettings(_votingSettings); _updateMinApprovals(_minApprovals); + + // todo set target config } /// @notice Checks if this or the parent contract supports an interface by its ID. @@ -362,7 +373,7 @@ abstract contract MajorityVotingBase is } /// @inheritdoc IMajorityVoting - function canExecute(uint256 _proposalId) public view virtual returns (bool) { + function canExecute(uint256 _proposalId) public view virtual override returns (bool) { return _canExecute(_proposalId); } @@ -540,14 +551,18 @@ abstract contract MajorityVotingBase is /// @notice Internal function to execute a vote. It assumes the queried proposal exists. /// @param _proposalId The ID of the proposal. function _execute(uint256 _proposalId) internal virtual { - proposals[_proposalId].executed = true; + Proposal storage proposal_ = proposals[_proposalId]; + + proposal_.executed = true; - _executeProposal( - dao(), + _execute( + proposal_.target, _proposalId, proposals[_proposalId].actions, proposals[_proposalId].allowFailureMap ); + + emit ProposalExecuted(_proposalId); } /// @notice Internal function to check if a voter can vote. It assumes the queried proposal exists. diff --git a/packages/contracts/src/TokenVoting.sol b/packages/contracts/src/TokenVoting.sol index 60c0bdbd..7429b4f6 100644 --- a/packages/contracts/src/TokenVoting.sol +++ b/packages/contracts/src/TokenVoting.sol @@ -60,7 +60,8 @@ contract TokenVoting is IMembership, MajorityVotingBase { IDAO _dao, VotingSettings calldata _votingSettings, IVotesUpgradeable _token, - uint256 _minApprovals + uint256 _minApprovals, + TargetConfig calldata _targetConfig ) external initializer { __MajorityVotingBase_init(_dao, _votingSettings, _minApprovals); @@ -69,10 +70,15 @@ contract TokenVoting is IMembership, MajorityVotingBase { emit MembershipContractAnnounced({definingContract: address(_token)}); } + // todo double check the reinitializer version must be 2 /// @notice Initializes the plugin after an upgrade from a previous version. /// @param _minApprovals The minimal amount of approvals the proposal needs to succeed. - function initializeFrom(uint256 _minApprovals) external reinitializer(2) { + function initializeFrom( + uint256 _minApprovals, + TargetConfig calldata _targetConfig + ) external reinitializer(2) { _updateMinApprovals(_minApprovals); + // todo set target } /// @notice Checks if this or the parent contract supports an interface by its ID. @@ -108,24 +114,7 @@ contract TokenVoting is IMembership, MajorityVotingBase { uint64 _endDate, VoteOption _voteOption, bool _tryEarlyExecution - ) external override returns (uint256 proposalId) { - // Check that either `_msgSender` owns enough tokens or has enough voting power from being a delegatee. - { - uint256 minProposerVotingPower_ = minProposerVotingPower(); - - if (minProposerVotingPower_ != 0) { - // Because of the checks in `TokenVotingSetup`, we can assume that `votingToken` - // is an [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token. - if ( - votingToken.getVotes(_msgSender()) < minProposerVotingPower_ && - IERC20Upgradeable(address(votingToken)).balanceOf(_msgSender()) < - minProposerVotingPower_ - ) { - revert ProposalCreationForbidden(_msgSender()); - } - } - } - + ) external override auth(CREATE_PROPOSAL_PERMISSION_ID) returns (uint256 proposalId) { uint256 snapshotBlock; unchecked { // The snapshot block must be mined already to @@ -141,6 +130,7 @@ contract TokenVoting is IMembership, MajorityVotingBase { (_startDate, _endDate) = _validateProposalDates(_startDate, _endDate); + // todo think this should be changed since create Proposal is no longe in commons contract proposalId = _createProposal({ _creator: _msgSender(), _metadata: _metadata, @@ -165,6 +155,16 @@ contract TokenVoting is IMembership, MajorityVotingBase { proposal_.minApprovalPower = _applyRatioCeiled(totalVotingPower_, minApproval()); + TargetConfig memory currentTarget = getTargetConfig(); + + if (currentTarget.target == address(0)) { + proposal_.target = address(dao()); + proposal_.operation = Operation.Call; + } else { + proposal_.target = currentTarget.target; + proposal_.operation = currentTarget.operation; + } + // Reduce costs if (_allowFailureMap != 0) { proposal_.allowFailureMap = _allowFailureMap; @@ -180,6 +180,18 @@ contract TokenVoting is IMembership, MajorityVotingBase { if (_voteOption != VoteOption.None) { vote(proposalId, _voteOption, _tryEarlyExecution); } + + // todo will need to emit event + } + + function createProposal( + bytes calldata _metadata, + IDAO.Action[] calldata _actions, + uint64 _startDate, + uint64 _endDate + ) external override returns (uint256 proposalId) { + // Calls public function for permission check. + proposalId = createProposal(_metadata, _actions, 0, false, false, _startDate, _endDate); } /// @inheritdoc IMembership @@ -190,6 +202,25 @@ contract TokenVoting is IMembership, MajorityVotingBase { IERC20Upgradeable(address(votingToken)).balanceOf(_account) > 0; } + /// @notice Hashing function used to (re)build the proposal id from the proposal details.. + /// @dev The proposal id is produced by hashing the ABI encoded `targets` array, the `values` array, the `calldatas` array + /// and the descriptionHash (bytes32 which itself is the keccak256 hash of the description string). This proposal id + /// can be produced from the proposal data which is part of the {ProposalCreated} event. It can even be computed in + /// advance, before the proposal is submitted. + /// The chainId and the governor address are not part of the proposal id computation. Consequently, the + /// same proposal (with same operation and same description) will have the same id if submitted on multiple governors + /// across multiple networks. This also means that in order to execute the same operation twice (on the same + /// governor) the proposer will have to change the description in order to avoid proposal id conflicts. + /// @param _actions The actions that will be executed after the proposal passes. + /// @param _metadata The metadata of the proposal. + /// @return proposalId The ID of the proposal. + function createProposalId( + IDAO.Action[] calldata _actions, + bytes memory _metadata + ) public pure override returns (uint256) { + return uint256(keccak256(abi.encode(_actions, _metadata))); + } + /// @inheritdoc MajorityVotingBase function _vote( uint256 _proposalId,