Platform: Code4rena
Start Date: 03/05/2023
Pot Size: $60,500 USDC
Total HM: 25
Participants: 114
Period: 8 days
Judge: Picodes
Total Solo HM: 6
Id: 234
League: ETH
Rank: 57/114
Findings: 2
Award: $107.57
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: hyh
Also found by: Jiamin, Juntao, aashar, bytes032, circlelooper, mrpathfindr
71.327 USDC - $71.33
Proposals received 0 vote in funding stage may still be eligible for funding
Once the voting period ends, the winning proposals that can be executed are decided through a one week challenge period. In the challenge period, anyone can submit a set of winning proposals to execute. If is is more optimal than the previously submitted slate of proposals, then it becomes the new winning slate. This is implemented in function updateSlate(uint256[] calldata proposalIds_, uint24 distributionId_). In L310, function validateSlate(uint24 distributionId, uint256 endBlock, uint256 distributionPeriodFundsAvailable_, uint256[] calldata proposalIds_, uint256 numProposalsInSlate_) is called to check the validity of a potential slate of proposals to execute, and sum the slate's fundingVotesReceived.
function _validateSlate(uint24 distributionId_, uint256 endBlock, uint256 distributionPeriodFundsAvailable_, uint256[] calldata proposalIds_, uint256 numProposalsInSlate_) internal view returns (uint256 sum_) { // check that the function is being called within the challenge period if (block.number <= endBlock || block.number > _getChallengeStageEndBlock(endBlock)) { revert InvalidProposalSlate(); } // check that the slate has no duplicates if (_hasDuplicates(proposalIds_)) revert InvalidProposalSlate(); uint256 gbc = distributionPeriodFundsAvailable_; uint256 totalTokensRequested = 0; // check each proposal in the slate is valid for (uint i = 0; i < numProposalsInSlate_; ) { Proposal memory proposal = _standardFundingProposals[proposalIds_[i]]; // check if Proposal is in the topTenProposals list if (_findProposalIndex(proposalIds_[i], _topTenProposals[distributionId_]) == -1) revert InvalidProposalSlate(); // account for fundingVotesReceived possibly being negative if (proposal.fundingVotesReceived < 0) revert InvalidProposalSlate(); // update counters sum_ += uint128(proposal.fundingVotesReceived); // since we are converting from int128 to uint128, we can safely assume that the value will not overflow totalTokensRequested += proposal.tokensRequested; // check if slate of proposals exceeded budget constraint ( 90% of GBC ) if (totalTokensRequested > (gbc * 9 / 10)) { revert InvalidProposalSlate(); } unchecked { ++i; } } }
If the proposals in the submitted slate are in the topTenProposals list, and none of the proposals' funding votes received being negative, then the total funding votes received by all proposals in the proposed slate will be returned. As confirmed with sponsor team, proposals received 0 vote should not be eligible for funding, however, as it can be seen in L441, there is no checking for fundingVotesReceived being 0, which means proposals received 0 vote in may still be eligible for funding as long as there is no other proposal with higher votes.
if (proposal.fundingVotesReceived < 0) revert InvalidProposalSlate();
Manual Review
Please consider to change L441 as following:
if (proposal.fundingVotesReceived <= 0) revert InvalidProposalSlate();
Access Control
#0 - c4-judge
2023-05-18T14:11:48Z
Picodes marked the issue as duplicate of #465
#1 - c4-judge
2023-05-30T20:13:03Z
Picodes marked the issue as satisfactory