Extraordinary proposals can receive more tokens than eligible
Summary
Two extraordinary proposals proposed at similar times (while no other extraordinary proposals are executed) can request the same amount of tokens (proposal.tokensRequested). But if the second proposal is proposed after the first proposal is successfully funded and executed, the second proposal could not request the same amount of tokens. It would have to be lower due to ExtraordinaryFunding.sol#L86.
Vulnerability Detail
Proposing an extraordinary funding proposal with the ExtraordinaryFunding.proposeExtraordinary function verifies that the requested token amount is within (less than) a certain limit in ExtraordinaryFunding.sol#L86. The limit is based on the treasury Ajna token balance and decreases with an increasing number of funded extraordinary proposals. A maximum number of 10 extraordinary proposals can be funded.
If multiple extraordinary proposals are proposed while the number of funded proposals is unchanged, the limit for the tokens requested is the same for those proposals. At a later time, if those proposals pass voting, the proposals are executed and receive the requested (stale) token amount.
Impact
The requested token amount of an extraordinary proposal is not checked when executing if it's within the same limits imposed by L86.
If another extraordinary proposal was successfully funded and executed in the meantime of the 1 month voting period for the proposal, the limit for the requested token amount is already lower than the proposal.tokensRequested amount.
This means that if multiple extraordinary proposals are proposed at a similar time and pass voting, the tokensRequested amount is stale and potentially too much.
067: function proposeExtraordinary(
068: uint256 endBlock_,
069: address[] memory targets_,
070: uint256[] memory values_,
071: bytes[] memory calldatas_,
072: string memory description_) external returns (uint256 proposalId_) {
073:
074: proposalId_ = hashProposal(targets_, values_, calldatas_, keccak256(bytes(description_)));
075:
076: if (extraordinaryFundingProposals[proposalId_].proposalId != 0) revert ProposalAlreadyExists();
077:
078: // check proposal length is within limits of 1 month maximum and it hasn't already been submitted
079: if (block.number + MAX_EFM_PROPOSAL_LENGTH < endBlock_ || extraordinaryFundingProposals[proposalId_].proposalId != 0) {
080: revert ExtraordinaryFundingProposalInvalid();
081: }
082:
083: uint256 totalTokensRequested = _validateCallDatas(targets_, values_, calldatas_);
084:
085: // check tokens requested is within limits
086: if (totalTokensRequested > getSliceOfTreasury(Maths.WAD - _getMinimumThresholdPercentage())) revert ExtraordinaryFundingProposalInvalid();
087:
088: // store newly created proposal
089: ExtraordinaryFundingProposal storage newProposal = extraordinaryFundingProposals[proposalId_];
090: newProposal.proposalId = proposalId_;
091: newProposal.startBlock = block.number;
092: newProposal.endBlock = endBlock_;
093: newProposal.tokensRequested = totalTokensRequested;
094:
095: emit ProposalCreated(
096: proposalId_,
097: msg.sender,
098: targets_,
099: values_,
100: new string[](targets_.length),
101: calldatas_,
102: block.number,
103: endBlock_,
104: description_);
105: }
106:
Tool used
Manual Review
Recommendation
Consider re-checking the tokensRequested amount when executing an extraordinary proposal and make sure it's within the same or similar limits as when proposing.
berndartmueller
medium
Extraordinary proposals can receive more tokens than eligible
Summary
Two extraordinary proposals proposed at similar times (while no other extraordinary proposals are executed) can request the same amount of tokens (
proposal.tokensRequested
). But if the second proposal is proposed after the first proposal is successfully funded and executed, the second proposal could not request the same amount of tokens. It would have to be lower due toExtraordinaryFunding.sol#L86
.Vulnerability Detail
Proposing an extraordinary funding proposal with the
ExtraordinaryFunding.proposeExtraordinary
function verifies that the requested token amount is within (less than) a certain limit inExtraordinaryFunding.sol#L86
. The limit is based on the treasury Ajna token balance and decreases with an increasing number of funded extraordinary proposals. A maximum number of 10 extraordinary proposals can be funded.If multiple extraordinary proposals are proposed while the number of funded proposals is unchanged, the limit for the tokens requested is the same for those proposals. At a later time, if those proposals pass voting, the proposals are executed and receive the requested (stale) token amount.
Impact
The requested token amount of an extraordinary proposal is not checked when executing if it's within the same limits imposed by L86.
If another extraordinary proposal was successfully funded and executed in the meantime of the 1 month voting period for the proposal, the limit for the requested token amount is already lower than the
proposal.tokensRequested
amount.This means that if multiple extraordinary proposals are proposed at a similar time and pass voting, the
tokensRequested
amount is stale and potentially too much.Code Snippet
ecosystem-coordination/src/grants/base/ExtraordinaryFunding.sol#L86
Tool used
Manual Review
Recommendation
Consider re-checking the
tokensRequested
amount when executing an extraordinary proposal and make sure it's within the same or similar limits as when proposing.