The probability that proposers receive Proposal Fee may be reduced.
Summary
When proposers propose Standard Proposal, they pay Proposal Fee to the protocols.
If this Proposal is executed, the proposer will be rewarded again.
However, if StandardGovernor is Reset, the probability of returning fee is reduced by not having vote for the proposals in the Pending and Active.
Vulnerability Detail
The StandardGovernor.sol#propose function is as follows.
function propose(
address[] memory targets_,
uint256[] memory values_,
bytes[] memory callDatas_,
string memory description_
) external override returns (uint256 proposalId_) {
uint256 voteStart_;
(proposalId_, voteStart_) = _propose(targets_, values_, callDatas_, description_);
// If this is the first proposal for the `voteStart_` epoch, inflate its target total supply of `PowerToken`.
if (++numberOfProposalsAt[voteStart_] == 1) {
IPowerToken(voteToken).markNextVotingEpochAsActive();
}
uint256 proposalFee_ = proposalFee;
if (proposalFee_ == 0) return proposalId_;
address cashToken_ = cashToken;
_proposalFees[proposalId_] = ProposalFeeInfo({ cashToken: cashToken_, fee: proposalFee_ });
L167: if (!ERC20Helper.transferFrom(cashToken_, msg.sender, address(this), proposalFee_)) revert TransferFromFailed();
}
As shown in the above L167 the proposers must pay Proposal Fee when they propose.
And it is rewarded when proposal is executed as shown in following L139.
function execute(
address[] memory,
uint256[] memory,
bytes[] memory callDatas_,
bytes32
) external payable returns (uint256 proposalId_) {
// Proposals have voteStart=N and voteEnd=N, and can be executed only during epoch N+1.
uint16 latestPossibleVoteStart_ = _clock() - 1;
proposalId_ = _tryExecute(callDatas_[0], latestPossibleVoteStart_, latestPossibleVoteStart_);
ProposalFeeInfo storage proposalFeeInfo_ = _proposalFees[proposalId_];
uint256 proposalFee_ = proposalFeeInfo_.fee;
address cashToken_ = proposalFeeInfo_.cashToken;
if (proposalFee_ > 0) {
delete _proposalFees[proposalId_];
L139: _transfer(cashToken_, _proposals[proposalId_].proposer, proposalFee_);
}
}
The ZeroGovernor.sol#_deployEphemeralContracts function is as follows.
As we see in the above ZeroGovernor.sol#_deployEphemeralContracts function, when deploying the new StandardGovernor, there is no processing for Proposal Fee.
The status of Proposal used in TTG includes Pending, Active, Defeated, Succeeded, Expired.
If the state is Expired or Defeated, fee is sent to DistributionVault using the StandardGovernor.sol #sendProposalFeeToVault function. And if the state is Succeeded, you can execute and return the fee to the proposer.
On the other hand, because PowerToken has been updated, the old StandardGovernor.sol cannot perform Cast vote because of the access control modifiers.
Since voting is not available, the processing of Proposal' in thePendingandActive` states is not done properly, and fee distribution is not done properly.
dany.armstrong90
high
The probability that proposers receive
Proposal Fee
may be reduced.Summary
When proposers propose
Standard Proposal
, they payProposal Fee
to the protocols. If thisProposal
is executed, the proposer will be rewarded again. However, ifStandardGovernor
isReset
, the probability of returning fee is reduced by not havingvote
for the proposals in thePending
andActive
.Vulnerability Detail
The
StandardGovernor.sol#propose
function is as follows.As shown in the above
L167
the proposers must payProposal Fee
when they propose. And it is rewarded when proposal is executed as shown in followingL139
.The
ZeroGovernor.sol#_deployEphemeralContracts
function is as follows.As we see in the above
ZeroGovernor.sol#_deployEphemeralContracts
function, when deploying the newStandardGovernor
, there is no processing forProposal Fee
. The status ofProposal
used in TTG includesPending, Active, Defeated, Succeeded, Expired
. If the state isExpired
orDefeated
, fee is sent toDistributionVault
using theStandardGovernor.sol #sendProposalFeeToVault
function. And if the state isSucceeded
, you can execute and return the fee to the proposer.On the other hand, because
PowerToken
has been updated, the oldStandardGovernor.sol
cannot performCast vote
because of the access control modifiers. Since voting is not available, the processing ofProposal' in the
Pendingand
Active` states is not done properly, and fee distribution is not done properly.Impact
The fee distribution is not exactly done.
Code Snippet
https://github.com/sherlock-audit/2023-10-mzero/blob/main/ttg/src/ZeroGovernor.sol#L162
Tool used
Manual Review
Recommendation
When
Reset Proposal
is executed, we handle fee for proposals in thePending
andActive
states.Duplicate of #53